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