| /******************************************************************************* |
| * Copyright (c) 2000, 2020 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking |
| * Jesper S Moller -. Contribution for bug 400830: [1.8][formatter] Code formatter for Java 8 |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.compiler.parser; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.function.Predicate; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.core.compiler.InvalidInputException; |
| import org.eclipse.jdt.internal.compiler.CompilationResult; |
| import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; |
| import org.eclipse.jdt.internal.compiler.ast.Statement; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.eclipse.jdt.internal.compiler.impl.JavaFeature; |
| import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; |
| import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; |
| import org.eclipse.jdt.internal.compiler.util.Util; |
| |
| /** |
| * IMPORTANT NOTE: Internal Scanner implementation. It is mirrored in |
| * org.eclipse.jdt.core.compiler public package where it is API. |
| * The mirror implementation is using the backward compatible ITerminalSymbols constant |
| * definitions (stable with 2.0), whereas the internal implementation uses TerminalTokens |
| * which constant values reflect the latest parser generation state. |
| */ |
| public class Scanner implements TerminalTokens { |
| |
| //public int newIdentCount = 0; |
| |
| /* APIs ares |
| - getNextToken() which return the current type of the token |
| (this value is not memorized by the scanner) |
| - getCurrentTokenSource() which provides with the token "REAL" source |
| (aka all unicode have been transformed into a correct char) |
| - sourceStart gives the position into the stream |
| - currentPosition-1 gives the sourceEnd position into the stream |
| */ |
| public long sourceLevel; |
| public long complianceLevel; |
| |
| // 1.4 feature |
| public boolean useAssertAsAnIndentifier = false; |
| //flag indicating if processed source contains occurrences of keyword assert |
| public boolean containsAssertKeyword = false; |
| public boolean previewEnabled; |
| |
| // 1.5 feature |
| public boolean useEnumAsAnIndentifier = false; |
| |
| public boolean recordLineSeparator = false; |
| public char currentCharacter; |
| public int startPosition; |
| public int currentPosition; |
| public int initialPosition, eofPosition; |
| // after this position eof are generated instead of real token from the source |
| |
| public boolean skipComments = false; |
| public boolean tokenizeComments = false; |
| public boolean tokenizeWhiteSpace = false; |
| |
| //source should be viewed as a window (aka a part) |
| //of a entire very large stream |
| public char source[]; |
| |
| //unicode support |
| public char[] withoutUnicodeBuffer; |
| public int withoutUnicodePtr; //when == 0 ==> no unicode in the current token |
| public boolean unicodeAsBackSlash = false; |
| |
| public boolean scanningFloatLiteral = false; |
| |
| //support for /** comments |
| public final static int COMMENT_ARRAYS_SIZE = 30; |
| public int[] commentStops = new int[COMMENT_ARRAYS_SIZE]; |
| public int[] commentStarts = new int[COMMENT_ARRAYS_SIZE]; |
| public int[] commentTagStarts = new int[COMMENT_ARRAYS_SIZE]; |
| public int commentPtr = -1; // no comment test with commentPtr value -1 |
| public int lastCommentLinePosition = -1; |
| |
| // task tag support |
| public char[][] foundTaskTags = null; |
| public char[][] foundTaskMessages; |
| public char[][] foundTaskPriorities = null; |
| public int[][] foundTaskPositions; |
| public int foundTaskCount = 0; |
| public char[][] taskTags = null; |
| public char[][] taskPriorities = null; |
| public boolean isTaskCaseSensitive = true; |
| |
| //diet parsing support - jump over some method body when requested |
| public boolean diet = false; |
| |
| //support for the poor-line-debuggers .... |
| //remember the position of the cr/lf |
| public int[] lineEnds = new int[250]; |
| public int linePtr = -1; |
| public boolean wasAcr = false; |
| |
| public boolean fakeInModule = false; |
| int caseStartPosition = -1; |
| boolean inCondition = false; |
| /* package */ int yieldColons = -1; |
| boolean breakPreviewAllowed = false; |
| /** |
| * The current context of the scanner w.r.t restricted keywords |
| * |
| */ |
| enum ScanContext { |
| EXPECTING_KEYWORD, EXPECTING_IDENTIFIER, AFTER_REQUIRES, INACTIVE |
| } |
| protected ScanContext scanContext = null; |
| protected boolean insideModuleInfo = false; |
| public static final String END_OF_SOURCE = "End_Of_Source"; //$NON-NLS-1$ |
| |
| public static final String INVALID_HEXA = "Invalid_Hexa_Literal"; //$NON-NLS-1$ |
| public static final String INVALID_OCTAL = "Invalid_Octal_Literal"; //$NON-NLS-1$ |
| public static final String INVALID_CHARACTER_CONSTANT = "Invalid_Character_Constant"; //$NON-NLS-1$ |
| public static final String INVALID_ESCAPE = "Invalid_Escape"; //$NON-NLS-1$ |
| public static final String INVALID_INPUT = "Invalid_Input"; //$NON-NLS-1$ |
| public static final String INVALID_TEXTBLOCK = "Invalid_Textblock"; //$NON-NLS-1$ |
| public static final String INVALID_UNICODE_ESCAPE = "Invalid_Unicode_Escape"; //$NON-NLS-1$ |
| public static final String INVALID_FLOAT = "Invalid_Float_Literal"; //$NON-NLS-1$ |
| public static final String INVALID_LOW_SURROGATE = "Invalid_Low_Surrogate"; //$NON-NLS-1$ |
| public static final String INVALID_HIGH_SURROGATE = "Invalid_High_Surrogate"; //$NON-NLS-1$ |
| |
| public static final String NULL_SOURCE_STRING = "Null_Source_String"; //$NON-NLS-1$ |
| public static final String UNTERMINATED_STRING = "Unterminated_String"; //$NON-NLS-1$ |
| public static final String UNTERMINATED_TEXT_BLOCK = "Unterminated_Text_Block"; //$NON-NLS-1$ |
| public static final String UNTERMINATED_COMMENT = "Unterminated_Comment"; //$NON-NLS-1$ |
| public static final String INVALID_CHAR_IN_STRING = "Invalid_Char_In_String"; //$NON-NLS-1$ |
| public static final String INVALID_DIGIT = "Invalid_Digit"; //$NON-NLS-1$ |
| private static final int[] EMPTY_LINE_ENDS = Util.EMPTY_INT_ARRAY; |
| |
| public static final String INVALID_BINARY = "Invalid_Binary_Literal"; //$NON-NLS-1$ |
| public static final String BINARY_LITERAL_NOT_BELOW_17 = "Binary_Literal_Not_Below_17"; //$NON-NLS-1$ |
| public static final String ILLEGAL_HEXA_LITERAL = "Illegal_Hexa_Literal"; //$NON-NLS-1$ |
| public static final String INVALID_UNDERSCORE = "Invalid_Underscore"; //$NON-NLS-1$ |
| public static final String UNDERSCORES_IN_LITERALS_NOT_BELOW_17 = "Underscores_In_Literals_Not_Below_17"; //$NON-NLS-1$ |
| |
| //----------------optimized identifier managment------------------ |
| static final char[] charArray_a = new char[] {'a'}, |
| charArray_b = new char[] {'b'}, |
| charArray_c = new char[] {'c'}, |
| charArray_d = new char[] {'d'}, |
| charArray_e = new char[] {'e'}, |
| charArray_f = new char[] {'f'}, |
| charArray_g = new char[] {'g'}, |
| charArray_h = new char[] {'h'}, |
| charArray_i = new char[] {'i'}, |
| charArray_j = new char[] {'j'}, |
| charArray_k = new char[] {'k'}, |
| charArray_l = new char[] {'l'}, |
| charArray_m = new char[] {'m'}, |
| charArray_n = new char[] {'n'}, |
| charArray_o = new char[] {'o'}, |
| charArray_p = new char[] {'p'}, |
| charArray_q = new char[] {'q'}, |
| charArray_r = new char[] {'r'}, |
| charArray_s = new char[] {'s'}, |
| charArray_t = new char[] {'t'}, |
| charArray_u = new char[] {'u'}, |
| charArray_v = new char[] {'v'}, |
| charArray_w = new char[] {'w'}, |
| charArray_x = new char[] {'x'}, |
| charArray_y = new char[] {'y'}, |
| charArray_z = new char[] {'z'}; |
| |
| static final char[] initCharArray = |
| new char[] {'\u0000', '\u0000', '\u0000', '\u0000', '\u0000', '\u0000'}; |
| static final int TableSize = 30, InternalTableSize = 6; //30*6 =210 entries |
| |
| public static final int OptimizedLength = 7; |
| public /*static*/ final char[][][][] charArray_length = |
| new char[OptimizedLength][TableSize][InternalTableSize][]; |
| // support for detecting non-externalized string literals |
| public static final char[] TAG_PREFIX= "//$NON-NLS-".toCharArray(); //$NON-NLS-1$ |
| public static final int TAG_PREFIX_LENGTH= TAG_PREFIX.length; |
| public static final char TAG_POSTFIX= '$'; |
| public static final int TAG_POSTFIX_LENGTH= 1; |
| |
| // support for complaining on uninterned type comparisons. |
| public static final char[] IDENTITY_COMPARISON_TAG = "//$IDENTITY-COMPARISON$".toCharArray(); //$NON-NLS-1$ |
| public boolean [] validIdentityComparisonLines; |
| public boolean checkUninternedIdentityComparison; |
| |
| private NLSTag[] nlsTags = null; |
| protected int nlsTagsPtr; |
| public boolean checkNonExternalizedStringLiterals; |
| |
| protected int lastPosition; |
| |
| // generic support |
| public boolean returnOnlyGreater = false; |
| |
| /*static*/ { |
| for (int i = 0; i < 6; i++) { |
| for (int j = 0; j < TableSize; j++) { |
| for (int k = 0; k < InternalTableSize; k++) { |
| this.charArray_length[i][j][k] = initCharArray; |
| } |
| } |
| } |
| } |
| /*static*/ int newEntry2 = 0, |
| newEntry3 = 0, |
| newEntry4 = 0, |
| newEntry5 = 0, |
| newEntry6 = 0; |
| public boolean insideRecovery = false; |
| int lookBack[] = new int[2]; // fall back to spring forward. |
| protected int nextToken = TokenNameNotAToken; // allows for one token push back, only the most recent token can be reliably ungotten. |
| private VanguardScanner vanguardScanner; |
| private VanguardParser vanguardParser; |
| ConflictedParser activeParser = null; |
| private boolean consumingEllipsisAnnotations = false; |
| |
| public static final int RoundBracket = 0; |
| public static final int SquareBracket = 1; |
| public static final int CurlyBracket = 2; |
| public static final int BracketKinds = 3; |
| |
| // extended unicode support |
| public static final int LOW_SURROGATE_MIN_VALUE = 0xDC00; |
| public static final int HIGH_SURROGATE_MIN_VALUE = 0xD800; |
| public static final int HIGH_SURROGATE_MAX_VALUE = 0xDBFF; |
| public static final int LOW_SURROGATE_MAX_VALUE = 0xDFFF; |
| |
| // text block support - 13 |
| protected int rawStart = -1; |
| |
| //Java 15 - first _ keyword appears |
| Map<String, Integer> _Keywords = null; |
| |
| public Scanner() { |
| this(false /*comment*/, false /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_3 /*sourceLevel*/, null/*taskTag*/, null/*taskPriorities*/, true /*taskCaseSensitive*/); |
| } |
| |
| public Scanner( |
| boolean tokenizeComments, |
| boolean tokenizeWhiteSpace, |
| boolean checkNonExternalizedStringLiterals, |
| long sourceLevel, |
| long complianceLevel, |
| char[][] taskTags, |
| char[][] taskPriorities, |
| boolean isTaskCaseSensitive, |
| boolean isPreviewEnabled) { |
| |
| this.eofPosition = Integer.MAX_VALUE; |
| this.tokenizeComments = tokenizeComments; |
| this.tokenizeWhiteSpace = tokenizeWhiteSpace; |
| this.sourceLevel = sourceLevel; |
| this.lookBack[0] = this.lookBack[1] = this.nextToken = TokenNameNotAToken; |
| this.consumingEllipsisAnnotations = false; |
| this.complianceLevel = complianceLevel; |
| this.checkNonExternalizedStringLiterals = checkNonExternalizedStringLiterals; |
| this.previewEnabled = isPreviewEnabled; |
| this.caseStartPosition = -1; |
| if (taskTags != null) { |
| int taskTagsLength = taskTags.length; |
| int length = taskTagsLength; |
| if (taskPriorities != null) { |
| int taskPrioritiesLength = taskPriorities.length; |
| if (taskPrioritiesLength != taskTagsLength) { |
| if (taskPrioritiesLength > taskTagsLength) { |
| System.arraycopy(taskPriorities, 0, (taskPriorities = new char[taskTagsLength][]), 0, taskTagsLength); |
| } else { |
| System.arraycopy(taskTags, 0, (taskTags = new char[taskPrioritiesLength][]), 0, taskPrioritiesLength); |
| length = taskPrioritiesLength; |
| } |
| } |
| int[] initialIndexes = new int[length]; |
| for (int i = 0; i < length; i++) { |
| initialIndexes[i] = i; |
| } |
| Util.reverseQuickSort(taskTags, 0, length - 1, initialIndexes); |
| char[][] temp = new char[length][]; |
| for (int i = 0; i < length; i++) { |
| temp[i] = taskPriorities[initialIndexes[i]]; |
| } |
| this.taskPriorities = temp; |
| } else { |
| Util.reverseQuickSort(taskTags, 0, length - 1); |
| } |
| this.taskTags = taskTags; |
| this.isTaskCaseSensitive = isTaskCaseSensitive; |
| } |
| } |
| |
| public Scanner( |
| boolean tokenizeComments, |
| boolean tokenizeWhiteSpace, |
| boolean checkNonExternalizedStringLiterals, |
| long sourceLevel, |
| char[][] taskTags, |
| char[][] taskPriorities, |
| boolean isTaskCaseSensitive, |
| boolean isPreviewEnabled) { |
| |
| this( |
| tokenizeComments, |
| tokenizeWhiteSpace, |
| checkNonExternalizedStringLiterals, |
| sourceLevel, |
| sourceLevel, |
| taskTags, |
| taskPriorities, |
| isTaskCaseSensitive, |
| isPreviewEnabled); |
| } |
| |
| public Scanner( |
| boolean tokenizeComments, |
| boolean tokenizeWhiteSpace, |
| boolean checkNonExternalizedStringLiterals, |
| long sourceLevel, |
| char[][] taskTags, |
| char[][] taskPriorities, |
| boolean isTaskCaseSensitive) { |
| |
| this( |
| tokenizeComments, |
| tokenizeWhiteSpace, |
| checkNonExternalizedStringLiterals, |
| sourceLevel, |
| sourceLevel, |
| taskTags, |
| taskPriorities, |
| isTaskCaseSensitive, |
| false); |
| } |
| public final boolean atEnd() { |
| // This code is not relevant if source is |
| // Only a part of the real stream input |
| |
| return this.eofPosition <= this.currentPosition; |
| } |
| |
| // chech presence of task: tags |
| // TODO (frederic) see if we need to take unicode characters into account... |
| public void checkTaskTag(int commentStart, int commentEnd) throws InvalidInputException { |
| char[] src = this.source; |
| |
| // only look for newer task: tags |
| if (this.foundTaskCount > 0 |
| && this.foundTaskPositions[this.foundTaskCount - 1][0] >= commentStart) { |
| return; |
| } |
| int foundTaskIndex = this.foundTaskCount; |
| char previous = src[commentStart+1]; // should be '*' or '/' |
| for ( |
| int i = commentStart + 2; i < commentEnd && i < this.eofPosition; i++) { |
| char[] tag = null; |
| char[] priority = null; |
| // check for tag occurrence only if not ambiguous with javadoc tag |
| if (previous != '@') { |
| nextTag : for (int itag = 0; itag < this.taskTags.length; itag++) { |
| tag = this.taskTags[itag]; |
| int tagLength = tag.length; |
| if (tagLength == 0) continue nextTag; |
| |
| // ensure tag is not leaded with letter if tag starts with a letter |
| if (ScannerHelper.isJavaIdentifierStart(this.complianceLevel, tag[0])) { |
| if (ScannerHelper.isJavaIdentifierPart(this.complianceLevel, previous)) { |
| continue nextTag; |
| } |
| } |
| |
| for (int t = 0; t < tagLength; t++) { |
| char sc, tc; |
| int x = i+t; |
| if (x >= this.eofPosition || x >= commentEnd) continue nextTag; |
| // case sensitive check |
| if ((sc = src[i + t]) != (tc = tag[t])) { |
| // case insensitive check |
| if (this.isTaskCaseSensitive || (ScannerHelper.toLowerCase(sc) != ScannerHelper.toLowerCase(tc))) { |
| continue nextTag; |
| } |
| } |
| } |
| // ensure tag is not followed with letter if tag finishes with a letter |
| if (i+tagLength < commentEnd && ScannerHelper.isJavaIdentifierPart(this.complianceLevel, src[i+tagLength-1])) { |
| if (ScannerHelper.isJavaIdentifierPart(this.complianceLevel, src[i + tagLength])) |
| continue nextTag; |
| } |
| if (this.foundTaskTags == null) { |
| this.foundTaskTags = new char[5][]; |
| this.foundTaskMessages = new char[5][]; |
| this.foundTaskPriorities = new char[5][]; |
| this.foundTaskPositions = new int[5][]; |
| } else if (this.foundTaskCount == this.foundTaskTags.length) { |
| System.arraycopy(this.foundTaskTags, 0, this.foundTaskTags = new char[this.foundTaskCount * 2][], 0, this.foundTaskCount); |
| System.arraycopy(this.foundTaskMessages, 0, this.foundTaskMessages = new char[this.foundTaskCount * 2][], 0, this.foundTaskCount); |
| System.arraycopy(this.foundTaskPriorities, 0, this.foundTaskPriorities = new char[this.foundTaskCount * 2][], 0, this.foundTaskCount); |
| System.arraycopy(this.foundTaskPositions, 0, this.foundTaskPositions = new int[this.foundTaskCount * 2][], 0, this.foundTaskCount); |
| } |
| |
| priority = this.taskPriorities != null && itag < this.taskPriorities.length |
| ? this.taskPriorities[itag] |
| : null; |
| |
| this.foundTaskTags[this.foundTaskCount] = tag; |
| this.foundTaskPriorities[this.foundTaskCount] = priority; |
| this.foundTaskPositions[this.foundTaskCount] = new int[] { i, i + tagLength - 1 }; |
| this.foundTaskMessages[this.foundTaskCount] = CharOperation.NO_CHAR; |
| this.foundTaskCount++; |
| i += tagLength - 1; // will be incremented when looping |
| break nextTag; |
| } |
| } |
| previous = src[i]; |
| } |
| boolean containsEmptyTask = false; |
| for (int i = foundTaskIndex; i < this.foundTaskCount; i++) { |
| // retrieve message start and end positions |
| int msgStart = this.foundTaskPositions[i][0] + this.foundTaskTags[i].length; |
| int max_value = i + 1 < this.foundTaskCount |
| ? this.foundTaskPositions[i + 1][0] - 1 |
| : commentEnd - 1; |
| // at most beginning of next task |
| if (max_value < msgStart) { |
| max_value = msgStart; // would only occur if tag is before EOF. |
| } |
| int end = -1; |
| char c; |
| for (int j = msgStart; j < max_value; j++) { |
| if ((c = src[j]) == '\n' || c == '\r') { |
| end = j - 1; |
| break; |
| } |
| } |
| if (end == -1) { |
| for (int j = max_value; j > msgStart; j--) { |
| if ((c = src[j]) == '*') { |
| end = j - 1; |
| break; |
| } |
| } |
| if (end == -1) |
| end = max_value; |
| } |
| if (msgStart == end) { |
| // if the description is empty, we might want to see if two tags are not sharing the same message |
| // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=110797 |
| containsEmptyTask = true; |
| continue; |
| } |
| // trim the message |
| // we don't trim the beginning of the message to be able to show it after the task tag |
| while (CharOperation.isWhitespace(src[end]) && msgStart <= end) |
| end--; |
| // update the end position of the task |
| this.foundTaskPositions[i][1] = end; |
| // get the message source |
| final int messageLength = end - msgStart + 1; |
| char[] message = new char[messageLength]; |
| System.arraycopy(src, msgStart, message, 0, messageLength); |
| this.foundTaskMessages[i] = message; |
| } |
| if (containsEmptyTask) { |
| for (int i = foundTaskIndex, max = this.foundTaskCount; i < max; i++) { |
| if (this.foundTaskMessages[i].length == 0) { |
| loop: for (int j = i + 1; j < max; j++) { |
| if (this.foundTaskMessages[j].length != 0) { |
| this.foundTaskMessages[i] = this.foundTaskMessages[j]; |
| this.foundTaskPositions[i][1] = this.foundTaskPositions[j][1]; |
| break loop; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| public char[] getCurrentIdentifierSource() { |
| //return the token REAL source (aka unicodes are precomputed) |
| if (this.withoutUnicodePtr != 0) { |
| //0 is used as a fast test flag so the real first char is in position 1 |
| char[] result = new char[this.withoutUnicodePtr]; |
| System.arraycopy( |
| this.withoutUnicodeBuffer, |
| 1, |
| result, |
| 0, |
| this.withoutUnicodePtr); |
| return result; |
| } |
| int length = this.currentPosition - this.startPosition; |
| if (length == this.eofPosition) return this.source; |
| switch (length) { // see OptimizedLength |
| case 1 : |
| return optimizedCurrentTokenSource1(); |
| case 2 : |
| return optimizedCurrentTokenSource2(); |
| case 3 : |
| return optimizedCurrentTokenSource3(); |
| case 4 : |
| return optimizedCurrentTokenSource4(); |
| case 5 : |
| return optimizedCurrentTokenSource5(); |
| case 6 : |
| return optimizedCurrentTokenSource6(); |
| } |
| char[] result = new char[length]; |
| System.arraycopy(this.source, this.startPosition, result, 0, length); |
| return result; |
| } |
| public int getCurrentTokenEndPosition(){ |
| return this.currentPosition - 1; |
| } |
| public char[] getCurrentTokenSource() { |
| // Return the token REAL source (aka unicodes are precomputed) |
| |
| char[] result; |
| if (this.withoutUnicodePtr != 0) |
| // 0 is used as a fast test flag so the real first char is in position 1 |
| System.arraycopy( |
| this.withoutUnicodeBuffer, |
| 1, |
| result = new char[this.withoutUnicodePtr], |
| 0, |
| this.withoutUnicodePtr); |
| else { |
| int length; |
| System.arraycopy( |
| this.source, |
| this.startPosition, |
| result = new char[length = this.currentPosition - this.startPosition], |
| 0, |
| length); |
| } |
| return result; |
| } |
| public final String getCurrentTokenString() { |
| // Return current token as a string |
| |
| if (this.withoutUnicodePtr != 0) { |
| // 0 is used as a fast test flag so the real first char is in position 1 |
| return new String( |
| this.withoutUnicodeBuffer, |
| 1, |
| this.withoutUnicodePtr); |
| } |
| return new String( |
| this.source, |
| this.startPosition, |
| this.currentPosition - this.startPosition); |
| } |
| public char[] getCurrentTokenSourceString() { |
| //return the token REAL source (aka unicodes are precomputed). |
| //REMOVE the two " that are at the beginning and the end. |
| |
| char[] result; |
| if (this.withoutUnicodePtr != 0) |
| //0 is used as a fast test flag so the real first char is in position 1 |
| System.arraycopy(this.withoutUnicodeBuffer, 2, |
| //2 is 1 (real start) + 1 (to jump over the ") |
| result = new char[this.withoutUnicodePtr - 2], 0, this.withoutUnicodePtr - 2); |
| else { |
| int length; |
| System.arraycopy( |
| this.source, |
| this.startPosition + 1, |
| result = new char[length = this.currentPosition - this.startPosition - 2], |
| 0, |
| length); |
| } |
| return result; |
| } |
| protected final boolean scanForTextBlockBeginning() { |
| try { |
| // Don't change the position and current character unless we are certain |
| // to be dealing with a text block. For producing all errors like before |
| // in case of a valid """ but missing \r or \n, just return false and not |
| // throw any error. |
| int temp = this.currentPosition; |
| if ((this.source[temp++] == '\"' && this.source[temp++] == '\"')) { |
| char c = this.source[temp++]; |
| while (ScannerHelper.isWhitespace(c)) { |
| switch (c) { |
| case 10 : /* \ u000a: LINE FEED */ |
| this.currentCharacter = c; |
| this.currentPosition = temp; |
| return true; |
| default: |
| break; |
| } |
| c = this.source[temp++]; |
| } |
| } |
| } catch(IndexOutOfBoundsException e) { |
| //let it return false; |
| } |
| return false; |
| } |
| protected final boolean scanForTextBlockClose() throws InvalidInputException { |
| try { |
| if (this.source[this.currentPosition] == '\"' && this.source[this.currentPosition + 1] == '\"') { |
| return true; |
| } |
| } catch(IndexOutOfBoundsException e) { |
| //let it return false; |
| } |
| return false; |
| } |
| public char[] getCurrentTextBlock() { |
| // 1. Normalize, i.e. convert all CR CRLF to LF |
| char[] all; |
| if (this.withoutUnicodePtr != 0) { |
| all = CharOperation.subarray(this.withoutUnicodeBuffer, this.rawStart + 1, this.withoutUnicodePtr + 1 ); |
| } else { |
| all = CharOperation.subarray(this.source, this.startPosition + this.rawStart, this.currentPosition - 3); |
| if (all == null) { |
| all = new char[0]; |
| } |
| } |
| all = normalize(all); |
| // 2. Split into lines. Consider both \n and \r as line separators |
| char[][] lines = CharOperation.splitOn('\n', all); |
| int size = lines.length; |
| List<char[]> list = new ArrayList<>(lines.length); |
| for(int i = 0; i < lines.length; i++) { |
| char[] line = lines[i]; |
| if (i + 1 == size && line.length == 0) { |
| list.add(line); |
| break; |
| } |
| char[][] sub = CharOperation.splitOn('\r', line); |
| if (sub.length == 0) { |
| list.add(line); |
| } else { |
| for (char[] cs : sub) { |
| list.add(cs); |
| } |
| } |
| } |
| size = list.size(); |
| lines = list.toArray(new char[size][]); |
| |
| // 3. Handle incidental white space |
| // 3.1. Split into lines and identify determining lines |
| int prefix = -1; |
| for(int i = 0; i < size; i++) { |
| char[] line = lines[i]; |
| boolean blank = true; |
| int whitespaces = 0; |
| for (char c : line) { |
| if (blank) { |
| if (ScannerHelper.isWhitespace(c)) { |
| whitespaces++; |
| } else { |
| blank = false; |
| } |
| } |
| } |
| // The last line with closing delimiter is part of the |
| // determining line list even if empty |
| if (!blank || (i+1 == size)) { |
| if (prefix < 0 || whitespaces < prefix) { |
| prefix = whitespaces; |
| } |
| } |
| } |
| // 3.2. Remove the common white space prefix |
| // 4. Handle escape sequences that are not already done in getNextToken0() |
| if (prefix == -1) |
| prefix = 0; |
| StringBuilder result = new StringBuilder(); |
| boolean newLine = false; |
| for(int i = 0; i < lines.length; i++) { |
| char[] l = lines[i]; |
| // Remove the common prefix from each line |
| // And remove all trailing whitespace |
| // Finally append the \n at the end of the line (except the last line) |
| int length = l.length; |
| int trail = length; |
| for(;trail > 0;) { |
| if (!ScannerHelper.isWhitespace(l[trail-1])) { |
| break; |
| } |
| trail--; |
| } |
| if (i >= (size -1)) { |
| if (newLine) result.append('\n'); |
| if (trail < prefix) |
| continue; |
| newLine = getLineContent(result, l, prefix, trail-1, false, true); |
| } else { |
| if (i > 0 && newLine) |
| result.append('\n'); |
| if (trail <= prefix) { |
| newLine = true; |
| } else { |
| boolean merge = length > 0 && l[length - 1] == '\\'; |
| newLine = getLineContent(result, l, prefix, trail-1, merge, false); |
| } |
| } |
| } |
| // get rid of all the cached values |
| this.rawStart = -1; |
| return result.toString().toCharArray(); |
| } |
| private char[] normalize(char[] content) { |
| StringBuilder result = new StringBuilder(); |
| boolean isCR = false; |
| for (char c : content) { |
| switch (c) { |
| case '\r': |
| result.append(c); |
| isCR = true; |
| break; |
| case '\n': |
| if (!isCR) { |
| result.append(c); |
| } |
| isCR = false; |
| break; |
| default: |
| result.append(c); |
| isCR = false; |
| break; |
| } |
| } |
| return result.toString().toCharArray(); |
| } |
| // This method is for handling the left over escaped characters during the first |
| // scanning (scanForStringLiteral). Admittedly this goes over the text block |
| // content again char by char, but this is required in order to correctly |
| // treat all the white space and line endings |
| private boolean getLineContent(StringBuilder result, char[] line, int start, int end, boolean merge, boolean lastLine) { |
| int lastPointer = 0; |
| for(int i = start; i < end;) { |
| char c = line[i]; |
| if (c != '\\') { |
| i++; |
| continue; |
| } |
| if (i < end) { |
| if (lastPointer + 1 <= i) { |
| result.append(CharOperation.subarray(line, lastPointer == 0 ? start : lastPointer, i)); |
| } |
| char next = line[++i]; |
| switch (next) { |
| case '\\' : |
| result.append('\\'); |
| if (i == end) |
| merge = false; |
| break; |
| case 's' : |
| result.append(' '); |
| break; |
| case 'b' : |
| result.append('\b'); |
| break; |
| case 'n' : |
| result.append('\n'); |
| break; |
| case 'r' : |
| result.append('\r'); |
| break; |
| case 't' : |
| result.append('\t'); |
| break; |
| case 'f' : |
| result.append('\f'); |
| break; |
| default : |
| // Direct copy from scanEscapeCharacter |
| int pos = i + 1; |
| int number = ScannerHelper.getHexadecimalValue(next); |
| if (number >= 0 && number <= 7) { |
| boolean zeroToThreeNot = number > 3; |
| try { |
| if (ScannerHelper.isDigit(next = line[pos])) { |
| pos++; |
| int digit = ScannerHelper.getHexadecimalValue(next); |
| if (digit >= 0 && digit <= 7) { |
| number = (number * 8) + digit; |
| if (ScannerHelper.isDigit(next = line[pos])) { |
| pos++; |
| if (zeroToThreeNot) { |
| // has read \NotZeroToThree OctalDigit Digit --> ignore last character |
| } else { |
| digit = ScannerHelper.getHexadecimalValue(next); |
| if (digit >= 0 && digit <= 7){ // has read \ZeroToThree OctalDigit OctalDigit |
| number = (number * 8) + digit; |
| } else { |
| // has read \ZeroToThree OctalDigit NonOctalDigit --> ignore last character |
| } |
| } |
| } else { |
| // has read \OctalDigit NonDigit--> ignore last character |
| } |
| } else { |
| // has read \OctalDigit NonOctalDigit--> ignore last character |
| } |
| } else { |
| // has read \OctalDigit --> ignore last character |
| } |
| } catch (InvalidInputException e) { |
| // Unlikely as this has already been processed in scanForStringLiteral() |
| } |
| if (number < 255) { |
| next = (char) number; |
| } |
| result.append(next); |
| lastPointer = i = pos; |
| continue; |
| } else { |
| // Dealing with just '\' |
| result.append(c); |
| lastPointer = i; |
| continue; |
| } |
| } |
| lastPointer = ++i; |
| } |
| } |
| end = merge ? end : end >= line.length ? end : end + 1; |
| char[] chars = lastPointer == 0 ? |
| CharOperation.subarray(line, start, end) : |
| CharOperation.subarray(line, lastPointer, end); |
| // The below check is because CharOperation.subarray tend to return null when the |
| // boundaries produce a zero sized char[] |
| if (chars != null && chars.length > 0) |
| result.append(chars); |
| return (!merge && !lastLine); |
| } |
| public final String getCurrentStringLiteral() { |
| //return the token REAL source (aka unicodes are precomputed). |
| //REMOVE the two " that are at the beginning and the end. |
| |
| if (this.withoutUnicodePtr != 0) |
| //0 is used as a fast test flag so the real first char is in position 1 |
| //2 is 1 (real start) + 1 (to jump over the ") |
| return new String(this.withoutUnicodeBuffer, 2, this.withoutUnicodePtr - 2); |
| else { |
| return new String(this.source, this.startPosition + 1, this.currentPosition - this.startPosition - 2); |
| } |
| } |
| public final char[] getRawTokenSource() { |
| int length = this.currentPosition - this.startPosition; |
| char[] tokenSource = new char[length]; |
| System.arraycopy(this.source, this.startPosition, tokenSource, 0, length); |
| return tokenSource; |
| } |
| |
| public final char[] getRawTokenSourceEnd() { |
| int length = this.eofPosition - this.currentPosition - 1; |
| char[] sourceEnd = new char[length]; |
| System.arraycopy(this.source, this.currentPosition, sourceEnd, 0, length); |
| return sourceEnd; |
| } |
| |
| public int getCurrentTokenStartPosition(){ |
| return this.startPosition; |
| } |
| /* |
| * Search the source position corresponding to the end of a given line number |
| * |
| * Line numbers are 1-based, and relative to the scanner initialPosition. |
| * Character positions are 0-based. |
| * |
| * In case the given line number is inconsistent, answers -1. |
| */ |
| public final int getLineEnd(int lineNumber) { |
| |
| if (this.lineEnds == null || this.linePtr == -1) |
| return -1; |
| if (lineNumber > this.lineEnds.length+1) |
| return -1; |
| if (lineNumber <= 0) |
| return -1; |
| if (lineNumber == this.lineEnds.length + 1) |
| return this.eofPosition; |
| return this.lineEnds[lineNumber-1]; // next line start one character behind the lineEnd of the previous line |
| } |
| |
| public final int[] getLineEnds() { |
| //return a bounded copy of this.lineEnds |
| if (this.linePtr == -1) { |
| return EMPTY_LINE_ENDS; |
| } |
| int[] copy; |
| System.arraycopy(this.lineEnds, 0, copy = new int[this.linePtr + 1], 0, this.linePtr + 1); |
| return copy; |
| } |
| |
| /** |
| * Search the source position corresponding to the beginning of a given line number |
| * |
| * Line numbers are 1-based, and relative to the scanner initialPosition. |
| * Character positions are 0-based. |
| * |
| * e.g. getLineStart(1) --> 0 indicates that the first line starts at character 0. |
| * |
| * In case the given line number is inconsistent, answers -1. |
| * |
| * @param lineNumber int |
| * @return int |
| */ |
| public final int getLineStart(int lineNumber) { |
| |
| if (this.lineEnds == null || this.linePtr == -1) |
| return -1; |
| if (lineNumber > this.lineEnds.length + 1) |
| return -1; |
| if (lineNumber <= 0) |
| return -1; |
| |
| if (lineNumber == 1) |
| return this.initialPosition; |
| return this.lineEnds[lineNumber-2]+1; // next line start one character behind the lineEnd of the previous line |
| } |
| public final int getNextChar() { |
| try { |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } else { |
| this.unicodeAsBackSlash = false; |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| return this.currentCharacter; |
| } catch(IndexOutOfBoundsException | InvalidInputException e) { |
| return -1; |
| } |
| } |
| public final int getNextCharWithBoundChecks() { |
| if (this.currentPosition >= this.eofPosition) { |
| return -1; |
| } |
| this.currentCharacter = this.source[this.currentPosition++]; |
| if (this.currentPosition >= this.eofPosition) { |
| this.unicodeAsBackSlash = false; |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| return this.currentCharacter; |
| } |
| if (this.currentCharacter == '\\' && this.source[this.currentPosition] == 'u') { |
| try { |
| getNextUnicodeChar(); |
| } catch (InvalidInputException e) { |
| return -1; |
| } |
| } else { |
| this.unicodeAsBackSlash = false; |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| return this.currentCharacter; |
| } |
| public final boolean getNextChar(char testedChar) { |
| //BOOLEAN |
| //handle the case of unicode. |
| //when a unicode appears then we must use a buffer that holds char internal values |
| //At the end of this method currentCharacter holds the new visited char |
| //and currentPosition points right next after it |
| //Both previous lines are true if the currentCharacter is == to the testedChar |
| //On false, no side effect has occured. |
| |
| //ALL getNextChar.... ARE OPTIMIZED COPIES |
| |
| if (this.currentPosition >= this.eofPosition) { // handle the obvious case upfront |
| this.unicodeAsBackSlash = false; |
| return false; |
| } |
| |
| int temp = this.currentPosition; |
| try { |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| if (this.currentCharacter != testedChar) { |
| this.currentPosition = temp; |
| this.withoutUnicodePtr--; |
| return false; |
| } |
| return true; |
| } //-------------end unicode traitement-------------- |
| else { |
| if (this.currentCharacter != testedChar) { |
| this.currentPosition = temp; |
| return false; |
| } |
| this.unicodeAsBackSlash = false; |
| if (this.withoutUnicodePtr != 0) |
| unicodeStore(); |
| return true; |
| } |
| } catch(IndexOutOfBoundsException | InvalidInputException e) { |
| this.unicodeAsBackSlash = false; |
| this.currentPosition = temp; |
| return false; |
| } |
| } |
| public final int getNextChar(char testedChar1, char testedChar2) { |
| //INT 0 : testChar1 \\\\///\\\\ 1 : testedChar2 \\\\///\\\\ -1 : others |
| //test can be done with (x==0) for the first and (x>0) for the second |
| //handle the case of unicode. |
| //when a unicode appears then we must use a buffer that holds char internal values |
| //At the end of this method currentCharacter holds the new visited char |
| //and currentPosition points right next after it |
| //Both previous lines are true if the currentCharacter is == to the testedChar1/2 |
| //On false, no side effect has occured. |
| |
| //ALL getNextChar.... ARE OPTIMIZED COPIES |
| if (this.currentPosition >= this.eofPosition) // handle the obvious case upfront |
| return -1; |
| |
| int temp = this.currentPosition; |
| try { |
| int result; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| if (this.currentCharacter == testedChar1) { |
| result = 0; |
| } else if (this.currentCharacter == testedChar2) { |
| result = 1; |
| } else { |
| this.currentPosition = temp; |
| this.withoutUnicodePtr--; |
| result = -1; |
| } |
| return result; |
| } else { |
| if (this.currentCharacter == testedChar1) { |
| result = 0; |
| } else if (this.currentCharacter == testedChar2) { |
| result = 1; |
| } else { |
| this.currentPosition = temp; |
| return -1; |
| } |
| |
| if (this.withoutUnicodePtr != 0) |
| unicodeStore(); |
| return result; |
| } |
| } catch(IndexOutOfBoundsException | InvalidInputException e) { |
| this.currentPosition = temp; |
| return -1; |
| } |
| } |
| /* |
| * This method consumes digits as well as underscores if underscores are located between digits |
| * @throws InvalidInputException if underscores are not located between digits or if underscores are used in source < 1.7 |
| */ |
| private final void consumeDigits(int radix) throws InvalidInputException { |
| consumeDigits(radix, false); |
| } |
| /* |
| * This method consumes digits as well as underscores if underscores are located between digits |
| * @throws InvalidInputException if underscores are not located between digits or if underscores are used in source < 1.7 |
| */ |
| private final void consumeDigits(int radix, boolean expectingDigitFirst) throws InvalidInputException { |
| final int USING_UNDERSCORE = 1; |
| final int INVALID_POSITION = 2; |
| switch(consumeDigits0(radix, USING_UNDERSCORE, INVALID_POSITION, expectingDigitFirst)) { |
| case USING_UNDERSCORE : |
| if (this.sourceLevel < ClassFileConstants.JDK1_7) { |
| throw new InvalidInputException(UNDERSCORES_IN_LITERALS_NOT_BELOW_17); |
| } |
| break; |
| case INVALID_POSITION : |
| if (this.sourceLevel < ClassFileConstants.JDK1_7) { |
| throw new InvalidInputException(UNDERSCORES_IN_LITERALS_NOT_BELOW_17); |
| } |
| throw new InvalidInputException(INVALID_UNDERSCORE); |
| } |
| } |
| private final int consumeDigits0(int radix, int usingUnderscore, int invalidPosition, boolean expectingDigitFirst) throws InvalidInputException { |
| int kind = 0; |
| if (getNextChar('_')) { |
| if (expectingDigitFirst) { |
| return invalidPosition; |
| } |
| kind = usingUnderscore; |
| while (getNextChar('_')) {/*empty */} |
| } |
| if (getNextCharAsDigit(radix)) { |
| // continue to read digits or underscore |
| while (getNextCharAsDigit(radix)) {/*empty */} |
| int kind2 = consumeDigits0(radix, usingUnderscore, invalidPosition, false); |
| if (kind2 == 0) { |
| return kind; |
| } |
| return kind2; |
| } |
| if (kind == usingUnderscore) return invalidPosition; |
| return kind; |
| } |
| public final boolean getNextCharAsDigit() throws InvalidInputException { |
| //BOOLEAN |
| //handle the case of unicode. |
| //when a unicode appears then we must use a buffer that holds char internal values |
| //At the end of this method currentCharacter holds the new visited char |
| //and currentPosition points right next after it |
| //Both previous lines are true if the currentCharacter is a digit |
| //On false, no side effect has occured. |
| |
| //ALL getNextChar.... ARE OPTIMIZED COPIES |
| if (this.currentPosition >= this.eofPosition) // handle the obvious case upfront |
| return false; |
| |
| int temp = this.currentPosition; |
| try { |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| if (!ScannerHelper.isDigit(this.currentCharacter)) { |
| this.currentPosition = temp; |
| this.withoutUnicodePtr--; |
| return false; |
| } |
| return true; |
| } else { |
| if (!ScannerHelper.isDigit(this.currentCharacter)) { |
| this.currentPosition = temp; |
| return false; |
| } |
| if (this.withoutUnicodePtr != 0) |
| unicodeStore(); |
| return true; |
| } |
| } catch(IndexOutOfBoundsException | InvalidInputException e) { |
| this.currentPosition = temp; |
| return false; |
| } |
| } |
| public final boolean getNextCharAsDigit(int radix) { |
| //BOOLEAN |
| //handle the case of unicode. |
| //when a unicode appears then we must use a buffer that holds char internal values |
| //At the end of this method currentCharacter holds the new visited char |
| //and currentPosition points right next after it |
| //Both previous lines are true if the currentCharacter is a digit base on radix |
| //On false, no side effect has occured. |
| |
| //ALL getNextChar.... ARE OPTIMIZED COPIES |
| if (this.currentPosition >= this.eofPosition) // handle the obvious case upfront |
| return false; |
| |
| int temp = this.currentPosition; |
| try { |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| if (ScannerHelper.digit(this.currentCharacter, radix) == -1) { |
| this.currentPosition = temp; |
| this.withoutUnicodePtr--; |
| return false; |
| } |
| return true; |
| } else { |
| if (ScannerHelper.digit(this.currentCharacter, radix) == -1) { |
| this.currentPosition = temp; |
| return false; |
| } |
| if (this.withoutUnicodePtr != 0) |
| unicodeStore(); |
| return true; |
| } |
| } catch(IndexOutOfBoundsException | InvalidInputException e) { |
| this.currentPosition = temp; |
| return false; |
| } |
| } |
| public boolean getNextCharAsJavaIdentifierPartWithBoundCheck() { |
| //BOOLEAN |
| //handle the case of unicode. |
| //when a unicode appears then we must use a buffer that holds char internal values |
| //At the end of this method currentCharacter holds the new visited char |
| //and currentPosition points right next after it |
| //Both previous lines are true if the currentCharacter is a JavaIdentifierPart |
| //On false, no side effect has occured. |
| |
| //ALL getNextChar.... ARE OPTIMIZED COPIES |
| int pos = this.currentPosition; |
| if (pos >= this.eofPosition) // handle the obvious case upfront |
| return false; |
| |
| int temp2 = this.withoutUnicodePtr; |
| try { |
| boolean unicode = false; |
| this.currentCharacter = this.source[this.currentPosition++]; |
| if (this.currentPosition < this.eofPosition) { |
| if (this.currentCharacter == '\\' && this.source[this.currentPosition] == 'u') { |
| getNextUnicodeChar(); |
| unicode = true; |
| } |
| } |
| char c = this.currentCharacter; |
| boolean isJavaIdentifierPart = false; |
| if (c >= HIGH_SURROGATE_MIN_VALUE && c <= HIGH_SURROGATE_MAX_VALUE) { |
| if (this.complianceLevel < ClassFileConstants.JDK1_5) { |
| this.currentPosition = pos; |
| this.withoutUnicodePtr = temp2; |
| return false; |
| } |
| // Unicode 4 detection |
| char low = (char) getNextCharWithBoundChecks(); |
| if (low < LOW_SURROGATE_MIN_VALUE || low > LOW_SURROGATE_MAX_VALUE) { |
| // illegal low surrogate |
| this.currentPosition = pos; |
| this.withoutUnicodePtr = temp2; |
| return false; |
| } |
| isJavaIdentifierPart = ScannerHelper.isJavaIdentifierPart(this.complianceLevel, c, low); |
| } |
| else if (c >= LOW_SURROGATE_MIN_VALUE && c <= LOW_SURROGATE_MAX_VALUE) { |
| this.currentPosition = pos; |
| this.withoutUnicodePtr = temp2; |
| return false; |
| } else { |
| isJavaIdentifierPart = ScannerHelper.isJavaIdentifierPart(this.complianceLevel, c); |
| } |
| if (unicode) { |
| if (!isJavaIdentifierPart) { |
| this.currentPosition = pos; |
| this.withoutUnicodePtr = temp2; |
| return false; |
| } |
| return true; |
| } else { |
| if (!isJavaIdentifierPart) { |
| this.currentPosition = pos; |
| return false; |
| } |
| |
| if (this.withoutUnicodePtr != 0) |
| unicodeStore(); |
| return true; |
| } |
| } catch(InvalidInputException e) { |
| this.currentPosition = pos; |
| this.withoutUnicodePtr = temp2; |
| return false; |
| } |
| } |
| public boolean getNextCharAsJavaIdentifierPart() { |
| //BOOLEAN |
| //handle the case of unicode. |
| //when a unicode appears then we must use a buffer that holds char internal values |
| //At the end of this method currentCharacter holds the new visited char |
| //and currentPosition points right next after it |
| //Both previous lines are true if the currentCharacter is a JavaIdentifierPart |
| //On false, no side effect has occured. |
| |
| //ALL getNextChar.... ARE OPTIMIZED COPIES |
| int pos; |
| if ((pos = this.currentPosition) >= this.eofPosition) // handle the obvious case upfront |
| return false; |
| |
| int temp2 = this.withoutUnicodePtr; |
| try { |
| boolean unicode = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| unicode = true; |
| } |
| char c = this.currentCharacter; |
| boolean isJavaIdentifierPart = false; |
| if (c >= HIGH_SURROGATE_MIN_VALUE && c <= HIGH_SURROGATE_MAX_VALUE) { |
| if (this.complianceLevel < ClassFileConstants.JDK1_5) { |
| this.currentPosition = pos; |
| this.withoutUnicodePtr = temp2; |
| return false; |
| } |
| // Unicode 4 detection |
| char low = (char) getNextChar(); |
| if (low < LOW_SURROGATE_MIN_VALUE || low > LOW_SURROGATE_MAX_VALUE) { |
| // illegal low surrogate |
| this.currentPosition = pos; |
| this.withoutUnicodePtr = temp2; |
| return false; |
| } |
| isJavaIdentifierPart = ScannerHelper.isJavaIdentifierPart(this.complianceLevel, c, low); |
| } |
| else if (c >= LOW_SURROGATE_MIN_VALUE && c <= LOW_SURROGATE_MAX_VALUE) { |
| this.currentPosition = pos; |
| this.withoutUnicodePtr = temp2; |
| return false; |
| } else { |
| isJavaIdentifierPart = ScannerHelper.isJavaIdentifierPart(this.complianceLevel, c); |
| } |
| if (unicode) { |
| if (!isJavaIdentifierPart) { |
| this.currentPosition = pos; |
| this.withoutUnicodePtr = temp2; |
| return false; |
| } |
| return true; |
| } else { |
| if (!isJavaIdentifierPart) { |
| this.currentPosition = pos; |
| return false; |
| } |
| |
| if (this.withoutUnicodePtr != 0) |
| unicodeStore(); |
| return true; |
| } |
| } catch(IndexOutOfBoundsException | InvalidInputException e) { |
| this.currentPosition = pos; |
| this.withoutUnicodePtr = temp2; |
| return false; |
| } |
| } |
| /* |
| * External API in JavaConventions. |
| * This is used to optimize the case where the scanner is used to scan a single identifier. |
| * In this case, the AIOOBE is slower to handle than a bound check |
| */ |
| public int scanIdentifier() throws InvalidInputException { |
| int whiteStart = 0; |
| while (true) { //loop for jumping over comments |
| this.withoutUnicodePtr = 0; |
| //start with a new token (even comment written with unicode ) |
| // ---------Consume white space and handles startPosition--------- |
| whiteStart = this.currentPosition; |
| boolean isWhiteSpace, hasWhiteSpaces = false; |
| int offset; |
| int unicodePtr; |
| boolean checkIfUnicode = false; |
| do { |
| unicodePtr = this.withoutUnicodePtr; |
| offset = this.currentPosition; |
| this.startPosition = this.currentPosition; |
| if (this.currentPosition < this.eofPosition) { |
| this.currentCharacter = this.source[this.currentPosition++]; |
| checkIfUnicode = this.currentPosition < this.eofPosition |
| && this.currentCharacter == '\\' |
| && this.source[this.currentPosition] == 'u'; |
| } else if (this.tokenizeWhiteSpace && (whiteStart != this.currentPosition - 1)) { |
| // reposition scanner in case we are interested by spaces as tokens |
| this.currentPosition--; |
| this.startPosition = whiteStart; |
| return TokenNameWHITESPACE; |
| } else { |
| return TokenNameEOF; |
| } |
| if (checkIfUnicode) { |
| isWhiteSpace = jumpOverUnicodeWhiteSpace(); |
| offset = this.currentPosition - offset; |
| } else { |
| offset = this.currentPosition - offset; |
| // inline version of: |
| //isWhiteSpace = |
| // (this.currentCharacter == ' ') || ScannerHelper.isWhitespace(this.currentCharacter); |
| switch (this.currentCharacter) { |
| case 10 : /* \ u000a: LINE FEED */ |
| case 12 : /* \ u000c: FORM FEED */ |
| case 13 : /* \ u000d: CARRIAGE RETURN */ |
| case 32 : /* \ u0020: SPACE */ |
| case 9 : /* \ u0009: HORIZONTAL TABULATION */ |
| isWhiteSpace = true; |
| break; |
| default : |
| isWhiteSpace = false; |
| } |
| } |
| if (isWhiteSpace) { |
| hasWhiteSpaces = true; |
| } |
| } while (isWhiteSpace); |
| if (hasWhiteSpaces) { |
| if (this.tokenizeWhiteSpace) { |
| // reposition scanner in case we are interested by spaces as tokens |
| this.currentPosition-=offset; |
| this.startPosition = whiteStart; |
| if (checkIfUnicode) { |
| this.withoutUnicodePtr = unicodePtr; |
| } |
| return TokenNameWHITESPACE; |
| } else if (checkIfUnicode) { |
| this.withoutUnicodePtr = 0; |
| unicodeStore(); |
| } else { |
| this.withoutUnicodePtr = 0; |
| } |
| } |
| char c = this.currentCharacter; |
| if (c < ScannerHelper.MAX_OBVIOUS) { |
| if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_IDENT_START) != 0) { |
| return scanIdentifierOrKeywordWithBoundCheck(); |
| } |
| return TokenNameERROR; |
| } |
| boolean isJavaIdStart; |
| if (c >= HIGH_SURROGATE_MIN_VALUE && c <= HIGH_SURROGATE_MAX_VALUE) { |
| if (this.complianceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(INVALID_UNICODE_ESCAPE); |
| } |
| // Unicode 4 detection |
| char low = (char) getNextCharWithBoundChecks(); |
| if (low < LOW_SURROGATE_MIN_VALUE || low > LOW_SURROGATE_MAX_VALUE) { |
| // illegal low surrogate |
| throw new InvalidInputException(INVALID_LOW_SURROGATE); |
| } |
| isJavaIdStart = ScannerHelper.isJavaIdentifierStart(this.complianceLevel, c, low); |
| } else if (c >= LOW_SURROGATE_MIN_VALUE && c <= LOW_SURROGATE_MAX_VALUE) { |
| if (this.complianceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(INVALID_UNICODE_ESCAPE); |
| } |
| throw new InvalidInputException(INVALID_HIGH_SURROGATE); |
| } else { |
| // optimized case already checked |
| isJavaIdStart = ScannerHelper.isJavaIdentifierStart(this.complianceLevel, c); |
| } |
| if (isJavaIdStart) |
| return scanIdentifierOrKeywordWithBoundCheck(); |
| return TokenNameERROR; |
| } |
| } |
| public void ungetToken(int unambiguousToken) { |
| if (this.nextToken != TokenNameNotAToken) { |
| throw new ArrayIndexOutOfBoundsException("Single cell array overflow"); //$NON-NLS-1$ |
| } |
| this.nextToken = unambiguousToken; |
| } |
| private void updateCase(int token) { |
| if (token == TokenNamecase) { |
| this.caseStartPosition = this.startPosition; |
| this.breakPreviewAllowed = true; |
| } |
| } |
| public int getNextToken() throws InvalidInputException { |
| |
| int token; |
| if (this.nextToken != TokenNameNotAToken) { |
| token = this.nextToken; |
| this.nextToken = TokenNameNotAToken; |
| return token; // presumed to be unambiguous. |
| } |
| if (this.scanContext == null) { // init lazily, since isInModuleDeclaration needs the parser to be known |
| this.scanContext = isInModuleDeclaration() ? ScanContext.EXPECTING_KEYWORD : ScanContext.INACTIVE; |
| } |
| token = getNextToken0(); |
| if (areRestrictedModuleKeywordsActive()) { |
| if (isRestrictedKeyword(token)) |
| token = disambiguatedRestrictedKeyword(token); |
| updateScanContext(token); |
| } |
| if (this.activeParser == null) { // anybody interested in the grammatical structure of the program should have registered. |
| return token; |
| } |
| if (token == TokenNameLPAREN || token == TokenNameLESS || token == TokenNameAT || token == TokenNameARROW) { |
| token = disambiguatedToken(token); |
| } else if (token == TokenNameELLIPSIS) { |
| this.consumingEllipsisAnnotations = false; |
| } |
| this.lookBack[0] = this.lookBack[1]; |
| this.lookBack[1] = token; |
| updateCase(token); |
| return token; |
| } |
| protected int getNextToken0() throws InvalidInputException { |
| this.wasAcr = false; |
| if (this.diet) { |
| jumpOverMethodBody(); |
| this.diet = false; |
| return this.currentPosition > this.eofPosition ? TokenNameEOF : TokenNameRBRACE; |
| } |
| int whiteStart = 0; |
| try { |
| while (true) { //loop for jumping over comments |
| this.withoutUnicodePtr = 0; |
| //start with a new token (even comment written with unicode ) |
| |
| // ---------Consume white space and handles startPosition--------- |
| whiteStart = this.currentPosition; |
| boolean isWhiteSpace, hasWhiteSpaces = false; |
| int offset; |
| int unicodePtr; |
| boolean checkIfUnicode = false; |
| do { |
| unicodePtr = this.withoutUnicodePtr; |
| offset = this.currentPosition; |
| this.startPosition = this.currentPosition; |
| try { |
| checkIfUnicode = ((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u'); |
| } catch(IndexOutOfBoundsException e) { |
| if (this.tokenizeWhiteSpace && (whiteStart != this.currentPosition - 1)) { |
| // reposition scanner in case we are interested by spaces as tokens |
| this.currentPosition--; |
| this.startPosition = whiteStart; |
| return TokenNameWHITESPACE; |
| } |
| if (this.currentPosition > this.eofPosition) |
| return TokenNameEOF; |
| } |
| if (this.currentPosition > this.eofPosition) { |
| if (this.tokenizeWhiteSpace && (whiteStart != this.currentPosition - 1)) { |
| this.currentPosition--; |
| // reposition scanner in case we are interested by spaces as tokens |
| this.startPosition = whiteStart; |
| return TokenNameWHITESPACE; |
| } |
| return TokenNameEOF; |
| } |
| if (checkIfUnicode) { |
| isWhiteSpace = jumpOverUnicodeWhiteSpace(); |
| offset = this.currentPosition - offset; |
| } else { |
| offset = this.currentPosition - offset; |
| if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) { |
| if (this.recordLineSeparator) { |
| pushLineSeparator(); |
| } |
| } |
| // inline version of: |
| //isWhiteSpace = |
| // (this.currentCharacter == ' ') || ScannerHelper.isWhitespace(this.currentCharacter); |
| switch (this.currentCharacter) { |
| case 10 : /* \ u000a: LINE FEED */ |
| case 12 : /* \ u000c: FORM FEED */ |
| case 13 : /* \ u000d: CARRIAGE RETURN */ |
| case 32 : /* \ u0020: SPACE */ |
| case 9 : /* \ u0009: HORIZONTAL TABULATION */ |
| isWhiteSpace = true; |
| break; |
| default : |
| isWhiteSpace = false; |
| } |
| } |
| if (isWhiteSpace) { |
| hasWhiteSpaces = true; |
| } |
| } while (isWhiteSpace); |
| if (hasWhiteSpaces) { |
| if (this.tokenizeWhiteSpace) { |
| // reposition scanner in case we are interested by spaces as tokens |
| this.currentPosition-=offset; |
| this.startPosition = whiteStart; |
| if (checkIfUnicode) { |
| this.withoutUnicodePtr = unicodePtr; |
| } |
| return TokenNameWHITESPACE; |
| } else if (checkIfUnicode) { |
| this.withoutUnicodePtr = 0; |
| unicodeStore(); |
| } else { |
| this.withoutUnicodePtr = 0; |
| } |
| } |
| // ---------Identify the next token------------- |
| switch (this.currentCharacter) { |
| case '@' : |
| /* if (this.sourceLevel >= ClassFileConstants.JDK1_5) { |
| return TokenNameAT; |
| } else { |
| return TokenNameERROR; |
| }*/ |
| return TokenNameAT; |
| case '(' : |
| return TokenNameLPAREN; |
| case ')' : |
| return TokenNameRPAREN; |
| case '{' : |
| return TokenNameLBRACE; |
| case '}' : |
| return TokenNameRBRACE; |
| case '[' : |
| return TokenNameLBRACKET; |
| case ']' : |
| return TokenNameRBRACKET; |
| case ';' : |
| return TokenNameSEMICOLON; |
| case ',' : |
| return TokenNameCOMMA; |
| case '.' : |
| if (getNextCharAsDigit()) { |
| return scanNumber(true); |
| } |
| int temp = this.currentPosition; |
| if (getNextChar('.')) { |
| if (getNextChar('.')) { |
| return TokenNameELLIPSIS; |
| } else { |
| this.currentPosition = temp; |
| return TokenNameDOT; |
| } |
| } else { |
| this.currentPosition = temp; |
| return TokenNameDOT; |
| } |
| case '+' : |
| { |
| int test; |
| if ((test = getNextChar('+', '=')) == 0) |
| return TokenNamePLUS_PLUS; |
| if (test > 0) |
| return TokenNamePLUS_EQUAL; |
| return TokenNamePLUS; |
| } |
| case '-' : |
| { |
| int test; |
| if ((test = getNextChar('-', '=')) == 0) |
| return TokenNameMINUS_MINUS; |
| if (test > 0) |
| return TokenNameMINUS_EQUAL; |
| if (getNextChar('>')) |
| return TokenNameARROW; |
| return TokenNameMINUS; |
| } |
| case '~' : |
| return TokenNameTWIDDLE; |
| case '!' : |
| if (getNextChar('=')) |
| return TokenNameNOT_EQUAL; |
| return TokenNameNOT; |
| case '*' : |
| if (getNextChar('=')) |
| return TokenNameMULTIPLY_EQUAL; |
| return TokenNameMULTIPLY; |
| case '%' : |
| if (getNextChar('=')) |
| return TokenNameREMAINDER_EQUAL; |
| return TokenNameREMAINDER; |
| case '<' : |
| { |
| int test; |
| if ((test = getNextChar('=', '<')) == 0) |
| return TokenNameLESS_EQUAL; |
| if (test > 0) { |
| if (getNextChar('=')) |
| return TokenNameLEFT_SHIFT_EQUAL; |
| return TokenNameLEFT_SHIFT; |
| } |
| return TokenNameLESS; |
| } |
| case '>' : |
| { |
| int test; |
| if (this.returnOnlyGreater) { |
| return TokenNameGREATER; |
| } |
| if ((test = getNextChar('=', '>')) == 0) |
| return TokenNameGREATER_EQUAL; |
| if (test > 0) { |
| if ((test = getNextChar('=', '>')) == 0) |
| return TokenNameRIGHT_SHIFT_EQUAL; |
| if (test > 0) { |
| if (getNextChar('=')) |
| return TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL; |
| return TokenNameUNSIGNED_RIGHT_SHIFT; |
| } |
| return TokenNameRIGHT_SHIFT; |
| } |
| return TokenNameGREATER; |
| } |
| case '=' : |
| if (getNextChar('=')) |
| return TokenNameEQUAL_EQUAL; |
| return TokenNameEQUAL; |
| case '&' : |
| { |
| int test; |
| if ((test = getNextChar('&', '=')) == 0) |
| return TokenNameAND_AND; |
| if (test > 0) |
| return TokenNameAND_EQUAL; |
| return TokenNameAND; |
| } |
| case '|' : |
| { |
| int test; |
| if ((test = getNextChar('|', '=')) == 0) |
| return TokenNameOR_OR; |
| if (test > 0) |
| return TokenNameOR_EQUAL; |
| return TokenNameOR; |
| } |
| case '^' : |
| if (getNextChar('=')) |
| return TokenNameXOR_EQUAL; |
| return TokenNameXOR; |
| case '?' : |
| return TokenNameQUESTION; |
| case ':' : |
| if (getNextChar(':')) |
| return TokenNameCOLON_COLON; |
| ++this.yieldColons; |
| return TokenNameCOLON; |
| case '\'' : |
| { |
| int test; |
| if ((test = getNextChar('\n', '\r')) == 0) { |
| throw new InvalidInputException(INVALID_CHARACTER_CONSTANT); |
| } |
| if (test > 0) { |
| // relocate if finding another quote fairly close: thus unicode '/u000D' will be fully consumed |
| for (int lookAhead = 0; lookAhead < 3; lookAhead++) { |
| if (this.currentPosition + lookAhead == this.eofPosition) |
| break; |
| if (this.source[this.currentPosition + lookAhead] == '\n') |
| break; |
| if (this.source[this.currentPosition + lookAhead] == '\'') { |
| this.currentPosition += lookAhead + 1; |
| break; |
| } |
| } |
| throw new InvalidInputException(INVALID_CHARACTER_CONSTANT); |
| } |
| } |
| if (getNextChar('\'')) { |
| // relocate if finding another quote fairly close: thus unicode '/u000D' will be fully consumed |
| for (int lookAhead = 0; lookAhead < 3; lookAhead++) { |
| if (this.currentPosition + lookAhead == this.eofPosition) |
| break; |
| if (this.source[this.currentPosition + lookAhead] == '\n') |
| break; |
| if (this.source[this.currentPosition + lookAhead] == '\'') { |
| this.currentPosition += lookAhead + 1; |
| break; |
| } |
| } |
| throw new InvalidInputException(INVALID_CHARACTER_CONSTANT); |
| } |
| if (getNextChar('\\')) { |
| if (this.unicodeAsBackSlash) { |
| // consume next character |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } else { |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| } else { |
| this.currentCharacter = this.source[this.currentPosition++]; |
| } |
| scanEscapeCharacter(); |
| } else { // consume next character |
| this.unicodeAsBackSlash = false; |
| checkIfUnicode = false; |
| try { |
| checkIfUnicode = ((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u'); |
| } catch(IndexOutOfBoundsException e) { |
| this.currentPosition--; |
| throw new InvalidInputException(INVALID_CHARACTER_CONSTANT); |
| } |
| if (checkIfUnicode) { |
| getNextUnicodeChar(); |
| } else { |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| } |
| if (getNextChar('\'')) |
| return TokenNameCharacterLiteral; |
| // relocate if finding another quote fairly close: thus unicode '/u000D' will be fully consumed |
| for (int lookAhead = 0; lookAhead < 20; lookAhead++) { |
| if (this.currentPosition + lookAhead == this.eofPosition) |
| break; |
| if (this.source[this.currentPosition + lookAhead] == '\n') |
| break; |
| if (this.source[this.currentPosition + lookAhead] == '\'') { |
| this.currentPosition += lookAhead + 1; |
| break; |
| } |
| } |
| throw new InvalidInputException(INVALID_CHARACTER_CONSTANT); |
| case '"' : |
| return scanForStringLiteral(); |
| case '/' : |
| if (!this.skipComments) { |
| int test = getNextChar('/', '*'); |
| if (test == 0) { //line comment |
| this.lastCommentLinePosition = this.currentPosition; |
| try { //get the next char |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } |
| |
| //handle the \\u case manually into comment |
| if (this.currentCharacter == '\\') { |
| if (this.source[this.currentPosition] == '\\') |
| this.currentPosition++; |
| } //jump over the \\ |
| boolean isUnicode = false; |
| while (this.currentCharacter != '\r' && this.currentCharacter != '\n') { |
| if (this.currentPosition >= this.eofPosition) { |
| this.lastCommentLinePosition = this.currentPosition; |
| this.currentPosition ++; |
| // this avoids duplicating the code in the catch(IndexOutOfBoundsException e) |
| throw new IndexOutOfBoundsException(); |
| } |
| this.lastCommentLinePosition = this.currentPosition; |
| //get the next char |
| isUnicode = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| isUnicode = true; |
| } |
| //handle the \\u case manually into comment |
| if (this.currentCharacter == '\\') { |
| if (this.source[this.currentPosition] == '\\') |
| this.currentPosition++; |
| } //jump over the \\ |
| } |
| /* |
| * We need to completely consume the line break |
| */ |
| if (this.currentCharacter == '\r' |
| && this.eofPosition > this.currentPosition) { |
| if (this.source[this.currentPosition] == '\n') { |
| this.currentPosition++; |
| this.currentCharacter = '\n'; |
| } else if ((this.source[this.currentPosition] == '\\') |
| && (this.source[this.currentPosition + 1] == 'u')) { |
| getNextUnicodeChar(); |
| isUnicode = true; |
| } |
| } |
| recordComment(TokenNameCOMMENT_LINE); |
| if (this.taskTags != null) checkTaskTag(this.startPosition, this.currentPosition); |
| if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) { |
| if ((this.checkNonExternalizedStringLiterals || this.checkUninternedIdentityComparison) && |
| this.lastPosition < this.currentPosition) { |
| parseTags(); |
| } |
| if (this.recordLineSeparator) { |
| if (isUnicode) { |
| pushUnicodeLineSeparator(); |
| } else { |
| pushLineSeparator(); |
| } |
| } |
| } |
| if (this.tokenizeComments) { |
| return TokenNameCOMMENT_LINE; |
| } |
| } catch (IndexOutOfBoundsException e) { |
| this.currentPosition--; |
| recordComment(TokenNameCOMMENT_LINE); |
| if (this.taskTags != null) checkTaskTag(this.startPosition, this.currentPosition); |
| if ((this.checkNonExternalizedStringLiterals || this.checkUninternedIdentityComparison) && |
| this.lastPosition < this.currentPosition) { |
| parseTags(); |
| } |
| if (this.tokenizeComments) { |
| return TokenNameCOMMENT_LINE; |
| } else { |
| this.currentPosition++; |
| } |
| } |
| break; |
| } |
| if (test > 0) { //traditional and javadoc comment |
| try { //get the next char |
| boolean isJavadoc = false, star = false; |
| boolean isUnicode = false; |
| int previous; |
| // consume next character |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| isUnicode = true; |
| } else { |
| isUnicode = false; |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| |
| if (this.currentCharacter == '*') { |
| isJavadoc = true; |
| star = true; |
| } |
| if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) { |
| if (this.recordLineSeparator) { |
| if (isUnicode) { |
| pushUnicodeLineSeparator(); |
| } else { |
| pushLineSeparator(); |
| } |
| } |
| } |
| isUnicode = false; |
| previous = this.currentPosition; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| //-------------unicode traitement ------------ |
| getNextUnicodeChar(); |
| isUnicode = true; |
| } else { |
| isUnicode = false; |
| } |
| //handle the \\u case manually into comment |
| if (this.currentCharacter == '\\') { |
| if (this.source[this.currentPosition] == '\\') |
| this.currentPosition++; //jump over the \\ |
| } |
| // empty comment is not a javadoc /**/ |
| if (this.currentCharacter == '/') { |
| isJavadoc = false; |
| } |
| //loop until end of comment */ |
| int firstTag = 0; |
| while ((this.currentCharacter != '/') || (!star)) { |
| if (this.currentPosition >= this.eofPosition) { |
| throw new InvalidInputException(UNTERMINATED_COMMENT); |
| } |
| if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) { |
| if (this.recordLineSeparator) { |
| if (isUnicode) { |
| pushUnicodeLineSeparator(); |
| } else { |
| pushLineSeparator(); |
| } |
| } |
| } |
| switch (this.currentCharacter) { |
| case '*': |
| star = true; |
| break; |
| case '@': |
| if (firstTag == 0 && this.isFirstTag()) { |
| firstTag = previous; |
| } |
| //$FALL-THROUGH$ default case to set star to false |
| default: |
| star = false; |
| } |
| //get next char |
| previous = this.currentPosition; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| //-------------unicode traitement ------------ |
| getNextUnicodeChar(); |
| isUnicode = true; |
| } else { |
| isUnicode = false; |
| } |
| //handle the \\u case manually into comment |
| if (this.currentCharacter == '\\') { |
| if (this.source[this.currentPosition] == '\\') |
| this.currentPosition++; |
| } //jump over the \\ |
| } |
| int token = isJavadoc ? TokenNameCOMMENT_JAVADOC : TokenNameCOMMENT_BLOCK; |
| recordComment(token); |
| this.commentTagStarts[this.commentPtr] = firstTag; |
| if (this.taskTags != null) checkTaskTag(this.startPosition, this.currentPosition); |
| if (this.tokenizeComments) { |
| /* |
| if (isJavadoc) |
| return TokenNameCOMMENT_JAVADOC; |
| return TokenNameCOMMENT_BLOCK; |
| */ |
| return token; |
| } |
| } catch (IndexOutOfBoundsException e) { |
| this.currentPosition--; |
| throw new InvalidInputException(UNTERMINATED_COMMENT); |
| } |
| break; |
| } |
| } |
| if (getNextChar('=')) |
| return TokenNameDIVIDE_EQUAL; |
| return TokenNameDIVIDE; |
| case '\u001a' : |
| if (atEnd()) |
| return TokenNameEOF; |
| //the atEnd may not be <currentPosition == source.length> if source is only some part of a real (external) stream |
| throw new InvalidInputException("Ctrl-Z"); //$NON-NLS-1$ |
| default : |
| char c = this.currentCharacter; |
| if (c < ScannerHelper.MAX_OBVIOUS) { |
| if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_IDENT_START) != 0) { |
| return scanIdentifierOrKeyword(); |
| } else if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_DIGIT) != 0) { |
| return scanNumber(false); |
| } else { |
| return TokenNameERROR; |
| } |
| } |
| boolean isJavaIdStart; |
| if (c >= HIGH_SURROGATE_MIN_VALUE && c <= HIGH_SURROGATE_MAX_VALUE) { |
| if (this.complianceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(INVALID_UNICODE_ESCAPE); |
| } |
| // Unicode 4 detection |
| char low = (char) getNextChar(); |
| if (low < LOW_SURROGATE_MIN_VALUE || low > LOW_SURROGATE_MAX_VALUE) { |
| // illegal low surrogate |
| throw new InvalidInputException(INVALID_LOW_SURROGATE); |
| } |
| isJavaIdStart = ScannerHelper.isJavaIdentifierStart(this.complianceLevel, c, low); |
| } |
| else if (c >= LOW_SURROGATE_MIN_VALUE && c <= LOW_SURROGATE_MAX_VALUE) { |
| if (this.complianceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(INVALID_UNICODE_ESCAPE); |
| } |
| throw new InvalidInputException(INVALID_HIGH_SURROGATE); |
| } else { |
| // optimized case already checked |
| isJavaIdStart = ScannerHelper.isJavaIdentifierStart(this.complianceLevel, c); |
| } |
| if (isJavaIdStart) |
| return scanIdentifierOrKeyword(); |
| if (ScannerHelper.isDigit(this.currentCharacter)) { |
| return scanNumber(false); |
| } |
| return TokenNameERROR; |
| } |
| } |
| } //-----------------end switch while try-------------------- |
| catch (IndexOutOfBoundsException e) { |
| if (this.tokenizeWhiteSpace && (whiteStart != this.currentPosition - 1)) { |
| // reposition scanner in case we are interested by spaces as tokens |
| this.currentPosition--; |
| this.startPosition = whiteStart; |
| return TokenNameWHITESPACE; |
| } |
| } |
| return TokenNameEOF; |
| } |
| private int scanForStringLiteral() throws InvalidInputException { |
| boolean isTextBlock = false; |
| |
| // consume next character |
| this.unicodeAsBackSlash = false; |
| boolean isUnicode = false; |
| isTextBlock = scanForTextBlockBeginning(); |
| if (isTextBlock) { |
| return scanForTextBlock(); |
| } else { |
| try { |
| // consume next character |
| this.unicodeAsBackSlash = false; |
| isUnicode = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| isUnicode = true; |
| } else { |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| |
| while (this.currentCharacter != '"') { |
| if (this.currentPosition >= this.eofPosition) { |
| throw new InvalidInputException(UNTERMINATED_STRING); |
| } |
| /**** \r and \n are not valid in string literals ****/ |
| if ((this.currentCharacter == '\n') || (this.currentCharacter == '\r')) { |
| // relocate if finding another quote fairly close: thus unicode '/u000D' will be fully consumed |
| if (isUnicode) { |
| int start = this.currentPosition; |
| for (int lookAhead = 0; lookAhead < 50; lookAhead++) { |
| if (this.currentPosition >= this.eofPosition) { |
| this.currentPosition = start; |
| break; |
| } |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') && (this.source[this.currentPosition] == 'u')) { |
| isUnicode = true; |
| getNextUnicodeChar(); |
| } else { |
| isUnicode = false; |
| } |
| if (!isUnicode && this.currentCharacter == '\n') { |
| this.currentPosition--; // set current position on new line character |
| break; |
| } |
| if (this.currentCharacter == '\"') { |
| throw new InvalidInputException(INVALID_CHAR_IN_STRING); |
| } |
| } |
| } else { |
| this.currentPosition--; // set current position on new line character |
| } |
| throw new InvalidInputException(INVALID_CHAR_IN_STRING); |
| } |
| if (this.currentCharacter == '\\') { |
| if (this.unicodeAsBackSlash) { |
| this.withoutUnicodePtr--; |
| // consume next character |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| isUnicode = true; |
| this.withoutUnicodePtr--; |
| } else { |
| isUnicode = false; |
| } |
| } else { |
| if (this.withoutUnicodePtr == 0) { |
| unicodeInitializeBuffer(this.currentPosition - this.startPosition); |
| } |
| this.withoutUnicodePtr --; |
| this.currentCharacter = this.source[this.currentPosition++]; |
| } |
| // we need to compute the escape character in a separate buffer |
| scanEscapeCharacter(); |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| // consume next character |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| isUnicode = true; |
| } else { |
| isUnicode = false; |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| |
| } |
| } catch (IndexOutOfBoundsException e) { |
| this.currentPosition--; |
| throw new InvalidInputException(UNTERMINATED_STRING); |
| } catch (InvalidInputException e) { |
| if (e.getMessage().equals(INVALID_ESCAPE)) { |
| // relocate if finding another quote fairly close: thus unicode '/u000D' will be fully consumed |
| for (int lookAhead = 0; lookAhead < 50; lookAhead++) { |
| if (this.currentPosition + lookAhead == this.eofPosition) |
| break; |
| if (this.source[this.currentPosition + lookAhead] == '\n') |
| break; |
| if (this.source[this.currentPosition + lookAhead] == '\"') { |
| this.currentPosition += lookAhead + 1; |
| break; |
| } |
| } |
| |
| } |
| throw e; // rethrow |
| } |
| return TokenNameStringLiteral; |
| } |
| } |
| |
| protected int scanForTextBlock() throws InvalidInputException { |
| int lastQuotePos = 0; |
| try { |
| this.rawStart = this.currentPosition - this.startPosition; |
| while (this.currentPosition <= this.eofPosition) { |
| if (this.currentCharacter == '"') { |
| lastQuotePos = this.currentPosition; |
| // look for text block delimiter |
| if (scanForTextBlockClose()) { |
| this.currentPosition += 2; |
| return TerminalTokens.TokenNameTextBlock; |
| } |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } else { |
| if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) { |
| if (this.recordLineSeparator) { |
| pushLineSeparator(); |
| } |
| } |
| } |
| outer: if (this.currentCharacter == '\\') { |
| switch(this.source[this.currentPosition]) { |
| case 'n' : |
| case 'r' : |
| case 'f' : |
| break outer; |
| case '\n' : |
| case '\r' : |
| this.currentCharacter = '\\'; |
| this.currentPosition++; |
| break; |
| case '\\' : |
| this.currentPosition++; |
| break; |
| default : |
| if (this.unicodeAsBackSlash) { |
| this.withoutUnicodePtr--; |
| // consume next character |
| if (this.currentPosition >= this.eofPosition) { |
| break; |
| } |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| this.withoutUnicodePtr--; |
| } |
| } else { |
| if (this.withoutUnicodePtr == 0) { |
| unicodeInitializeBuffer(this.currentPosition - this.startPosition); |
| } |
| this.withoutUnicodePtr --; |
| this.currentCharacter = this.source[this.currentPosition++]; |
| } |
| int oldPos = this.currentPosition - 1; |
| scanEscapeCharacter(); |
| if (ScannerHelper.isWhitespace(this.currentCharacter)) { |
| if (this.withoutUnicodePtr == 0) { |
| unicodeInitializeBuffer(this.currentPosition - this.startPosition); |
| } |
| unicodeStore('\\'); |
| this.currentPosition = oldPos; |
| this.currentCharacter = this.source[this.currentPosition]; |
| break outer; |
| } |
| } |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| // consume next character |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } else { |
| if (this.currentCharacter == '"'/* || skipWhitespace*/) |
| continue; |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| } |
| if (lastQuotePos > 0) |
| this.currentPosition = lastQuotePos; |
| this.currentPosition = (lastQuotePos > 0) ? lastQuotePos : this.startPosition + this.rawStart; |
| throw new InvalidInputException(UNTERMINATED_TEXT_BLOCK); |
| } catch (IndexOutOfBoundsException e) { |
| this.currentPosition = (lastQuotePos > 0) ? lastQuotePos : this.startPosition + this.rawStart; |
| throw new InvalidInputException(UNTERMINATED_TEXT_BLOCK); |
| } |
| } |
| public void getNextUnicodeChar() |
| throws InvalidInputException { |
| //VOID |
| //handle the case of unicode. |
| //when a unicode appears then we must use a buffer that holds char internal values |
| //At the end of this method currentCharacter holds the new visited char |
| //and currentPosition points right next after it |
| |
| //ALL getNextChar.... ARE OPTIMIZED COPIES |
| int c1 = 0, c2 = 0, c3 = 0, c4 = 0, unicodeSize = 6; |
| this.currentPosition++; |
| if (this.currentPosition < this.eofPosition) { |
| while (this.source[this.currentPosition] == 'u') { |
| this.currentPosition++; |
| if (this.currentPosition >= this.eofPosition) { |
| this.currentPosition--; |
| throw new InvalidInputException(INVALID_UNICODE_ESCAPE); |
| } |
| unicodeSize++; |
| } |
| } else { |
| this.currentPosition--; |
| throw new InvalidInputException(INVALID_UNICODE_ESCAPE); |
| } |
| |
| if ((this.currentPosition + 4) > this.eofPosition) { |
| this.currentPosition += (this.eofPosition - this.currentPosition); |
| throw new InvalidInputException(INVALID_UNICODE_ESCAPE); |
| } |
| if ((c1 = ScannerHelper.getHexadecimalValue(this.source[this.currentPosition++])) > 15 |
| || c1 < 0 |
| || (c2 = ScannerHelper.getHexadecimalValue(this.source[this.currentPosition++])) > 15 |
| || c2 < 0 |
| || (c3 = ScannerHelper.getHexadecimalValue(this.source[this.currentPosition++])) > 15 |
| || c3 < 0 |
| || (c4 = ScannerHelper.getHexadecimalValue(this.source[this.currentPosition++])) > 15 |
| || c4 < 0){ |
| throw new InvalidInputException(INVALID_UNICODE_ESCAPE); |
| } |
| this.currentCharacter = (char) (((c1 * 16 + c2) * 16 + c3) * 16 + c4); |
| //need the unicode buffer |
| if (this.withoutUnicodePtr == 0) { |
| //buffer all the entries that have been left aside.... |
| unicodeInitializeBuffer(this.currentPosition - unicodeSize - this.startPosition); |
| } |
| //fill the buffer with the char |
| unicodeStore(); |
| this.unicodeAsBackSlash = this.currentCharacter == '\\'; |
| } |
| public NLSTag[] getNLSTags() { |
| final int length = this.nlsTagsPtr; |
| if (length != 0) { |
| NLSTag[] result = new NLSTag[length]; |
| System.arraycopy(this.nlsTags, 0, result, 0, length); |
| this.nlsTagsPtr = 0; |
| return result; |
| } |
| return null; |
| } |
| public boolean[] getIdentityComparisonLines() { |
| boolean [] retVal = this.validIdentityComparisonLines; |
| this.validIdentityComparisonLines = null; |
| return retVal; |
| } |
| public char[] getSource(){ |
| return this.source; |
| } |
| protected boolean isFirstTag() { |
| return true; |
| } |
| public final void jumpOverMethodBody() { |
| |
| this.wasAcr = false; |
| int found = 1; |
| try { |
| while (true) { //loop for jumping over comments |
| this.withoutUnicodePtr = 0; |
| // ---------Consume white space and handles startPosition--------- |
| boolean isWhiteSpace; |
| do { |
| this.startPosition = this.currentPosition; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| isWhiteSpace = jumpOverUnicodeWhiteSpace(); |
| } else { |
| if (this.recordLineSeparator |
| && ((this.currentCharacter == '\r') || (this.currentCharacter == '\n'))) { |
| pushLineSeparator(); |
| } |
| isWhiteSpace = CharOperation.isWhitespace(this.currentCharacter); |
| } |
| } while (isWhiteSpace); |
| |
| // -------consume token until } is found--------- |
| NextToken: switch (this.currentCharacter) { |
| case '{' : |
| found++; |
| break NextToken; |
| case '}' : |
| found--; |
| if (found == 0) |
| return; |
| break NextToken; |
| case '\'' : |
| { |
| boolean test; |
| test = getNextChar('\\'); |
| if (test) { |
| try { |
| if (this.unicodeAsBackSlash) { |
| // consume next character |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } else { |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| } else { |
| this.currentCharacter = this.source[this.currentPosition++]; |
| } |
| scanEscapeCharacter(); |
| } catch (InvalidInputException ex) { |
| // ignore |
| } |
| } else { |
| try { // consume next character |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } else { |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| } catch (InvalidInputException ex) { |
| // ignore |
| } |
| } |
| getNextChar('\''); |
| break NextToken; |
| } |
| case '"' : |
| boolean isTextBlock = false; |
| int firstClosingBrace = 0; |
| try { |
| try { // consume next character |
| isTextBlock = scanForTextBlockBeginning(); |
| if (!isTextBlock) { |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } else { |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| } |
| } catch (InvalidInputException ex) { |
| // ignore |
| } |
| |
| Inner: while (this.currentPosition <= this.eofPosition) { |
| if (isTextBlock) { |
| switch (this.currentCharacter) { |
| case '"': |
| // look for text block delimiter |
| if (scanForTextBlockClose()) { |
| this.currentPosition += 2; |
| this.currentCharacter = this.source[this.currentPosition]; |
| isTextBlock = false; |
| break Inner; |
| } |
| break; |
| case '}': |
| if (firstClosingBrace == 0) |
| firstClosingBrace = this.currentPosition; |
| break; |
| case '\r' : |
| if (this.source[this.currentPosition] == '\n') |
| this.currentPosition++; |
| //$FALL-THROUGH$ |
| case '\n' : |
| pushLineSeparator(); |
| //$FALL-THROUGH$ |
| default: |
| if (this.currentCharacter == '\\' && this.source[this.currentPosition++] == '"') { |
| this.currentPosition++; |
| } |
| this.currentCharacter = this.source[this.currentPosition++]; |
| continue Inner; |
| } |
| } else if (this.currentCharacter == '"') { |
| break Inner; |
| } |
| if (this.currentCharacter == '\r'){ |
| if (this.source[this.currentPosition] == '\n') this.currentPosition++; |
| break NextToken; // the string cannot go further that the line |
| } |
| if (this.currentCharacter == '\n'){ |
| break; // the string cannot go further that the line |
| } |
| if (this.currentCharacter == '\\') { |
| try { |
| if (this.unicodeAsBackSlash) { |
| // consume next character |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } else { |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| } else { |
| this.currentCharacter = this.source[this.currentPosition++]; |
| } |
| scanEscapeCharacter(); |
| } catch (InvalidInputException ex) { |
| // ignore |
| } |
| } |
| try { // consume next character |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } else { |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| } catch (InvalidInputException ex) { |
| // ignore |
| } |
| } |
| } catch (IndexOutOfBoundsException e) { |
| if(isTextBlock) { |
| // Pull it back to the first closing brace after the beginning |
| // of the unclosed text block and let recovery take over. |
| if (firstClosingBrace > 0) { |
| this.currentPosition = firstClosingBrace - 1; |
| } |
| } |
| } |
| break NextToken; |
| case '/' : |
| { |
| int test; |
| if ((test = getNextChar('/', '*')) == 0) { //line comment |
| try { |
| this.lastCommentLinePosition = this.currentPosition; |
| //get the next char |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } |
| //handle the \\u case manually into comment |
| if (this.currentCharacter == '\\') { |
| if (this.source[this.currentPosition] == '\\') |
| this.currentPosition++; |
| } //jump over the \\ |
| boolean isUnicode = false; |
| while (this.currentCharacter != '\r' && this.currentCharacter != '\n') { |
| if (this.currentPosition >= this.eofPosition) { |
| this.lastCommentLinePosition = this.currentPosition; |
| this.currentPosition ++; |
| // this avoids duplicating the code inside the catch(IndexOutOfBoundsException e) below |
| throw new IndexOutOfBoundsException(); |
| } |
| this.lastCommentLinePosition = this.currentPosition; |
| //get the next char |
| isUnicode = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| isUnicode = true; |
| getNextUnicodeChar(); |
| } |
| //handle the \\u case manually into comment |
| if (this.currentCharacter == '\\') { |
| if (this.source[this.currentPosition] == '\\') |
| this.currentPosition++; |
| } //jump over the \\ |
| } |
| /* |
| * We need to completely consume the line break |
| */ |
| if (this.currentCharacter == '\r' |
| && this.eofPosition > this.currentPosition) { |
| if (this.source[this.currentPosition] == '\n') { |
| this.currentPosition++; |
| this.currentCharacter = '\n'; |
| } else if ((this.source[this.currentPosition] == '\\') |
| && (this.source[this.currentPosition + 1] == 'u')) { |
| isUnicode = true; |
| getNextUnicodeChar(); |
| } |
| } |
| recordComment(TokenNameCOMMENT_LINE); |
| if (this.recordLineSeparator |
| && ((this.currentCharacter == '\r') || (this.currentCharacter == '\n'))) { |
| if ((this.checkNonExternalizedStringLiterals || this.checkUninternedIdentityComparison) && |
| this.lastPosition < this.currentPosition) { |
| parseTags(); |
| } |
| if (this.recordLineSeparator) { |
| if (isUnicode) { |
| pushUnicodeLineSeparator(); |
| } else { |
| pushLineSeparator(); |
| } |
| } |
| } |
| } catch (IndexOutOfBoundsException e) { |
| //an eof will then be generated |
| this.currentPosition--; |
| recordComment(TokenNameCOMMENT_LINE); |
| if ((this.checkNonExternalizedStringLiterals || this.checkUninternedIdentityComparison) && |
| this.lastPosition < this.currentPosition) { |
| parseTags(); |
| } |
| if (!this.tokenizeComments) { |
| this.currentPosition++; |
| } |
| } |
| break NextToken; |
| } |
| if (test > 0) { //traditional and javadoc comment |
| boolean isJavadoc = false; |
| try { //get the next char |
| boolean star = false; |
| int previous; |
| boolean isUnicode = false; |
| // consume next character |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| isUnicode = true; |
| } else { |
| isUnicode = false; |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| |
| if (this.currentCharacter == '*') { |
| isJavadoc = true; |
| star = true; |
| } |
| if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) { |
| if (this.recordLineSeparator) { |
| if (isUnicode) { |
| pushUnicodeLineSeparator(); |
| } else { |
| pushLineSeparator(); |
| } |
| } |
| } |
| isUnicode = false; |
| previous = this.currentPosition; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| isUnicode = true; |
| } else { |
| isUnicode = false; |
| } |
| //handle the \\u case manually into comment |
| if (this.currentCharacter == '\\') { |
| if (this.source[this.currentPosition] == '\\') |
| this.currentPosition++; //jump over the \\ |
| } |
| // empty comment is not a javadoc /**/ |
| if (this.currentCharacter == '/') { |
| isJavadoc = false; |
| } |
| //loop until end of comment */ |
| int firstTag = 0; |
| while ((this.currentCharacter != '/') || (!star)) { |
| if (this.currentPosition >= this.eofPosition) { |
| return; |
| } |
| if ((this.currentCharacter == '\r') || (this.currentCharacter == '\n')) { |
| if (this.recordLineSeparator) { |
| if (isUnicode) { |
| pushUnicodeLineSeparator(); |
| } else { |
| pushLineSeparator(); |
| } |
| } |
| } |
| switch (this.currentCharacter) { |
| case '*': |
| star = true; |
| break; |
| case '@': |
| if (firstTag == 0 && this.isFirstTag()) { |
| firstTag = previous; |
| } |
| //$FALL-THROUGH$ default case to set star to false |
| default: |
| star = false; |
| } |
| //get next char |
| previous = this.currentPosition; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| isUnicode = true; |
| } else { |
| isUnicode = false; |
| } |
| //handle the \\u case manually into comment |
| if (this.currentCharacter == '\\') { |
| if (this.source[this.currentPosition] == '\\') |
| this.currentPosition++; |
| } //jump over the \\ |
| } |
| recordComment(isJavadoc ? TokenNameCOMMENT_JAVADOC : TokenNameCOMMENT_BLOCK); |
| this.commentTagStarts[this.commentPtr] = firstTag; |
| } catch (IndexOutOfBoundsException e) { |
| return; |
| } |
| break NextToken; |
| } |
| break NextToken; |
| } |
| |
| default : |
| try { |
| char c = this.currentCharacter; |
| if (c < ScannerHelper.MAX_OBVIOUS) { |
| if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_IDENT_START) != 0) { |
| scanIdentifierOrKeyword(); |
| break NextToken; |
| } else if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_DIGIT) != 0) { |
| scanNumber(false); |
| break NextToken; |
| } else { |
| break NextToken; |
| } |
| } |
| boolean isJavaIdStart; |
| if (c >= HIGH_SURROGATE_MIN_VALUE && c <= HIGH_SURROGATE_MAX_VALUE) { |
| if (this.complianceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(INVALID_UNICODE_ESCAPE); |
| } |
| // Unicode 4 detection |
| char low = (char) getNextChar(); |
| if (low < LOW_SURROGATE_MIN_VALUE || low > LOW_SURROGATE_MAX_VALUE) { |
| // illegal low surrogate |
| break NextToken; |
| } |
| isJavaIdStart = ScannerHelper.isJavaIdentifierStart(this.complianceLevel, c, low); |
| } else if (c >= LOW_SURROGATE_MIN_VALUE && c <= LOW_SURROGATE_MAX_VALUE) { |
| break NextToken; |
| } else { |
| // optimized case already checked |
| isJavaIdStart = ScannerHelper.isJavaIdentifierStart(this.complianceLevel, c); |
| } |
| if (isJavaIdStart) { |
| scanIdentifierOrKeyword(); |
| break NextToken; |
| } |
| // if (ScannerHelper.isDigit(this.currentCharacter)) { |
| // scanNumber(false); |
| // break NextToken; |
| // } |
| } catch (InvalidInputException ex) { |
| // ignore |
| } |
| } |
| } |
| //-----------------end switch while try-------------------- |
| } catch (IndexOutOfBoundsException | InvalidInputException e) { |
| // ignore |
| } |
| return; |
| } |
| public final boolean jumpOverUnicodeWhiteSpace() throws InvalidInputException { |
| //BOOLEAN |
| //handle the case of unicode. Jump over the next whiteSpace |
| //making startPosition pointing on the next available char |
| //On false, the currentCharacter is filled up with a potential |
| //correct char |
| |
| this.wasAcr = false; |
| getNextUnicodeChar(); |
| return CharOperation.isWhitespace(this.currentCharacter); |
| } |
| |
| final char[] optimizedCurrentTokenSource1() { |
| //return always the same char[] build only once |
| |
| //optimization at no speed cost of 99.5 % of the singleCharIdentifier |
| char charOne = this.source[this.startPosition]; |
| switch (charOne) { |
| case 'a' : |
| return charArray_a; |
| case 'b' : |
| return charArray_b; |
| case 'c' : |
| return charArray_c; |
| case 'd' : |
| return charArray_d; |
| case 'e' : |
| return charArray_e; |
| case 'f' : |
| return charArray_f; |
| case 'g' : |
| return charArray_g; |
| case 'h' : |
| return charArray_h; |
| case 'i' : |
| return charArray_i; |
| case 'j' : |
| return charArray_j; |
| case 'k' : |
| return charArray_k; |
| case 'l' : |
| return charArray_l; |
| case 'm' : |
| return charArray_m; |
| case 'n' : |
| return charArray_n; |
| case 'o' : |
| return charArray_o; |
| case 'p' : |
| return charArray_p; |
| case 'q' : |
| return charArray_q; |
| case 'r' : |
| return charArray_r; |
| case 's' : |
| return charArray_s; |
| case 't' : |
| return charArray_t; |
| case 'u' : |
| return charArray_u; |
| case 'v' : |
| return charArray_v; |
| case 'w' : |
| return charArray_w; |
| case 'x' : |
| return charArray_x; |
| case 'y' : |
| return charArray_y; |
| case 'z' : |
| return charArray_z; |
| default : |
| return new char[] {charOne}; |
| } |
| } |
| final char[] optimizedCurrentTokenSource2() { |
| //try to return the same char[] build only once |
| |
| char[] src = this.source; |
| int start = this.startPosition; |
| char c0 , c1; |
| int hash = (((c0=src[start]) << 6) + (c1=src[start+1])) % TableSize; |
| char[][] table = this.charArray_length[0][hash]; |
| int i = this.newEntry2; |
| while (++i < InternalTableSize) { |
| char[] charArray = table[i]; |
| if ((c0 == charArray[0]) && (c1 == charArray[1])) |
| return charArray; |
| } |
| //---------other side--------- |
| i = -1; |
| int max = this.newEntry2; |
| while (++i <= max) { |
| char[] charArray = table[i]; |
| if ((c0 == charArray[0]) && (c1 == charArray[1])) |
| return charArray; |
| } |
| //--------add the entry------- |
| if (++max >= InternalTableSize) max = 0; |
| char[] r; |
| System.arraycopy(src, start, r= new char[2], 0, 2); |
| //newIdentCount++; |
| return table[this.newEntry2 = max] = r; //(r = new char[] {c0, c1}); |
| } |
| final char[] optimizedCurrentTokenSource3() { |
| //try to return the same char[] build only once |
| |
| char[] src = this.source; |
| int start = this.startPosition; |
| char c0, c1=src[start+1], c2; |
| int hash = (((c0=src[start])<< 6) + (c2=src[start+2])) % TableSize; |
| // int hash = ((c0 << 12) + (c1<< 6) + c2) % TableSize; |
| char[][] table = this.charArray_length[1][hash]; |
| int i = this.newEntry3; |
| while (++i < InternalTableSize) { |
| char[] charArray = table[i]; |
| if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2])) |
| return charArray; |
| } |
| //---------other side--------- |
| i = -1; |
| int max = this.newEntry3; |
| while (++i <= max) { |
| char[] charArray = table[i]; |
| if ((c0 == charArray[0]) && (c1 == charArray[1]) && (c2 == charArray[2])) |
| return charArray; |
| } |
| //--------add the entry------- |
| if (++max >= InternalTableSize) max = 0; |
| char[] r; |
| System.arraycopy(src, start, r= new char[3], 0, 3); |
| //newIdentCount++; |
| return table[this.newEntry3 = max] = r; //(r = new char[] {c0, c1, c2}); |
| } |
| final char[] optimizedCurrentTokenSource4() { |
| //try to return the same char[] build only once |
| |
| char[] src = this.source; |
| int start = this.startPosition; |
| char c0, c1 = src[start+1], c2, c3 = src[start+3]; |
| int hash = (((c0=src[start]) << 6) + (c2=src[start+2])) % TableSize; |
| // int hash = (int) (((((long) c0) << 18) + (c1 << 12) + (c2 << 6) + c3) % TableSize); |
| char[][] table = this.charArray_length[2][hash]; |
| int i = this.newEntry4; |
| while (++i < InternalTableSize) { |
| char[] charArray = table[i]; |
| if ((c0 == charArray[0]) |
| && (c1 == charArray[1]) |
| && (c2 == charArray[2]) |
| && (c3 == charArray[3])) |
| return charArray; |
| } |
| //---------other side--------- |
| i = -1; |
| int max = this.newEntry4; |
| while (++i <= max) { |
| char[] charArray = table[i]; |
| if ((c0 == charArray[0]) |
| && (c1 == charArray[1]) |
| && (c2 == charArray[2]) |
| && (c3 == charArray[3])) |
| return charArray; |
| } |
| //--------add the entry------- |
| if (++max >= InternalTableSize) max = 0; |
| char[] r; |
| System.arraycopy(src, start, r= new char[4], 0, 4); |
| //newIdentCount++; |
| return table[this.newEntry4 = max] = r; //(r = new char[] {c0, c1, c2, c3}); |
| } |
| final char[] optimizedCurrentTokenSource5() { |
| //try to return the same char[] build only once |
| |
| char[] src = this.source; |
| int start = this.startPosition; |
| char c0, c1 = src[start+1], c2, c3 = src[start+3], c4; |
| int hash = (((c0=src[start]) << 12) +((c2=src[start+2]) << 6) + (c4=src[start+4])) % TableSize; |
| // int hash = (int) (((((long) c0) << 24) + (((long) c1) << 18) + (c2 << 12) + (c3 << 6) + c4) % TableSize); |
| char[][] table = this.charArray_length[3][hash]; |
| int i = this.newEntry5; |
| while (++i < InternalTableSize) { |
| char[] charArray = table[i]; |
| if ((c0 == charArray[0]) |
| && (c1 == charArray[1]) |
| && (c2 == charArray[2]) |
| && (c3 == charArray[3]) |
| && (c4 == charArray[4])) |
| return charArray; |
| } |
| //---------other side--------- |
| i = -1; |
| int max = this.newEntry5; |
| while (++i <= max) { |
| char[] charArray = table[i]; |
| if ((c0 == charArray[0]) |
| && (c1 == charArray[1]) |
| && (c2 == charArray[2]) |
| && (c3 == charArray[3]) |
| && (c4 == charArray[4])) |
| return charArray; |
| } |
| //--------add the entry------- |
| if (++max >= InternalTableSize) max = 0; |
| char[] r; |
| System.arraycopy(src, start, r= new char[5], 0, 5); |
| //newIdentCount++; |
| return table[this.newEntry5 = max] = r; //(r = new char[] {c0, c1, c2, c3, c4}); |
| } |
| final char[] optimizedCurrentTokenSource6() { |
| //try to return the same char[] build only once |
| |
| char[] src = this.source; |
| int start = this.startPosition; |
| char c0, c1 = src[start+1], c2, c3 = src[start+3], c4, c5 = src[start+5]; |
| int hash = (((c0=src[start]) << 12) +((c2=src[start+2]) << 6) + (c4=src[start+4])) % TableSize; |
| // int hash = (int)(((((long) c0) << 32) + (((long) c1) << 24) + (((long) c2) << 18) + (c3 << 12) + (c4 << 6) + c5) % TableSize); |
| char[][] table = this.charArray_length[4][hash]; |
| int i = this.newEntry6; |
| while (++i < InternalTableSize) { |
| char[] charArray = table[i]; |
| if ((c0 == charArray[0]) |
| && (c1 == charArray[1]) |
| && (c2 == charArray[2]) |
| && (c3 == charArray[3]) |
| && (c4 == charArray[4]) |
| && (c5 == charArray[5])) |
| return charArray; |
| } |
| //---------other side--------- |
| i = -1; |
| int max = this.newEntry6; |
| while (++i <= max) { |
| char[] charArray = table[i]; |
| if ((c0 == charArray[0]) |
| && (c1 == charArray[1]) |
| && (c2 == charArray[2]) |
| && (c3 == charArray[3]) |
| && (c4 == charArray[4]) |
| && (c5 == charArray[5])) |
| return charArray; |
| } |
| //--------add the entry------- |
| if (++max >= InternalTableSize) max = 0; |
| char[] r; |
| System.arraycopy(src, start, r= new char[6], 0, 6); |
| //newIdentCount++; |
| return table[this.newEntry6 = max] = r; //(r = new char[] {c0, c1, c2, c3, c4, c5}); |
| } |
| public boolean isInModuleDeclaration() { |
| return this.fakeInModule || this.insideModuleInfo || |
| (this.activeParser != null ? this.activeParser.isParsingModuleDeclaration() : false); |
| } |
| protected boolean areRestrictedModuleKeywordsActive() { |
| return this.scanContext != null && this.scanContext != ScanContext.INACTIVE; |
| } |
| void updateScanContext(int token) { |
| switch (token) { |
| case TerminalTokens.TokenNameSEMICOLON: // next could be a KEYWORD |
| case TerminalTokens.TokenNameRBRACE: |
| case TokenNameRPAREN: |
| this.scanContext = ScanContext.EXPECTING_KEYWORD; |
| break; |
| case TokenNameopen: |
| this.scanContext = ScanContext.EXPECTING_KEYWORD; |
| break; |
| case TokenNamerequires: |
| this.scanContext = ScanContext.AFTER_REQUIRES; |
| break; |
| case TokenNamemodule: |
| case TokenNameexports: |
| case TokenNameopens: |
| case TokenNameuses: |
| case TokenNameprovides: |
| case TokenNameto: |
| case TokenNamewith: |
| case TokenNametransitive: |
| case TokenNameDOT: |
| case TokenNameimport: |
| case TokenNameAT: |
| case TokenNameAT308: |
| case TokenNameCOMMA: |
| this.scanContext = ScanContext.EXPECTING_IDENTIFIER; |
| break; |
| case TokenNameIdentifier: |
| this.scanContext = ScanContext.EXPECTING_KEYWORD; |
| break; |
| case TerminalTokens.TokenNameLBRACE: |
| this.scanContext = ScanContext.EXPECTING_KEYWORD; |
| break; |
| default: // anything else is unexpected and should not alter the context |
| break; |
| } |
| } |
| |
| private void parseTags() { |
| int position = 0; |
| final int currentStartPosition = this.startPosition; |
| final int currentLinePtr = this.linePtr; |
| if (currentLinePtr >= 0) { |
| position = this.lineEnds[currentLinePtr] + 1; |
| } |
| while (ScannerHelper.isWhitespace(this.source[position])) { |
| position++; |
| } |
| if (currentStartPosition == position) { |
| // the whole line is commented out |
| return; |
| } |
| char[] s = null; |
| int sourceEnd = this.currentPosition; |
| int sourceStart = currentStartPosition; |
| int sourceDelta = 0; |
| if (this.withoutUnicodePtr != 0) { |
| // 0 is used as a fast test flag so the real first char is in position 1 |
| System.arraycopy( |
| this.withoutUnicodeBuffer, |
| 1, |
| s = new char[this.withoutUnicodePtr], |
| 0, |
| this.withoutUnicodePtr); |
| sourceEnd = this.withoutUnicodePtr; |
| sourceStart = 1; |
| sourceDelta = currentStartPosition; |
| } else { |
| s = this.source; |
| } |
| int pos; |
| if (this.checkNonExternalizedStringLiterals && |
| (pos = CharOperation.indexOf(TAG_PREFIX, s, true, sourceStart, sourceEnd)) != -1) { |
| if (this.nlsTags == null) { |
| this.nlsTags = new NLSTag[10]; |
| this.nlsTagsPtr = 0; |
| } |
| while (pos != -1) { |
| int start = pos + TAG_PREFIX_LENGTH; |
| int end = CharOperation.indexOf(TAG_POSTFIX, s, start, sourceEnd); |
| if (end != -1) { |
| NLSTag currentTag = null; |
| final int currentLine = currentLinePtr + 1; |
| try { |
| currentTag = new NLSTag(pos + sourceDelta, end + sourceDelta, currentLine, extractInt(s, start, end)); |
| } catch (NumberFormatException e) { |
| currentTag = new NLSTag(pos + sourceDelta, end + sourceDelta, currentLine, -1); |
| } |
| if (this.nlsTagsPtr == this.nlsTags.length) { |
| // resize |
| System.arraycopy(this.nlsTags, 0, (this.nlsTags = new NLSTag[this.nlsTagsPtr + 10]), 0, this.nlsTagsPtr); |
| } |
| this.nlsTags[this.nlsTagsPtr++] = currentTag; |
| } else { |
| end = start; |
| } |
| pos = CharOperation.indexOf(TAG_PREFIX, s, true, end, sourceEnd); |
| } |
| } |
| |
| if (this.checkUninternedIdentityComparison && |
| (pos = CharOperation.indexOf(IDENTITY_COMPARISON_TAG, s, true, sourceStart, sourceEnd)) != -1) { |
| if (this.validIdentityComparisonLines == null) { |
| this.validIdentityComparisonLines = new boolean[0]; |
| } |
| int currentLine = currentLinePtr + 1; |
| int length = this.validIdentityComparisonLines.length; |
| System.arraycopy(this.validIdentityComparisonLines, 0, this.validIdentityComparisonLines = new boolean[currentLine + 1], 0, length); |
| this.validIdentityComparisonLines[currentLine] = true; |
| } |
| } |
| private int extractInt(char[] array, int start, int end) { |
| int value = 0; |
| for (int i = start; i < end; i++) { |
| final char currentChar = array[i]; |
| int digit = 0; |
| switch(currentChar) { |
| case '0' : |
| digit = 0; |
| break; |
| case '1' : |
| digit = 1; |
| break; |
| case '2' : |
| digit = 2; |
| break; |
| case '3' : |
| digit = 3; |
| break; |
| case '4' : |
| digit = 4; |
| break; |
| case '5' : |
| digit = 5; |
| break; |
| case '6' : |
| digit = 6; |
| break; |
| case '7' : |
| digit = 7; |
| break; |
| case '8' : |
| digit = 8; |
| break; |
| case '9' : |
| digit = 9; |
| break; |
| default : |
| throw new NumberFormatException(); |
| } |
| value *= 10; |
| if (digit < 0) throw new NumberFormatException(); |
| value += digit; |
| } |
| return value; |
| } |
| public final void pushLineSeparator() { |
| //see comment on isLineDelimiter(char) for the use of '\n' and '\r' |
| final int INCREMENT = 250; |
| //currentCharacter is at position currentPosition-1 |
| // cr 000D |
| if (this.currentCharacter == '\r') { |
| int separatorPos = this.currentPosition - 1; |
| if ((this.linePtr >= 0) && (this.lineEnds[this.linePtr] >= separatorPos)) return; |
| int length = this.lineEnds.length; |
| if (++this.linePtr >= length) |
| System.arraycopy(this.lineEnds, 0, this.lineEnds = new int[2*length + INCREMENT], 0, length); |
| this.lineEnds[this.linePtr] = separatorPos; |
| // look-ahead for merged cr+lf |
| try { |
| if (this.source[this.currentPosition] == '\n') { |
| //System.out.println("look-ahead LF-" + this.currentPosition); |
| this.lineEnds[this.linePtr] = this.currentPosition; |
| this.currentPosition++; |
| this.wasAcr = false; |
| } else { |
| this.wasAcr = true; |
| } |
| } catch(IndexOutOfBoundsException e) { |
| this.wasAcr = true; |
| } |
| } else { |
| // lf 000A |
| if (this.currentCharacter == '\n') { //must merge eventual cr followed by lf |
| if (this.wasAcr && (this.lineEnds[this.linePtr] == (this.currentPosition - 2))) { |
| //System.out.println("merge LF-" + (this.currentPosition - 1)); |
| this.lineEnds[this.linePtr] = this.currentPosition - 1; |
| } else { |
| int separatorPos = this.currentPosition - 1; |
| if ((this.linePtr >= 0) && (this.lineEnds[this.linePtr] >= separatorPos)) return; |
| int length = this.lineEnds.length; |
| if (++this.linePtr >= length) |
| System.arraycopy(this.lineEnds, 0, this.lineEnds = new int[2*length + INCREMENT], 0, length); |
| this.lineEnds[this.linePtr] = separatorPos; |
| } |
| this.wasAcr = false; |
| } |
| } |
| } |
| public final void pushUnicodeLineSeparator() { |
| // cr 000D |
| if (this.currentCharacter == '\r') { |
| if (this.source[this.currentPosition] == '\n') { |
| this.wasAcr = false; |
| } else { |
| this.wasAcr = true; |
| } |
| } else { |
| // lf 000A |
| if (this.currentCharacter == '\n') { //must merge eventual cr followed by lf |
| this.wasAcr = false; |
| } |
| } |
| } |
| |
| public void recordComment(int token) { |
| // compute position |
| int commentStart = this.startPosition; |
| int stopPosition = this.currentPosition; |
| switch (token) { |
| case TokenNameCOMMENT_LINE: |
| // both positions are negative |
| commentStart = -this.startPosition; |
| stopPosition = -this.lastCommentLinePosition; |
| break; |
| case TokenNameCOMMENT_BLOCK: |
| // only end position is negative |
| stopPosition = -this.currentPosition; |
| break; |
| } |
| |
| // a new comment is recorded |
| int length = this.commentStops.length; |
| if (++this.commentPtr >= length) { |
| int newLength = length + COMMENT_ARRAYS_SIZE*10; |
| System.arraycopy(this.commentStops, 0, this.commentStops = new int[newLength], 0, length); |
| System.arraycopy(this.commentStarts, 0, this.commentStarts = new int[newLength], 0, length); |
| System.arraycopy(this.commentTagStarts, 0, this.commentTagStarts = new int[newLength], 0, length); |
| } |
| this.commentStops[this.commentPtr] = stopPosition; |
| this.commentStarts[this.commentPtr] = commentStart; |
| } |
| |
| /** |
| * Reposition the scanner on some portion of the original source. The given endPosition is the last valid position. |
| * Beyond this position, the scanner will answer EOF tokens (<code>ITerminalSymbols.TokenNameEOF</code>). |
| * |
| * @param begin the given start position |
| * @param end the given end position |
| */ |
| public void resetTo(int begin, int end) { |
| resetTo(begin, end, isInModuleDeclaration()); |
| } |
| public void resetTo(int begin, int end, boolean isModuleInfo) { |
| resetTo(begin, end, isModuleInfo, null); |
| } |
| /** |
| * Reposition the scanner on some portion of the original source. The given endPosition is the last valid position. |
| * Beyond this position, the scanner will answer EOF tokens (<code>ITerminalSymbols.TokenNameEOF</code>). |
| * |
| * @param begin the given start position |
| * @param end the given end position |
| * @param isModuleInfo if true apply rules for restricted keywords even without a connection to a properly configured parser |
| * @param context The scan context to use for restricted keyword support, use null to compute |
| */ |
| public void resetTo(int begin, int end, boolean isModuleInfo, ScanContext context) { |
| //reset the scanner to a given position where it may rescan again |
| |
| this.diet = false; |
| this.initialPosition = this.startPosition = this.currentPosition = begin; |
| if (this.source != null && this.source.length < end) { |
| this.eofPosition = this.source.length; |
| } else { |
| this.eofPosition = end < Integer.MAX_VALUE ? end + 1 : end; |
| } |
| this.commentPtr = -1; // reset comment stack |
| this.foundTaskCount = 0; |
| this.lookBack[0] = this.lookBack[1] = this.nextToken = TokenNameNotAToken; |
| this.consumingEllipsisAnnotations = false; |
| this.insideModuleInfo = isModuleInfo; |
| this.scanContext = context == null ? getScanContext(begin) : context; |
| } |
| |
| private ScanContext getScanContext(int begin) { |
| if (!isInModuleDeclaration()) |
| return ScanContext.INACTIVE; |
| if (begin == 0) |
| return ScanContext.EXPECTING_KEYWORD; |
| CompilerOptions options = new CompilerOptions(); |
| options.complianceLevel = this.complianceLevel; |
| options.sourceLevel = this.sourceLevel; |
| ScanContextDetector parser = new ScanContextDetector(options); |
| return parser.getScanContext(this.source, begin - 1); |
| } |
| protected final void scanEscapeCharacter() throws InvalidInputException { |
| // the string with "\\u" is a legal string of two chars \ and u |
| //thus we use a direct access to the source (for regular cases). |
| switch (this.currentCharacter) { |
| case 'b' : |
| this.currentCharacter = '\b'; |
| break; |
| case 't' : |
| this.currentCharacter = '\t'; |
| break; |
| case 'n' : |
| this.currentCharacter = '\n'; |
| break; |
| case 'f' : |
| this.currentCharacter = '\f'; |
| break; |
| case 'r' : |
| this.currentCharacter = '\r'; |
| break; |
| case '\"' : |
| this.currentCharacter = '\"'; |
| break; |
| case '\'' : |
| this.currentCharacter = '\''; |
| break; |
| case 's' : |
| this.currentCharacter = ' '; |
| break; |
| case '\\' : |
| this.currentCharacter = '\\'; |
| break; |
| default : |
| // -----------octal escape-------------- |
| // OctalDigit |
| // OctalDigit OctalDigit |
| // ZeroToThree OctalDigit OctalDigit |
| |
| int number = ScannerHelper.getHexadecimalValue(this.currentCharacter); |
| if (number >= 0 && number <= 7) { |
| boolean zeroToThreeNot = number > 3; |
| if (ScannerHelper.isDigit(this.currentCharacter = this.source[this.currentPosition++])) { |
| int digit = ScannerHelper.getHexadecimalValue(this.currentCharacter); |
| if (digit >= 0 && digit <= 7) { |
| number = (number * 8) + digit; |
| if (ScannerHelper.isDigit(this.currentCharacter = this.source[this.currentPosition++])) { |
| if (zeroToThreeNot) {// has read \NotZeroToThree OctalDigit Digit --> ignore last character |
| this.currentPosition--; |
| } else { |
| digit = ScannerHelper.getHexadecimalValue(this.currentCharacter); |
| if (digit >= 0 && digit <= 7){ // has read \ZeroToThree OctalDigit OctalDigit |
| number = (number * 8) + digit; |
| } else {// has read \ZeroToThree OctalDigit NonOctalDigit --> ignore last character |
| this.currentPosition--; |
| } |
| } |
| } else { // has read \OctalDigit NonDigit--> ignore last character |
| this.currentPosition--; |
| } |
| } else { // has read \OctalDigit NonOctalDigit--> ignore last character |
| this.currentPosition--; |
| } |
| } else { // has read \OctalDigit --> ignore last character |
| this.currentPosition--; |
| } |
| if (number > 255) |
| throw new InvalidInputException(INVALID_ESCAPE); |
| this.currentCharacter = (char) number; |
| } else |
| throw new InvalidInputException(INVALID_ESCAPE); |
| } |
| } |
| public int scanIdentifierOrKeywordWithBoundCheck() { |
| //test keywords |
| |
| //first dispatch on the first char. |
| //then the length. If there are several |
| //keywors with the same length AND the same first char, then do another |
| //dispatch on the second char |
| this.useAssertAsAnIndentifier = false; |
| this.useEnumAsAnIndentifier = false; |
| |
| char[] src = this.source; |
| identLoop: { |
| int pos; |
| int srcLength = this.eofPosition; |
| while (true) { |
| if ((pos = this.currentPosition) >= srcLength) // handle the obvious case upfront |
| break identLoop; |
| char c = src[pos]; |
| if (c < ScannerHelper.MAX_OBVIOUS) { |
| if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & |
| (ScannerHelper.C_UPPER_LETTER | ScannerHelper.C_LOWER_LETTER | ScannerHelper.C_IDENT_PART | ScannerHelper.C_DIGIT)) != 0) { |
| if (this.withoutUnicodePtr != 0) { |
| this.currentCharacter = c; |
| unicodeStore(); |
| } |
| this.currentPosition++; |
| } else if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & (ScannerHelper.C_SEPARATOR | ScannerHelper.C_JLS_SPACE)) != 0) { |
| this.currentCharacter = c; |
| break identLoop; |
| } else { |
| //System.out.println("slow<=128: "+ c); |
| while (getNextCharAsJavaIdentifierPartWithBoundCheck()){/*empty*/} |
| break identLoop; |
| } |
| } else { |
| //System.out.println("slow>>128: "+ c); |
| while (getNextCharAsJavaIdentifierPartWithBoundCheck()){/*empty*/} |
| break identLoop; |
| } |
| } |
| } |
| |
| int index, length; |
| char[] data; |
| if (this.withoutUnicodePtr == 0) { |
| //quick test on length == 1 but not on length > 12 while most identifier |
| //have a length which is <= 12...but there are lots of identifier with |
| //only one char.... |
| if ((length = this.currentPosition - this.startPosition) == 1) { |
| return TokenNameIdentifier; |
| } |
| data = this.source; |
| index = this.startPosition; |
| } else { |
| if ((length = this.withoutUnicodePtr) == 1) |
| return TokenNameIdentifier; |
| data = this.withoutUnicodeBuffer; |
| index = 1; |
| } |
| |
| return internalScanIdentifierOrKeyword(index, length, data); |
| } |
| public int scanIdentifierOrKeyword() { |
| //test keywords |
| |
| //first dispatch on the first char. |
| //then the length. If there are several |
| //keywords with the same length AND the same first char, then do another |
| //dispatch on the second char |
| this.useAssertAsAnIndentifier = false; |
| this.useEnumAsAnIndentifier = false; |
| |
| char[] src = this.source; |
| identLoop: { |
| int pos; |
| int srcLength = this.eofPosition; |
| while (true) { |
| if ((pos = this.currentPosition) >= srcLength) // handle the obvious case upfront |
| break identLoop; |
| char c = src[pos]; |
| if (c < ScannerHelper.MAX_OBVIOUS) { |
| if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & |
| (ScannerHelper.C_UPPER_LETTER | ScannerHelper.C_LOWER_LETTER | ScannerHelper.C_IDENT_PART | ScannerHelper.C_DIGIT)) != 0) { |
| if (this.withoutUnicodePtr != 0) { |
| this.currentCharacter = c; |
| unicodeStore(); |
| } |
| this.currentPosition++; |
| } else if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & (ScannerHelper.C_SEPARATOR | ScannerHelper.C_JLS_SPACE)) != 0) { |
| this.currentCharacter = c; |
| break identLoop; |
| } else { |
| //System.out.println("slow<=128: "+ c); |
| while (getNextCharAsJavaIdentifierPart()){/*empty*/} |
| break identLoop; |
| } |
| } else { |
| //System.out.println("slow>>128: "+ c); |
| while (getNextCharAsJavaIdentifierPart()){/*empty*/} |
| break identLoop; |
| } |
| } |
| } |
| |
| int index, length; |
| char[] data; |
| if (this.withoutUnicodePtr == 0) { |
| //quick test on length == 1 but not on length > 12 while most identifier |
| //have a length which is <= 12...but there are lots of identifier with |
| //only one char.... |
| if ((length = this.currentPosition - this.startPosition) == 1) { |
| return TokenNameIdentifier; |
| } |
| data = this.source; |
| index = this.startPosition; |
| } else { |
| if ((length = this.withoutUnicodePtr) == 1) |
| return TokenNameIdentifier; |
| data = this.withoutUnicodeBuffer; |
| index = 1; |
| } |
| |
| return internalScanIdentifierOrKeyword(index, length, data); |
| } |
| private int internalScanIdentifierOrKeyword(int index, int length, char[] data) { |
| switch (data[index]) { |
| case 'a' : |
| switch(length) { |
| case 8: //abstract |
| if ((data[++index] == 'b') |
| && (data[++index] == 's') |
| && (data[++index] == 't') |
| && (data[++index] == 'r') |
| && (data[++index] == 'a') |
| && (data[++index] == 'c') |
| && (data[++index] == 't')) { |
| return TokenNameabstract; |
| } else { |
| return TokenNameIdentifier; |
| } |
| case 6: // assert |
| if ((data[++index] == 's') |
| && (data[++index] == 's') |
| && (data[++index] == 'e') |
| && (data[++index] == 'r') |
| && (data[++index] == 't')) { |
| if (this.sourceLevel >= ClassFileConstants.JDK1_4) { |
| this.containsAssertKeyword = true; |
| return TokenNameassert; |
| } else { |
| this.useAssertAsAnIndentifier = true; |
| return TokenNameIdentifier; |
| } |
| } else { |
| return TokenNameIdentifier; |
| } |
| default: |
| return TokenNameIdentifier; |
| } |
| case 'b' : //boolean break byte |
| switch (length) { |
| case 4 : |
| if ((data[++index] == 'y') && (data[++index] == 't') && (data[++index] == 'e')) |
| return TokenNamebyte; |
| else |
| return TokenNameIdentifier; |
| case 5 : |
| if ((data[++index] == 'r') |
| && (data[++index] == 'e') |
| && (data[++index] == 'a') |
| && (data[++index] == 'k')) |
| return TokenNamebreak; |
| else |
| return TokenNameIdentifier; |
| case 7 : |
| if ((data[++index] == 'o') |
| && (data[++index] == 'o') |
| && (data[++index] == 'l') |
| && (data[++index] == 'e') |
| && (data[++index] == 'a') |
| && (data[++index] == 'n')) |
| return TokenNameboolean; |
| else |
| return TokenNameIdentifier; |
| default : |
| return TokenNameIdentifier; |
| } |
| |
| case 'c' : //case char catch const class continue |
| switch (length) { |
| case 4 : |
| if (data[++index] == 'a') |
| if ((data[++index] == 's') && (data[++index] == 'e')) |
| return TokenNamecase; |
| else |
| return TokenNameIdentifier; |
| else |
| if ((data[index] == 'h') && (data[++index] == 'a') && (data[++index] == 'r')) |
| return TokenNamechar; |
| else |
| return TokenNameIdentifier; |
| case 5 : |
| if (data[++index] == 'a') |
| if ((data[++index] == 't') && (data[++index] == 'c') && (data[++index] == 'h')) |
| return TokenNamecatch; |
| else |
| return TokenNameIdentifier; |
| else |
| if (data[index] == 'l') |
| if ((data[++index] == 'a') |
| && (data[++index] == 's') |
| && (data[++index] == 's')) |
| return TokenNameclass; |
| else |
| return TokenNameIdentifier; |
| else if ((data[index] == 'o') |
| && (data[++index] == 'n') |
| && (data[++index] == 's') |
| && (data[++index] == 't')) |
| return TokenNameconst; //const is not used in java ??????? |
| else |
| return TokenNameIdentifier; |
| case 8 : |
| if ((data[++index] == 'o') |
| && (data[++index] == 'n') |
| && (data[++index] == 't') |
| && (data[++index] == 'i') |
| && (data[++index] == 'n') |
| && (data[++index] == 'u') |
| && (data[++index] == 'e')) |
| return TokenNamecontinue; |
| else |
| return TokenNameIdentifier; |
| default : |
| return TokenNameIdentifier; |
| } |
| |
| case 'd' : //default do double |
| switch (length) { |
| case 2 : |
| if ((data[++index] == 'o')) |
| return TokenNamedo; |
| else |
| return TokenNameIdentifier; |
| case 6 : |
| if ((data[++index] == 'o') |
| && (data[++index] == 'u') |
| && (data[++index] == 'b') |
| && (data[++index] == 'l') |
| && (data[++index] == 'e')) |
| return TokenNamedouble; |
| else |
| return TokenNameIdentifier; |
| case 7 : |
| if ((data[++index] == 'e') |
| && (data[++index] == 'f') |
| && (data[++index] == 'a') |
| && (data[++index] == 'u') |
| && (data[++index] == 'l') |
| && (data[++index] == 't')) |
| return TokenNamedefault; |
| else |
| return TokenNameIdentifier; |
| default : |
| return TokenNameIdentifier; |
| } |
| case 'e' : //else extends exports |
| switch (length) { |
| case 4 : |
| if (data[++index] == 'l') { |
| if ((data[++index] == 's') && (data[++index] == 'e')) { |
| return TokenNameelse; |
| } else { |
| return TokenNameIdentifier; |
| } |
| } else if ((data[index] == 'n') |
| && (data[++index] == 'u') |
| && (data[++index] == 'm')) { |
| if (this.sourceLevel >= ClassFileConstants.JDK1_5) { |
| return TokenNameenum; |
| } else { |
| this.useEnumAsAnIndentifier = true; |
| return TokenNameIdentifier; |
| } |
| } |
| return TokenNameIdentifier; |
| case 7 : |
| if ((data[++index] == 'x')) { |
| if ((data[++index] == 't') && (data[++index] == 'e') && (data[++index] == 'n') |
| && (data[++index] == 'd') && (data[++index] == 's')) { |
| return TokenNameextends; |
| } else if (areRestrictedModuleKeywordsActive() |
| && (data[index] == 'p') && (data[++index] == 'o') && (data[++index] == 'r') |
| && (data[++index] == 't') && (data[++index] == 's')) { |
| return TokenNameexports; |
| } else |
| return TokenNameIdentifier; |
| } else |
| return TokenNameIdentifier; |
| default : |
| return TokenNameIdentifier; |
| } |
| |
| case 'f' : //final finally float for false |
| switch (length) { |
| case 3 : |
| if ((data[++index] == 'o') && (data[++index] == 'r')) |
| return TokenNamefor; |
| else |
| return TokenNameIdentifier; |
| case 5 : |
| if (data[++index] == 'i') |
| if ((data[++index] == 'n') |
| && (data[++index] == 'a') |
| && (data[++index] == 'l')) { |
| return TokenNamefinal; |
| } else |
| return TokenNameIdentifier; |
| else |
| if (data[index] == 'l') |
| if ((data[++index] == 'o') |
| && (data[++index] == 'a') |
| && (data[++index] == 't')) |
| return TokenNamefloat; |
| else |
| return TokenNameIdentifier; |
| else |
| if ((data[index] == 'a') |
| && (data[++index] == 'l') |
| && (data[++index] == 's') |
| && (data[++index] == 'e')) |
| return TokenNamefalse; |
| else |
| return TokenNameIdentifier; |
| case 7 : |
| if ((data[++index] == 'i') |
| && (data[++index] == 'n') |
| && (data[++index] == 'a') |
| && (data[++index] == 'l') |
| && (data[++index] == 'l') |
| && (data[++index] == 'y')) |
| return TokenNamefinally; |
| else |
| return TokenNameIdentifier; |
| |
| default : |
| return TokenNameIdentifier; |
| } |
| case 'g' : //goto |
| if (length == 4) { |
| if ((data[++index] == 'o') |
| && (data[++index] == 't') |
| && (data[++index] == 'o')) { |
| return TokenNamegoto; |
| } |
| } //no goto in java are allowed, so why java removes this keyword ??? |
| return TokenNameIdentifier; |
| |
| case 'i' : //if implements import instanceof int interface |
| switch (length) { |
| case 2 : |
| if (data[++index] == 'f') |
| return TokenNameif; |
| else |
| return TokenNameIdentifier; |
| case 3 : |
| if ((data[++index] == 'n') && (data[++index] == 't')) |
| return TokenNameint; |
| else |
| return TokenNameIdentifier; |
| case 6 : |
| if ((data[++index] == 'm') |
| && (data[++index] == 'p') |
| && (data[++index] == 'o') |
| && (data[++index] == 'r') |
| && (data[++index] == 't')) |
| return TokenNameimport; |
| else |
| return TokenNameIdentifier; |
| case 9 : |
| if ((data[++index] == 'n') |
| && (data[++index] == 't') |
| && (data[++index] == 'e') |
| && (data[++index] == 'r') |
| && (data[++index] == 'f') |
| && (data[++index] == 'a') |
| && (data[++index] == 'c') |
| && (data[++index] == 'e')) |
| return TokenNameinterface; |
| else |
| return TokenNameIdentifier; |
| case 10 : |
| if (data[++index] == 'm') |
| if ((data[++index] == 'p') |
| && (data[++index] == 'l') |
| && (data[++index] == 'e') |
| && (data[++index] == 'm') |
| && (data[++index] == 'e') |
| && (data[++index] == 'n') |
| && (data[++index] == 't') |
| && (data[++index] == 's')) |
| return TokenNameimplements; |
| else |
| return TokenNameIdentifier; |
| else |
| if ((data[index] == 'n') |
| && (data[++index] == 's') |
| && (data[++index] == 't') |
| && (data[++index] == 'a') |
| && (data[++index] == 'n') |
| && (data[++index] == 'c') |
| && (data[++index] == 'e') |
| && (data[++index] == 'o') |
| && (data[++index] == 'f')) |
| return TokenNameinstanceof; |
| else |
| return TokenNameIdentifier; |
| |
| default : |
| return TokenNameIdentifier; |
| } |
| |
| case 'l' : //long |
| if (length == 4) { |
| if ((data[++index] == 'o') |
| && (data[++index] == 'n') |
| && (data[++index] == 'g')) { |
| return TokenNamelong; |
| } |
| } |
| return TokenNameIdentifier; |
| |
| case 'm': //module |
| switch (length) { |
| case 6 : |
| if (areRestrictedModuleKeywordsActive() |
| && (data[++index] == 'o') |
| && (data[++index] == 'd') |
| && (data[++index] == 'u') |
| && (data[++index] == 'l') |
| && (data[++index] == 'e')) |
| return TokenNamemodule; |
| else |
| return TokenNameIdentifier; |
| default : |
| return TokenNameIdentifier; |
| } |
| |
| case 'n' : //native new null |
| switch (length) { |
| case 3 : |
| if ((data[++index] == 'e') && (data[++index] == 'w')) |
| return TokenNamenew; |
| else { |
| int token = checkFor_KeyWord(index - 1, length, data); |
| return token != TokenNameNotAToken ? token : TokenNameIdentifier; |
| } |
| case 4 : |
| if ((data[++index] == 'u') && (data[++index] == 'l') && (data[++index] == 'l')) |
| return TokenNamenull; |
| else |
| return TokenNameIdentifier; |
| case 6 : |
| if ((data[++index] == 'a') |
| && (data[++index] == 't') |
| && (data[++index] == 'i') |
| && (data[++index] == 'v') |
| && (data[++index] == 'e')) { |
| return TokenNamenative; |
| } else |
| return TokenNameIdentifier; |
| default : |
| return TokenNameIdentifier; |
| } |
| |
| case 'o': |
| switch (length) { |
| case 4 : |
| if (areRestrictedModuleKeywordsActive() && (data[++index] == 'p') && (data[++index] == 'e') && (data[++index] == 'n')) |
| return TokenNameopen; |
| else |
| return TokenNameIdentifier; |
| case 5 : |
| if (areRestrictedModuleKeywordsActive() |
| && (data[++index] == 'p') |
| && (data[++index] == 'e') |
| && (data[++index] == 'n') |
| && (data[++index] == 's')) |
| return TokenNameopens; |
| else |
| return TokenNameIdentifier; |
| default : |
| return TokenNameIdentifier; |
| } |
| case 'p' : //package private protected public provides |
| switch (length) { |
| case 6 : |
| if ((data[++index] == 'u') |
| && (data[++index] == 'b') |
| && (data[++index] == 'l') |
| && (data[++index] == 'i') |
| && (data[++index] == 'c')) { |
| return TokenNamepublic; |
| } else |
| return TokenNameIdentifier; |
| case 7 : |
| if (data[++index] == 'a') { |
| if ((data[++index] == 'c') |
| && (data[++index] == 'k') |
| && (data[++index] == 'a') |
| && (data[++index] == 'g') |
| && (data[++index] == 'e')) |
| return TokenNamepackage; |
| else |
| return TokenNameIdentifier; |
| } else { |
| if ((data[index] == 'r') |
| && (data[++index] == 'i') |
| && (data[++index] == 'v') |
| && (data[++index] == 'a') |
| && (data[++index] == 't') |
| && (data[++index] == 'e')) { |
| return TokenNameprivate; |
| } else if ((data[index] == 'e') |
| && (data[++index] == 'r') |
| && (data[++index] == 'm') |
| && (data[++index] == 'i') |
| && (data[++index] == 't') |
| && (data[++index] == 's')) { |
| return disambiguatedRestrictedIdentifierpermits(TokenNameRestrictedIdentifierpermits); |
| } else |
| return TokenNameIdentifier; |
| } |
| case 8 : |
| if (areRestrictedModuleKeywordsActive() |
| && (data[++index] == 'r') |
| && (data[++index] == 'o') |
| && (data[++index] == 'v') |
| && (data[++index] == 'i') |
| && (data[++index] == 'd') |
| && (data[++index] == 'e') |
| && (data[++index] == 's')) { |
| return TokenNameprovides; |
| } else |
| return TokenNameIdentifier; |
| case 9 : |
| if ((data[++index] == 'r') |
| && (data[++index] == 'o') |
| && (data[++index] == 't') |
| && (data[++index] == 'e') |
| && (data[++index] == 'c') |
| && (data[++index] == 't') |
| && (data[++index] == 'e') |
| && (data[++index] == 'd')) { |
| return TokenNameprotected; |
| } else |
| return TokenNameIdentifier; |
| |
| default : |
| return TokenNameIdentifier; |
| } |
| |
| case 'r' : //return requires |
| switch (length) { |
| case 6: |
| if (data[++index] == 'e') { |
| if ((data[++index] == 't') |
| && (data[++index] == 'u') |
| && (data[++index] == 'r') |
| && (data[++index] == 'n')) |
| return TokenNamereturn; |
| else if ((data[index] == 'c') |
| && (data[++index] == 'o') |
| && (data[++index] == 'r') |
| && (data[++index] == 'd')) |
| return disambiguatedRestrictedIdentifierrecord(TokenNameRestrictedIdentifierrecord); |
| } |
| return TokenNameIdentifier; |
| case 8: |
| if (areRestrictedModuleKeywordsActive() |
| && (data[++index] == 'e') |
| && (data[++index] == 'q') |
| && (data[++index] == 'u') |
| && (data[++index] == 'i') |
| && (data[++index] == 'r') |
| && (data[++index] == 'e') |
| && (data[++index] == 's')) { |
| return TokenNamerequires; |
| } else |
| return TokenNameIdentifier; |
| } |
| return TokenNameIdentifier; |
| |
| case 's' : //short static super switch synchronized strictfp |
| switch (length) { |
| case 5 : |
| if (data[++index] == 'h') |
| if ((data[++index] == 'o') && (data[++index] == 'r') && (data[++index] == 't')) |
| return TokenNameshort; |
| else |
| return TokenNameIdentifier; |
| else |
| if ((data[index] == 'u') |
| && (data[++index] == 'p') |
| && (data[++index] == 'e') |
| && (data[++index] == 'r')) |
| return TokenNamesuper; |
| else |
| return TokenNameIdentifier; |
| |
| case 6 : |
| if (data[++index] == 't') |
| if ((data[++index] == 'a') |
| && (data[++index] == 't') |
| && (data[++index] == 'i') |
| && (data[++index] == 'c')) { |
| return TokenNamestatic; |
| } else |
| return TokenNameIdentifier; |
| else |
| if ((data[index] == 'w') |
| && (data[++index] == 'i') |
| && (data[++index] == 't') |
| && (data[++index] == 'c') |
| && (data[++index] == 'h')) { |
| return TokenNameswitch; |
| } else if ((data[index] == 'e') |
| && (data[++index] == 'a') |
| && (data[++index] == 'l') |
| && (data[++index] == 'e') |
| && (data[++index] == 'd')) { |
| return disambiguatedRestrictedIdentifiersealed(TokenNameRestrictedIdentifiersealed); |
| } else |
| return TokenNameIdentifier; |
| case 8 : |
| if ((data[++index] == 't') |
| && (data[++index] == 'r') |
| && (data[++index] == 'i') |
| && (data[++index] == 'c') |
| && (data[++index] == 't') |
| && (data[++index] == 'f') |
| && (data[++index] == 'p')) |
| return TokenNamestrictfp; |
| else |
| return TokenNameIdentifier; |
| case 12 : |
| if ((data[++index] == 'y') |
| && (data[++index] == 'n') |
| && (data[++index] == 'c') |
| && (data[++index] == 'h') |
| && (data[++index] == 'r') |
| && (data[++index] == 'o') |
| && (data[++index] == 'n') |
| && (data[++index] == 'i') |
| && (data[++index] == 'z') |
| && (data[++index] == 'e') |
| && (data[++index] == 'd')) { |
| return TokenNamesynchronized; |
| } else |
| return TokenNameIdentifier; |
| default : |
| return TokenNameIdentifier; |
| } |
| |
| case 't' : //try throw throws transient this true |
| switch (length) { |
| case 2: |
| if (areRestrictedModuleKeywordsActive() && data[++index] == 'o') |
| return TokenNameto; |
| else |
| return TokenNameIdentifier; |
| case 3 : |
| if ((data[++index] == 'r') && (data[++index] == 'y')) |
| return TokenNametry; |
| else |
| return TokenNameIdentifier; |
| case 4 : |
| if (data[++index] == 'h') |
| if ((data[++index] == 'i') && (data[++index] == 's')) |
| return TokenNamethis; |
| else |
| return TokenNameIdentifier; |
| else |
| if ((data[index] == 'r') && (data[++index] == 'u') && (data[++index] == 'e')) |
| return TokenNametrue; |
| else |
| return TokenNameIdentifier; |
| case 5 : |
| if ((data[++index] == 'h') |
| && (data[++index] == 'r') |
| && (data[++index] == 'o') |
| && (data[++index] == 'w')) |
| return TokenNamethrow; |
| else |
| return TokenNameIdentifier; |
| case 6 : |
| if ((data[++index] == 'h') |
| && (data[++index] == 'r') |
| && (data[++index] == 'o') |
| && (data[++index] == 'w') |
| && (data[++index] == 's')) |
| return TokenNamethrows; |
| else |
| return TokenNameIdentifier; |
| case 9 : |
| if ((data[++index] == 'r') |
| && (data[++index] == 'a') |
| && (data[++index] == 'n') |
| && (data[++index] == 's') |
| && (data[++index] == 'i') |
| && (data[++index] == 'e') |
| && (data[++index] == 'n') |
| && (data[++index] == 't')) { |
| return TokenNametransient; |
| } else |
| return TokenNameIdentifier; |
| case 10: |
| if (areRestrictedModuleKeywordsActive() && (data[++index] == 'r') |
| && (data[++index] == 'a') |
| && (data[++index] == 'n') |
| && (data[++index] == 's') |
| && (data[++index] == 'i') |
| && (data[++index] == 't') |
| && (data[++index] == 'i') |
| && (data[++index] == 'v') |
| && (data[++index] == 'e')) { |
| return TokenNametransitive; |
| } else |
| return TokenNameIdentifier; |
| default : |
| return TokenNameIdentifier; |
| } |
| case 'u' : //uses |
| switch(length) { |
| case 4 : |
| if (areRestrictedModuleKeywordsActive() |
| && (data[++index] == 's') && (data[++index] == 'e') && (data[++index] == 's')) |
| return TokenNameuses; |
| else |
| return TokenNameIdentifier; |
| default : |
| return TokenNameIdentifier; |
| } |
| case 'v' : //void volatile |
| switch (length) { |
| case 4 : |
| if ((data[++index] == 'o') && (data[++index] == 'i') && (data[++index] == 'd')) |
| return TokenNamevoid; |
| else |
| return TokenNameIdentifier; |
| case 8 : |
| if ((data[++index] == 'o') |
| && (data[++index] == 'l') |
| && (data[++index] == 'a') |
| && (data[++index] == 't') |
| && (data[++index] == 'i') |
| && (data[++index] == 'l') |
| && (data[++index] == 'e')) { |
| return TokenNamevolatile; |
| } else |
| return TokenNameIdentifier; |
| |
| default : |
| return TokenNameIdentifier; |
| } |
| |
| case 'w' : //while widefp with |
| switch (length) { |
| case 4: |
| if (areRestrictedModuleKeywordsActive() |
| && (data[++index] == 'i') |
| && (data[++index] == 't') |
| && (data[++index] == 'h')) |
| return TokenNamewith; |
| else |
| return TokenNameIdentifier; |
| case 5 : |
| if ((data[++index] == 'h') |
| && (data[++index] == 'i') |
| && (data[++index] == 'l') |
| && (data[++index] == 'e')) |
| return TokenNamewhile; |
| else |
| return TokenNameIdentifier; |
| //case 6:if ( (data[++index] =='i') && (data[++index]=='d') && (data[++index]=='e') && (data[++index]=='f')&& (data[++index]=='p')) |
| //return TokenNamewidefp ; |
| //else |
| //return TokenNameIdentifier; |
| default : |
| return TokenNameIdentifier; |
| } |
| |
| case 'y' : |
| switch (length) { |
| case 5 : |
| if ((data[++index] == 'i') |
| && (data[++index] == 'e') |
| && (data[++index] == 'l') |
| && (data[++index] == 'd')) |
| return disambiguatedRestrictedIdentifierYield(TokenNameRestrictedIdentifierYield); |
| //$FALL-THROUGH$ |
| default : |
| return TokenNameIdentifier; |
| } |
| |
| default : |
| return TokenNameIdentifier; |
| } |
| } |
| |
| |
| private int checkFor_KeyWord(int index, int length, char[] data) { |
| if (this._Keywords == null) { |
| this._Keywords = new HashMap<>(0); |
| if (JavaFeature.RECORDS.isSupported(this.complianceLevel, this.previewEnabled)) { |
| this._Keywords.put("non-sealed", TerminalTokens.TokenNamenon_sealed); //$NON-NLS-1$ |
| } |
| } |
| for (String key : this._Keywords.keySet()) { |
| if (CharOperation.prefixEquals(key.toCharArray(), data, true /* isCaseSensitive */, index)) { |
| this.currentPosition = this.currentPosition - length + key.length(); |
| if (this.currentPosition < this.eofPosition) |
| this.currentCharacter = data[this.currentPosition]; |
| return this._Keywords.get(key); |
| } |
| } |
| return TokenNameNotAToken; |
| } |
| |
| public int scanNumber(boolean dotPrefix) throws InvalidInputException { |
| |
| //when entering this method the currentCharacter is the first |
| //digit of the number. It may be preceeded by a '.' when |
| //dotPrefix is true |
| |
| boolean floating = dotPrefix; |
| if (!dotPrefix && (this.currentCharacter == '0')) { |
| if (getNextChar('x', 'X') >= 0) { //----------hexa----------------- |
| int start = this.currentPosition; |
| consumeDigits(16, true); |
| int end = this.currentPosition; |
| if (getNextChar('l', 'L') >= 0) { |
| if (end == start) { |
| throw new InvalidInputException(INVALID_HEXA); |
| } |
| return TokenNameLongLiteral; |
| } else if (getNextChar('.')) { |
| // hexadecimal floating point literal |
| // read decimal part |
| boolean hasNoDigitsBeforeDot = end == start; |
| start = this.currentPosition; |
| consumeDigits(16, true); |
| end = this.currentPosition; |
| if (hasNoDigitsBeforeDot && end == start) { |
| if (this.sourceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(ILLEGAL_HEXA_LITERAL); |
| } |
| throw new InvalidInputException(INVALID_HEXA); |
| } |
| |
| if (getNextChar('p', 'P') >= 0) { // consume next character |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } else { |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| |
| if ((this.currentCharacter == '-') |
| || (this.currentCharacter == '+')) { // consume next character |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } else { |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| } |
| if (!ScannerHelper.isDigit(this.currentCharacter)) { |
| if (this.sourceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(ILLEGAL_HEXA_LITERAL); |
| } |
| if (this.currentCharacter == '_') { |
| // wrongly place '_' |
| consumeDigits(10); |
| throw new InvalidInputException(INVALID_UNDERSCORE); |
| } |
| throw new InvalidInputException(INVALID_HEXA); |
| } |
| consumeDigits(10); |
| if (getNextChar('f', 'F') >= 0) { |
| if (this.sourceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(ILLEGAL_HEXA_LITERAL); |
| } |
| return TokenNameFloatingPointLiteral; |
| } |
| if (getNextChar('d', 'D') >= 0) { |
| if (this.sourceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(ILLEGAL_HEXA_LITERAL); |
| } |
| return TokenNameDoubleLiteral; |
| } |
| if (getNextChar('l', 'L') >= 0) { |
| if (this.sourceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(ILLEGAL_HEXA_LITERAL); |
| } |
| throw new InvalidInputException(INVALID_HEXA); |
| } |
| if (this.sourceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(ILLEGAL_HEXA_LITERAL); |
| } |
| return TokenNameDoubleLiteral; |
| } else { |
| if (this.sourceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(ILLEGAL_HEXA_LITERAL); |
| } |
| throw new InvalidInputException(INVALID_HEXA); |
| } |
| } else if (getNextChar('p', 'P') >= 0) { // consume next character |
| if (end == start) { // Has no digits before exponent |
| if (this.sourceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(ILLEGAL_HEXA_LITERAL); |
| } |
| throw new InvalidInputException(INVALID_HEXA); |
| } |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } else { |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| |
| if ((this.currentCharacter == '-') |
| || (this.currentCharacter == '+')) { // consume next character |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } else { |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| } |
| if (!ScannerHelper.isDigit(this.currentCharacter)) { |
| if (this.sourceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(ILLEGAL_HEXA_LITERAL); |
| } |
| if (this.currentCharacter == '_') { |
| // wrongly place '_' |
| consumeDigits(10); |
| throw new InvalidInputException(INVALID_UNDERSCORE); |
| } |
| throw new InvalidInputException(INVALID_FLOAT); |
| } |
| consumeDigits(10); |
| if (getNextChar('f', 'F') >= 0) { |
| if (this.sourceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(ILLEGAL_HEXA_LITERAL); |
| } |
| return TokenNameFloatingPointLiteral; |
| } |
| if (getNextChar('d', 'D') >= 0) { |
| if (this.sourceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(ILLEGAL_HEXA_LITERAL); |
| } |
| return TokenNameDoubleLiteral; |
| } |
| if (getNextChar('l', 'L') >= 0) { |
| if (this.sourceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(ILLEGAL_HEXA_LITERAL); |
| } |
| throw new InvalidInputException(INVALID_HEXA); |
| } |
| if (this.sourceLevel < ClassFileConstants.JDK1_5) { |
| throw new InvalidInputException(ILLEGAL_HEXA_LITERAL); |
| } |
| return TokenNameDoubleLiteral; |
| } else { |
| if (end == start) |
| throw new InvalidInputException(INVALID_HEXA); |
| return TokenNameIntegerLiteral; |
| } |
| } else if (getNextChar('b', 'B') >= 0) { //----------binary----------------- |
| int start = this.currentPosition; |
| consumeDigits(2, true); |
| int end = this.currentPosition; |
| if (end == start) { |
| if (this.sourceLevel < ClassFileConstants.JDK1_7) { |
| throw new InvalidInputException(BINARY_LITERAL_NOT_BELOW_17); |
| } |
| throw new InvalidInputException(INVALID_BINARY); |
| } |
| if (getNextChar('l', 'L') >= 0) { |
| if (this.sourceLevel < ClassFileConstants.JDK1_7) { |
| throw new InvalidInputException(BINARY_LITERAL_NOT_BELOW_17); |
| } |
| return TokenNameLongLiteral; |
| } |
| if (this.sourceLevel < ClassFileConstants.JDK1_7) { |
| throw new InvalidInputException(BINARY_LITERAL_NOT_BELOW_17); |
| } |
| return TokenNameIntegerLiteral; |
| } |
| |
| //there is no x or X nor b or B in the number |
| //potential octal |
| if (getNextCharAsDigit()) { //-------------potential octal----------------- |
| consumeDigits(10); |
| |
| if (getNextChar('l', 'L') >= 0) { |
| return TokenNameLongLiteral; |
| } |
| |
| if (getNextChar('f', 'F') >= 0) { |
| return TokenNameFloatingPointLiteral; |
| } |
| |
| if (getNextChar('d', 'D') >= 0) { |
| return TokenNameDoubleLiteral; |
| } else { //make the distinction between octal and float .... |
| boolean isInteger = true; |
| if (getNextChar('.')) { |
| isInteger = false; |
| consumeDigits(10); |
| } |
| if (getNextChar('e', 'E') >= 0) { // consume next character |
| isInteger = false; |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } else { |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| |
| if ((this.currentCharacter == '-') |
| || (this.currentCharacter == '+')) { // consume next character |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } else { |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| } |
| if (!ScannerHelper.isDigit(this.currentCharacter)) { |
| if (this.currentCharacter == '_') { |
| // wrongly place '_' |
| consumeDigits(10); |
| throw new InvalidInputException(INVALID_UNDERSCORE); |
| } |
| throw new InvalidInputException(INVALID_FLOAT); |
| } |
| consumeDigits(10); |
| } |
| if (getNextChar('f', 'F') >= 0) |
| return TokenNameFloatingPointLiteral; |
| if (getNextChar('d', 'D') >= 0 || !isInteger) |
| return TokenNameDoubleLiteral; |
| return TokenNameIntegerLiteral; |
| } |
| } else { |
| /* carry on */ |
| } |
| } |
| |
| consumeDigits(10); |
| |
| if ((!dotPrefix) && (getNextChar('l', 'L') >= 0)) |
| return TokenNameLongLiteral; |
| |
| if ((!dotPrefix) && (getNextChar('.'))) { //decimal part that can be empty |
| consumeDigits(10, true); |
| floating = true; |
| } |
| |
| //if floating is true both exponant and suffix may be optional |
| |
| if (getNextChar('e', 'E') >= 0) { |
| floating = true; |
| // consume next character |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } else { |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| |
| if ((this.currentCharacter == '-') |
| || (this.currentCharacter == '+')) { // consume next character |
| this.unicodeAsBackSlash = false; |
| if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\') |
| && (this.source[this.currentPosition] == 'u')) { |
| getNextUnicodeChar(); |
| } else { |
| if (this.withoutUnicodePtr != 0) { |
| unicodeStore(); |
| } |
| } |
| } |
| if (!ScannerHelper.isDigit(this.currentCharacter)) { |
| if (this.currentCharacter == '_') { |
| // wrongly place '_' |
| consumeDigits(10); |
| throw new InvalidInputException(INVALID_UNDERSCORE); |
| } |
| throw new InvalidInputException(INVALID_FLOAT); |
| } |
| // current character is a digit so we expect no digit first (the next character could be an underscore) |
| consumeDigits(10); |
| } |
| |
| if (getNextChar('d', 'D') >= 0) |
| return TokenNameDoubleLiteral; |
| if (getNextChar('f', 'F') >= 0) |
| return TokenNameFloatingPointLiteral; |
| |
| //the long flag has been tested before |
| |
| return floating ? TokenNameDoubleLiteral : TokenNameIntegerLiteral; |
| } |
| |
| /** |
| * Search the line number corresponding to a specific position |
| * @param position int |
| * @return int |
| */ |
| public final int getLineNumber(int position) { |
| return Util.getLineNumber(position, this.lineEnds, 0, this.linePtr); |
| } |
| public final void setSource(char[] sourceString){ |
| //the source-buffer is set to sourceString |
| |
| int sourceLength; |
| if (sourceString == null) { |
| this.source = CharOperation.NO_CHAR; |
| sourceLength = 0; |
| } else { |
| this.source = sourceString; |
| sourceLength = sourceString.length; |
| } |
| this.startPosition = -1; |
| this.eofPosition = sourceLength; |
| this.initialPosition = this.currentPosition = 0; |
| this.containsAssertKeyword = false; |
| this.linePtr = -1; |
| this.scanContext = null; |
| this.yieldColons = -1; |
| this.insideModuleInfo = false; |
| } |
| /* |
| * Should be used if a parse (usually a diet parse) has already been performed on the unit, |
| * so as to get the already computed line end positions. |
| */ |
| public final void setSource(char[] contents, CompilationResult compilationResult) { |
| if (contents == null) { |
| char[] cuContents = compilationResult.compilationUnit.getContents(); |
| setSource(cuContents); |
| } else { |
| setSource(contents); |
| } |
| int[] lineSeparatorPositions = compilationResult.lineSeparatorPositions; |
| if (lineSeparatorPositions != null) { |
| this.lineEnds = lineSeparatorPositions; |
| this.linePtr = lineSeparatorPositions.length - 1; |
| } |
| } |
| /* |
| * Should be used if a parse (usually a diet parse) has already been performed on the unit, |
| * so as to get the already computed line end positions. |
| */ |
| public final void setSource(CompilationResult compilationResult) { |
| setSource(null, compilationResult); |
| } |
| @Override |
| public String toString() { |
| if (this.startPosition == this.eofPosition) |
| return "EOF\n\n" + new String(this.source); //$NON-NLS-1$ |
| if (this.currentPosition > this.eofPosition) |
| return "behind the EOF\n\n" + new String(this.source); //$NON-NLS-1$ |
| if (this.currentPosition <= 0) |
| return "NOT started!\n\n"+ (this.source != null ? new String(this.source) : ""); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| StringBuilder buffer = new StringBuilder(); |
| if (this.startPosition < 1000) { |
| buffer.append(this.source, 0, this.startPosition); |
| } else { |
| buffer.append("<source beginning>\n...\n"); //$NON-NLS-1$ |
| int line = Util.getLineNumber(this.startPosition-1000, this.lineEnds, 0, this.linePtr); |
| int lineStart = getLineStart(line); |
| buffer.append(this.source, lineStart, this.startPosition-lineStart); |
| } |
| |
| buffer.append("\n===============================\nStarts here -->"); //$NON-NLS-1$ |
| int middleLength = (this.currentPosition - 1) - this.startPosition + 1; |
| if (middleLength > -1) { |
| buffer.append(this.source, this.startPosition, middleLength); |
| } |
| if (this.nextToken != TerminalTokens.TokenNameNotAToken) { |
| buffer.append("<-- Ends here [in pipeline " + toStringAction(this.nextToken) + "]\n===============================\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } else { |
| buffer.append("<-- Ends here\n===============================\n"); //$NON-NLS-1$ |
| } |
| |
| buffer.append(this.source, (this.currentPosition - 1) + 1, this.eofPosition - (this.currentPosition - 1) - 1); |
| |
| return buffer.toString(); |
| } |
| public String toStringAction(int act) { |
| switch (act) { |
| case TokenNameIdentifier : |
| return "Identifier(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ |
| case TokenNameRestrictedIdentifierYield : |
| return "yield"; //$NON-NLS-1$ |
| case TokenNameRestrictedIdentifierrecord : |
| return "record"; //$NON-NLS-1$ |
| case TokenNameabstract : |
| return "abstract"; //$NON-NLS-1$ |
| case TokenNameboolean : |
| return "boolean"; //$NON-NLS-1$ |
| case TokenNamebreak : |
| return "break"; //$NON-NLS-1$ |
| case TokenNamebyte : |
| return "byte"; //$NON-NLS-1$ |
| case TokenNamecase : |
| return "case"; //$NON-NLS-1$ |
| case TokenNamecatch : |
| return "catch"; //$NON-NLS-1$ |
| case TokenNamechar : |
| return "char"; //$NON-NLS-1$ |
| case TokenNameclass : |
| return "class"; //$NON-NLS-1$ |
| case TokenNamecontinue : |
| return "continue"; //$NON-NLS-1$ |
| case TokenNamedefault : |
| return "default"; //$NON-NLS-1$ |
| case TokenNamedo : |
| return "do"; //$NON-NLS-1$ |
| case TokenNamedouble : |
| return "double"; //$NON-NLS-1$ |
| case TokenNameelse : |
| return "else"; //$NON-NLS-1$ |
| case TokenNameextends : |
| return "extends"; //$NON-NLS-1$ |
| case TokenNamefalse : |
| return "false"; //$NON-NLS-1$ |
| case TokenNamefinal : |
| return "final"; //$NON-NLS-1$ |
| case TokenNamefinally : |
| return "finally"; //$NON-NLS-1$ |
| case TokenNamefloat : |
| return "float"; //$NON-NLS-1$ |
| case TokenNamefor : |
| return "for"; //$NON-NLS-1$ |
| case TokenNameif : |
| return "if"; //$NON-NLS-1$ |
| case TokenNameimplements : |
| return "implements"; //$NON-NLS-1$ |
| case TokenNameimport : |
| return "import"; //$NON-NLS-1$ |
| case TokenNameinstanceof : |
| return "instanceof"; //$NON-NLS-1$ |
| case TokenNameint : |
| return "int"; //$NON-NLS-1$ |
| case TokenNameinterface : |
| return "interface"; //$NON-NLS-1$ |
| case TokenNamelong : |
| return "long"; //$NON-NLS-1$ |
| case TokenNamenative : |
| return "native"; //$NON-NLS-1$ |
| case TokenNamenew : |
| return "new"; //$NON-NLS-1$ |
| case TokenNamenon_sealed: |
| return "non-sealed"; //$NON-NLS-1$ |
| case TokenNamenull : |
| return "null"; //$NON-NLS-1$ |
| case TokenNamepackage : |
| return "package"; //$NON-NLS-1$ |
| case TokenNameRestrictedIdentifierpermits: |
| return "permits"; //$NON-NLS-1$ |
| case TokenNameprivate : |
| return "private"; //$NON-NLS-1$ |
| case TokenNameprotected : |
| return "protected"; //$NON-NLS-1$ |
| case TokenNamepublic : |
| return "public"; //$NON-NLS-1$ |
| case TokenNamereturn : |
| return "return"; //$NON-NLS-1$ |
| case TokenNameRestrictedIdentifiersealed: |
| return "sealed"; //$NON-NLS-1$ |
| case TokenNameshort : |
| return "short"; //$NON-NLS-1$ |
| case TokenNamestatic : |
| return "static"; //$NON-NLS-1$ |
| case TokenNamesuper : |
| return "super"; //$NON-NLS-1$ |
| case TokenNameswitch : |
| return "switch"; //$NON-NLS-1$ |
| case TokenNamesynchronized : |
| return "synchronized"; //$NON-NLS-1$ |
| case TokenNamethis : |
| return "this"; //$NON-NLS-1$ |
| case TokenNamethrow : |
| return "throw"; //$NON-NLS-1$ |
| case TokenNamethrows : |
| return "throws"; //$NON-NLS-1$ |
| case TokenNametransient : |
| return "transient"; //$NON-NLS-1$ |
| case TokenNametrue : |
| return "true"; //$NON-NLS-1$ |
| case TokenNametry : |
| return "try"; //$NON-NLS-1$ |
| case TokenNamevoid : |
| return "void"; //$NON-NLS-1$ |
| case TokenNamevolatile : |
| return "volatile"; //$NON-NLS-1$ |
| case TokenNamewhile : |
| return "while"; //$NON-NLS-1$ |
| case TokenNamemodule : |
| return "module"; //$NON-NLS-1$ |
| case TokenNamerequires : |
| return "requires"; //$NON-NLS-1$ |
| case TokenNameexports : |
| return "exports"; //$NON-NLS-1$ |
| |
| case TokenNameIntegerLiteral : |
| return "Integer(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ |
| case TokenNameLongLiteral : |
| return "Long(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ |
| case TokenNameFloatingPointLiteral : |
| return "Float(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ |
| case TokenNameDoubleLiteral : |
| return "Double(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ |
| case TokenNameCharacterLiteral : |
| return "Char(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ |
| case TokenNameStringLiteral : |
| return "String(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ |
| case TokenNameTextBlock : |
| return "String(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ |
| case TokenNamePLUS_PLUS : |
| return "++"; //$NON-NLS-1$ |
| case TokenNameMINUS_MINUS : |
| return "--"; //$NON-NLS-1$ |
| case TokenNameEQUAL_EQUAL : |
| return "=="; //$NON-NLS-1$ |
| case TokenNameLESS_EQUAL : |
| return "<="; //$NON-NLS-1$ |
| case TokenNameGREATER_EQUAL : |
| return ">="; //$NON-NLS-1$ |
| case TokenNameNOT_EQUAL : |
| return "!="; //$NON-NLS-1$ |
| case TokenNameLEFT_SHIFT : |
| return "<<"; //$NON-NLS-1$ |
| case TokenNameRIGHT_SHIFT : |
| return ">>"; //$NON-NLS-1$ |
| case TokenNameUNSIGNED_RIGHT_SHIFT : |
| return ">>>"; //$NON-NLS-1$ |
| case TokenNamePLUS_EQUAL : |
| return "+="; //$NON-NLS-1$ |
| case TokenNameMINUS_EQUAL : |
| return "-="; //$NON-NLS-1$ |
| case TokenNameARROW : |
| return "->"; //$NON-NLS-1$ |
| case TokenNameMULTIPLY_EQUAL : |
| return "*="; //$NON-NLS-1$ |
| case TokenNameDIVIDE_EQUAL : |
| return "/="; //$NON-NLS-1$ |
| case TokenNameAND_EQUAL : |
| return "&="; //$NON-NLS-1$ |
| case TokenNameOR_EQUAL : |
| return "|="; //$NON-NLS-1$ |
| case TokenNameXOR_EQUAL : |
| return "^="; //$NON-NLS-1$ |
| case TokenNameREMAINDER_EQUAL : |
| return "%="; //$NON-NLS-1$ |
| case TokenNameLEFT_SHIFT_EQUAL : |
| return "<<="; //$NON-NLS-1$ |
| case TokenNameRIGHT_SHIFT_EQUAL : |
| return ">>="; //$NON-NLS-1$ |
| case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : |
| return ">>>="; //$NON-NLS-1$ |
| case TokenNameOR_OR : |
| return "||"; //$NON-NLS-1$ |
| case TokenNameAND_AND : |
| return "&&"; //$NON-NLS-1$ |
| case TokenNamePLUS : |
| return "+"; //$NON-NLS-1$ |
| case TokenNameMINUS : |
| return "-"; //$NON-NLS-1$ |
| case TokenNameNOT : |
| return "!"; //$NON-NLS-1$ |
| case TokenNameREMAINDER : |
| return "%"; //$NON-NLS-1$ |
| case TokenNameXOR : |
| return "^"; //$NON-NLS-1$ |
| case TokenNameAND : |
| return "&"; //$NON-NLS-1$ |
| case TokenNameMULTIPLY : |
| return "*"; //$NON-NLS-1$ |
| case TokenNameOR : |
| return "|"; //$NON-NLS-1$ |
| case TokenNameTWIDDLE : |
| return "~"; //$NON-NLS-1$ |
| case TokenNameDIVIDE : |
| return "/"; //$NON-NLS-1$ |
| case TokenNameGREATER : |
| return ">"; //$NON-NLS-1$ |
| case TokenNameLESS : |
| return "<"; //$NON-NLS-1$ |
| case TokenNameLPAREN : |
| return "("; //$NON-NLS-1$ |
| case TokenNameRPAREN : |
| return ")"; //$NON-NLS-1$ |
| case TokenNameLBRACE : |
| return "{"; //$NON-NLS-1$ |
| case TokenNameRBRACE : |
| return "}"; //$NON-NLS-1$ |
| case TokenNameLBRACKET : |
| return "["; //$NON-NLS-1$ |
| case TokenNameRBRACKET : |
| return "]"; //$NON-NLS-1$ |
| case TokenNameSEMICOLON : |
| return ";"; //$NON-NLS-1$ |
| case TokenNameQUESTION : |
| return "?"; //$NON-NLS-1$ |
| case TokenNameCOLON : |
| return ":"; //$NON-NLS-1$ |
| case TokenNameCOLON_COLON : |
| return "::"; //$NON-NLS-1$ |
| case TokenNameCOMMA : |
| return ","; //$NON-NLS-1$ |
| case TokenNameDOT : |
| return "."; //$NON-NLS-1$ |
| case TokenNameEQUAL : |
| return "="; //$NON-NLS-1$ |
| case TokenNameEOF : |
| return "EOF"; //$NON-NLS-1$ |
| case TokenNameWHITESPACE : |
| return "white_space(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ |
| default : |
| return "not-a-token"; //$NON-NLS-1$ |
| } |
| } |
| public void unicodeInitializeBuffer(int length) { |
| this.withoutUnicodePtr = length; |
| if (this.withoutUnicodeBuffer == null) this.withoutUnicodeBuffer = new char[length+(1+10)]; |
| int bLength = this.withoutUnicodeBuffer.length; |
| if (1+length >= bLength) { |
| System.arraycopy(this.withoutUnicodeBuffer, 0, this.withoutUnicodeBuffer = new char[length + (1+10)], 0, bLength); |
| } |
| System.arraycopy(this.source, this.startPosition, this.withoutUnicodeBuffer, 1, length); |
| } |
| public void unicodeStore() { |
| int pos = ++this.withoutUnicodePtr; |
| if (this.withoutUnicodeBuffer == null) this.withoutUnicodeBuffer = new char[10]; |
| int length = this.withoutUnicodeBuffer.length; |
| if (pos == length) { |
| System.arraycopy(this.withoutUnicodeBuffer, 0, this.withoutUnicodeBuffer = new char[length * 2], 0, length); |
| } |
| this.withoutUnicodeBuffer[pos] = this.currentCharacter; |
| } |
| public void unicodeStore(char character) { |
| int pos = ++this.withoutUnicodePtr; |
| if (this.withoutUnicodeBuffer == null) this.withoutUnicodeBuffer = new char[10]; |
| int length = this.withoutUnicodeBuffer.length; |
| if (pos == length) { |
| System.arraycopy(this.withoutUnicodeBuffer, 0, this.withoutUnicodeBuffer = new char[length * 2], 0, length); |
| } |
| this.withoutUnicodeBuffer[pos] = character; |
| } |
| |
| public static boolean isIdentifier(int token) { |
| return token == TerminalTokens.TokenNameIdentifier; |
| } |
| |
| public static boolean isLiteral(int token) { |
| switch(token) { |
| case TerminalTokens.TokenNameIntegerLiteral: |
| case TerminalTokens.TokenNameLongLiteral: |
| case TerminalTokens.TokenNameFloatingPointLiteral: |
| case TerminalTokens.TokenNameDoubleLiteral: |
| case TerminalTokens.TokenNameStringLiteral: |
| case TerminalTokens.TokenNameTextBlock: |
| case TerminalTokens.TokenNameCharacterLiteral: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| public static boolean isKeyword(int token) { |
| switch(token) { |
| case TerminalTokens.TokenNameabstract: |
| case TerminalTokens.TokenNameassert: |
| case TerminalTokens.TokenNamebyte: |
| case TerminalTokens.TokenNamebreak: |
| case TerminalTokens.TokenNameboolean: |
| case TerminalTokens.TokenNamecase: |
| case TerminalTokens.TokenNamechar: |
| case TerminalTokens.TokenNamecatch: |
| case TerminalTokens.TokenNameclass: |
| case TerminalTokens.TokenNamecontinue: |
| case TerminalTokens.TokenNamedo: |
| case TerminalTokens.TokenNamedouble: |
| case TerminalTokens.TokenNamedefault: |
| case TerminalTokens.TokenNameelse: |
| case TerminalTokens.TokenNameextends: |
| case TerminalTokens.TokenNamefor: |
| case TerminalTokens.TokenNamefinal: |
| case TerminalTokens.TokenNamefloat: |
| case TerminalTokens.TokenNamefalse: |
| case TerminalTokens.TokenNamefinally: |
| case TerminalTokens.TokenNameif: |
| case TerminalTokens.TokenNameint: |
| case TerminalTokens.TokenNameimport: |
| case TerminalTokens.TokenNameinterface: |
| case TerminalTokens.TokenNameimplements: |
| case TerminalTokens.TokenNameinstanceof: |
| case TerminalTokens.TokenNamelong: |
| case TerminalTokens.TokenNamenew: |
| case TerminalTokens.TokenNamenon_sealed: |
| case TerminalTokens.TokenNamenull: |
| case TerminalTokens.TokenNamenative: |
| case TerminalTokens.TokenNamepublic: |
| case TerminalTokens.TokenNamepackage: |
| case TerminalTokens.TokenNameprivate: |
| case TerminalTokens.TokenNameprotected: |
| case TerminalTokens.TokenNamereturn: |
| case TerminalTokens.TokenNameshort: |
| case TerminalTokens.TokenNamesuper: |
| case TerminalTokens.TokenNamestatic: |
| case TerminalTokens.TokenNameswitch: |
| case TerminalTokens.TokenNamestrictfp: |
| case TerminalTokens.TokenNamesynchronized: |
| case TerminalTokens.TokenNametry: |
| case TerminalTokens.TokenNamethis: |
| case TerminalTokens.TokenNametrue: |
| case TerminalTokens.TokenNamethrow: |
| case TerminalTokens.TokenNamethrows: |
| case TerminalTokens.TokenNametransient: |
| case TerminalTokens.TokenNamevoid: |
| case TerminalTokens.TokenNamevolatile: |
| case TerminalTokens.TokenNamewhile: |
| return true; |
| case TerminalTokens.TokenNameRestrictedIdentifierYield: |
| case TerminalTokens.TokenNameRestrictedIdentifierrecord: |
| case TerminalTokens.TokenNameRestrictedIdentifiersealed: |
| case TerminalTokens.TokenNameRestrictedIdentifierpermits: |
| // making explicit - not a (restricted) keyword but restricted identifier. |
| //$FALL-THROUGH$ |
| default: |
| return false; |
| } |
| } |
| |
| // Vanguard Scanner - A Private utility helper class for the scanner. |
| private static final class VanguardScanner extends Scanner { |
| |
| public VanguardScanner(long sourceLevel, long complianceLevel, boolean previewEnabled) { |
| super (false /*comment*/, false /*whitespace*/, false /*nls*/, sourceLevel, complianceLevel, null/*taskTag*/, |
| null/*taskPriorities*/, false /*taskCaseSensitive*/, previewEnabled); |
| } |
| |
| @Override |
| public int getNextToken() throws InvalidInputException { |
| int token; |
| if (this.nextToken != TokenNameNotAToken) { |
| token = this.nextToken; |
| this.nextToken = TokenNameNotAToken; |
| return token; // presumed to be unambiguous. |
| } |
| if (this.scanContext == null) { // init lazily, since isInModuleDeclaration may need the parser to be known |
| this.scanContext = isInModuleDeclaration() ? ScanContext.EXPECTING_KEYWORD : ScanContext.INACTIVE; |
| } |
| token = getNextToken0(); |
| if (areRestrictedModuleKeywordsActive()) { |
| if (isRestrictedKeyword(token)) |
| token = disambiguatedRestrictedKeyword(token); |
| updateScanContext(token); |
| } |
| if (token == TokenNameAT && atTypeAnnotation()) { |
| if (((VanguardParser) this.activeParser).currentGoal == Goal.LambdaParameterListGoal) { |
| token = disambiguatedToken(token); |
| } else { |
| token = TokenNameAT308; |
| } |
| } |
| return token == TokenNameEOF ? TokenNameNotAToken : token; |
| } |
| } |
| |
| private static class Goal { |
| |
| int first; // steer the parser towards a single minded pursuit. |
| int [] follow; // the definite terminal symbols that signal the successful reduction to goal. |
| int[] rules; |
| |
| static int LambdaParameterListRule = 0; |
| static int IntersectionCastRule = 0; |
| static int ReferenceExpressionRule = 0; |
| static int VarargTypeAnnotationsRule = 0; |
| static int BlockStatementoptRule = 0; |
| static int YieldStatementRule = 0; |
| static int SwitchLabelCaseLhsRule = 0; |
| static int[] RestrictedIdentifierSealedRule; |
| static int[] RestrictedIdentifierPermitsRule; |
| |
| static Goal LambdaParameterListGoal; |
| static Goal IntersectionCastGoal; |
| static Goal VarargTypeAnnotationGoal; |
| static Goal ReferenceExpressionGoal; |
| static Goal BlockStatementoptGoal; |
| static Goal YieldStatementGoal; |
| static Goal SwitchLabelCaseLhsGoal; |
| static Goal RestrictedIdentifierSealedGoal; |
| static Goal RestrictedIdentifierPermitsGoal; |
| |
| static int[] RestrictedIdentifierSealedFollow = { TokenNameclass, TokenNameinterface, |
| TokenNameenum, TokenNameRestrictedIdentifierrecord };// Note: enum/record allowed as error flagging rules. |
| static int[] RestrictedIdentifierPermitsFollow = { TokenNameLBRACE }; |
| |
| static { |
| |
| List<Integer> ridSealed = new ArrayList<>(2); |
| List<Integer> ridPermits = new ArrayList<>(); |
| for (int i = 1; i <= ParserBasicInformation.NUM_RULES; i++) { // 0 == $acc |
| if ("ParenthesizedLambdaParameterList".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ |
| LambdaParameterListRule = i; |
| else |
| if ("ParenthesizedCastNameAndBounds".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ |
| IntersectionCastRule = i; |
| else |
| if ("ReferenceExpressionTypeArgumentsAndTrunk".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ |
| ReferenceExpressionRule = i; |
| else |
| if ("TypeAnnotations".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ |
| VarargTypeAnnotationsRule = i; |
| else |
| if ("BlockStatementopt".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ |
| BlockStatementoptRule = i; |
| else |
| if ("YieldStatement".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ |
| YieldStatementRule = i; |
| else |
| if ("Modifiersopt".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ |
| ridSealed.add(i); |
| else |
| if ("PermittedSubclasses".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ |
| ridPermits.add(i); |
| else |
| if ("SwitchLabelCaseLhs".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ |
| SwitchLabelCaseLhsRule = i; |
| |
| } |
| RestrictedIdentifierSealedRule = ridSealed.stream().mapToInt(Integer :: intValue).toArray(); // overkill but future-proof |
| RestrictedIdentifierPermitsRule = ridPermits.stream().mapToInt(Integer :: intValue).toArray(); |
| |
| LambdaParameterListGoal = new Goal(TokenNameARROW, new int[] { TokenNameARROW }, LambdaParameterListRule); |
| IntersectionCastGoal = new Goal(TokenNameLPAREN, followSetOfCast(), IntersectionCastRule); |
| VarargTypeAnnotationGoal = new Goal(TokenNameAT, new int[] { TokenNameELLIPSIS }, VarargTypeAnnotationsRule); |
| ReferenceExpressionGoal = new Goal(TokenNameLESS, new int[] { TokenNameCOLON_COLON }, ReferenceExpressionRule); |
| BlockStatementoptGoal = new Goal(TokenNameLBRACE, new int [0], BlockStatementoptRule); |
| YieldStatementGoal = new Goal(TokenNameARROW, new int [0], YieldStatementRule); |
| SwitchLabelCaseLhsGoal = new Goal(TokenNameARROW, new int [0], SwitchLabelCaseLhsRule); |
| RestrictedIdentifierSealedGoal = new Goal(TokenNameRestrictedIdentifiersealed, RestrictedIdentifierSealedFollow, RestrictedIdentifierSealedRule); |
| RestrictedIdentifierPermitsGoal = new Goal(TokenNameRestrictedIdentifierpermits, RestrictedIdentifierPermitsFollow, RestrictedIdentifierPermitsRule); |
| } |
| |
| |
| Goal(int first, int [] follow, int rule) { |
| this.first = first; |
| this.follow = follow; |
| this.rules = new int[] {rule}; |
| } |
| |
| Goal(int first, int [] follow, int[] rules) { |
| this.first = first; |
| this.follow = follow; |
| this.rules = rules; |
| } |
| |
| boolean hasBeenReached(int act, int token) { |
| /* |
| System.out.println("[Goal = " + Parser.name[Parser.non_terminal_index[Parser.lhs[this.rule]]] + "] " + "Saw: " + Parser.name[Parser.non_terminal_index[Parser.lhs[act]]] + "::" + //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
| Parser.name[Parser.terminal_index[token]]); |
| */ |
| boolean foundRule = false; |
| for (int i : this.rules) { |
| if (act == i) { |
| foundRule = true; |
| break; |
| } |
| } |
| if (foundRule) { |
| final int length = this.follow.length; |
| if (length == 0) |
| return true; |
| for (int i = 0; i < length; i++) |
| if (this.follow[i] == token) |
| return true; |
| } |
| return false; |
| } |
| |
| private static int [] followSetOfCast() { |
| return new int [] { TokenNameIdentifier, TokenNamenew, TokenNamesuper, TokenNamethis, |
| TokenNamefalse, TokenNametrue, TokenNamenull, |
| TokenNameIntegerLiteral, TokenNameLongLiteral, TokenNameFloatingPointLiteral, TokenNameDoubleLiteral, TokenNameCharacterLiteral, TokenNameStringLiteral, TokenNameTextBlock, |
| TokenNameNOT, TokenNameTWIDDLE, TokenNameLPAREN |
| }; |
| } |
| } |
| // Vanguard Parser - A Private utility helper class for the scanner. |
| private static class VanguardParser extends Parser { |
| |
| public static final boolean SUCCESS = true; |
| public static final boolean FAILURE = false; |
| |
| Goal currentGoal; |
| |
| public VanguardParser(VanguardScanner scanner) { |
| this.scanner = scanner; |
| } |
| |
| public VanguardParser(ProblemReporter reporter) { |
| super(reporter, false); |
| } |
| |
| // Canonical LALR pushdown automaton identical to Parser.parse() minus side effects of any kind, returns the rule reduced. |
| protected boolean parse(Goal goal) { |
| this.currentGoal = goal; |
| try { |
| int act = START_STATE; |
| this.stateStackTop = -1; |
| this.currentToken = goal.first; |
| ProcessTerminals : for (;;) { |
| int stackLength = this.stack.length; |
| if (++this.stateStackTop >= stackLength) { |
| System.arraycopy( |
| this.stack, 0, |
| this.stack = new int[stackLength + StackIncrement], 0, |
| stackLength); |
| } |
| this.stack[this.stateStackTop] = act; |
| |
| act = Parser.tAction(act, this.currentToken); |
| if (act == ERROR_ACTION) { |
| return FAILURE; |
| } |
| if (act <= NUM_RULES) { |
| this.stateStackTop--; |
| } else if (act > ERROR_ACTION) { /* shift-reduce */ |
| this.unstackedAct = act; |
| try { |
| this.currentToken = this.scanner.getNextToken(); |
| } finally { |
| this.unstackedAct = ERROR_ACTION; |
| } |
| act -= ERROR_ACTION; |
| } else { |
| if (act < ACCEPT_ACTION) { /* shift */ |
| this.unstackedAct = act; |
| try { |
| this.currentToken = this.scanner.getNextToken(); |
| } finally { |
| this.unstackedAct = ERROR_ACTION; |
| } |
| continue ProcessTerminals; |
| } |
| return FAILURE; // accept - we should never reach this state, we accept at reduce with a right member of follow set below. |
| } |
| |
| // ProcessNonTerminals : |
| do { /* reduce */ |
| if (goal.hasBeenReached(act, this.currentToken)) |
| return SUCCESS; |
| this.stateStackTop -= (Parser.rhs[act] - 1); |
| act = Parser.ntAction(this.stack[this.stateStackTop], Parser.lhs[act]); |
| } while (act <= NUM_RULES); |
| } |
| } catch (Exception e) { |
| return FAILURE; |
| } |
| } |
| @Override |
| public String toString() { |
| return "\n\n\n----------------Scanner--------------\n" + this.scanner.toString(); //$NON-NLS-1$; |
| } |
| } |
| |
| private class ScanContextDetector extends VanguardParser { |
| ScanContextDetector(CompilerOptions options) { |
| super(new ProblemReporter( |
| DefaultErrorHandlingPolicies.ignoreAllProblems(), |
| options, |
| new DefaultProblemFactory())); |
| this.problemReporter.options.performStatementsRecovery = false; |
| this.reportSyntaxErrorIsRequired = false; |
| this.reportOnlyOneSyntaxError = false; |
| } |
| |
| @Override |
| public void initializeScanner(){ |
| this.scanner = new Scanner( |
| false /*comment*/, |
| false /*whitespace*/, |
| false, /* will be set in initialize(boolean) */ |
| this.options.sourceLevel /*sourceLevel*/, |
| this.options.complianceLevel /*complianceLevel*/, |
| this.options.taskTags/*taskTags*/, |
| this.options.taskPriorities/*taskPriorities*/, |
| this.options.isTaskCaseSensitive/*taskCaseSensitive*/, |
| this.options.enablePreviewFeatures /*isPreviewEnabled*/) |
| { |
| @Override |
| void updateScanContext(int token) { |
| if (token != TokenNameEOF) |
| super.updateScanContext(token); |
| } |
| }; |
| this.scanner.recordLineSeparator = false; |
| this.scanner.setActiveParser(this); |
| this.scanner.previewEnabled = this.options.enablePreviewFeatures; |
| } |
| |
| @Override |
| public boolean isParsingModuleDeclaration() { |
| return true; |
| } |
| |
| public ScanContext getScanContext(char[] src, int begin) { |
| this.scanner.setSource(src); |
| this.scanner.resetTo(0, begin); |
| goForCompilationUnit(); |
| Goal goal = new Goal(TokenNamePLUS_PLUS, null, 0) { |
| @Override |
| boolean hasBeenReached(int act, int token) { |
| return token == TokenNameEOF; |
| } |
| }; |
| parse(goal); |
| return this.scanner.scanContext; |
| } |
| } |
| |
| private VanguardParser getVanguardParser() { |
| if (this.vanguardParser == null) { |
| this.vanguardScanner = new VanguardScanner(this.sourceLevel, this.complianceLevel, this.previewEnabled); |
| this.vanguardParser = new VanguardParser(this.vanguardScanner); |
| this.vanguardScanner.setActiveParser(this.vanguardParser); |
| } |
| this.vanguardScanner.setSource(this.source); |
| this.vanguardScanner.resetTo(this.startPosition, this.eofPosition - 1, isInModuleDeclaration(), this.scanContext); |
| return this.vanguardParser; |
| } |
| private VanguardParser getNewVanguardParser() { |
| VanguardScanner vs = getNewVanguardScanner(); |
| VanguardParser vp = new VanguardParser(vs); |
| vs.setActiveParser(vp); |
| return vp; |
| } |
| private VanguardScanner getNewVanguardScanner() { |
| VanguardScanner vs = new VanguardScanner(this.sourceLevel, this.complianceLevel, this.previewEnabled); |
| vs.setSource(this.source); |
| vs.resetTo(this.startPosition, this.eofPosition - 1, isInModuleDeclaration(), this.scanContext); |
| return vs; |
| } |
| protected final boolean mayBeAtBreakPreview() { |
| return this.breakPreviewAllowed && this.lookBack[1] != TokenNameARROW; |
| } |
| |
| protected final boolean maybeAtLambdaOrCast() { // Could the '(' we saw just now herald a lambda parameter list or a cast expression ? (the possible locations for both are identical.) |
| |
| switch (this.lookBack[1]) { |
| case TokenNameIdentifier: |
| case TokenNamecatch: |
| case TokenNamethis: |
| case TokenNamesuper: |
| case TokenNameif: |
| case TokenNameswitch: |
| case TokenNamewhile: |
| case TokenNamefor: |
| case TokenNamesynchronized: |
| case TokenNametry: |
| return false; // not a viable prefix for cast or lambda. |
| default: |
| return this.activeParser.atConflictScenario(TokenNameLPAREN); |
| } |
| } |
| |
| |
| protected final boolean maybeAtReferenceExpression() { // Did the '<' we saw just now herald a reference expression's type arguments and trunk ? |
| switch (this.lookBack[1]) { |
| case TokenNameIdentifier: |
| switch (this.lookBack[0]) { |
| case TokenNameSEMICOLON: // for (int i = 0; i < 10; i++); |
| case TokenNameRBRACE: // class X { void foo() {} X<String> x = null; } |
| case TokenNameclass: // class X<T> {} |
| case TokenNameinterface: // interface I<T> {} |
| case TokenNameenum: // enum E<T> {} |
| case TokenNamefinal: // final Collection<String> |
| case TokenNameLESS: // Collection<IScalarData<AbstractData>> |
| case TokenNameGREATER: // public <T> List<T> foo() { /* */ } |
| case TokenNameRIGHT_SHIFT:// static <T extends SelfType<T>> List<T> makeSingletonList(T t) { /* */ } |
| case TokenNamenew: // new ArrayList<String>(); |
| case TokenNamenon_sealed: // non-sealed X<T> |
| case TokenNameRestrictedIdentifiersealed: // sealed X<T> |
| case TokenNamepublic: // public List<String> foo() {} |
| case TokenNameabstract: // abstract List<String> foo() {} |
| case TokenNameprivate: // private List<String> foo() {} |
| case TokenNameprotected: // protected List<String> foo() {} |
| case TokenNamestatic: // public static List<String> foo() {} |
| case TokenNameextends: // <T extends Y<Z>> |
| case TokenNamesuper: // ? super Context<N> |
| case TokenNameAND: // T extends Object & Comparable<? super T> |
| case TokenNameimplements: // class A implements I<Z> |
| case TokenNameRestrictedIdentifierpermits: // class A permits I<Z> |
| case TokenNamethrows: // throws Y<Z> |
| case TokenNameAT: // @Deprecated <T> void foo() {} |
| case TokenNameinstanceof: // if (o instanceof List<E>[]) |
| case TokenNamedefault: |
| return false; |
| default: |
| break; |
| } |
| break; |
| case TokenNameNotAToken: // Not kosher, don't touch. |
| break; |
| default: |
| return false; |
| } |
| return this.activeParser.atConflictScenario(TokenNameLESS); |
| } |
| private final boolean maybeAtEllipsisAnnotationsStart() { // Did the '@' we saw just now herald a type annotation on a ... ? Presumed to be at type annotation already. |
| if (this.consumingEllipsisAnnotations) |
| return false; |
| switch (this.lookBack[1]) { |
| case TokenNamenew: |
| case TokenNameCOMMA: |
| case TokenNameextends: |
| case TokenNamesuper: |
| case TokenNameimplements: |
| case TokenNameRestrictedIdentifierpermits: |
| case TokenNameDOT: |
| case TokenNameLBRACE: |
| case TokenNameinstanceof: |
| case TokenNameLESS: |
| case TokenNameAND: |
| case TokenNamethrows: |
| return false; |
| default: |
| return true; |
| } |
| } |
| protected final boolean atTypeAnnotation() { // Did the '@' we saw just now herald a type annotation ? We should not ask the parser whether it would shift @308 ! |
| return !this.activeParser.atConflictScenario(TokenNameAT); |
| } |
| |
| public void setActiveParser(ConflictedParser parser) { |
| this.activeParser = parser; |
| this.lookBack[0] = this.lookBack[1] = TokenNameNotAToken; // no hand me downs please. |
| if (parser != null) { |
| this.insideModuleInfo = parser.isParsingModuleDeclaration(); |
| } |
| } |
| public static boolean isRestrictedKeyword(int token) { |
| switch(token) { |
| case TokenNameopen: |
| case TokenNamemodule: |
| case TokenNamerequires: |
| case TokenNametransitive: |
| case TokenNameexports: |
| case TokenNameto: |
| case TokenNameopens: |
| case TokenNameuses: |
| case TokenNameprovides: |
| case TokenNamewith: |
| return true; |
| default: |
| return false; |
| } |
| } |
| private boolean mayBeAtAnYieldStatement() { |
| // preceded by ;, {, }, ), or -> [Ref: http://mail.openjdk.java.net/pipermail/amber-spec-experts/2019-May/001401.html] |
| // above comment is super-seded by http://mail.openjdk.java.net/pipermail/amber-spec-experts/2019-May/001414.html |
| switch (this.lookBack[1]) { |
| case TokenNameLBRACE: |
| case TokenNameRBRACE: |
| case TokenNameRPAREN: |
| case TokenNameSEMICOLON: |
| case TokenNameelse: |
| case TokenNamedo: |
| return true; |
| case TokenNameCOLON: |
| return this.lookBack[0] == TokenNamedefault || this.yieldColons == 1; |
| case TokenNameDOT: |
| case TokenNameARROW: |
| default: |
| return false; |
| } |
| } |
| private boolean mayBeAtARestricedIdentifier(int restrictedIdentifier) { |
| switch (restrictedIdentifier) { |
| case TokenNameRestrictedIdentifiersealed: |
| break; |
| case TokenNameRestrictedIdentifierpermits: |
| break; |
| } |
| return true; |
| } |
| int disambiguatedRestrictedIdentifierrecord(int restrictedIdentifierToken) { |
| // and here's the kludge |
| if (restrictedIdentifierToken != TokenNameRestrictedIdentifierrecord) |
| return restrictedIdentifierToken; |
| if (!JavaFeature.RECORDS.isSupported(this.complianceLevel, this.previewEnabled)) |
| return TokenNameIdentifier; |
| |
| return disambiguaterecordWithLookAhead() ? |
| restrictedIdentifierToken : TokenNameIdentifier; |
| } |
| private int getNextTokenAfterTypeParameterHeader() { |
| int count = 1; |
| try { |
| int token; |
| while ((token = this.vanguardScanner.getNextToken()) != TokenNameNotAToken) { |
| if (token == TokenNameEOF) |
| break; |
| if (token == TokenNameLESS) |
| ++count; |
| if (token == TokenNameGREATER) |
| --count; |
| if (token == TokenNameRIGHT_SHIFT) |
| count= count -2; |
| if (token == TokenNameUNSIGNED_RIGHT_SHIFT) |
| count= count -3; |
| if (count <= 0) |
| return this.vanguardScanner.getNextToken(); |
| } |
| } catch (InvalidInputException e) { |
| if (e.getMessage().equals(INVALID_CHAR_IN_STRING)) { |
| //Ignore |
| } else { |
| // Shouldn't happen, but log the error |
| e.printStackTrace(); |
| } |
| } |
| return TokenNameEOF; |
| } |
| private boolean disambiguaterecordWithLookAhead() { |
| if (isInModuleDeclaration()) |
| return false; |
| getVanguardParser(); |
| this.vanguardScanner.resetTo(this.currentPosition, this.eofPosition - 1); |
| try { |
| int lookAhead1 = this.vanguardScanner.getNextToken(); |
| if (lookAhead1 == TokenNameIdentifier) { |
| int lookAhead2 = this.vanguardScanner.getNextToken(); |
| lookAhead2 = lookAhead2 == TokenNameLESS ? getNextTokenAfterTypeParameterHeader() : lookAhead2; |
| if (lookAhead2 == TokenNameLBRACE) { |
| // record X {} is considered a record (albeit illegal), |
| // This is so that we can issue an appropriate syntax error |
| return true; |
| } |
| return lookAhead2 == TokenNameLPAREN; |
| } |
| } catch (InvalidInputException e) { |
| if (e.getMessage().equals(INVALID_CHAR_IN_STRING)) { |
| //Ignore |
| } else { |
| // Shouldn't happen, but log the error |
| e.printStackTrace(); |
| } |
| } |
| return false; // IIE event; |
| } |
| private boolean disambiguateYieldWithLookAhead() { |
| getVanguardParser(); |
| this.vanguardScanner.resetTo(this.currentPosition, this.eofPosition - 1); |
| try { |
| int lookAhead1 = this.vanguardScanner.getNextToken(); |
| switch (lookAhead1) { |
| case TokenNameEQUAL_EQUAL : |
| case TokenNameLESS_EQUAL : |
| case TokenNameGREATER_EQUAL : |
| case TokenNameNOT_EQUAL : |
| case TokenNameLEFT_SHIFT : |
| case TokenNameRIGHT_SHIFT : |
| case TokenNameUNSIGNED_RIGHT_SHIFT : |
| case TokenNamePLUS_EQUAL : |
| case TokenNameMINUS_EQUAL : |
| case TokenNameMULTIPLY_EQUAL : |
| case TokenNameDIVIDE_EQUAL : |
| case TokenNameAND_EQUAL : |
| case TokenNameOR_EQUAL : |
| case TokenNameXOR_EQUAL : |
| case TokenNameREMAINDER_EQUAL : |
| case TokenNameLEFT_SHIFT_EQUAL : |
| case TokenNameRIGHT_SHIFT_EQUAL : |
| case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : |
| case TokenNameOR_OR : |
| case TokenNameAND_AND : |
| case TokenNameREMAINDER : |
| case TokenNameXOR : |
| case TokenNameAND : |
| case TokenNameMULTIPLY : |
| case TokenNameOR : |
| case TokenNameTWIDDLE : |
| case TokenNameDIVIDE : |
| case TokenNameGREATER : |
| case TokenNameLESS : |
| case TokenNameLBRACE : |
| case TokenNameRBRACE : |
| case TokenNameLBRACKET : |
| case TokenNameRBRACKET : |
| case TokenNameSEMICOLON : |
| case TokenNameQUESTION : |
| case TokenNameCOLON : |
| case TokenNameCOMMA : |
| case TokenNameDOT : |
| case TokenNameEQUAL : |
| case TokenNameAT : |
| case TokenNameELLIPSIS : |
| case TokenNameARROW : |
| case TokenNameCOLON_COLON : |
| return false; |
| case TokenNameMINUS_MINUS : |
| case TokenNamePLUS_PLUS : |
| int lookAhead2 = this.vanguardScanner.getNextToken(); |
| return lookAhead2 == TokenNameIdentifier; |
| default : return true; |
| } |
| } catch (InvalidInputException e) { |
| if (e.getMessage().equals(INVALID_CHAR_IN_STRING)) { |
| //Ignore |
| } else { |
| // Shouldn't happen, but log the error |
| e.printStackTrace(); |
| } |
| } |
| return false; // IIE event; |
| } |
| int disambiguatedRestrictedIdentifierpermits(int restrictedIdentifierToken) { |
| // and here's the kludge |
| if (restrictedIdentifierToken != TokenNameRestrictedIdentifierpermits) |
| return restrictedIdentifierToken; |
| if (!JavaFeature.RECORDS.isSupported(this.complianceLevel, this.previewEnabled)) |
| return TokenNameIdentifier; |
| |
| return disambiguatesRestrictedIdentifierWithLookAhead(this::mayBeAtARestricedIdentifier, |
| restrictedIdentifierToken, Goal.RestrictedIdentifierPermitsGoal); |
| } |
| int disambiguatedRestrictedIdentifiersealed(int restrictedIdentifierToken) { |
| // and here's the kludge |
| if (restrictedIdentifierToken != TokenNameRestrictedIdentifiersealed) |
| return restrictedIdentifierToken; |
| if (!JavaFeature.RECORDS.isSupported(this.complianceLevel, this.previewEnabled)) |
| return TokenNameIdentifier; |
| |
| return disambiguatesRestrictedIdentifierWithLookAhead(this::mayBeAtARestricedIdentifier, |
| restrictedIdentifierToken, Goal.RestrictedIdentifierSealedGoal); |
| } |
| int disambiguatedRestrictedIdentifierYield(int restrictedIdentifierToken) { |
| // and here's the kludge |
| if (restrictedIdentifierToken != TokenNameRestrictedIdentifierYield) |
| return restrictedIdentifierToken; |
| if (this.sourceLevel < ClassFileConstants.JDK14) |
| return TokenNameIdentifier; |
| |
| return mayBeAtAnYieldStatement() && disambiguateYieldWithLookAhead() ? |
| restrictedIdentifierToken : TokenNameIdentifier; |
| } |
| int disambiguatedRestrictedKeyword(int restrictedKeywordToken) { |
| int token = restrictedKeywordToken; |
| if (this.scanContext == ScanContext.EXPECTING_IDENTIFIER) |
| return TokenNameIdentifier; |
| |
| switch(restrictedKeywordToken) { |
| case TokenNametransitive: |
| if (this.scanContext != ScanContext.AFTER_REQUIRES) { |
| token = TokenNameIdentifier; |
| } else { |
| getVanguardParser(); |
| this.vanguardScanner.resetTo(this.currentPosition, this.eofPosition - 1, true, ScanContext.EXPECTING_IDENTIFIER); |
| try { |
| int lookAhead = this.vanguardScanner.getNextToken(); |
| if (lookAhead == TokenNameSEMICOLON) |
| token = TokenNameIdentifier; |
| } catch (InvalidInputException e) { |
| // |
| } |
| } |
| break; |
| case TokenNameopen: |
| case TokenNamemodule: |
| case TokenNameexports: |
| case TokenNameopens: |
| case TokenNamerequires: |
| case TokenNameprovides: |
| case TokenNameuses: |
| case TokenNameto: |
| case TokenNamewith: |
| if (this.scanContext != ScanContext.EXPECTING_KEYWORD) { |
| token = TokenNameIdentifier; |
| } |
| break; |
| } |
| return token; |
| } |
| int disambiguatesRestrictedIdentifierWithLookAhead(Predicate<Integer> checkPrecondition, int restrictedIdentifierToken, Goal goal) { |
| if (checkPrecondition.test(restrictedIdentifierToken)) { |
| VanguardParser vp = getNewVanguardParser(); |
| VanguardScanner vs = (VanguardScanner) vp.scanner; |
| vs.resetTo(this.currentPosition, this.eofPosition - 1); |
| if (vp.parse(goal) == VanguardParser.SUCCESS) |
| return restrictedIdentifierToken; |
| } |
| return TokenNameIdentifier; |
| } |
| |
| private VanguardScanner getNewVanguardScanner(char[] src) { |
| VanguardScanner vs = new VanguardScanner(this.sourceLevel, this.complianceLevel, this.previewEnabled); |
| vs.setSource(src); |
| vs.resetTo(0, src.length, isInModuleDeclaration(), this.scanContext); |
| return vs; |
| } |
| private VanguardParser getNewVanguardParser(char[] src) { |
| VanguardScanner vs = getNewVanguardScanner(src); |
| VanguardParser vp = new VanguardParser(vs); |
| vs.setActiveParser(vp); |
| return vp; |
| } |
| int disambiguatedToken(int token) { |
| final VanguardParser parser = getVanguardParser(); |
| if (token == TokenNameARROW && mayBeAtCaseLabelExpr() && this.caseStartPosition < this.startPosition) { |
| // this.caseStartPosition > this.startPositionpossible on recovery - bother only about correct ones. |
| int nSz = this.startPosition - this.caseStartPosition; |
| // add fake token of TokenNameCOLON, call vanguard on this modified source |
| // TODO: Inefficient method due to redoing of the same source, investigate alternate |
| // Can we do a dup of parsing/check the transition of the state? |
| String s = new String(this.source, this.caseStartPosition, nSz); |
| String modSource = s.concat(new String(new char[] {':'})); |
| char[] nSource = modSource.toCharArray(); |
| VanguardParser vp = getNewVanguardParser(nSource); |
| if (vp.parse(Goal.SwitchLabelCaseLhsGoal) == VanguardParser.SUCCESS) { |
| this.nextToken = TokenNameARROW; |
| return TokenNameBeginCaseExpr; |
| } |
| } else if (token == TokenNameLPAREN && maybeAtLambdaOrCast()) { |
| if (parser.parse(Goal.LambdaParameterListGoal) == VanguardParser.SUCCESS) { |
| this.nextToken = TokenNameLPAREN; |
| return TokenNameBeginLambda; |
| } |
| this.vanguardScanner.resetTo(this.startPosition, this.eofPosition - 1); |
| if (parser.parse(Goal.IntersectionCastGoal) == VanguardParser.SUCCESS) { |
| this.nextToken = TokenNameLPAREN; |
| return TokenNameBeginIntersectionCast; |
| } |
| } else if (token == TokenNameLESS && maybeAtReferenceExpression()) { |
| if (parser.parse(Goal.ReferenceExpressionGoal) == VanguardParser.SUCCESS) { |
| this.nextToken = TokenNameLESS; |
| return TokenNameBeginTypeArguments; |
| } |
| } else if (token == TokenNameAT && atTypeAnnotation()) { |
| token = TokenNameAT308; |
| if (maybeAtEllipsisAnnotationsStart()) { |
| if (parser.parse(Goal.VarargTypeAnnotationGoal) == VanguardParser.SUCCESS) { |
| this.consumingEllipsisAnnotations = true; |
| this.nextToken = TokenNameAT308; |
| return TokenNameAT308DOTDOTDOT; |
| } |
| } |
| } |
| return token; |
| } |
| private boolean mayBeAtCaseLabelExpr() { |
| if (this.lookBack[1] == TokenNamedefault || this.caseStartPosition <= 0) |
| return false; |
| return true; |
| } |
| |
| protected boolean isAtAssistIdentifier() { |
| return false; |
| } |
| |
| // Position the scanner at the next block statement and return the start token. We recognize empty statements. |
| public int fastForward(Statement unused) { |
| |
| int token; |
| |
| while (true) { |
| try { |
| token = getNextToken(); |
| } catch (InvalidInputException e) { |
| return TokenNameEOF; |
| } |
| /* FOLLOW map of BlockStatement, since the non-terminal is recursive is a super set of its own FIRST set. |
| We use FOLLOW rather than FIRST since we want to recognize empty statements. i.e if (x > 10) { x = 0 } |
| */ |
| switch(token) { |
| case TokenNameIdentifier: |
| if (isAtAssistIdentifier()) // do not fast forward past the assist identifier ! We don't handle collections as of now. |
| return token; |
| //$FALL-THROUGH$ |
| case TokenNameabstract: |
| case TokenNameassert: |
| case TokenNameboolean: |
| case TokenNamebreak: |
| case TokenNamebyte: |
| case TokenNamecase: |
| case TokenNamechar: |
| case TokenNameclass: |
| case TokenNamecontinue: |
| case TokenNamedefault: |
| case TokenNamedo: |
| case TokenNamedouble: |
| case TokenNameenum: |
| case TokenNamefalse: |
| case TokenNamefinal: |
| case TokenNamefloat: |
| case TokenNamefor: |
| case TokenNameif: |
| case TokenNameint: |
| case TokenNameinterface: |
| case TokenNamelong: |
| case TokenNamenative: |
| case TokenNamenew: |
| case TokenNamenon_sealed: |
| case TokenNamenull: |
| case TokenNameprivate: |
| case TokenNameprotected: |
| case TokenNamepublic: |
| case TokenNameRestrictedIdentifiersealed: |
| case TokenNamereturn: |
| case TokenNameshort: |
| case TokenNamestatic: |
| case TokenNamestrictfp: |
| case TokenNamesuper: |
| case TokenNameswitch: |
| case TokenNamesynchronized: |
| case TokenNamethis: |
| case TokenNamethrow: |
| case TokenNametransient: |
| case TokenNametrue: |
| case TokenNametry: |
| case TokenNamevoid: |
| case TokenNamevolatile: |
| case TokenNamewhile: |
| case TokenNameIntegerLiteral: // ??! |
| case TokenNameLongLiteral: |
| case TokenNameFloatingPointLiteral: |
| case TokenNameDoubleLiteral: |
| case TokenNameCharacterLiteral: |
| case TokenNameStringLiteral: |
| case TokenNameTextBlock: |
| case TokenNamePLUS_PLUS: |
| case TokenNameMINUS_MINUS: |
| case TokenNameLESS: |
| case TokenNameLPAREN: |
| case TokenNameLBRACE: |
| case TokenNameAT: |
| case TokenNameBeginLambda: |
| case TokenNameBeginCaseExpr: |
| case TokenNameAT308: |
| case TokenNameRestrictedIdentifierYield: // can be in FOLLOW of Block |
| if(getVanguardParser().parse(Goal.BlockStatementoptGoal) == VanguardParser.SUCCESS) |
| return token; |
| break; |
| case TokenNameSEMICOLON: |
| case TokenNameEOF: |
| return token; |
| case TokenNameRBRACE: // simulate empty statement. |
| ungetToken(token); |
| return TokenNameSEMICOLON; |
| default: |
| break; |
| } |
| } |
| } |
| |
| /** Overridable hook, to allow CompletionScanner to hide a faked identifier token. */ |
| protected int getNextNotFakedToken() throws InvalidInputException { |
| return getNextToken(); |
| } |
| } |