diff options
Diffstat (limited to 'org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java')
-rw-r--r-- | org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java | 113 |
1 files changed, 93 insertions, 20 deletions
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java index c6e034923..df2f36595 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java @@ -367,6 +367,17 @@ public class Scanner implements TerminalTokens { public boolean returnOnlyGreater = false; public boolean insideRecovery = false; + /** + * Look back for the two most recent tokens. + * <ul> + * <li><code>lookBack[1]</code> is the previous token</li> + * <li><code>lookBack[0]</code> is the token before <code>lookBack[1]</code></li> + * </ul> + * As this look back is intended for resolving ambiguities and conflicts, it ignores whitespace and comments. + * + * @see #resetLookBack() Reset the look back and clear all stored tokens + * @see #addTokenToLookBack(int) Add a token to the look back, removing the oldest entry + */ 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; @@ -413,7 +424,8 @@ public Scanner( this.tokenizeComments = tokenizeComments; this.tokenizeWhiteSpace = tokenizeWhiteSpace; this.sourceLevel = sourceLevel; - this.lookBack[0] = this.lookBack[1] = this.nextToken = TokenNameNotAToken; + this.resetLookBack(); + this.nextToken = TokenNameNotAToken; this.consumingEllipsisAnnotations = false; this.complianceLevel = complianceLevel; this.checkNonExternalizedStringLiterals = checkNonExternalizedStringLiterals; @@ -1619,8 +1631,7 @@ public int getNextToken() throws InvalidInputException { } if (this.activeParser == null) { // anybody interested in the grammatical structure of the program should have registered. if (token != TokenNameWHITESPACE) { - this.lookBack[0] = this.lookBack[1]; - this.lookBack[1] = token; + addTokenToLookBack(token); this.multiCaseLabelComma = false; } return token; @@ -1632,8 +1643,7 @@ public int getNextToken() throws InvalidInputException { } else if (mayBeAtCasePattern(token)) { token = disambiguateCasePattern(token, this); } - this.lookBack[0] = this.lookBack[1]; - this.lookBack[1] = token; + addTokenToLookBack(token); this.multiCaseLabelComma = false; return token; } @@ -3692,7 +3702,8 @@ public void resetTo(int begin, int end, boolean isModuleInfo, ScanContext contex } this.commentPtr = -1; // reset comment stack this.foundTaskCount = 0; - this.lookBack[0] = this.lookBack[1] = this.nextToken = TokenNameNotAToken; + resetLookBack(); + this.nextToken = TokenNameNotAToken; this.consumingEllipsisAnnotations = false; //{ObjectTeams: lookahead on '->': this._insideParameterMapping = false; @@ -3702,7 +3713,27 @@ public void resetTo(int begin, int end, boolean isModuleInfo, ScanContext contex this.scanContext = context == null ? getScanContext(begin) : context; this.multiCaseLabelComma = false; } - +/** + * @see #lookBack + */ +final void resetLookBack() { + this.lookBack[0] = this.lookBack[1] = TokenNameNotAToken; +} +/** + * @see #lookBack + */ +final void addTokenToLookBack(int newToken) { + // ignore whitespace and comments + switch (newToken) { + case TokenNameWHITESPACE: + case TokenNameCOMMENT_LINE: + case TokenNameCOMMENT_BLOCK: + case TokenNameCOMMENT_JAVADOC: + return; + } + this.lookBack[0] = this.lookBack[1]; + this.lookBack[1] = newToken; +} private ScanContext getScanContext(int begin) { if (!isInModuleDeclaration()) return ScanContext.INACTIVE; @@ -4793,8 +4824,14 @@ private int internalScanIdentifierOrKeyword(int index, int length, char[] data) && (data[++index] == 't') && (data[++index] == 'h')) return TokenNamewith; - else //{ObjectTeams: check for with, when keywords + else +/* orig: + else if ((data[++index] == 'h') + && (data[++index] == 'e') + && (data[++index] == 'n')) + return disambiguatedRestrictedIdentifierWhen(TokenNameRestrictedIdentifierWhen); + :giro */ switch (data[++index]) { case 'i': if ( this._isOTSource @@ -4806,7 +4843,7 @@ private int internalScanIdentifierOrKeyword(int index, int length, char[] data) if ( this._isOTSource && (data[++index] == 'e') && (data[++index] == 'n')) - return TokenNamewhen; + return disambiguatedRestrictedIdentifierWhen(TokenNameRestrictedIdentifierWhen); } //Markus Witte} return TokenNameIdentifier; @@ -5278,6 +5315,8 @@ public String toStringAction(int act) { switch (act) { case TokenNameIdentifier : return "Identifier(" + new String(getCurrentTokenSource()) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + case TokenNameRestrictedIdentifierWhen : + return "when"; //$NON-NLS-1$ case TokenNameRestrictedIdentifierYield : return "yield"; //$NON-NLS-1$ case TokenNameRestrictedIdentifierrecord : @@ -5646,6 +5685,7 @@ public static boolean isKeyword(int token) { case TerminalTokens.TokenNameRestrictedIdentifierrecord: case TerminalTokens.TokenNameRestrictedIdentifiersealed: case TerminalTokens.TokenNameRestrictedIdentifierpermits: + case TerminalTokens.TokenNameRestrictedIdentifierWhen: // making explicit - not a (restricted) keyword but restricted identifier. //$FALL-THROUGH$ default: @@ -5694,8 +5734,7 @@ private static final class VanguardScanner extends Scanner { token = TokenNameAT308; } } - this.lookBack[0] = this.lookBack[1]; - this.lookBack[1] = token; + this.addTokenToLookBack(token); this.multiCaseLabelComma = false; return token == TokenNameEOF ? TokenNameNotAToken : token; } @@ -5714,8 +5753,10 @@ private static class Goal { static int BlockStatementoptRule = 0; static int YieldStatementRule = 0; static int SwitchLabelCaseLhsRule = 0; + static int GuardRule = 0; static int[] RestrictedIdentifierSealedRule; static int[] RestrictedIdentifierPermitsRule; + static int[] RestrictedIdentifierWhenRule; static int[] PatternRules; static Goal LambdaParameterListGoal; @@ -5727,12 +5768,15 @@ private static class Goal { static Goal SwitchLabelCaseLhsGoal; static Goal RestrictedIdentifierSealedGoal; static Goal RestrictedIdentifierPermitsGoal; + static Goal RestrictedIdentifierWhenGoal; static Goal PatternGoal; + static int[] EMPTY_FOLLOW_SET = new int[0]; static int[] RestrictedIdentifierSealedFollow = { TokenNameclass, TokenNameinterface, TokenNameenum, TokenNameRestrictedIdentifierrecord };// Note: enum/record allowed as error flagging rules. static int[] RestrictedIdentifierPermitsFollow = { TokenNameLBRACE }; - static int[] PatternCaseLabelFollow = {TokenNameCOLON, TokenNameARROW, TokenNameCOMMA, TokenNameBeginCaseExpr}; + static int[] PatternCaseLabelFollow = {TokenNameCOLON, TokenNameARROW, TokenNameCOMMA, TokenNameBeginCaseExpr, TokenNameRestrictedIdentifierWhen}; + static int[] GuardFollow = EMPTY_FOLLOW_SET; static { @@ -5771,17 +5815,18 @@ private static class Goal { if ("TypePattern".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ patternStates.add(i); else - if ("PrimaryPattern".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ + if ("Pattern".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ patternStates.add(i); else - if ("GuardedPattern".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ + if ("ParenthesizedPattern".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ patternStates.add(i); else - if ("Pattern".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ + if ("RecordPattern".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ patternStates.add(i); else - if ("ParenthesizedPattern".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ - patternStates.add(i); + if ("Expression".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$ + GuardRule = i; + } RestrictedIdentifierSealedRule = ridSealed.stream().mapToInt(Integer :: intValue).toArray(); // overkill but future-proof RestrictedIdentifierPermitsRule = ridPermits.stream().mapToInt(Integer :: intValue).toArray(); @@ -5796,6 +5841,7 @@ private static class Goal { SwitchLabelCaseLhsGoal = new Goal(TokenNameARROW, new int [0], SwitchLabelCaseLhsRule); RestrictedIdentifierSealedGoal = new Goal(TokenNameRestrictedIdentifiersealed, RestrictedIdentifierSealedFollow, RestrictedIdentifierSealedRule); RestrictedIdentifierPermitsGoal = new Goal(TokenNameRestrictedIdentifierpermits, RestrictedIdentifierPermitsFollow, RestrictedIdentifierPermitsRule); + RestrictedIdentifierWhenGoal = new Goal(TokenNameRestrictedIdentifierWhen, GuardFollow, GuardRule); PatternGoal = new Goal(TokenNameBeginCaseElement, PatternCaseLabelFollow, PatternRules); } @@ -5814,7 +5860,7 @@ private static class Goal { 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$ + System.out.println("[Goal = " + Parser.name[Parser.non_terminal_index[Parser.lhs[act]]] + "] " + "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; @@ -6010,6 +6056,24 @@ protected final boolean mayBeAtCasePattern(int token) { return (!isInModuleDeclaration() && JavaFeature.PATTERN_MATCHING_IN_SWITCH.isSupported(this.complianceLevel, this.previewEnabled)) && (token == TokenNamecase || this.multiCaseLabelComma); } +protected boolean mayBeAtGuard(int token) { + if (isInModuleDeclaration()) + return false; + if (!JavaFeature.PATTERN_MATCHING_IN_SWITCH.isSupported(this.complianceLevel, this.previewEnabled)) + return false; + /* + * A simple elimination optimization for some common possible cases. According to the JLS 19 including + * patterns-switch and record-patterns Section 14.30.1, a guard may only be preceded by either right parentheses or + * an identifier. However, we may still encounter comments, whitespace or the not-a-token token. + */ + switch (this.lookBack[1]) { + case TokenNameRPAREN: + case TokenNameIdentifier: + case TokenNameNotAToken: // TODO is this useful? Some tests start scanning at "when", but this makes no sense as a Pattern is required by the JLS + return true; + } + return false; +} protected final boolean mayBeAtBreakPreview() { return !isInModuleDeclaration() && this.breakPreviewAllowed && this.lookBack[1] != TokenNameARROW; } @@ -6114,7 +6178,7 @@ protected final boolean atTypeAnchor() { // Did the '@' we saw just now herald a public void setActiveParser(ConflictedParser parser) { this.activeParser = parser; - this.lookBack[0] = this.lookBack[1] = TokenNameNotAToken; // no hand me downs please. + this.resetLookBack(); // no hand me downs please. if (parser != null) { this.insideModuleInfo = parser.isParsingModuleDeclaration(); } @@ -6320,6 +6384,16 @@ int disambiguatedRestrictedIdentifiersealed(int restrictedIdentifierToken) { return disambiguatesRestrictedIdentifierWithLookAhead(this::mayBeAtASealedRestricedIdentifier, restrictedIdentifierToken, Goal.RestrictedIdentifierSealedGoal); } +int disambiguatedRestrictedIdentifierWhen(int restrictedIdentifierToken) { + // and here's the kludge + if (restrictedIdentifierToken != TokenNameRestrictedIdentifierWhen) + return restrictedIdentifierToken; + if (!JavaFeature.PATTERN_MATCHING_IN_SWITCH.isSupported(this.complianceLevel, this.previewEnabled)) + return TokenNameIdentifier; + + return disambiguatesRestrictedIdentifierWithLookAhead(this::mayBeAtGuard, + restrictedIdentifierToken, Goal.RestrictedIdentifierWhenGoal); +} int disambiguatedRestrictedIdentifierYield(int restrictedIdentifierToken) { // and here's the kludge if (restrictedIdentifierToken != TokenNameRestrictedIdentifierYield) @@ -6452,7 +6526,6 @@ protected int disambiguateArrowWithCaseExpr(Scanner scanner, int retToken) { * Assumption: mayBeAtCasePattern(token) is true before calling this method. */ int disambiguateCasePattern(int token, Scanner scanner) { - assert mayBeAtCasePattern(token); int delta = token == TokenNamecase ? 4 : 0; // 4 for case. final VanguardParser parser = getNewVanguardParser(); parser.scanner.resetTo(parser.scanner.currentPosition + delta, parser.scanner.eofPosition); |