Bug 384992 - [java8] adopt grammar changes for Java 8 from JDT/Core
8. implement scanner look-ahead to disambiguate '->'
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
index 24254e5..b88effe 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
@@ -6911,6 +6911,10 @@
pushOnAstLengthStack(0);
pushOnIntStack(0); // flagging no mappings
+ this.scanner._insideParameterMapping = false;
+}
+protected void consumeParameterMappings() {
+ this.scanner._insideParameterMapping = false;
}
protected void consumeParameterMappingList() {
// CallinParameterMappingList ::= CallinParameterMappingList ',' ParameterMapping
@@ -6941,11 +6945,8 @@
char[] ident = getIdentifier();
long pos = this.identifierPositionStack[this.identifierPtr--];
- this.intPtr--; // start pos of '->' token (unused)
- int bindingKind = this.intStack[this.intPtr--];
- assert(bindingKind == TerminalTokens.TokenNameBINDOUT);
- pushOnAstStack(new ParameterMapping(bindingKind, expression, new SingleNameReference(ident, pos)));
+ pushOnAstStack(new ParameterMapping(TerminalTokens.TokenNameBINDOUT, expression, new SingleNameReference(ident, pos)));
}
/** consumes what is known to be a single identifier (currently only ident of param map) */
protected char[] getIdentifier() {
@@ -6968,7 +6969,7 @@
pushOnIntStack(1); // signal that we have param mappings
pushOnAstLengthStack(0); // no mappings yet
- // FIXME tell the scanner to enty param mapping mode?
+ this.scanner._insideParameterMapping = true;
}
//SH}
@@ -8133,6 +8134,10 @@
consumeParameterMappingsEmpty();
break;
+ case 277 : if (DEBUG) { System.out.println("CalloutParameterMappings ::= with NestedParamMappings..."); } //$NON-NLS-1$
+ consumeParameterMappings();
+ break;
+
case 280 : if (DEBUG) { System.out.println("CalloutParameterMappingList ::=..."); } //$NON-NLS-1$
consumeParameterMappingList();
break;
@@ -8225,6 +8230,10 @@
consumeParameterMappingsEmpty();
break;
+ case 304 : if (DEBUG) { System.out.println("CallinParameterMappings ::= with NestedParamMappings..."); } //$NON-NLS-1$
+ consumeParameterMappings();
+ break;
+
case 307 : if (DEBUG) { System.out.println("CallinParameterMappingList ::=..."); } //$NON-NLS-1$
consumeParameterMappingList();
break;
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 f78b098..fc495f7 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
@@ -106,12 +106,83 @@
// after a '.' even 'team' can be an identifier:
private int _dotSeen = 0; // 0: no, 1: previos, 2: this token
+ public boolean _insideParameterMapping = false;
+ enum LookaheadState { INIT, ONE_TOKEN, ID_SEEN, MATCH, ID_CONSUMED }
+ /**
+ * A little state machine for lookahead of up-to 2 tokens.
+ * This is used to disambiguate whether a '->' inside parameter mappings is
+ * an ARRAW (lambda) or a SYNTHBINDOUT (parameter mapping role-to-base)
+ */
+ class BindoutLookahead {
+
+ LookaheadState state = LookaheadState.INIT;
+ int[] tokens = new int[2];
+ char[] identifier = null;
+ int[][] positions = new int[2][];
+
+ public BindoutLookahead() throws InvalidInputException {
+ int token = this.tokens[0] = getNextToken0();
+ this.positions[0] = new int[] {Scanner.this.startPosition, Scanner.this.currentPosition};
+ if (token == TerminalTokens.TokenNameIdentifier) {
+ this.state = LookaheadState.ID_SEEN;
+ this.identifier = getCurrentIdentifierSource();
+ }
+ }
+ public char[] getIdentifier() {
+ if (this.state == LookaheadState.MATCH) {
+ return this.identifier;
+ }
+ return null;
+ }
+ public int getNextToken() throws InvalidInputException {
+ switch (this.state) {
+ case INIT :
+ // == initialization failed to match anything => signal no match
+ this.state = LookaheadState.ONE_TOKEN;
+ return TerminalTokens.TokenNameNotAToken;
+ case ONE_TOKEN :
+ // == we have one non-matching token => pop that token and unregister
+ Scanner.this._bindoutLookahead = null;
+ return popToken(0);
+ case ID_SEEN :
+ // == we've seen an identifier => check if second token matches, too
+ this.tokens[1] = Scanner.this.getNextToken0();
+ switch (this.tokens[1]) {
+ case TerminalTokens.TokenNameCOMMA : // more mappings?
+ case TerminalTokens.TokenNameSEMICOLON :// more mappings? (wrong delimiter, though)
+ case TerminalTokens.TokenNameRBRACE : // end of parameter mappings?
+ this.positions[1] = new int[] {Scanner.this.startPosition, Scanner.this.currentPosition};
+ this.state = LookaheadState.MATCH;
+ return TokenNameSYNTHBINDOUT; // match, tweak '->' to mean SYNTHBINDOUT
+ }
+ return TerminalTokens.TokenNameARROW; // no match, '->' should be interpreted as normal
+ case MATCH :
+ // == we've seen a full match => pop the identifier now
+ this.state = LookaheadState.ID_CONSUMED;
+ return popToken(0);
+ case ID_CONSUMED :
+ // == identifier has already been consumed => pop the second token and unregister
+ Scanner.this._bindoutLookahead = null;
+ return popToken(1);
+ }
+ return TerminalTokens.TokenNameNotAToken;
+ }
+ private int popToken(int i) {
+ Scanner.this.startPosition = this.positions[i][0];
+ Scanner.this.currentPosition = this.positions[i][1];
+ return this.tokens[i];
+ }
+ }
+ BindoutLookahead _bindoutLookahead = null;
+
public void resetOTFlags() {
this._isOTSource = this.parseOTJonly;
this._teamKeywordSeen = false;
this._forceBaseIsIdentifier = false;
this._callinSeen = false;
this._calloutSeen = false;
+ this._insideParameterMapping = false;
+ this._bindoutLookahead = null;
}
public void setOTFlags(CompilerOptions options) {
@@ -542,6 +613,13 @@
}
public char[] getCurrentIdentifierSource() {
+//{ObjectTeams: respect look-ahead:
+ if (this._bindoutLookahead != null) {
+ char[] result = this._bindoutLookahead.getIdentifier();
+ if (result != null)
+ return result;
+ }
+// SH}
//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
@@ -1264,6 +1342,14 @@
return token; // presumed to be unambiguous.
}
+//{ObjectTeams: consume lookahead for parameter mappings:
+ if (this._bindoutLookahead != null) {
+ int result = this._bindoutLookahead.getNextToken();
+ if (result != TerminalTokens.TokenNameNotAToken)
+ return result;
+ }
+// SH}
+
token = getNextToken0();
if (this.activeParser == null) { // anybody interested in the grammatical structure of the program should have registered.
return token;
@@ -1441,8 +1527,13 @@
return TokenNameARROW;
:giro */
if (getNextChar('>')) {
- if (this._isOTSource)
- this._calloutSeen = true;
+ if (this._isOTSource) {
+ this._calloutSeen = true; // TODO distinguish from ARROW?
+ if (this._insideParameterMapping) {
+ this._bindoutLookahead = new BindoutLookahead();
+ return this._bindoutLookahead.getNextToken();
+ }
+ }
return TokenNameARROW;
}
// Markus Witte}
@@ -3281,6 +3372,10 @@
this.commentPtr = -1; // reset comment stack
this.foundTaskCount = 0;
this.lookBack[0] = this.lookBack[1] = this.nextToken = TokenNameNotAToken;
+//{ObjectTeams: lookahead on '->':
+ this._insideParameterMapping = false;
+ this._bindoutLookahead = null;
+// SH}
}
protected final void scanEscapeCharacter() throws InvalidInputException {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/diagnose/DiagnoseParser.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/diagnose/DiagnoseParser.java
index ea6829b..5074355 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/diagnose/DiagnoseParser.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/diagnose/DiagnoseParser.java
@@ -462,8 +462,9 @@
//{ObjectTeams: WATCHOUT: needs to be updated with each new grammar!!!!
private void setScannerState(int act) {
switch(act) {
- case 237: this.lexStream.forceBaseIsIdentifier(); break; // ForceBaseIsIdentifier
- case 238: this.lexStream.restoreBaseKeyword(); break; // RestoreBaseKeyword
+ case 183: this.lexStream.forceBaseIsIdentifier(); break; // ForceBaseIsIdentifier
+ case 184: this.lexStream.restoreBaseKeyword(); break; // RestoreBaseKeyword
+ case 283: this.parser.scanner._insideParameterMapping = true; break;
}
}
//SH}
@@ -1484,6 +1485,9 @@
do {
this.tempStackTop -= (Parser.rhs[act]-1);
int lhs_symbol = Parser.lhs[act];
+//{ObjectTeams: integrate stateful scanner:
+ setScannerState(act);
+// SH}
act = (this.tempStackTop > max_pos
? this.tempStack[this.tempStackTop]
: stck[this.tempStackTop]);
@@ -1569,6 +1573,9 @@
continue next;
}
top -= (Parser.rhs[act]-1);
+//{ObjectTeams: integrate stateful scanner:
+ setScannerState(act);
+// SH}
act = Parser.ntAction(stck[top], Parser.lhs[act]);
}
top++;
@@ -2182,6 +2189,7 @@
max_pos = max_pos < this.tempStackTop ? max_pos : this.tempStackTop;
} // process_terminal;
}
+
private void reportError(int msgCode, int nameIndex, int leftToken, int rightToken) {
reportError(msgCode, nameIndex, leftToken, rightToken, 0);
}
diff --git a/org.eclipse.jdt.core/grammar/java.g b/org.eclipse.jdt.core/grammar/java.g
index 1444e79..abc0b7e 100644
--- a/org.eclipse.jdt.core/grammar/java.g
+++ b/org.eclipse.jdt.core/grammar/java.g
@@ -125,6 +125,7 @@
-- {ObjectTeams
BINDIN
CALLOUT_OVERRIDE
+ SYNTHBINDOUT
-- Markus Witte}
-- BodyMarker
@@ -1035,6 +1036,7 @@
CalloutParameterMappings ::= 'with' NestedParamMappings '{' CalloutParameterMappingList ,opt '}'
+/.$putCase consumeParameterMappings(); $break ./
/:$readableName CalloutParameterMappings:/
-- note that this rule is needed for diagnose parsing where bodies of parameter mappings are ignored
@@ -1046,44 +1048,10 @@
/.$putCase consumeParameterMappingList(); $break ./
/:$readableName CalloutParameterMappingList:/
-ParameterMapping ::= ExpressionNoLambda '->' 'Identifier'
+
+ParameterMapping ::= Expression SYNTHBINDOUT 'Identifier'
/.$putCase consumeParameterMappingOut(); $break ./
--- SYNTAX CHANGE TO AVOID CONFLICT WITH JSR-335: to allow all expressions on the LHS incl. lambda use a different binding token
-ParameterMapping ::= Expression '=>' 'Identifier'
-/.$putCase consumeParameterMappingOut(); $break ./
-
--- (INCOMPLETE) SUBSET OF EXPRESSION PERMISSIBLE WITH OLD BINDING TOKEN '->':
-ExpressionNoLambda -> ArrayCreationWithArrayInitializer
-ExpressionNoLambda -> ArrayCreationWithoutArrayInitializer
-/:$readableName Expression:/
-ExpressionNoLambda -> ReferenceExpression
-ExpressionNoLambda -> Literal
-ExpressionNoLambda ::= 'this'
-/.$putCase consumePrimaryNoNewArrayThis(); $break ./
-ExpressionNoLambda ::= PushLPAREN Expression_NotName PushRPAREN
-/.$putCase consumePrimaryNoNewArray(); $break ./
-ExpressionNoLambda ::= PushLPAREN Name PushRPAREN
-/.$putCase consumePrimaryNoNewArrayWithName(); $break ./
-ExpressionNoLambda -> ClassInstanceCreationExpression
-ExpressionNoLambda -> BaseConstructorExpression
-ExpressionNoLambda -> FieldAccess
-ExpressionNoLambda ::= Name '.' 'this'
-/.$putCase consumePrimaryNoNewArrayNameThis(); $break ./
-ExpressionNoLambda ::= Name '.' 'super'
-/.$putCase consumePrimaryNoNewArrayNameSuper(); $break ./
-ExpressionNoLambda ::= Name '.' 'class'
-/.$putCase consumePrimaryNoNewArrayName(); $break ./
-ExpressionNoLambda ::= Name Dims '.' 'class'
-/.$putCase consumePrimaryNoNewArrayArrayType(); $break ./
-ExpressionNoLambda ::= PrimitiveType Dims '.' 'class'
-/.$putCase consumePrimaryNoNewArrayPrimitiveArrayType(); $break ./
-ExpressionNoLambda ::= PrimitiveType '.' 'class'
-/.$putCase consumePrimaryNoNewArrayPrimitiveType(); $break ./
-ExpressionNoLambda -> MethodInvocation
-ExpressionNoLambda -> ArrayAccess
-
-
ParameterMapping ::= 'Identifier' '<-' ForceBaseIsIdentifier Expression RestoreBaseKeyword
/.$putCase consumeParameterMappingIn(); $break ./
/:$readableName ParameterMapping:/
@@ -1182,6 +1150,7 @@
/:$readableName EmptyParameterMappings:/
CallinParameterMappings ::= 'with' NestedParamMappings '{' CallinParameterMappingList ,opt '}'
+/.$putCase consumeParameterMappings(); $break ./
/:$readableName CallinParameterMappings:/
-- note that this rule is needed for diagnose parsing where bodies of parameter mappings are ignored
@@ -3410,6 +3379,7 @@
-- {ObjectTeams
BINDIN ::= '<-'
CALLOUT_OVERRIDE ::= '=>'
+SYNTHBINDOUT ::= '->'
-- Markus Witte}
$end