/*******************************************************************************
* Copyright (c) 2008, 2013 Borland Software Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Borland Software Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.m2m.internal.qvt.oml.editor.ui.completion.keywordhandler.scopedvariablesextraction;
import lpg.runtime.IToken;
import org.eclipse.m2m.internal.qvt.oml.cst.parser.QVTOParsersym;
import org.eclipse.m2m.internal.qvt.oml.editor.ui.completion.LightweightParserUtil;
import org.eclipse.m2m.internal.qvt.oml.editor.ui.completion.QvtCompletionData;
/**
* @author aigdalov
* Created on Nov 21, 2007
*/
public class ScopedVariablesExtractor {
private static final int NOT_A_TOKEN = -1;
// starting from ')'
public String extractVariables(IToken startToken, QvtCompletionData data) {
Scope scope = new Scope(null);
IToken currentToken = startToken;
if (QvtCompletionData.isKindOf(data.getParentImperativeOperation(), QVTOParsersym.TK_mapping)) {
while ((currentToken = getNextToken(currentToken, data)) != null) {
if (QvtCompletionData.isKindOf(currentToken, QVTOParsersym.TK_LBRACE)) {
break;
}
}
}
if (currentToken != null) {
while ((currentToken = getNextToken(currentToken, data)) != null) {
Result innerScopeResult = null;
if (QvtCompletionData.isKindOf(currentToken, QVTOParsersym.TK_init, QVTOParsersym.TK_end, QVTOParsersym.TK_population)) {
currentToken = LightweightParserUtil.getNextToken(currentToken);
if (currentToken == null) {
break;
}
if (QvtCompletionData.isKindOf(currentToken, QVTOParsersym.TK_LBRACE)) {
innerScopeResult = analyseScopedVarVariables(currentToken, data, scope, true);
}
} else if (QvtCompletionData.isKindOf(currentToken, QVTOParsersym.TK_var)) {
innerScopeResult = analyseScopedVarVariables(LightweightParserUtil.getPreviousToken(currentToken), data, scope, true);
} else {
innerScopeResult = analyseVariableDeclaringExpressions(currentToken, data, scope);
}
if (innerScopeResult != null) {
scope = innerScopeResult.getScope();
currentToken = innerScopeResult.getEndToken();
}
}
}
return scope.toString();
}
// starting from '{' or from any token within the '{' scope
private Result analyseScopedVarVariables(IToken startToken, QvtCompletionData data, Scope scope, boolean isMandatoryScope) {
IToken currentToken = startToken;
Scope varScope = new Scope(scope);
IToken nextToken;
while ((nextToken = getNextToken(currentToken, data)) != null) {
if (QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_RBRACE)) {
if (isMandatoryScope) {
return new Result(startToken, nextToken, null, varScope);
}
return new Result(startToken, nextToken, null, scope);
}
if (QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_var)) {
nextToken = getNextToken(nextToken, data);
if (nextToken == null) {
break;
}
Result extractVarVariableResult = extractVariable(nextToken, null, data, varScope,
new int[] {QVTOParsersym.TK_RESET_ASSIGN}, NOT_A_TOKEN, QVTOParsersym.TK_SEMICOLON, QVTOParsersym.TK_RBRACE);
if (extractVarVariableResult.getScope() != varScope) {
return extractVarVariableResult;
}
nextToken = extractVarVariableResult.getEndToken();
varScope.addVariable(extractVarVariableResult.getString());
} else {
Result innerScopeResult = analyseVariableDeclaringExpressions(nextToken, data, varScope);
if (innerScopeResult != null) {
if (innerScopeResult.getScope() != varScope) {
return innerScopeResult;
}
nextToken = innerScopeResult.getEndToken();
}
}
currentToken = nextToken;
}
return new Result(startToken, currentToken, null, varScope);
}
private Result analyseVariableDeclaringExpressions(IToken currentToken, QvtCompletionData data, Scope scope) {
if (QvtCompletionData.isKindOf(currentToken, QVTOParsersym.TK_let)) {
return analyseLetExpression(currentToken, data, scope);
} else if (QvtCompletionData.isKindOf(currentToken, LightweightParserUtil.QVTO_ITERATOR_TERMINALS)
|| QvtCompletionData.isKindOf(currentToken, LightweightParserUtil.OCL_ITERATOR_TERMINALS)
|| QvtCompletionData.isKindOf(currentToken, "iterate")) { //$NON-NLS-1$
return analyseIteratorExpression(currentToken, data, scope);
} else if (QvtCompletionData.isKindOf(currentToken, LightweightParserUtil.RESOLVE_FAMILY_TERMINALS)) {
return analyseResolveExpression(currentToken, data, scope);
} else if (QvtCompletionData.isKindOf(currentToken, QVTOParsersym.TK_while)) {
return analyseWhileExpression(currentToken, data, scope);
} else if (QvtCompletionData.isKindOf(currentToken, QVTOParsersym.TK_switch)) {
return analyseSwitchExpression(currentToken, data, scope);
} else if (QvtCompletionData.isKindOf(currentToken, LightweightParserUtil.FOR_EXP_TERMINALS)) {
return analyseForExpression(currentToken, data, scope);
} else if (QvtCompletionData.isKindOf(currentToken, QVTOParsersym.TK_LBRACE)) {
return analyseScopedVarVariables(currentToken, data, scope, false);
} else if (QvtCompletionData.isKindOf(currentToken, QVTOParsersym.TK_compute)) {
return analyseComputeExpression(currentToken, data, scope);
}
return null;
}
// starting 1 token before ')'
private Result analyseWhileLikeBody(IToken startToken, IToken preParenToken, IToken lastKnownGoodToken,
QvtCompletionData data, Scope whileLikeScope) {
IToken nextToken = getNextToken(preParenToken, data); // ')' expected
if (nextToken != null) {
lastKnownGoodToken = nextToken;
if (!QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_RPAREN)) {
return new Result(startToken, nextToken, null, whileLikeScope.getParent());
}
nextToken = getNextToken(nextToken, data); // '{' expected
if (nextToken != null) {
lastKnownGoodToken = nextToken;
if (!QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_LBRACE)) {
return new Result(startToken, nextToken, null, whileLikeScope.getParent());
}
Result forBodyExpResult = analyseScopedVarVariables(nextToken, data, whileLikeScope, false);
if (forBodyExpResult.getScope() != whileLikeScope) {
return forBodyExpResult;
}
return new Result(startToken, forBodyExpResult.getEndToken(), null, whileLikeScope.getParent());
}
}
return new Result(startToken, lastKnownGoodToken, null, whileLikeScope);
}
// starting from 'compute'
private Result analyseComputeExpression(IToken startToken, QvtCompletionData data, Scope scope) {
IToken nextToken = getNextToken(startToken, data);
if (nextToken == null) {
return new Result(startToken, startToken, null, new Scope(null));
}
if (!QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_LPAREN)) {
return null;
}
IToken currentToken = nextToken;
nextToken = getNextToken(nextToken, data);
if (nextToken == null) {
return new Result(startToken, currentToken, null, new Scope(null));
}
Scope computeExpScope = new Scope(scope);
Result extractVarVariableResult = extractVariable(nextToken, null, data, computeExpScope,
new int[] {QVTOParsersym.TK_RESET_ASSIGN, QVTOParsersym.TK_EQUAL}, NOT_A_TOKEN, QVTOParsersym.TK_RBRACE);
if (extractVarVariableResult.getScope() != computeExpScope) {
return extractVarVariableResult;
}
computeExpScope.addVariable(extractVarVariableResult.getString());
nextToken = extractVarVariableResult.getEndToken();
return analyseWhileLikeBody(startToken, nextToken, nextToken, data, computeExpScope);
}
// starting from 'forEach' or 'forOne'
private Result analyseForExpression(IToken startToken, QvtCompletionData data, Scope scope) {
IToken nextToken = getNextToken(startToken, data);
if (nextToken == null) {
return new Result(startToken, startToken, null, new Scope(null));
}
if (!QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_LPAREN)) {
return null;
}
Scope forExpScope = new Scope(scope);
Result addVariableListResult = addVariableList(nextToken, startToken, data, forExpScope,
new int[] {QVTOParsersym.TK_BAR, QVTOParsersym.TK_RPAREN},
NOT_A_TOKEN, QVTOParsersym.TK_COMMA, QVTOParsersym.TK_BAR, QVTOParsersym.TK_RPAREN);
if (addVariableListResult.getScope() != forExpScope) {
return addVariableListResult;
}
nextToken = addVariableListResult.getEndToken(); // Either '|' or ')' expected - optional condition
// extracting condition if available
if (QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_BAR)) {
IToken currentToken = nextToken;
nextToken = getNextToken(nextToken, data);
if (nextToken == null) {
return new Result(startToken, currentToken, null, forExpScope);
}
Result oclExpressionResult = extractOclExpression(nextToken, data, forExpScope);
if (oclExpressionResult.getScope() != forExpScope) {
return oclExpressionResult;
}
nextToken = oclExpressionResult.getEndToken();
} else { // see analyseWhileLikeBody contract
nextToken = LightweightParserUtil.getPreviousToken(nextToken);
}
return analyseWhileLikeBody(startToken, nextToken, nextToken, data, forExpScope);
}
// starting from 'while'
private Result analyseWhileExpression(IToken startToken, QvtCompletionData data, Scope scope) {
Scope whileScope = new Scope(scope);
IToken nextToken = getNextToken(startToken, data);
if (nextToken == null) {
return new Result(startToken, startToken, null, new Scope(null));
}
if (!QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_LPAREN)) {
return null;
}
nextToken = getNextToken(nextToken, data);
if (nextToken == null) {
return null;
}
if (!QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_IDENTIFIER)) {
return null;
}
Result variableResult = extractVariable(nextToken, null,
data, whileScope, new int[] {QVTOParsersym.TK_EQUAL, QVTOParsersym.TK_RESET_ASSIGN}, NOT_A_TOKEN, QVTOParsersym.TK_SEMICOLON);
if (variableResult == null) {
return null;
}
if (variableResult.getScope() != whileScope) {
return variableResult;
} else {
whileScope.addVariable(variableResult.getString());
nextToken = variableResult.getEndToken();
}
nextToken = getNextToken(nextToken, data);
assert QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_SEMICOLON);
IToken lastKnownGoodToken = nextToken;
nextToken = getNextToken(nextToken, data); // whileExp condition start
if (nextToken != null) {
Result whileConditionExpResult = extractOclExpression(nextToken, data, whileScope);
if (whileConditionExpResult.getScope() != whileScope) {
return whileConditionExpResult;
}
nextToken = whileConditionExpResult.getEndToken();
lastKnownGoodToken = nextToken;
return analyseWhileLikeBody(startToken, nextToken, lastKnownGoodToken, data, whileScope);
}
return new Result(startToken, lastKnownGoodToken, null, whileScope);
}
// starting from 'switch'
private Result analyseSwitchExpression(IToken startToken, QvtCompletionData data, Scope scope) {
Scope switchScope = new Scope(scope);
IToken nextToken = getNextToken(startToken, data);
if (nextToken == null) {
return new Result(startToken, startToken, null, new Scope(null));
}
if (!QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_LPAREN)) {
return null;
}
nextToken = getNextToken(nextToken, data);
if (nextToken == null) {
return null;
}
if (!QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_IDENTIFIER)) {
return null;
}
Result variableResult = extractVariable(nextToken, startToken,
data, switchScope, new int[] {QVTOParsersym.TK_EQUAL, QVTOParsersym.TK_RESET_ASSIGN}, NOT_A_TOKEN, QVTOParsersym.TK_RPAREN);
if (variableResult == null) {
return null;
}
if (variableResult.getScope() != switchScope) {
return variableResult;
} else {
switchScope.addVariable(variableResult.getString());
nextToken = variableResult.getEndToken();
}
nextToken = getNextToken(nextToken, data);
assert QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_RPAREN);
IToken lastKnownGoodToken = nextToken;
nextToken = getNextToken(nextToken, data); // '{' expected
if (nextToken != null) {
lastKnownGoodToken = nextToken;
if (!QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_LBRACE)) {
return new Result(startToken, nextToken, null, scope);
}
Result switchBodyExpResult = analyseScopedVarVariables(nextToken, data, switchScope, false);
if (switchBodyExpResult.getScope() != switchScope) {
return switchBodyExpResult;
}
return new Result(startToken, switchBodyExpResult.getEndToken(), null, scope);
}
return new Result(startToken, lastKnownGoodToken, null, switchScope);
}
// starting from 'let'
private Result analyseLetExpression(IToken startToken, QvtCompletionData data, Scope scope) {
return analyseLetLikeExpression(startToken, null, data, scope, new int[] {QVTOParsersym.TK_in}, NOT_A_TOKEN,
QVTOParsersym.TK_COMMA, QVTOParsersym.TK_in);
}
// starting from 'iteratorExpCSToken' or 'iterate'
private Result analyseIteratorExpression(IToken startToken, QvtCompletionData data, Scope scope) {
IToken nextToken = getNextToken(startToken, data);
if (nextToken == null) {
return new Result(startToken, startToken, null, new Scope(null));
}
if (!QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_LPAREN)) {
return null;
}
Result letLikeExpressionResult = analyseLetLikeExpression(nextToken, startToken, data, scope,
new int[] {QVTOParsersym.TK_BAR, QVTOParsersym.TK_RPAREN}, NOT_A_TOKEN, QVTOParsersym.TK_COMMA,
QVTOParsersym.TK_SEMICOLON, QVTOParsersym.TK_BAR);
if (letLikeExpressionResult.getScope() != scope) {
return letLikeExpressionResult;
}
nextToken = getNextToken(letLikeExpressionResult.getEndToken(), data);
if (nextToken == null) {
return new Result(startToken, letLikeExpressionResult.getEndToken(), null, new Scope(null));
}
if (!QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_RPAREN)) {
return new Result(startToken, letLikeExpressionResult.getEndToken(), null, scope);
}
return new Result(startToken, nextToken, null, scope);
}
// starting from 'resolveOp' or 'resolveInOp'
private Result analyseResolveExpression(IToken startToken, QvtCompletionData data, Scope scope) {
IToken nextToken = getNextToken(startToken, data);
if (nextToken == null) {
return new Result(startToken, startToken, null, new Scope(null));
}
if (!QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_LPAREN)) {
return null;
}
if (QvtCompletionData.isKindOf(startToken, LightweightParserUtil.RESOLVEIN_FAMILY_TERMINALS)) {
while ((nextToken = getNextToken(nextToken, data)) != null) {
if (QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_COMMA)) {
break;
}
if (QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_RPAREN)) {
return new Result(startToken, nextToken, null, scope);
}
}
}
if (nextToken == null) {
return null;
}
Result letLikeExpressionResult = analyseLetLikeExpression(nextToken, null, data, scope,
new int[] {QVTOParsersym.TK_BAR}, QVTOParsersym.TK_RPAREN, QVTOParsersym.TK_BAR);
if (letLikeExpressionResult.getScope() != scope) {
return letLikeExpressionResult;
}
nextToken = getNextToken(letLikeExpressionResult.getEndToken(), data);
if (nextToken == null) {
return new Result(startToken, letLikeExpressionResult.getEndToken(), null, new Scope(scope));
}
if (!QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_RPAREN)) {
return new Result(startToken, letLikeExpressionResult.getEndToken(), null, scope);
}
return new Result(startToken, nextToken, null, scope);
}
private Result addVariableList(IToken startToken, IToken iteratorExpressionStart,
QvtCompletionData data, Scope updatedScope, int[] varDeclTerminators, int unexpectedTerminator, int... delimiters) {
IToken nextToken = startToken;
IToken lastKnownGoodToken = startToken;
do {
nextToken = getNextToken(nextToken, data);
if (nextToken != null) {
Result variableResult = extractVariable(nextToken, iteratorExpressionStart,
data, updatedScope, new int[] {QVTOParsersym.TK_EQUAL}, unexpectedTerminator, delimiters);
if (variableResult == null) {
IToken followToken = getNextToken(nextToken, data);
return new Result(startToken, followToken != null ? followToken : nextToken, null, updatedScope.getParent());
}
if (variableResult.getScope() != updatedScope) {
return variableResult;
} else {
updatedScope.addVariable(variableResult.getString());
nextToken = variableResult.getEndToken();
lastKnownGoodToken = nextToken;
}
nextToken = getNextToken(nextToken, data);
}
} while ((nextToken != null) && !QvtCompletionData.isKindOf(nextToken, varDeclTerminators) && QvtCompletionData.isKindOf(nextToken, delimiters));
if (nextToken == null) {
return new Result(startToken, nextToken, null, new Scope(updatedScope), lastKnownGoodToken);
}
return new Result(startToken, nextToken, null, updatedScope, lastKnownGoodToken);
}
// starting from 'let'-like token
private Result analyseLetLikeExpression(IToken startToken, IToken iteratorExpressionStart,
QvtCompletionData data, Scope scope, int[] varDeclTerminators, int unexpectedTerminator, int... delimiters) {
Scope letLikeScope = new Scope(scope);
Result addVariableListResult = addVariableList(startToken, iteratorExpressionStart, data, letLikeScope, varDeclTerminators, unexpectedTerminator, delimiters);
if (addVariableListResult.getScope() != letLikeScope) {
return addVariableListResult;
}
IToken nextToken = addVariableListResult.getEndToken();
if ((nextToken != null) && QvtCompletionData.isKindOf(nextToken, varDeclTerminators)) {
nextToken = getNextToken(nextToken, data);
if (nextToken != null) {
Result oclExpressionResult = extractOclExpression(nextToken, data, letLikeScope);
if (oclExpressionResult.getScope() != letLikeScope) {
return oclExpressionResult;
}
return new Result(startToken, oclExpressionResult.getEndToken(), null, scope);
}
}
return new Result(startToken, addVariableListResult.getLastKnownGoodToken(), null, letLikeScope);
}
// starting from 'IDENTIFIER'
private Result extractVariable(IToken startToken, IToken iteratorExpressionStart, QvtCompletionData data,
Scope scope, int[] initializers, int unexpectedTerminator, int... delimiters) {
int parenCount = 0;
boolean isTypeDefined = false;
IToken currentToken = startToken;
IToken nextToken;
while ((nextToken = getNextToken(currentToken, data)) != null) {
if (QvtCompletionData.isKindOf(nextToken, unexpectedTerminator)) {
return null;
}
if (QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_COLON)) {
isTypeDefined = true;
}
if (QvtCompletionData.isKindOf(nextToken, delimiters)) {
if (isTypeDefined) {
return new Result(startToken, currentToken,
"var " + LightweightParserUtil.getText(startToken, currentToken) + ';', scope); //$NON-NLS-1$
}
// Implicit types may be in iterator-variables of iterating expressions
String deducedType = deduceImplicitCollectionElementType(iteratorExpressionStart, data);
if (deducedType == null) {
return new Result(startToken, currentToken, "", scope); //$NON-NLS-1$
}
String varText = "var " + LightweightParserUtil.getText(startToken, currentToken) //$NON-NLS-1$
+ " := " + deducedType + ';'; //$NON-NLS-1$
return new Result(startToken, currentToken, varText, scope);
}
if (QvtCompletionData.isKindOf(currentToken, initializers)) {
isTypeDefined = true;
Result oclExpressionResult = extractOclExpression(nextToken, data, scope);
if (oclExpressionResult.getScope() != scope) {
return oclExpressionResult;
}
IToken previousToken = LightweightParserUtil.getPreviousToken(currentToken);
String identifierAndOrType = ((previousToken != null) && (startToken.getTokenIndex() <= previousToken.getTokenIndex()))
? LightweightParserUtil.getText(startToken, previousToken) : ""; //$NON-NLS-1$
return new Result(startToken, oclExpressionResult.getEndToken(),
"var " + identifierAndOrType //$NON-NLS-1$
+ " := " + oclExpressionResult.getString() + ';', scope); //$NON-NLS-1$
}
if (QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_LPAREN)) {
parenCount++;
}
if (QvtCompletionData.isKindOf(nextToken, QVTOParsersym.TK_RPAREN)) {
parenCount--;
if (parenCount < 0) {
return new Result(startToken, startToken, "", scope); //$NON-NLS-1$
}
}
currentToken = nextToken;
}
return new Result(startToken, currentToken, null, new Scope(scope));
}
private String deduceImplicitCollectionElementType(IToken iteratorExpressionStart, QvtCompletionData data) {
if (iteratorExpressionStart == null) {
return null;
}
IToken accessorToken = LightweightParserUtil.getPreviousToken(iteratorExpressionStart);
if ((accessorToken != null) && QvtCompletionData.isKindOf(accessorToken,
QVTOParsersym.TK_DOT, QVTOParsersym.TK_ARROW)) {
IToken[] oclExpressionCSTokens = LightweightParserUtil.extractOclExpressionCSTokens(accessorToken, data);
if (oclExpressionCSTokens != null) {
String collectionType = LightweightParserUtil.getText(oclExpressionCSTokens);
return collectionType + "->any(true)"; //$NON-NLS-1$
}
}
return null;
}
// starting from the first token of the OCL expression
private Result extractOclExpression(IToken startToken, QvtCompletionData data, Scope scope) {
IToken currentToken = startToken;
int bracingMode = -1;
int depth = 0;
IToken nextToken;
while ((nextToken = getNextToken(currentToken, data)) != null) {
Result innerScopeResult = analyseVariableDeclaringExpressions(currentToken, data, scope);
if (innerScopeResult != null) {
if (innerScopeResult.getScope() != scope) {
return innerScopeResult;
}
currentToken = innerScopeResult.getEndToken();
nextToken = getNextToken(currentToken, data);
if (nextToken == null) {
return new Result(startToken, currentToken, null, new Scope(scope));
}
} else {
int bracingPairKind = LightweightParserUtil.getBracingPairKind(currentToken, true);
if (depth == 0) {
if (bracingPairKind >= 0) {
bracingMode = bracingPairKind;
depth++;
}
} else {
if (bracingPairKind == bracingMode) {
depth++;
} else {
if (bracingMode == LightweightParserUtil.getBracingPairKind(currentToken, false)) {
depth--;
}
}
}
}
if ((depth == 0) && QvtCompletionData.isKindOf(nextToken, LightweightParserUtil.OCLEXPRESSION_END_TOKENS)) {
return new Result(startToken, currentToken, LightweightParserUtil.getText(startToken, currentToken), scope);
}
currentToken = nextToken;
}
return new Result(startToken, currentToken, null, new Scope(scope));
}
private static IToken getNextToken(IToken token, QvtCompletionData data) {
if (token == null) {
// we mustn't be here; however, in case we are - that
// won't be a problem
return null;
}
if (token == data.getLeftToken()) {
return null;
}
IToken nextToken = LightweightParserUtil.getNextToken(token);
if (nextToken == null) {
return null;
}
return nextToken;
}
}