blob: 0e4db9e6320fe2399f52926e432a33a175069aa0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2021 Obeo.
* 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.acceleo.query.parser;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EmptyStackException;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import org.antlr.v4.runtime.ANTLRErrorListener;
import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.eclipse.acceleo.query.ast.And;
import org.eclipse.acceleo.query.ast.Binding;
import org.eclipse.acceleo.query.ast.BooleanLiteral;
import org.eclipse.acceleo.query.ast.Call;
import org.eclipse.acceleo.query.ast.CallType;
import org.eclipse.acceleo.query.ast.CollectionTypeLiteral;
import org.eclipse.acceleo.query.ast.Conditional;
import org.eclipse.acceleo.query.ast.EnumLiteral;
import org.eclipse.acceleo.query.ast.Error;
import org.eclipse.acceleo.query.ast.ErrorBinding;
import org.eclipse.acceleo.query.ast.ErrorCall;
import org.eclipse.acceleo.query.ast.ErrorConditional;
import org.eclipse.acceleo.query.ast.ErrorEClassifierTypeLiteral;
import org.eclipse.acceleo.query.ast.ErrorEnumLiteral;
import org.eclipse.acceleo.query.ast.ErrorExpression;
import org.eclipse.acceleo.query.ast.ErrorStringLiteral;
import org.eclipse.acceleo.query.ast.ErrorTypeLiteral;
import org.eclipse.acceleo.query.ast.ErrorVariableDeclaration;
import org.eclipse.acceleo.query.ast.Expression;
import org.eclipse.acceleo.query.ast.Implies;
import org.eclipse.acceleo.query.ast.IntegerLiteral;
import org.eclipse.acceleo.query.ast.Lambda;
import org.eclipse.acceleo.query.ast.Let;
import org.eclipse.acceleo.query.ast.Literal;
import org.eclipse.acceleo.query.ast.NullLiteral;
import org.eclipse.acceleo.query.ast.Or;
import org.eclipse.acceleo.query.ast.RealLiteral;
import org.eclipse.acceleo.query.ast.SequenceInExtensionLiteral;
import org.eclipse.acceleo.query.ast.SetInExtensionLiteral;
import org.eclipse.acceleo.query.ast.StringLiteral;
import org.eclipse.acceleo.query.ast.TypeLiteral;
import org.eclipse.acceleo.query.ast.TypeSetLiteral;
import org.eclipse.acceleo.query.ast.VarRef;
import org.eclipse.acceleo.query.ast.VariableDeclaration;
import org.eclipse.acceleo.query.parser.QueryParser.AddContext;
import org.eclipse.acceleo.query.parser.QueryParser.AndContext;
import org.eclipse.acceleo.query.parser.QueryParser.BindingContext;
import org.eclipse.acceleo.query.parser.QueryParser.BooleanTypeContext;
import org.eclipse.acceleo.query.parser.QueryParser.CallExpContext;
import org.eclipse.acceleo.query.parser.QueryParser.CallOrApplyContext;
import org.eclipse.acceleo.query.parser.QueryParser.ClassifierSetTypeContext;
import org.eclipse.acceleo.query.parser.QueryParser.ClassifierTypeContext;
import org.eclipse.acceleo.query.parser.QueryParser.ClassifierTypeRuleContext;
import org.eclipse.acceleo.query.parser.QueryParser.CollectionCallContext;
import org.eclipse.acceleo.query.parser.QueryParser.CompContext;
import org.eclipse.acceleo.query.parser.QueryParser.ConditionalContext;
import org.eclipse.acceleo.query.parser.QueryParser.EnumLitContext;
import org.eclipse.acceleo.query.parser.QueryParser.ErrorClassifierTypeContext;
import org.eclipse.acceleo.query.parser.QueryParser.ErrorEnumLitContext;
import org.eclipse.acceleo.query.parser.QueryParser.ErrorStringLitContext;
import org.eclipse.acceleo.query.parser.QueryParser.ExplicitSeqLitContext;
import org.eclipse.acceleo.query.parser.QueryParser.ExplicitSetLitContext;
import org.eclipse.acceleo.query.parser.QueryParser.ExpressionContext;
import org.eclipse.acceleo.query.parser.QueryParser.FalseLitContext;
import org.eclipse.acceleo.query.parser.QueryParser.FeatureContext;
import org.eclipse.acceleo.query.parser.QueryParser.ImpliesContext;
import org.eclipse.acceleo.query.parser.QueryParser.IntTypeContext;
import org.eclipse.acceleo.query.parser.QueryParser.IntegerLitContext;
import org.eclipse.acceleo.query.parser.QueryParser.IterationCallContext;
import org.eclipse.acceleo.query.parser.QueryParser.LetExprContext;
import org.eclipse.acceleo.query.parser.QueryParser.LiteralContext;
import org.eclipse.acceleo.query.parser.QueryParser.MinContext;
import org.eclipse.acceleo.query.parser.QueryParser.MultContext;
import org.eclipse.acceleo.query.parser.QueryParser.NavigationSegmentContext;
import org.eclipse.acceleo.query.parser.QueryParser.NotContext;
import org.eclipse.acceleo.query.parser.QueryParser.NullLitContext;
import org.eclipse.acceleo.query.parser.QueryParser.OrContext;
import org.eclipse.acceleo.query.parser.QueryParser.ParenContext;
import org.eclipse.acceleo.query.parser.QueryParser.RealLitContext;
import org.eclipse.acceleo.query.parser.QueryParser.RealTypeContext;
import org.eclipse.acceleo.query.parser.QueryParser.SeqTypeContext;
import org.eclipse.acceleo.query.parser.QueryParser.ServiceCallContext;
import org.eclipse.acceleo.query.parser.QueryParser.SetTypeContext;
import org.eclipse.acceleo.query.parser.QueryParser.StrTypeContext;
import org.eclipse.acceleo.query.parser.QueryParser.StringLitContext;
import org.eclipse.acceleo.query.parser.QueryParser.TrueLitContext;
import org.eclipse.acceleo.query.parser.QueryParser.TypeLiteralContext;
import org.eclipse.acceleo.query.parser.QueryParser.VarRefContext;
import org.eclipse.acceleo.query.parser.QueryParser.VariableDefinitionContext;
import org.eclipse.acceleo.query.parser.QueryParser.XorContext;
import org.eclipse.acceleo.query.runtime.AcceleoQueryEvaluationException;
import org.eclipse.acceleo.query.runtime.IQueryBuilderEngine.AstResult;
import org.eclipse.acceleo.query.runtime.IReadOnlyQueryEnvironment;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
/**
* The {@link AstBuilderListener} builds an AST when plugged into the parser.
*
* @author <a href="mailto:romain.guider@obeo.fr">Romain Guider</a>
*/
public class AstBuilderListener extends QueryBaseListener {
/**
* The plugin ID.
*/
public static final String PLUGIN_ID = "org.eclipse.acceleo.query";
/**
* Feature access service name.
*/
public static final String FEATURE_ACCESS_SERVICE_NAME = "aqlFeatureAccess";
/**
* OCL is kind of service name.
*/
public static final String OCL_IS_KIND_OF_SERVICE_NAME = "oclIsKindOf";
/**
* OCL is type of service name.
*/
public static final String OCL_IS_TYPE_OF_SERVICE_NAME = "oclIsTypeOf";
/**
* <code>if<code> operator.
*/
public static final String CONDITIONAL_OPERATOR = "if";
/**
* <code>not<code> service name.
*/
public static final String NOT_SERVICE_NAME = "not";
/**
* <code>not<code> operator.
*/
public static final String NOT_OPERATOR = "not";
/**
* <code>let<code> operator.
*/
public static final String LET_OPERATOR = "let";
/**
* <code>&lt;&gt;<code> service name.
*/
public static final String DIFFERS_SERVICE_NAME = "differs";
/**
* <code>&lt;&gt;<code> operator.
*/
public static final String DIFFERS_OPERATOR = "<>";
/**
* <code>!=<code> operator.
*/
public static final String DIFFERS_JAVA_OPERATOR = "!=";
/**
* <code>=<code> service name.
*/
public static final String EQUALS_SERVICE_NAME = "equals";
/**
* <code>=<code> operator.
*/
public static final String EQUALS_OPERATOR = "=";
/**
* <code>==<code> operator.
*/
public static final String EQUALS_JAVA_OPERATOR = "==";
/**
* <code>&gt;=<code> service name.
*/
public static final String GREATER_THAN_EQUAL_SERVICE_NAME = "greaterThanEqual";
/**
* <code>&gt;=<code> operator.
*/
public static final String GREATER_THAN_EQUAL_OPERATOR = ">=";
/**
* <code>&gt;<code> service name.
*/
public static final String GREATER_THAN_SERVICE_NAME = "greaterThan";
/**
* <code>&gt;<code> operator.
*/
public static final String GREATER_THAN_OPERATOR = ">";
/**
* <code>&lt;=<code> service name.
*/
public static final String LESS_THAN_EQUAL_SERVICE_NAME = "lessThanEqual";
/**
* <code>&lt;=<code> operator.
*/
public static final String LESS_THAN_EQUAL_OPERATOR = "<=";
/**
* <code>&lt;<code> service name.
*/
public static final String LESS_THAN_SERVICE_NAME = "lessThan";
/**
* <code>&lt;<code> operator.
*/
public static final String LESS_THAN_OPERATOR = "<";
/**
* <code>/<code> service name.
*/
public static final String DIV_SERVICE_NAME = "divOp";
/**
* <code>/<code> operator.
*/
public static final String DIV_OPERATOR = "/";
/**
* <code>*<code> service name.
*/
public static final String MULT_SERVICE_NAME = "mult";
/**
* <code>*<code> operator.
*/
public static final String MULT_OPERATOR = "*";
/**
* <code>-<code> service name.
*/
public static final String SUB_SERVICE_NAME = "sub";
/**
* <code>-<code> operator.
*/
public static final String SUB_OPERATOR = "-";
/**
* <code>+<code> service name.
*/
public static final String ADD_SERVICE_NAME = "add";
/**
* <code>+<code> operator.
*/
public static final String ADD_OPERATOR = "+";
/**
* <code>-<code> service name.
*/
public static final String UNARY_MIN_SERVICE_NAME = "unaryMin";
/**
* <code>-<code> operator.
*/
public static final String UNARY_MIN_OPERATOR = "-";
/**
* <code>and<code> service name.
*/
public static final String AND_SERVICE_NAME = "and";
/**
* <code>and<code> operator.
*/
public static final String AND_OPERATOR = "and";
/**
* <code>or<code> service name.
*/
public static final String OR_SERVICE_NAME = "or";
/**
* <code>or<code> operator.
*/
public static final String OR_OPERATOR = "or";
/**
* <code>xor<code> service name.
*/
public static final String XOR_SERVICE_NAME = "xor";
/**
* <code>xor<code> operator.
*/
public static final String XOR_OPERATOR = "xor";
/**
* <code>implies<code> service name.
*/
public static final String IMPLIES_SERVICE_NAME = "implies";
/**
* <code>implies<code> operator.
*/
public static final String IMPLIES_OPERATOR = "implies";
/**
* {@link Set} of operator service names.
*/
public static final Set<String> OPERATOR_SERVICE_NAMES = initOperatorServiceNames();
/**
* This should not happen.
*/
private static final String THIS_SHOULDN_T_HAPPEN = "This shouldn't happen.";
/**
* Invalid enum literal message.
*/
private static final String INVALID_ENUM_LITERAL = "invalid enum literal: %s";
/**
* Invalid type literal.
*/
private static final String INVALID_TYPE_LITERAL = "invalid type literal %s";
/**
* Ambiguous {@link EEnumLiteral} message.
*/
private static final String AMBIGUOUS_ENUM_LITERAL = "several enumliterals are matching the literal name: %s, eenum : %s and package name : %s";
/**
* Ambiguous {@link EClassifier} message.
*/
private static final String AMBIGUOUS_TYPE_LITERAL = "several types are matching the EClassifier name: %s , package name : %s";
/**
* Number of children in {@link ConditionalContext}.
*/
private static final int CONDITIONAL_CONTEXT_CHILD_COUNT = 7;
/**
* Error listener.
*
* @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
*/
private final class QueryErrorListener extends BaseErrorListener {
/**
* Missing expression message.
*/
private static final String MISSING_EXPRESSION = "missing expression";
@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line,
int charPositionInLine, String msg, RecognitionException e) {
if (e != null) {
if (e.getCtx() instanceof IterationCallContext) {
iterationCallContextError(e);
} else if (e.getCtx() instanceof TypeLiteralContext) {
typeLiteralContextError(offendingSymbol, msg, e);
} else if (e.getCtx() instanceof LiteralContext) {
literalContextError(offendingSymbol, msg, e);
} else if (e.getCtx() instanceof ClassifierTypeRuleContext) {
classifierTypeRuleContextError(offendingSymbol, msg, e);
} else if (e.getCtx() instanceof VariableDefinitionContext) {
variableDefinitionContextError(offendingSymbol, e);
} else if (e.getCtx() instanceof CallExpContext) {
callExpContextError(offendingSymbol, e);
} else if (e.getCtx() instanceof NavigationSegmentContext) {
navigationSegmentContextError(offendingSymbol);
} else if (e.getCtx() instanceof BindingContext) {
bindingContextError(offendingSymbol, e);
} else if (e.getCtx() instanceof ConditionalContext) {
errorRule = QueryParser.RULE_expression;
} else if (e.getCtx() instanceof ParenContext) {
// nothing to do here
} else {
defaultError(offendingSymbol, msg, e);
}
} else if (recognizer instanceof QueryParser) {
noRecognitionException(recognizer, offendingSymbol, msg);
} else {
final QueryParser parser = (QueryParser)recognizer;
final Integer startPosition = Integer.valueOf(((EnumLitContext)parser.getContext()).start
.getStartIndex());
final Integer endPosition = Integer.valueOf(((Token)offendingSymbol).getStopIndex() + 1);
diagnosticStack.push(new BasicDiagnostic(Diagnostic.WARNING, PLUGIN_ID, 0, msg, new Object[] {
startPosition, endPosition, }));
}
}
/**
* {@link ClassifierTypeRuleContext} error case.
*
* @param offendingSymbol
* the offending symbol
* @param msg
* the error message
* @param e
* the {@link RecognitionException}
*/
private void classifierTypeRuleContextError(Object offendingSymbol, String msg,
RecognitionException e) {
final ClassifierTypeRuleContext ctx = (ClassifierTypeRuleContext)e.getCtx();
if (e.getCtx().getParent().getParent() instanceof VariableDefinitionContext) {
errorRule = QueryParser.RULE_expression;
final String variableName = e.getCtx().getParent().getChild(0).getText();
final ErrorTypeLiteral errorTypeLiteral;
if (ctx.getChildCount() > 0) {
errorTypeLiteral = builder.errorEClassifierTypeLiteral(false, new String[] {ctx.getChild(
0).getText(), });
} else {
errorTypeLiteral = builder.errorEClassifierTypeLiteral(false, new String[] {});
}
setPositions(errorTypeLiteral, ctx.start, (Token)offendingSymbol);
diagnostics.add(new BasicDiagnostic(Diagnostic.ERROR, PLUGIN_ID, 0, String.format(
INVALID_TYPE_LITERAL, ctx.getText()), new Object[] {errorTypeLiteral }));
errors.add(errorTypeLiteral);
final Expression variableExpression = popExpression();
final VariableDeclaration variableDeclaration = builder.variableDeclaration(variableName,
errorTypeLiteral, variableExpression);
setPositions(variableDeclaration, ctx.start, (Token)offendingSymbol);
push(variableDeclaration);
final ErrorExpression errorExpression = builder.errorExpression();
pushError(errorExpression, MISSING_EXPRESSION);
setPositions(errorExpression, (Token)offendingSymbol, (Token)offendingSymbol);
} else {
errorRule = QueryParser.RULE_classifierTypeRule;
final ErrorTypeLiteral errorTypeLiteral;
if (ctx.getChildCount() > 0) {
errorTypeLiteral = builder.errorEClassifierTypeLiteral(false, new String[] {ctx.getChild(
0).getText(), });
} else {
errorTypeLiteral = builder.errorEClassifierTypeLiteral(false, new String[] {});
}
setPositions(errorTypeLiteral, ctx.start, (Token)offendingSymbol);
pushError(errorTypeLiteral, "missing classifier literal");
}
}
/**
* {@link IterationCallContext} error case.
*
* @param e
* the {@link RecognitionException}
*/
private void iterationCallContextError(RecognitionException e) {
if (e.getCtx().getChildCount() == 0) {
errorRule = QueryParser.RULE_expression;
final ErrorExpression errorExpression = builder.errorExpression();
pushError(errorExpression, MISSING_EXPRESSION);
final int position = ((IterationCallContext)e.getCtx()).start.getStartIndex();
final int line = ((IterationCallContext)e.getCtx()).start.getLine() - 1;
final int column = ((IterationCallContext)e.getCtx()).start.getCharPositionInLine();
setPositions(errorExpression, position, line, column);
}
}
/**
* {@link TypeLiteralContext} error case.
*
* @param offendingSymbol
* the offending symbol
* @param msg
* the error message
* @param e
* the {@link RecognitionException}
*/
private void typeLiteralContextError(Object offendingSymbol, String msg, RecognitionException e) {
if (e.getCtx().getParent() instanceof VariableDefinitionContext) {
errorRule = QueryParser.RULE_expression;
final String variableName = e.getCtx().getParent().getChild(0).getText();
final ErrorTypeLiteral type = builder.errorTypeLiteral(false, new String[] {});
setPositions(type, ((TypeLiteralContext)e.getCtx()).start, (Token)offendingSymbol);
diagnostics.add(new BasicDiagnostic(Diagnostic.ERROR, PLUGIN_ID, 0, String.format(
INVALID_TYPE_LITERAL, msg), new Object[] {type }));
errors.add(type);
final Expression variableExpression = popExpression();
final VariableDeclaration variableDeclaration = builder.variableDeclaration(variableName,
type, variableExpression);
setPositions(variableDeclaration, ((TypeLiteralContext)e.getCtx()).start,
(Token)offendingSymbol);
push(variableDeclaration);
final ErrorExpression errorExpression = builder.errorExpression();
pushError(errorExpression, MISSING_EXPRESSION);
setPositions(errorExpression, ((TypeLiteralContext)e.getCtx()).start, (Token)offendingSymbol);
} else if (stack.isEmpty() || !(stack.peek() instanceof TypeLiteral)) {
errorRule = QueryParser.RULE_typeLiteral;
final ErrorTypeLiteral errorTypeLiteral = builder.errorTypeLiteral(false, new String[] {});
setPositions(errorTypeLiteral, ((TypeLiteralContext)e.getCtx()).start,
(Token)offendingSymbol);
pushError(errorTypeLiteral, String.format(INVALID_TYPE_LITERAL, msg));
} else {
diagnosticStack.push(new BasicDiagnostic(Diagnostic.WARNING, PLUGIN_ID, 0, msg, new Object[] {
((TypeLiteralContext)e.getCtx()).start.getStartIndex(), ((Token)offendingSymbol)
.getStopIndex() + 1, }));
}
}
/**
* {@link LiteralContext} error case.
*
* @param offendingSymbol
* the offending symbol
* @param msg
* the error message
* @param e
* the {@link RecognitionException}
*/
private void literalContextError(Object offendingSymbol, String msg, RecognitionException e) {
final LiteralContext ctx = (LiteralContext)e.getCtx();
final Token start = ctx.start;
final Token end = (Token)offendingSymbol;
final String ePackage = ctx.getParent().getStart().getText();
errorRule = QueryParser.RULE_typeLiteral;
if (ctx.getChildCount() == 4) {
final String eEnumName = ctx.getChild(2).getText();
final ErrorEnumLiteral errorEnumLiteral = builder.errorEnumLiteral(false, ePackage,
eEnumName);
setPositions(errorEnumLiteral, start, end);
pushError(errorEnumLiteral, String.format(INVALID_ENUM_LITERAL, msg));
} else {
final ErrorTypeLiteral errorTypeLiteral = builder.errorTypeLiteral(false, new String[] {
ePackage, });
setPositions(errorTypeLiteral, start, end);
pushError(errorTypeLiteral, String.format(INVALID_TYPE_LITERAL, msg));
}
}
/**
* {@link VariableDefinitionContext} error case.
*
* @param offendingSymbol
* the offending symbol
* @param e
* the {@link RecognitionException}
*/
private void variableDefinitionContextError(Object offendingSymbol, RecognitionException e) {
if (e.getCtx().getChildCount() > 0) {
errorRule = QueryParser.RULE_expression;
final String variableName = e.getCtx().getChild(0).getText();
final TypeLiteral type;
if (e.getCtx().getChildCount() > 2) {
type = popTypeLiteral();
// overwrite the end position to the current error position (the start position is
// fine)
final int position = ((Token)offendingSymbol).getStopIndex() + 1;
final int line = ((Token)offendingSymbol).getLine() - 1;
final int column = ((Token)offendingSymbol).getCharPositionInLine()
+ ((Token)offendingSymbol).getText().length();
positions.setEndPositions(type, position);
positions.setEndLines(type, line);
positions.setEndColumns(type, column);
} else {
type = null;
}
final Expression variableExpression = popExpression();
final ErrorVariableDeclaration errorVariableDeclaration = builder.errorVariableDeclaration(
variableName, type, variableExpression);
setPositions(errorVariableDeclaration, ((VariableDefinitionContext)e.getCtx()).start,
(Token)offendingSymbol);
pushError(errorVariableDeclaration, "incomplete variable definition");
} else {
final Expression variableExpression = popExpression();
errorRule = QueryParser.RULE_variableDefinition;
final ErrorVariableDeclaration errorVariableDeclaration = builder.errorVariableDeclaration(
null, null, variableExpression);
setPositions(errorVariableDeclaration, ((VariableDefinitionContext)e.getCtx()).start,
(Token)offendingSymbol);
pushError(errorVariableDeclaration, "missing variable declaration");
}
if (((Token)offendingSymbol).getText().isEmpty() || ")".equals(((Token)offendingSymbol)
.getText())) {
final ErrorExpression errorExpression = builder.errorExpression();
// not missing ')' only missing expression
if (((Token)offendingSymbol).getStartIndex() == ((Token)offendingSymbol).getStopIndex()) {
// position is indeed before the closing parenthesis
final int position = ((Token)offendingSymbol).getStopIndex() /* + 1 - 1 */;
final int line = ((Token)offendingSymbol).getLine() - 1;
final int column = ((Token)offendingSymbol).getCharPositionInLine()
+ ((Token)offendingSymbol).getText().length() - 1;
setPositions(errorExpression, position, line, column);
} else {
final int position = ((Token)offendingSymbol).getStopIndex() + 1;
final int line = ((Token)offendingSymbol).getLine() - 1;
final int column = ((Token)offendingSymbol).getCharPositionInLine()
+ ((Token)offendingSymbol).getText().length();
setPositions(errorExpression, position, line, column);
}
pushError(errorExpression, MISSING_EXPRESSION);
}
}
/**
* {@link CallExpContext} error case.
*
* @param offendingSymbol
* the offending symbol
* @param e
* the {@link RecognitionException}
*/
private void callExpContextError(Object offendingSymbol, RecognitionException e) {
errorRule = QueryParser.RULE_navigationSegment;
final String name;
if (e.getCtx().getChildCount() > 0) {
name = e.getCtx().getChild(0).getText();
} else {
name = null;
}
final Expression receiver;
final ErrorCall errorCollectionCall;
if (e.getCtx().getChildCount() == 3) {
final int argc = getNumberOfArgs(e.getCtx().getChild(2).getChildCount());
final Expression[] args = new Expression[argc];
for (int i = argc - 1; i >= 0; i--) {
args[i] = popExpression();
}
receiver = args[0];
errorCollectionCall = builder.errorCall(name, false, args);
} else {
receiver = popExpression();
errorCollectionCall = builder.errorCall(null, false, receiver);
}
setPositions(errorCollectionCall, receiver, (Token)offendingSymbol);
pushError(errorCollectionCall, "missing collection service call");
}
/**
* {@link NavigationSegmentContext} error case.
*
* @param offendingSymbol
* the offending symbol
*/
private void navigationSegmentContextError(Object offendingSymbol) {
final Expression receiver = popExpression();
final ErrorCall errorCall = builder.errorCall(FEATURE_ACCESS_SERVICE_NAME, false, receiver);
errorCall.setType(CallType.CALLORAPPLY);
setPositions(errorCall, receiver, (Token)offendingSymbol);
pushError(errorCall, "missing feature access or service call");
}
/**
* {@link BindingContext} error case.
*
* @param offendingSymbol
* the offending symbol
* @param e
* the {@link RecognitionException}
*/
private void bindingContextError(Object offendingSymbol, RecognitionException e) {
errorRule = QueryParser.RULE_binding;
final String name;
final TypeLiteral type;
if (e.getCtx().getChildCount() > 0 && !"in".equals(e.getCtx().getChild(0).getText())) {
name = e.getCtx().getChild(0).getText();
if (e.getCtx().getChildCount() == 3) {
type = popTypeLiteral();
} else {
type = null;
}
} else {
name = null;
type = null;
}
final ErrorBinding errorBinding = builder.errorBinding(name, type);
final int position = ((Token)offendingSymbol).getStopIndex() + 1;
final int line = ((Token)offendingSymbol).getLine() - 1;
final int column = ((Token)offendingSymbol).getCharPositionInLine() + ((Token)offendingSymbol)
.getText().length();
setPositions(errorBinding, position, line, column);
pushError(errorBinding, "invalid variable declaration in let");
}
/**
* Default error case.
*
* @param offendingSymbol
* the offending symbol
* @param msg
* the error message
* @param e
* the {@link RecognitionException}
*/
private void defaultError(Object offendingSymbol, String msg, RecognitionException e) {
if (offendingSymbol == null && e.getCtx() == null) {
diagnostics.add(new BasicDiagnostic(Diagnostic.ERROR, PLUGIN_ID, 0, msg, new Object[] {}));
} else {
switch (e.getCtx().getRuleIndex()) {
case QueryParser.RULE_expression:
errorRule = QueryParser.RULE_expression;
final ErrorExpression errorExpression = builder.errorExpression();
final int position = ((ParserRuleContext)e.getCtx()).start.getStartIndex();
final int line = ((ParserRuleContext)e.getCtx()).start.getLine() - 1;
final int column = ((ParserRuleContext)e.getCtx()).start.getCharPositionInLine();
setPositions(errorExpression, position, line, column);
pushError(errorExpression, MISSING_EXPRESSION);
break;
default:
break;
}
}
}
/**
* Handles parser error when the {@link RecognitionException} is <code>null</code>.
*
* @param recognizer
* the {@link Recognizer}
* @param offendingSymbol
* the offending symbol
* @param msg
* the error message
*/
private void noRecognitionException(Recognizer<?, ?> recognizer, Object offendingSymbol, String msg) {
final QueryParser parser = (QueryParser)recognizer;
final Integer startPosition = Integer.valueOf(((ParserRuleContext)parser.getContext()).start
.getStartIndex());
final Integer endPosition = Integer.valueOf(((Token)offendingSymbol).getStopIndex() + 1);
diagnosticStack.push(new BasicDiagnostic(Diagnostic.WARNING, PLUGIN_ID, 0, msg, new Object[] {
startPosition, endPosition, }));
}
}
/**
* No error rule marker.
*/
private static final int NO_ERROR = -1;
/**
* Message logged when an internal error occurs.
*/
private static final String INTERNAL_ERROR_MSG = "Internal exception occured while evaluating an expression";
/**
* The evaluation stack used to hold temporary results.
*/
private Stack<Object> stack = new Stack<Object>();
/**
* The last rule index if any error. see {@link QueryParser}.
*/
private int errorRule = -1;
/**
* The parsed {@link Positions}.
*/
private Positions positions = new Positions();
/**
* The {@link List} of {@link Error}.
*/
private List<Error> errors = new ArrayList<Error>();
/**
* Temporary lexing warnings waiting for their {@link Expression}.
*/
private Stack<Diagnostic> diagnosticStack = new Stack<Diagnostic>();
/** Aggregated status of the parsing. */
private final List<Diagnostic> diagnostics = new ArrayList<Diagnostic>();
/**
* The {@link ANTLRErrorListener} pushing {@link org.eclipse.acceleo.query.ast.Error Error}.
*/
private final ANTLRErrorListener errorListener = new QueryErrorListener();
/**
* Ast Builder.
*/
private final AstBuilder builder = new AstBuilder();
/**
* The {@link IReadOnlyQueryEnvironment}.
*/
private final IReadOnlyQueryEnvironment environment;
/**
* Creates a new {@link AstBuilderListener}.
*
* @param environment
* the package provider
*/
public AstBuilderListener(IReadOnlyQueryEnvironment environment) {
this.environment = environment;
}
/**
* Gets the {@link Set} of operator service names.
*
* @return the {@link Set} of operator service names
*/
private static Set<String> initOperatorServiceNames() {
final Set<String> result = new LinkedHashSet<String>();
result.add(ADD_SERVICE_NAME);
result.add(AND_SERVICE_NAME);
result.add(DIFFERS_SERVICE_NAME);
result.add(DIV_SERVICE_NAME);
result.add(EQUALS_SERVICE_NAME);
result.add(GREATER_THAN_EQUAL_SERVICE_NAME);
result.add(GREATER_THAN_SERVICE_NAME);
result.add(IMPLIES_SERVICE_NAME);
result.add(LESS_THAN_EQUAL_SERVICE_NAME);
result.add(LESS_THAN_SERVICE_NAME);
result.add(MULT_SERVICE_NAME);
result.add(NOT_SERVICE_NAME);
result.add(OR_SERVICE_NAME);
result.add(SUB_SERVICE_NAME);
result.add(UNARY_MIN_SERVICE_NAME);
result.add(XOR_SERVICE_NAME);
return result;
}
/**
* Sets the positions of the given node.
*
* @param node
* the node
* @param position
* the position
* @param line
* the line
* @param column
* the column
*/
private void setPositions(EObject node, int position, int line, int column) {
positions.setStartPositions(node, position);
positions.setStartLines(node, line);
positions.setStartColumns(node, column);
positions.setEndPositions(node, position);
positions.setEndLines(node, line);
positions.setEndColumns(node, column);
}
/**
* Sets the positions of the given node.
*
* @param node
* the node
* @param start
* the start {@link Token}
* @param end
* the end {@link Token}
*/
private void setPositions(EObject node, Token start, Token end) {
positions.setStartPositions(node, Integer.valueOf(start.getStartIndex()));
positions.setStartLines(node, Integer.valueOf(start.getLine() - 1));
positions.setStartColumns(node, Integer.valueOf(start.getCharPositionInLine()));
positions.setEndPositions(node, Integer.valueOf(end.getStopIndex() + 1));
positions.setEndLines(node, Integer.valueOf(end.getLine() - 1));
positions.setEndColumns(node, Integer.valueOf(end.getCharPositionInLine() + end.getText().length()));
}
/**
* Sets the positions of the given node.
*
* @param node
* the node
* @param expressionStart
* the start {@link Expression}
* @param expressionEnd
* the end {@link Expression}
*/
private void setPositions(EObject node, Expression expressionStart, Expression expressionEnd) {
positions.setStartPositions(node, positions.getStartPositions(expressionStart));
positions.setStartLines(node, positions.getStartLines(expressionStart));
positions.setStartColumns(node, positions.getStartColumns(expressionStart));
positions.setEndPositions(node, positions.getEndPositions(expressionEnd));
positions.setEndLines(node, positions.getEndLines(expressionEnd));
positions.setEndColumns(node, positions.getEndColumns(expressionEnd));
}
/**
* Sets the node positions.
*
* @param node
* the node
* @param expressionStart
* the start {@link Expression}
* @param end
* the end {@link Token}
*/
private void setPositions(EObject node, Expression expressionStart, Token end) {
positions.setStartPositions(node, positions.getStartPositions(expressionStart));
positions.setStartLines(node, positions.getStartLines(expressionStart));
positions.setStartColumns(node, positions.getStartColumns(expressionStart));
positions.setEndPositions(node, Integer.valueOf(end.getStopIndex() + 1));
positions.setEndLines(node, Integer.valueOf(end.getLine() - 1));
positions.setEndColumns(node, Integer.valueOf(end.getCharPositionInLine() + end.getText().length()));
}
/**
* Returns the {@link AstResult}.
*
* @return the {@link AstResult}.
*/
public AstResult getAstResult() {
final Expression ast = popExpression();
final BasicDiagnostic diagnostic = new BasicDiagnostic();
for (Diagnostic diag : diagnostics) {
diagnostic.add(diag);
}
final Positions resultPositions = positions;
positions = new Positions();
final List<Error> resultErrors = errors;
errors = new ArrayList<Error>();
return new AstResult(ast, resultPositions, resultErrors, diagnostic);
}
/**
* Pop the top of the stack.
*
* @return the value on top of the stack
*/
private Expression popExpression() {
try {
final Expression expression = (Expression)pop();
if (!diagnosticStack.isEmpty()) {
final List<?> data = diagnosticStack.peek().getData();
if (data.get(0).equals(positions.getStartPositions(expression)) && data.get(1).equals(
positions.getEndPositions(expression))) {
final Diagnostic tmpDiagnostic = diagnosticStack.pop();
diagnostics.add(new BasicDiagnostic(tmpDiagnostic.getSeverity(), tmpDiagnostic
.getSource(), tmpDiagnostic.getCode(), tmpDiagnostic.getMessage(), new Object[] {
expression }));
}
}
return expression;
} catch (EmptyStackException e) {
throw new AcceleoQueryEvaluationException(INTERNAL_ERROR_MSG, e);
} catch (ClassCastException cce) {
throw new AcceleoQueryEvaluationException(INTERNAL_ERROR_MSG, cce);
}
}
/**
* Pop the top of the stack and returns it as a type literal.
*
* @return the value on top of the stack.
*/
private Binding popBinding() {
try {
return (Binding)pop();
} catch (EmptyStackException e) {
throw new AcceleoQueryEvaluationException(INTERNAL_ERROR_MSG, e);
} catch (ClassCastException e2) {
throw new AcceleoQueryEvaluationException(INTERNAL_ERROR_MSG, e2);
}
}
/**
* Pop the top of the stack and returns it as a {@link VariableDeclaration}.
*
* @return the value on top of the stack.
*/
private VariableDeclaration popVariableDeclaration() {
try {
return (VariableDeclaration)pop();
} catch (EmptyStackException e) {
throw new AcceleoQueryEvaluationException(INTERNAL_ERROR_MSG, e);
} catch (ClassCastException e2) {
throw new AcceleoQueryEvaluationException(INTERNAL_ERROR_MSG, e2);
}
}
/**
* Pop the top of the stack and returns it as a binding.
*
* @return the value on top of the stack.
*/
private TypeLiteral popTypeLiteral() {
try {
return (TypeLiteral)pop();
} catch (EmptyStackException e) {
throw new AcceleoQueryEvaluationException(INTERNAL_ERROR_MSG, e);
} catch (ClassCastException e2) {
throw new AcceleoQueryEvaluationException(INTERNAL_ERROR_MSG, e2);
}
}
/**
* Peeks the current {@link Call} at the top of the stack.
*
* @return the current {@link Call} at the top of the stack
*/
private Call peekCall() {
return (Call)stack.peek();
}
/**
* push an object on the stack.
*
* @param obj
* the pushed object.
*/
private void push(Object obj) {
this.stack.push(obj);
}
/**
* Pops the stack.
*
* @return the element at the top of the stack if any
*/
protected Object pop() {
final Object element = stack.pop();
return element;
}
/**
* Pushes an {@link Error} on the stack.
*
* @param error
* the {@link Error} to push
* @param msg
* the error message
*/
private void pushError(Error error, String msg) {
errors.add(error);
diagnostics.add(new BasicDiagnostic(Diagnostic.ERROR, PLUGIN_ID, 0, msg, new Object[] {error }));
push(error);
}
/**
* Pops the last {@link ErrorExpression}.
*/
private void popErrorExpression() {
if (!stack.isEmpty() && stack.peek() instanceof ErrorExpression) {
final ErrorExpression error = (ErrorExpression)pop();
errors.remove(error);
positions.remove(error);
diagnostics.remove(diagnostics.size() - 1);
}
}
@Override
public void exitIntType(IntTypeContext ctx) {
final Literal typeLiteral = builder.typeLiteral(java.lang.Integer.class);
setPositions(typeLiteral, ctx.start, ctx.stop);
push(typeLiteral);
}
@Override
public void exitFalseLit(FalseLitContext ctx) {
final BooleanLiteral booleanLiteral = builder.booleanLiteral(false);
setPositions(booleanLiteral, ctx.start, ctx.stop);
push(booleanLiteral);
}
@Override
public void exitRealType(RealTypeContext ctx) {
final Literal realLiteral = builder.typeLiteral(java.lang.Double.class);
setPositions(realLiteral, ctx.start, ctx.stop);
push(realLiteral);
}
@Override
public void exitTrueLit(TrueLitContext ctx) {
final BooleanLiteral booleanLiteral = builder.booleanLiteral(true);
setPositions(booleanLiteral, ctx.start, ctx.stop);
push(booleanLiteral);
}
@Override
public void exitSeqType(SeqTypeContext ctx) {
final TypeLiteral elementType = popTypeLiteral();
final CollectionTypeLiteral collectionTypeLiteral = builder.collectionTypeLiteral(List.class,
elementType);
setPositions(collectionTypeLiteral, ctx.start, ctx.stop);
push(collectionTypeLiteral);
}
@Override
public void exitSetType(SetTypeContext ctx) {
final TypeLiteral elementType = popTypeLiteral();
final CollectionTypeLiteral collectionTypeLiteral = builder.collectionTypeLiteral(Set.class,
elementType);
setPositions(collectionTypeLiteral, ctx.start, ctx.stop);
push(collectionTypeLiteral);
}
@Override
public void exitNot(NotContext ctx) {
final Call callService = builder.callService(NOT_SERVICE_NAME, popExpression());
setPositions(callService, ctx.start, ctx.stop);
push(callService);
}
@Override
public void exitStringLit(StringLitContext ctx) {
final String text = ctx.getText();
final StringLiteral stringLiteral = builder.stringLiteral(text.substring(1, text.length() - 1));
setPositions(stringLiteral, ctx.start, ctx.stop);
push(stringLiteral);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.parser.QueryBaseListener#exitErrorStringLit(org.eclipse.acceleo.query.parser.QueryParser.ErrorStringLitContext)
*/
@Override
public void exitErrorStringLit(ErrorStringLitContext ctx) {
final String text = ctx.getText();
final ErrorStringLiteral errorStringLiteral = builder.errorStringLiteral(text.substring(1, text
.length()));
setPositions(errorStringLiteral, ctx.start, ctx.stop);
pushError(errorStringLiteral, "String literal is not properly closed by a simple-quote.");
}
@Override
public void exitRealLit(RealLitContext ctx) {
final RealLiteral realLiteral = builder.realLiteral(Double.valueOf(ctx.getText()));
setPositions(realLiteral, ctx.start, ctx.stop);
push(realLiteral);
}
@Override
public void exitStrType(StrTypeContext ctx) {
final Literal typeLiteral = builder.typeLiteral(java.lang.String.class);
setPositions(typeLiteral, ctx.start, ctx.stop);
push(typeLiteral);
}
@Override
public void exitOr(OrContext ctx) {
Expression op2 = popExpression();
Expression op1 = popExpression();
final Or callService = builder.callOrService(op1, op2);
setPositions(callService, op1, op2);
push(callService);
}
@Override
public void exitXor(XorContext ctx) {
pushBinary(XOR_SERVICE_NAME, ctx);
}
@Override
public void exitImplies(ImpliesContext ctx) {
Expression op2 = popExpression();
Expression op1 = popExpression();
final Implies callService = builder.callImpliesService(op1, op2);
setPositions(callService, op1, op2);
push(callService);
}
@Override
public void exitBooleanType(BooleanTypeContext ctx) {
final Literal typeLiteral = builder.typeLiteral(java.lang.Boolean.class);
setPositions(typeLiteral, ctx.start, ctx.stop);
push(typeLiteral);
}
@Override
public void exitIntegerLit(IntegerLitContext ctx) {
final IntegerLiteral integerLiteral = builder.integerLiteral(Integer.valueOf(ctx.getText()));
setPositions(integerLiteral, ctx.start, ctx.stop);
push(integerLiteral);
}
@Override
public void exitAnd(AndContext ctx) {
Expression op2 = popExpression();
Expression op1 = popExpression();
final And callService = builder.callAndService(op1, op2);
setPositions(callService, op1, op2);
push(callService);
}
@Override
public void exitVarRef(VarRefContext ctx) {
final VarRef varRef = builder.varRef(ctx.getText());
setPositions(varRef, ctx.start, ctx.stop);
push(varRef);
}
@Override
public void exitFeature(FeatureContext ctx) {
final Expression receiver = popExpression();
final StringLiteral featureName = builder.stringLiteral(ctx.getChild(1).getText());
final Call call = builder.callService(FEATURE_ACCESS_SERVICE_NAME, receiver, featureName);
call.setType(CallType.CALLORAPPLY);
setPositions(featureName, ctx.stop, ctx.stop);
setPositions(call, receiver, featureName);
push(call);
}
/**
* Factorization of binary operation evaluation.
*
* @param service
* the called service.
* @param ctx
* the {@link ParserRuleContext}
*/
private void pushBinary(String service, ParserRuleContext ctx) {
Expression op2 = popExpression();
Expression op1 = popExpression();
final Call callService = builder.callService(service, op1, op2);
setPositions(callService, op1, op2);
push(callService);
}
/**
* Evaluation stack is as follows : [/.../,receiver, service name (string), START_EXPR_SEQ, expr*].
*
* @param ctx
* the parsing context
*/
@Override
public void exitServiceCall(ServiceCallContext ctx) {
if (errorRule != QueryParser.RULE_navigationSegment) {
final int argc = getNumberOfArgs(ctx.getChild(2).getChildCount());
final Expression[] args = new Expression[argc];
for (int i = argc - 1; i >= 0; i--) {
args[i] = popExpression();
}
final String serviceName = ctx.getChild(0).getText().replace("::", ".");
final Call call;
if (ctx.getChild(ctx.getChildCount() - 1) instanceof ErrorNode) {
call = builder.errorCall(serviceName, true, args);
pushError((Error)call, "missing ')'");
} else {
call = builder.callService(serviceName, args);
push(call);
}
setPositions(call, args[0], ctx.stop);
}
}
/**
* Gets the number of arguments separated by a token given a context child count.
*
* @param childCount
* the context child count
* @return the number of arguments separated by a token given a context child count
*/
private int getNumberOfArgs(int childCount) {
final int result;
if (childCount == 0) {
result = 1;
} else {
result = 1 + 1 + childCount / 2;
}
return result;
}
@Override
public void exitMin(MinContext ctx) {
final Call callService = builder.callService(UNARY_MIN_SERVICE_NAME, popExpression());
setPositions(callService, ctx.start, ctx.stop);
push(callService);
}
@Override
public void exitAdd(AddContext ctx) {
final String op = ctx.getChild(1).getText();
if (ADD_OPERATOR.equals(op)) {
pushBinary(ADD_SERVICE_NAME, ctx);
} else if (SUB_OPERATOR.equals(op)) {
pushBinary(SUB_SERVICE_NAME, ctx);
} else {
throw new AcceleoQueryEvaluationException(THIS_SHOULDN_T_HAPPEN);
}
}
@Override
public void exitMult(MultContext ctx) {
final String op = ctx.getChild(1).getText();
if (MULT_OPERATOR.equals(op)) {
pushBinary(MULT_SERVICE_NAME, ctx);
} else if (DIV_OPERATOR.equals(op)) {
pushBinary(DIV_SERVICE_NAME, ctx);
} else {
throw new AcceleoQueryEvaluationException(THIS_SHOULDN_T_HAPPEN);
}
}
@Override
public void exitComp(CompContext ctx) {
final String op = ctx.getChild(1).getText();
if (LESS_THAN_OPERATOR.equals(op)) {
pushBinary(LESS_THAN_SERVICE_NAME, ctx);
} else if (LESS_THAN_EQUAL_OPERATOR.equals(op)) {
pushBinary(LESS_THAN_EQUAL_SERVICE_NAME, ctx);
} else if (GREATER_THAN_OPERATOR.equals(op)) {
pushBinary(GREATER_THAN_SERVICE_NAME, ctx);
} else if (GREATER_THAN_EQUAL_OPERATOR.equals(op)) {
pushBinary(GREATER_THAN_EQUAL_SERVICE_NAME, ctx);
} else if (EQUALS_OPERATOR.equals(op) || EQUALS_JAVA_OPERATOR.equals(op)) {
pushBinary(EQUALS_SERVICE_NAME, ctx);
} else if (DIFFERS_OPERATOR.equals(op) || DIFFERS_JAVA_OPERATOR.equals(op)) {
pushBinary(DIFFERS_SERVICE_NAME, ctx);
} else {
throw new AcceleoQueryEvaluationException(THIS_SHOULDN_T_HAPPEN);
}
}
@Override
public void exitCollectionCall(CollectionCallContext ctx) {
peekCall().setType(CallType.COLLECTIONCALL);
}
@Override
public void exitCallOrApply(CallOrApplyContext ctx) {
peekCall().setType(CallType.CALLORAPPLY);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.parser.QueryBaseListener#exitVariableDefinition(org.eclipse.acceleo.query.parser.QueryParser.VariableDefinitionContext)
*/
@Override
public void exitVariableDefinition(VariableDefinitionContext ctx) {
// If the error flag is raised an error occurred and the variable is already
// there.
if (errorRule == NO_ERROR) {
final VariableDeclaration variableDeclaration;
final Token stop;
if (ctx.getChildCount() == 4) {
final TypeLiteral typeLiteral = popTypeLiteral();
final Expression variableExpression = popExpression();
variableDeclaration = builder.variableDeclaration(ctx.getChild(0).getText(), typeLiteral,
variableExpression);
stop = ((ParserRuleContext)ctx.getChild(2)).stop;
} else {
final Expression variableExpression = popExpression();
variableDeclaration = builder.variableDeclaration(ctx.getChild(0).getText(),
variableExpression);
stop = ((TerminalNode)ctx.getChild(0)).getSymbol();
}
setPositions(variableDeclaration, ctx.start, stop);
push(variableDeclaration);
} else {
errorRule = NO_ERROR;
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.parser.QueryBaseListener#exitSelect(org.eclipse.acceleo.query.parser.QueryParser.SelectContext)
*/
@Override
public void exitIterationCall(IterationCallContext ctx) {
// the stack contains [variableDef, expression]
final String serviceName = ctx.getChild(0).getText();
final Expression ast = popExpression();
popErrorExpression();
final VariableDeclaration iterator = popVariableDeclaration();
final Lambda lambda = builder.lambda(ast, iterator);
setPositions(lambda, ast, ast);
final Call call;
if (ctx.getChild(ctx.getChildCount() - 1) instanceof ErrorNode) {
// at this point ANTLR can report a missing ')' even is the closing parenthesis is present
// so we check by hand
final ParserRuleContext parenthesisNode = (ParserRuleContext)ctx.getChild(ctx.getChildCount() - 2)
.getChild(0);
final boolean missingParenthesis = parenthesisNode != null && !")".equals(parenthesisNode.stop
.getText());
call = builder.errorCall(serviceName, missingParenthesis, iterator.getExpression(), lambda);
if (missingParenthesis) {
pushError((Error)call, "missing ')'");
} else {
pushError((Error)call, "invalid iteration call");
}
} else {
call = builder.callService(serviceName, iterator.getExpression(), lambda);
push(call);
}
setPositions(call, iterator.getExpression(), ctx.stop);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.parser.QueryBaseListener#exitEnumLit(org.eclipse.acceleo.query.parser.QueryParser.EnumLitContext)
*/
@Override
public void exitEnumLit(EnumLitContext ctx) {
if (ctx.getChildCount() >= 5) {
final EnumLiteral toPush;
final String ePackageName = ctx.getChild(0).getText();
final String eEnumName = ctx.getChild(2).getText();
final String eEnumLiteralName = ctx.getChild(4).getText();
final Collection<EEnumLiteral> eEnumLiterals = environment.getEPackageProvider().getEnumLiterals(
ePackageName, eEnumName, eEnumLiteralName);
if (eEnumLiterals.size() == 0) {
List<String> segments = new ArrayList<String>(3);
segments.add(ePackageName);
segments.add(eEnumName);
if (!(ctx.getChild(4) instanceof ErrorNode)) {
segments.add(eEnumLiteralName);
}
toPush = builder.errorEnumLiteral(false, segments.toArray(new String[segments.size()]));
if (segments.size() == 3) {
pushError((Error)toPush, String.format(INVALID_ENUM_LITERAL,
"no literal registered with this name"));
} else {
pushError((Error)toPush, String.format(INVALID_ENUM_LITERAL, "missing literal name"));
}
} else {
toPush = builder.enumLiteral(eEnumLiterals.iterator().next());
push(toPush);
if (eEnumLiterals.size() > 1) {
final Integer startPosition = Integer.valueOf(ctx.start.getStartIndex());
final Integer stopPosition = Integer.valueOf(ctx.stop.getStopIndex() + 1);
diagnosticStack.push(new BasicDiagnostic(Diagnostic.WARNING, PLUGIN_ID, 0, String.format(
AMBIGUOUS_ENUM_LITERAL, eEnumLiteralName, eEnumName, ePackageName), new Object[] {
startPosition, stopPosition, }));
}
}
setPositions(toPush, ctx.start, ctx.stop);
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.parser.QueryBaseListener#exitErrorEnumLit(org.eclipse.acceleo.query.parser.QueryParser.ErrorEnumLitContext)
*/
@Override
public void exitErrorEnumLit(ErrorEnumLitContext ctx) {
if (errorRule == NO_ERROR) {
final String ePackageName = ctx.getChild(0).getText();
final String eEnumName = ctx.getChild(2).getText();
final ErrorEnumLiteral errorEnumLiteral = builder.errorEnumLiteral(true, ePackageName, eEnumName);
pushError(errorEnumLiteral, String.format(INVALID_ENUM_LITERAL, "':' instead of '::'"));
setPositions(errorEnumLiteral, ctx.start, ctx.stop);
} else {
errorRule = NO_ERROR;
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.parser.QueryBaseListener#exitClassifierType(org.eclipse.acceleo.query.parser.QueryParser.ClassifierTypeContext)
*/
@Override
public void exitClassifierType(ClassifierTypeContext ctx) {
if (errorRule == NO_ERROR) {
final Literal toPush;
final String ePackageName = ctx.getChild(0).getText();
final String eClassName;
Collection<EClassifier> type = Collections.emptySet();
if (ctx.getChild(2) == null || ctx.getChild(2) instanceof ErrorNode) {
eClassName = null;
type = Collections.emptySet();
} else {
eClassName = ctx.getChild(2).getText();
type = environment.getEPackageProvider().getTypes(ePackageName, eClassName);
}
if (type.size() == 0) {
List<String> segments = new ArrayList<String>(2);
segments.add(ePackageName);
if (eClassName != null) {
segments.add(eClassName);
}
toPush = builder.errorEClassifierTypeLiteral(false, segments.toArray(new String[segments
.size()]));
pushError((Error)toPush, String.format(INVALID_TYPE_LITERAL, ctx.getText()));
} else {
toPush = builder.typeLiteral(type);
push(toPush);
if (type.size() > 1) {
final Integer startPosition = Integer.valueOf(ctx.start.getStartIndex());
final Integer stopPosition = Integer.valueOf(ctx.stop.getStopIndex() + 1);
diagnosticStack.push(new BasicDiagnostic(Diagnostic.WARNING, PLUGIN_ID, 0, String.format(
AMBIGUOUS_TYPE_LITERAL, eClassName, ePackageName), new Object[] {startPosition,
stopPosition, }));
}
}
setPositions(toPush, ctx.start, ctx.stop);
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.parser.QueryBaseListener#exitErrorClassifierType(org.eclipse.acceleo.query.parser.QueryParser.ErrorClassifierTypeContext)
*/
@Override
public void exitErrorClassifierType(ErrorClassifierTypeContext ctx) {
final String ePackageName = ctx.getChild(0).getText();
final ErrorEClassifierTypeLiteral errorTypeLiteral = builder.errorEClassifierTypeLiteral(true,
ePackageName);
pushError((Error)errorTypeLiteral, String.format(INVALID_TYPE_LITERAL, ctx.getText()));
setPositions(errorTypeLiteral, ctx.start, ctx.stop);
}
/**
* Gets the {@link ANTLRErrorListener}.
*
* @return the {@link ANTLRErrorListener}
*/
public ANTLRErrorListener getErrorListener() {
return errorListener;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.parser.QueryBaseListener#exitNullLit(org.eclipse.acceleo.query.parser.QueryParser.NullLitContext)
*/
@Override
public void exitNullLit(NullLitContext ctx) {
final NullLiteral nullLiteral = builder.nullLiteral();
setPositions(nullLiteral, ctx.start, ctx.stop);
push(nullLiteral);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.parser.QueryBaseListener#exitExplicitSetLit(org.eclipse.acceleo.query.parser.QueryParser.ExplicitSetLitContext)
*/
@Override
public void exitExplicitSetLit(ExplicitSetLitContext ctx) {
final SetInExtensionLiteral setInExtension = builder.setInExtension(getExpressions(ctx));
setPositions(setInExtension, ctx.start, ctx.stop);
push(setInExtension);
}
/**
* Gets the {@link List} of {@link Expression} from the given {@link LiteralContext} (
* {@link SetLitContext} or {@link ExplicitSetLitContext} or {@link SeqLitContext} or
* {@link ExplicitSeqLitContext}).
*
* @param ctx
* the {@link LiteralContext} ( {@link SetLitContext} or {@link ExplicitSetLitContext} or
* {@link SeqLitContext} or {@link ExplicitSeqLitContext})
* @return the {@link List} of {@link Expression} from the given {@link LiteralContext} (
* {@link SetLitContext} or {@link ExplicitSetLitContext} or {@link SeqLitContext} or
* {@link ExplicitSeqLitContext})
*/
private List<Expression> getExpressions(LiteralContext ctx) {
final int nbExpressions = (ctx.getChild(1).getChildCount() + 1) / 2;
final Expression[] expressions = new Expression[nbExpressions];
for (int i = nbExpressions - 1; i >= 0; i--) {
expressions[i] = popExpression();
}
return Arrays.asList(expressions);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.parser.QueryBaseListener#exitExplicitSeqLit(org.eclipse.acceleo.query.parser.QueryParser.ExplicitSeqLitContext)
*/
@Override
public void exitExplicitSeqLit(ExplicitSeqLitContext ctx) {
final SequenceInExtensionLiteral sequenceInExtension = builder.sequenceInExtension(getExpressions(
ctx));
setPositions(sequenceInExtension, ctx.start, ctx.stop);
push(sequenceInExtension);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.parser.QueryBaseListener#exitConditional(org.eclipse.acceleo.query.parser.QueryParser.ConditionalContext)
*/
@Override
public void exitConditional(ConditionalContext ctx) {
final int count = ctx.getChildCount();
final Expression predicate;
final Expression trueBranch;
final Expression falseBranch;
if (count <= 3) {
predicate = popExpression();
trueBranch = null;
falseBranch = null;
} else if (count <= 5) {
trueBranch = popExpression();
predicate = popExpression();
falseBranch = null;
} else {
falseBranch = popExpression();
trueBranch = popExpression();
predicate = popExpression();
}
final Conditional conditional;
if (errorRule == QueryParser.RULE_expression || count == CONDITIONAL_CONTEXT_CHILD_COUNT && ctx
.getChild(6) instanceof ErrorNode) {
conditional = builder.errorConditional(predicate, trueBranch, falseBranch);
errorRule = NO_ERROR;
pushError((ErrorConditional)conditional, "incomplet conditional");
} else {
conditional = builder.conditional(predicate, trueBranch, falseBranch);
push(conditional);
}
setPositions(conditional, ctx.start, ctx.stop);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.parser.QueryBaseListener#exitBinding(org.eclipse.acceleo.query.parser.QueryParser.BindingContext)
*/
@Override
public void exitBinding(BindingContext ctx) {
// If the error flag is raised an error occurred and the binding is already
// there.
if (errorRule != QueryParser.RULE_binding) {
final String variable = ctx.getChild(0).getText();
final Expression expression = popExpression();
final TypeLiteral type;
if (ctx.getChildCount() == 5) {
type = popTypeLiteral();
} else {
type = null;
}
final Binding binding = builder.binding(variable, type, expression);
setPositions(binding, ctx.start, ctx.stop);
push(binding);
} else {
errorRule = NO_ERROR;
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.parser.QueryBaseListener#exitLetExpr(org.eclipse.acceleo.query.parser.QueryParser.LetExprContext)
*/
@Override
public void exitLetExpr(LetExprContext ctx) {
final Expression body;
final Binding[] bindings;
if (!(ctx.getChild(ctx.getChildCount() - 1) instanceof ExpressionContext)) {
popErrorExpression();
body = builder.errorExpression();
final int position = ctx.stop.getStopIndex() + 1;
final int line = ctx.stop.getLine() - 1;
final int column = ctx.stop.getCharPositionInLine() + ctx.stop.getText().length();
setPositions(body, position, line, column);
final List<Binding> bindingList = new ArrayList<Binding>();
while (!stack.isEmpty() && stack.peek() instanceof Binding) {
bindingList.add(popBinding());
}
bindings = bindingList.toArray(new Binding[bindingList.size()]);
} else {
body = popExpression();
int bindingNumber = 1 + (ctx.getChildCount() - 3) / 2;
bindings = new Binding[bindingNumber];
for (int i = bindingNumber - 1; i >= 0; i--) {
bindings[i] = popBinding();
}
}
final Let let = builder.let(body, bindings);
setPositions(let, ctx.start, ctx.stop);
push(let);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.parser.QueryBaseListener#exitClassifierSetType(org.eclipse.acceleo.query.parser.QueryParser.ClassifierSetTypeContext)
*/
@Override
public void exitClassifierSetType(ClassifierSetTypeContext ctx) {
final int nbTypes = (ctx.getChildCount() + 1) / 2 - 1;
final TypeLiteral[] types = new TypeLiteral[nbTypes];
for (int i = nbTypes - 1; i >= 0; i--) {
types[i] = popTypeLiteral();
}
final TypeSetLiteral classifierSetType = builder.typeSetLiteral(Arrays.asList(types));
setPositions(classifierSetType, ctx.start, ctx.stop);
push(classifierSetType);
}
}