/*******************************************************************************
* Copyright (c) 2007, 2010 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;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalEnv;
import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalEnvFactory;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.QvtOperationalVisitorCS;
import org.eclipse.m2m.internal.qvt.oml.compiler.QvtCompilerOptions;
import org.eclipse.m2m.internal.qvt.oml.compiler.UnitProxy;
import org.eclipse.m2m.internal.qvt.oml.cst.TypeSpecCS;
import org.eclipse.m2m.internal.qvt.oml.cst.completion.parser.LightweightParser;
import org.eclipse.m2m.internal.qvt.oml.cst.completion.parser.LightweightTypeParser;
import org.eclipse.m2m.internal.qvt.oml.cst.parser.AbstractQVTParser;
import org.eclipse.m2m.internal.qvt.oml.cst.parser.QVTOLexer;
import org.eclipse.m2m.internal.qvt.oml.cst.parser.QVTOParsersym;
import org.eclipse.m2m.internal.qvt.oml.editor.ui.Activator;
import org.eclipse.ocl.OCLInput;
import org.eclipse.ocl.ParserException;
import org.eclipse.ocl.cst.CSTNode;
import org.eclipse.ocl.cst.OCLExpressionCS;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.parser.OCLLexer;
import org.eclipse.ocl.utilities.PredefinedType;
import lpg.runtime.IPrsStream;
import lpg.runtime.IToken;
import lpg.runtime.PrsStream;
/**
* @author aigdalov
* Created on Oct 26, 2007
*/
public class LightweightParserUtil {
public enum ParserTypeEnum {
LIGHTWEIGHT_PARSER, LIGHTWEIGHT_TYPE_PARSER
}
public static final int[] IMPERATIVE_OPERATION_TOKENS = {
QVTOParsersym.TK_mapping,
QVTOParsersym.TK_helper,
QVTOParsersym.TK_query,
QVTOParsersym.TK_main
};
public static final int[] OCLEXPRESSION_START_TOKENS = {
QVTOParsersym.TK_RESET_ASSIGN, QVTOParsersym.TK_ADD_ASSIGN,
QVTOParsersym.TK_EQUAL, QVTOParsersym.TK_NOT_EQUAL, QVTOParsersym.TK_NOT_EQUAL_EXEQ,
QVTOParsersym.TK_GREATER, QVTOParsersym.TK_LESS,
QVTOParsersym.TK_GREATER_EQUAL, QVTOParsersym.TK_LESS_EQUAL,
QVTOParsersym.TK_and, QVTOParsersym.TK_or,
QVTOParsersym.TK_xor, QVTOParsersym.TK_implies,
QVTOParsersym.TK_not,
QVTOParsersym.TK_PLUS, QVTOParsersym.TK_MINUS,
QVTOParsersym.TK_MULTIPLY, QVTOParsersym.TK_DIVIDE,
QVTOParsersym.TK_BAR, QVTOParsersym.TK_QUESTIONMARK,
QVTOParsersym.TK_COMMA, QVTOParsersym.TK_SEMICOLON,
QVTOParsersym.TK_DOTDOT,
QVTOParsersym.TK_if, QVTOParsersym.TK_then, QVTOParsersym.TK_else,
QVTOParsersym.TK_LBRACE, QVTOParsersym.TK_LBRACKET, QVTOParsersym.TK_LPAREN,
QVTOParsersym.TK_in,
QVTOParsersym.TK_when,
QVTOParsersym.TK_return
};
public static final int[] OCLEXPRESSION_END_TOKENS = {
QVTOParsersym.TK_BAR, QVTOParsersym.TK_QUESTIONMARK,
QVTOParsersym.TK_COMMA, QVTOParsersym.TK_SEMICOLON,
QVTOParsersym.TK_DOTDOT,
QVTOParsersym.TK_then, QVTOParsersym.TK_else, QVTOParsersym.TK_endif,
QVTOParsersym.TK_RBRACE, QVTOParsersym.TK_RBRACKET, QVTOParsersym.TK_RPAREN,
QVTOParsersym.TK_in,
};
public static final int[] OCLEXPRESSION_MANDATORY_TERMINATION_TOKENS =
uniteIntArrays(IMPERATIVE_OPERATION_TOKENS, new int[] {
QVTOParsersym.TK_init, QVTOParsersym.TK_end,
QVTOParsersym.TK_transformation, QVTOParsersym.TK_modeltype,
QVTOParsersym.TK_uses,
QVTOParsersym.TK_import, QVTOParsersym.TK_library
});
public static final int[][] BRACING_PAIRS = {
{QVTOParsersym.TK_if, QVTOParsersym.TK_endif},
{QVTOParsersym.TK_LPAREN, QVTOParsersym.TK_RPAREN},
{QVTOParsersym.TK_LBRACE, QVTOParsersym.TK_RBRACE},
{QVTOParsersym.TK_LBRACKET, QVTOParsersym.TK_RBRACKET},
};
public static final int[] RESOLVE_FAMILY_TERMINALS = {
QVTOParsersym.TK_resolve,
QVTOParsersym.TK_resolveone,
QVTOParsersym.TK_resolveIn,
QVTOParsersym.TK_resolveoneIn,
QVTOParsersym.TK_invresolve,
QVTOParsersym.TK_invresolveone,
QVTOParsersym.TK_invresolveIn,
QVTOParsersym.TK_invresolveoneIn
};
public static final int[] RESOLVEIN_FAMILY_TERMINALS = {
QVTOParsersym.TK_resolveIn,
QVTOParsersym.TK_resolveoneIn,
QVTOParsersym.TK_invresolveIn,
QVTOParsersym.TK_invresolveoneIn
};
public static final String[] OCL_ITERATOR_TERMINALS = {
PredefinedType.SELECT_NAME,
PredefinedType.REJECT_NAME,
PredefinedType.COLLECT_NAME,
PredefinedType.FOR_ALL_NAME,
PredefinedType.EXISTS_NAME,
PredefinedType.IS_UNIQUE_NAME,
PredefinedType.ONE_NAME,
PredefinedType.ANY_NAME,
PredefinedType.COLLECT_NESTED_NAME,
PredefinedType.SORTED_BY_NAME,
PredefinedType.CLOSURE_NAME,
};
public static final int[] QVTO_ITERATOR_TERMINALS_WITH_IMPLICIT_ITERATOR = {
QVTOParsersym.TK_xselect,
QVTOParsersym.TK_xcollect,
QVTOParsersym.TK_selectOne,
QVTOParsersym.TK_collectOne
};
public static final int[] QVTO_ITERATOR_TERMINALS =
uniteIntArrays(QVTO_ITERATOR_TERMINALS_WITH_IMPLICIT_ITERATOR, new int[] {
QVTOParsersym.TK_collectselect,
QVTOParsersym.TK_collectselectOne
});
public static final int[] MAPPING_CLAUSE_TOKENS = {
QVTOParsersym.TK_when,
QVTOParsersym.TK_where
};
public static final int[] MAPPING_CALL_TERMINALS = {
QVTOParsersym.TK_map,
QVTOParsersym.TK_xmap
};
public static final int[] FOR_EXP_TERMINALS = {
QVTOParsersym.TK_forEach,
QVTOParsersym.TK_forOne
};
public static int[] uniteIntArrays(int[] array1, int[] array2) {
int[] result = new int[array1.length + array2.length];
System.arraycopy(array1, 0, result, 0, array1.length);
System.arraycopy(array2, 0, result, array1.length, array2.length);
return result;
}
public static final IToken getNextToken(IToken token) {
IPrsStream prsStream = token.getIPrsStream();
int nextTokenIndex = token.getTokenIndex() + 1;
if (nextTokenIndex < prsStream.getSize()) {
return prsStream.getTokenAt(nextTokenIndex);
}
return null;
}
public static final IToken getPreviousToken(IToken token) {
IPrsStream prsStream = token.getIPrsStream();
int nextTokenIndex = token.getTokenIndex() - 1;
if (nextTokenIndex >= 0) {
return prsStream.getTokenAt(nextTokenIndex);
}
return null;
}
public static final IToken getNextTokenByKind(IToken startToken, int kind) {
return getNextTokenByKind(startToken, new int[] {kind});
}
public static final IToken getNextTokenByKind(IToken startToken, int[] kinds) {
IPrsStream prsStream = startToken.getIPrsStream();
for (int i = startToken.getTokenIndex(), n = prsStream.getSize(); i < n; i++) {
IToken token = prsStream.getTokenAt(i);
if (QvtCompletionData.isKindOf(token, kinds)) {
return token;
}
}
return null;
}
public static final IToken getPreviousTokenByKind(IToken startToken, int kind) {
return getPreviousTokenByKind(startToken, new int[] {kind});
}
public static final IToken getPreviousTokenByKind(IToken startToken, int[] kinds) {
IToken currentToken = startToken;
while ((currentToken = LightweightParserUtil.getPreviousToken(currentToken)) != null) {
if (QvtCompletionData.isKindOf(currentToken, kinds)) {
return currentToken;
}
}
return null;
}
public static final String getTokenText(int tokenKind) {
return QVTOParsersym.orderedTerminalSymbols[tokenKind];
}
public static final OCLExpression<EClassifier> getOclExpression(IToken trailingToken, QvtCompletionData data, ParserTypeEnum parserType) {
OCLExpressionCS oclExpressionCS = getOclExpressionCS(trailingToken, data, parserType);
return getOclExpression(oclExpressionCS, data);
}
public static final OCLExpression<EClassifier> getOclExpression(IToken[] tokens, QvtCompletionData data, ParserTypeEnum parserType) {
OCLExpressionCS oclExpressionCS = getOclExpressionCS(tokens, data, parserType);
if (oclExpressionCS != null) {
return getOclExpression(oclExpressionCS, data);
}
return null;
}
public static final OCLExpression<EClassifier> getOclExpression(OCLExpressionCS oclExpressionCS, QvtCompletionData data) {
if (oclExpressionCS != null) {
OCLLexer oclLexer = new OCLLexer(data.getEnvironment(), new char[0], data.getCFile().getName(), 4);
QvtCompilerOptions options = new QvtCompilerOptions();
options.setReportErrors(false);
options.setShowAnnotations(false);
options.setSourceLineNumbersEnabled(false);
QvtOperationalVisitorCS visitor = new QvtOperationalVisitorCS(oclLexer, options);
return visitor.analyzeExpressionCS(oclExpressionCS, data.getEnvironment());
}
return null;
}
public static final OCLExpressionCS getOclExpressionCS(IToken[] tokens, QvtCompletionData data, ParserTypeEnum parserType) {
if (tokens != null) {
CSTNode cstNode = LightweightParserUtil.parse(tokens, data.getCFile(), parserType);
if (cstNode instanceof OCLExpressionCS) {
return (OCLExpressionCS) cstNode;
}
if (cstNode instanceof TypeSpecCS) {
return ((TypeSpecCS) cstNode).getTypeCS();
}
}
return null;
}
public static final String getText(IToken start, IToken end) {
int startOffset = start.getStartOffset();
int endOffset = end.getEndOffset();
IPrsStream prsStream = start.getIPrsStream();
return getText(startOffset, endOffset, prsStream);
}
public static final String getText(CSTNode cstNode, PrsStream prsStream) {
int startOffset = cstNode.getStartOffset();
int endOffset = cstNode.getEndOffset();
return getText(startOffset, endOffset, prsStream);
}
private static String getText(int startOffset, int endOffset, IPrsStream prsStream) {
return new String(prsStream.getInputChars(), startOffset, endOffset - startOffset + 1);
}
public static final String getText(IToken[] tokens) {
if (tokens.length == 0) {
return ""; //$NON-NLS-1$
}
return LightweightParserUtil.getText(tokens[0], tokens[tokens.length - 1]);
}
public static final CSTNode parse(IToken[] tokens, UnitProxy unit, ParserTypeEnum parserType) {
String script = LightweightParserUtil.getText(tokens);
return parse(script, unit, parserType);
}
public static final CSTNode parse(String script, UnitProxy unit, ParserTypeEnum parserType) {
try {
QvtOperationalEnv env = new QvtOperationalEnvFactory().createEnvironment();
QVTOLexer lexer = new QVTOLexer(env, new OCLInput(script).getContent(), unit.getName(), 4);
AbstractQVTParser parser = null;
switch (parserType) {
case LIGHTWEIGHT_PARSER: parser = new RunnableLightweightParser(lexer); break;
case LIGHTWEIGHT_TYPE_PARSER: parser = new RunnableLightweightTypeParser(lexer); break;
default: throw new RuntimeException("Unknown parserType: " + parserType); //$NON-NLS-1$
}
parser.getIPrsStream().resetTokenStream();
lexer.lexer(parser.getIPrsStream());
return (CSTNode) ((ILightweightParser) parser).runParser();
} catch (Exception ex) {
Activator.log(ex);
return null;
}
}
private static final OCLExpressionCS getOclExpressionCS(IToken trailingToken, QvtCompletionData data, ParserTypeEnum parserType) {
IToken[] tokens = extractOclExpressionCSTokens(trailingToken, data);
return getOclExpressionCS(tokens, data, parserType);
}
public static final IToken[] extractOclExpressionCSTokens(IToken trailingToken, QvtCompletionData data) {
List<IToken> tokens = new ArrayList<IToken>();
IPrsStream prsStream = data.getPrsStream();
int mode = BRACING_PAIRS.length;
int depth = 0;
for (int i = trailingToken.getTokenIndex() - 1; i >= 0; i--) {
IToken token = prsStream.getTokenAt(i);
if (QvtCompletionData.isKindOf(token, OCLEXPRESSION_MANDATORY_TERMINATION_TOKENS)) {
return null;
}
if (depth == 0) {
if (QvtCompletionData.isKindOf(token, OCLEXPRESSION_START_TOKENS)) {
return tokens.toArray(new IToken[tokens.size()]);
} else if (QvtCompletionData.isKindOf(token, QVTOParsersym.TK_RPAREN)) {
// Considering switch in 07-07-07 spec: switch { case (expr) /@*@/ expr ...
IToken lParen = getPairingBrace(token, false);
if (lParen != null) {
IToken caseToken = LightweightParserUtil.getPreviousToken(lParen);
if ((caseToken != null) && QvtCompletionData.isKindOf(caseToken, QVTOParsersym.TK_case)) {
return tokens.toArray(new IToken[tokens.size()]);
}
}
}
for (int j = 0; j < BRACING_PAIRS.length; j++) {
if (token.getKind() == BRACING_PAIRS[j][1]) {
mode = j;
depth++;
break;
}
}
} else {
if (token.getKind() == BRACING_PAIRS[mode][0]) {
depth--;
} else if (token.getKind() == BRACING_PAIRS[mode][1]) {
depth++;
}
}
tokens.add(0, token);
}
return null;
}
public static final IToken getPairingBrace(IToken brace, boolean isForward) {
int bracingPairKind = getBracingPairKind(brace, isForward);
int lBraceKind = BRACING_PAIRS[bracingPairKind][0];
int rBraceKind = BRACING_PAIRS[bracingPairKind][1];
int depth = 0;
for (IToken token = brace; token != null; token = (isForward) ? LightweightParserUtil.getNextToken(token) : LightweightParserUtil.getPreviousToken(token)) {
if (QvtCompletionData.isKindOf(token, lBraceKind)) {
depth++;
} else if (QvtCompletionData.isKindOf(token, rBraceKind)) {
depth--;
} else if (QvtCompletionData.isKindOf(token, OCLEXPRESSION_MANDATORY_TERMINATION_TOKENS)) {
return null;
}
if (depth == 0) {
return token;
}
}
return null;
}
public static final int getBracingPairKind(IToken token, boolean isStart) {
for (int i = 0; i < BRACING_PAIRS.length; i++) {
int kind = isStart ? BRACING_PAIRS[i][0] : BRACING_PAIRS[i][1];
if (QvtCompletionData.isKindOf(token, kind)) {
return i;
}
}
return -1;
}
public static final IToken[] getScopedIdentifier(IToken trailingToken) {
boolean isColonColonExpected = QvtCompletionData.isKindOf(trailingToken, QVTOParsersym.TK_COLONCOLON);
List<IToken> tokens = new LinkedList<IToken>();
IToken currentToken = trailingToken;
do {
if (isColonColonExpected) {
if (!QvtCompletionData.isKindOf(currentToken, QVTOParsersym.TK_COLONCOLON)) {
return tokens.toArray(new IToken[tokens.size()]);
}
} else {
if (QvtCompletionData.isKindOf(currentToken, QVTOParsersym.TK_IDENTIFIER,
QVTOParsersym.TK_main)) {
tokens.add(0, currentToken);
} else {
return null; // IDENTIFIER expected but smth else found!
}
}
isColonColonExpected = !isColonColonExpected;
} while ((currentToken = LightweightParserUtil.getPreviousToken(currentToken)) != null);
return null; // unexpected start of stream!
}
private static interface ILightweightParser {
public EObject runParser() throws ParserException;
}
private static class RunnableLightweightParser extends LightweightParser implements ILightweightParser {
public RunnableLightweightParser(QVTOLexer lexStream) {
super(lexStream);
}
public CSTNode runParser() throws ParserException {
return parser(-1);
}
}
private static class RunnableLightweightTypeParser extends LightweightTypeParser implements ILightweightParser {
public RunnableLightweightTypeParser(QVTOLexer lexStream) {
super(lexStream);
}
public CSTNode runParser() throws ParserException {
return parser(-1);
}
}
}