blob: 75dc9a18b3800fdbbe219c4416fdbb6641811114 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 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.aql.parser;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CommonToken;
import org.antlr.v4.runtime.CommonTokenFactory;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.UnbufferedCharStream;
import org.antlr.v4.runtime.UnbufferedTokenStream;
import org.eclipse.acceleo.ASTNode;
import org.eclipse.acceleo.AcceleoPackage;
import org.eclipse.acceleo.Binding;
import org.eclipse.acceleo.Block;
import org.eclipse.acceleo.BlockComment;
import org.eclipse.acceleo.Comment;
import org.eclipse.acceleo.CommentBody;
import org.eclipse.acceleo.Documentation;
import org.eclipse.acceleo.Error;
import org.eclipse.acceleo.ErrorBinding;
import org.eclipse.acceleo.ErrorComment;
import org.eclipse.acceleo.ErrorExpressionStatement;
import org.eclipse.acceleo.ErrorFileStatement;
import org.eclipse.acceleo.ErrorForStatement;
import org.eclipse.acceleo.ErrorIfStatement;
import org.eclipse.acceleo.ErrorImport;
import org.eclipse.acceleo.ErrorLetStatement;
import org.eclipse.acceleo.ErrorMetamodel;
import org.eclipse.acceleo.ErrorModule;
import org.eclipse.acceleo.ErrorModuleDocumentation;
import org.eclipse.acceleo.ErrorModuleElementDocumentation;
import org.eclipse.acceleo.ErrorModuleReference;
import org.eclipse.acceleo.ErrorProtectedArea;
import org.eclipse.acceleo.ErrorQuery;
import org.eclipse.acceleo.ErrorTemplate;
import org.eclipse.acceleo.ErrorVariable;
import org.eclipse.acceleo.Expression;
import org.eclipse.acceleo.ExpressionStatement;
import org.eclipse.acceleo.FileStatement;
import org.eclipse.acceleo.ForStatement;
import org.eclipse.acceleo.IfStatement;
import org.eclipse.acceleo.Import;
import org.eclipse.acceleo.LeafStatement;
import org.eclipse.acceleo.LetStatement;
import org.eclipse.acceleo.Metamodel;
import org.eclipse.acceleo.Module;
import org.eclipse.acceleo.ModuleDocumentation;
import org.eclipse.acceleo.ModuleElement;
import org.eclipse.acceleo.ModuleElementDocumentation;
import org.eclipse.acceleo.ModuleReference;
import org.eclipse.acceleo.NewLineStatement;
import org.eclipse.acceleo.OpenModeKind;
import org.eclipse.acceleo.ParameterDocumentation;
import org.eclipse.acceleo.ProtectedArea;
import org.eclipse.acceleo.Query;
import org.eclipse.acceleo.Statement;
import org.eclipse.acceleo.Template;
import org.eclipse.acceleo.TextStatement;
import org.eclipse.acceleo.Variable;
import org.eclipse.acceleo.VisibilityKind;
import org.eclipse.acceleo.query.ast.AstPackage;
import org.eclipse.acceleo.query.ast.ErrorTypeLiteral;
import org.eclipse.acceleo.query.parser.AstBuilderListener;
import org.eclipse.acceleo.query.parser.AstResult;
import org.eclipse.acceleo.query.parser.Positions;
import org.eclipse.acceleo.query.parser.QueryLexer;
import org.eclipse.acceleo.query.parser.QueryParser;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
/**
* Acceleo parser.
*
* @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
*/
public class AcceleoParser {
/**
* The acceleo URI protocol.
*/
public static final String ACCELEOENV_URI_PROTOCOL = "acceleoenv::";
/**
* The module file extension.
*/
public static final String MODULE_FILE_EXTENSION = "mtl";
/**
* New line.
*/
public static final String NEW_LINE = "\n";
/**
* In line end delimiter.
*/
public static final String NO_SLASH_END = "]";
/**
* In line end delimiter.
*/
public static final String SLASH_END = "/]";
/**
* End of {@link TextStatement}.
*/
public static final String TEXT_END = "[";
/**
* Start of {@link BlockComment}.
*/
public static final String BLOCK_COMMENT_START = "[comment]";
/**
* End of {@link BlockComment}.
*/
public static final String BLOCK_COMMENT_END = "[/comment]";
/**
* Start of {@link Comment}.
*/
public static final String COMMENT_START = "[comment ";
/**
* End of {@link Comment}.
*/
public static final String COMMENT_END = SLASH_END;
/**
* Start of {@link Module} header.
*/
public static final String MODULE_HEADER_START = "[module ";
/**
* End of {@link Module} header.
*/
public static final String MODULE_HEADER_END = SLASH_END;
/**
* Open parenthesis.
*/
public static final String OPEN_PARENTHESIS = "(";
/**
* Close parenthesis.
*/
public static final String CLOSE_PARENTHESIS = ")";
/**
* A comma.
*/
public static final String COMMA = ",";
/**
* A colon.
*/
public static final String COLON = ":";
/**
* A qualifier separator.
*/
public static final String QUALIFIER_SEPARATOR = "::";
/**
* An equal.
*/
public static final String EQUAL = "=";
/**
* A pipe.
*/
public static final String PIPE = "|";
/**
* A quote.
*/
public static final String QUOTE = "'";
/**
* Start of {@link Template} header.
*/
public static final String TEMPLATE_HEADER_START = "[template ";
/**
* Start of {@link Template} header.
*/
public static final String TEMPLATE_HEADER_END = NO_SLASH_END;
/**
* Guard of {@link Template}.
*/
public static final String TEMPLATE_GUARD = "?";
/**
* Post of {@link Template}.
*/
public static final String TEMPLATE_POST = "post(";
/**
* End block prefix.
*/
public static final String END_BLOCK_PREFIX = "[/";
/**
* End of {@link Template}.
*/
public static final String TEMPLATE_END = END_BLOCK_PREFIX + "template]";
/**
* Start of {@link ExpressionStatement}.
*/
public static final String EXPRESSION_STATEMENT_START = "[";
/**
* End of {@link ExpressionStatement}.
*/
public static final String EXPRESSION_STATEMENT_END = SLASH_END;
/**
* Start of {@link ProtectedArea} header.
*/
public static final String PROTECTED_AREA_HEADER_START = "[protected ";
/**
* End of {@link ProtectedArea} header.
*/
public static final String PROTECTED_AREA_HEADER_END = NO_SLASH_END;
/**
* End of {@link ProtectedArea}.
*/
public static final String PROTECTED_AREA_END = END_BLOCK_PREFIX + "protected]";
/**
* Start of {@link FileStatement} header.
*/
public static final String FILE_HEADER_START = "[file ";
/**
* End of {@link FileStatement} header.
*/
public static final String FILE_HEADER_END = NO_SLASH_END;
/**
* End of {@link FileStatement}.
*/
public static final String FILE_END = END_BLOCK_PREFIX + "file]";
/**
* Start of {@link LetStatement} header.
*/
public static final String LET_HEADER_START = "[let ";
/**
* End of {@link LetStatement} header.
*/
public static final String LET_HEADER_END = NO_SLASH_END;
/**
* End of {@link LetStatement}.
*/
public static final String LET_END = END_BLOCK_PREFIX + "let]";
/**
* Start of {@link ForStatement} header.
*/
public static final String FOR_HEADER_START = "[for ";
/**
* Start of {@link ForStatement} header.
*/
public static final String FOR_SEPARATOR = "separator(";
/**
* End of {@link ForStatement} header.
*/
public static final String FOR_HEADER_END = NO_SLASH_END;
/**
* End of {@link ForStatement}.
*/
public static final String FOR_END = END_BLOCK_PREFIX + "for]";
/**
* Start of {@link IfStatement} header.
*/
public static final String IF_HEADER_START = "[if ";
/**
* End of {@link IfStatement} header.
*/
public static final String IF_HEADER_END = NO_SLASH_END;
/**
* End of {@link IfStatement}.
*/
public static final String IF_ELSEIF = "[elseif ";
/**
* End of {@link IfStatement}.
*/
public static final String IF_ELSE = "[else]";
/**
* End of {@link IfStatement}.
*/
public static final String IF_END = END_BLOCK_PREFIX + "if]";
/**
* Start of {@link Query}.
*/
public static final String QUERY_START = "[query ";
/**
* End of {@link Query}.
*/
public static final String QUERY_END = SLASH_END;
/**
* Start of {@link Import}.
*/
public static final String IMPORT_START = "[import ";
/**
* End of {@link Import}.
*/
public static final String IMPORT_END = SLASH_END;
/**
* Start of {@link Documentation}.
*/
public static final String DOCUMENTATION_START = "[**";
/**
* End of {@link Documentation}.
*/
public static final String DOCUMENTATION_END = SLASH_END;
/**
* Main tag.
*/
public static final String MAIN_TAG = "@main";
/**
* Author tag.
*/
public static final String AUTHOR_TAG = "@author ";
/**
* Version tag.
*/
public static final String VERSION_TAG = "@version ";
/**
* Since tag.
*/
public static final String SINCE_TAG = "@since ";
/**
* Param tag.
*/
public static final String PARAM_TAG = "@param ";
/**
* Extends key work.
*/
public static final String EXTENDS = "extends ";
/**
* The mandatory indentation for not inlined {@link Block}.
*/
public static final int INDENTATION = 2;
/**
* The {@link Positions}.
*/
private Positions positions;
/**
* The parser currentPosition.
*/
private int currentPosition;
/**
* The line number at a given position.
*/
private int[] lines;
/**
* The column number at a given position.
*/
private int[] columns;
/**
* The source text.
*/
private String text;
/**
* The {@link List} of {@link Error}.
*/
private List<Error> errors;
/**
* Parses the given {@link InputStream}.
*
* @param source
* the {@link InputStream}
* @param charset
* the {@link Charset}
* @param moduleQualifiedName
* the qualified name of the {@link Module} (e.g. "path::to::module").
* @return the parsed {@link AstResult}
* @throws IOException
* if the {@link InputStream} can't be read
*/
public AcceleoAstResult parse(InputStream source, Charset charset, String moduleQualifiedName)
throws IOException {
return parse(getContent(source, charset), moduleQualifiedName);
}
/**
* Gets the content of the given {@link InputStream}.
*
* @param stream
* the {@link InputStream}
* @param charset
* the {@link Charset}
* @return a {@link CharSequence} of the content of the given {@link InputStream}
* @throws IOException
* if the {@link InputStream} can't be read
*/
private String getContent(InputStream stream, Charset charset) throws IOException {
final int len = 8192;
StringBuilder res = new StringBuilder(len);
try (InputStreamReader input = new InputStreamReader(new BufferedInputStream(stream), charset)) {
char[] buffer = new char[len];
int length = input.read(buffer);
while (length != -1) {
res.append(buffer, 0, length);
length = input.read(buffer);
}
input.close();
}
return res.toString();
}
/**
* Parses the given source text.
*
* @param source
* the source text
* @param qualifiedName
* the qualified name of the {@link Module} (e.g. "path::to::module").
* @return the parsed {@link AstResult}
*/
public AcceleoAstResult parse(String source, String qualifiedName) {
this.currentPosition = 0;
this.lines = new int[source.length() + 1];
this.columns = new int[source.length() + 1];
this.positions = new Positions();
this.text = source;
computeLinesAndColumns(text);
this.errors = new ArrayList<Error>();
final List<Comment> comments = parseCommentsOrModuleDocumentations();
final Module module = parseModule(comments);
final Resource containerEmfResource = new XMIResourceImpl(URI.createURI(ACCELEOENV_URI_PROTOCOL
+ qualifiedName));
containerEmfResource.getContents().add(module);
return new AcceleoAstResult(module, positions, errors);
}
/**
* Computes the lines and columns at given position.
*
* @param str
* the source
*/
// TODO do it on the fly to prevent reading the input twice
private void computeLinesAndColumns(String str) {
int currentLine = 0;
int currentColumn = 0;
for (int i = 0; i < str.length(); i++) {
lines[i] = currentLine;
columns[i] = currentColumn;
char currentCharacter = str.charAt(i);
if (currentCharacter == '\n') {
currentLine++;
currentColumn = 0;
} else {
currentColumn++;
}
}
// User cursor may be at the position right after the last character of the String.
lines[str.length()] = currentLine;
columns[str.length()] = currentColumn;
}
/**
* Sets the identifier start and identifer end positions for the given {@link ASTNode}.
*
* @param node
* the {@link ASTNode}
* @param start
* the start position
* @param end
* the end position
*/
private void setIdentifierPositions(ASTNode node, int start, int end) {
positions.setIdentifierStartPositions(node, start);
positions.setIdentifierStartLines(node, lines[start]);
positions.setIdentifierStartColumns(node, columns[start]);
positions.setIdentifierEndPositions(node, end);
positions.setIdentifierEndLines(node, lines[end]);
positions.setIdentifierEndColumns(node, columns[end]);
}
/**
* Sets the start and end positions for the given {@link ASTNode}.
*
* @param node
* the {@link ASTNode}
* @param start
* the start position
* @param end
* the end position
*/
private void setPositions(ASTNode node, int start, int end) {
positions.setStartPositions(node, start);
positions.setStartLines(node, lines[start]);
positions.setStartColumns(node, columns[start]);
positions.setEndPositions(node, end);
positions.setEndLines(node, lines[end]);
positions.setEndColumns(node, columns[end]);
}
/**
* Parses a {@link List} of {@link Comment} and {@link ModuleDocumentation}.
*
* @return the created {@link List} of {@link Comment} and {@link ModuleDocumentation}
*/
protected List<Comment> parseCommentsOrModuleDocumentations() {
final List<Comment> comments = new ArrayList<Comment>();
Comment comment = parseComment();
ModuleDocumentation documentation = parseModuleDocumentation();
while (comment != null || documentation != null) {
if (comment != null) {
comments.add(comment);
}
if (documentation != null) {
comments.add(documentation);
}
skipSpaces();
comment = parseComment();
documentation = parseModuleDocumentation();
}
return comments;
}
/**
* Skips {@link Character#isWhitespace(char) white spaces}.
*/
protected void skipSpaces() {
while (currentPosition < text.length() && Character.isWhitespace(text.charAt(currentPosition))) {
currentPosition++;
}
}
/**
* Reads the given {@link String}.
*
* @param str
* the {@link String} to read
* @return <code>true</code> if the given {@link String} is at the current position, <code>false</code>
* otherwise
*/
protected boolean readString(String str) {
final boolean res = text.startsWith(str, currentPosition);
if (res) {
currentPosition += str.length();
}
return res;
}
/**
* {@link AcceleoParser#readString(String) Reads} the given {@link String}.
*
* @param str
* the {@link String} to read
* @return <code>-1</code> if {@link AcceleoParser#readString(String) read} the given {@link String}
* succeed, the current position otherwise
*/
protected int readMissingString(String str) {
final int res;
if (readString(str)) {
res = -1;
} else {
res = currentPosition;
}
return res;
}
/**
* Parses and identifier.
*
* @return the identifier if any recognized, <code>null</code> otherwise
*/
protected String parseIdentifier() {
final String res;
if (currentPosition < text.length() && Character.isJavaIdentifierStart(text.charAt(
currentPosition))) {
final int identifierStart = currentPosition;
currentPosition++;
while (currentPosition < text.length() && Character.isJavaIdentifierPart(text.charAt(
currentPosition))) {
currentPosition++;
}
res = text.substring(identifierStart, currentPosition);
} else {
res = null;
}
return res;
}
/**
* Parses a {@link Comment} or a {@link BlockComment}.
*
* @return the created {@link Comment} or {@link BlockComment} if any recognized, <code>null</code>
* otherwise
*/
protected Comment parseComment() {
final Comment res;
if (text.startsWith(BLOCK_COMMENT_START, currentPosition)) {
res = createComment(BLOCK_COMMENT_START, BLOCK_COMMENT_END, AcceleoPackage.eINSTANCE
.getBlockComment(), AcceleoPackage.eINSTANCE.getErrorBlockComment());
} else if (text.startsWith(COMMENT_START, currentPosition)) {
res = createComment(COMMENT_START, COMMENT_END, AcceleoPackage.eINSTANCE.getComment(),
AcceleoPackage.eINSTANCE.getErrorComment());
} else {
res = null;
}
return res;
}
/**
* Creates the {@link Comment} or {@link ErrorComment}.
*
* @param startTag
* the start tag
* @param endTag
* the end tag
* @param commentClass
* the {@link Comment} {@link EClass}
* @param errorCommentClass
* the {@link ErrorComment} {@link EClass}
* @return the {@link Comment} or {@link ErrorComment}
*/
private Comment createComment(String startTag, String endTag, EClass commentClass,
EClass errorCommentClass) {
final Comment res;
final int startPosition = currentPosition;
final int startOfCommentBody = currentPosition + startTag.length();
int endOfCommentBody = getNext(endTag);
if (endOfCommentBody < 0) {
endOfCommentBody = text.length();
res = (Comment)EcoreUtil.create(errorCommentClass);
setPositions(res, startPosition, endOfCommentBody);
((ErrorComment)res).setMissingEndHeader(endOfCommentBody);
errors.add((Error)res);
currentPosition = endOfCommentBody;
} else {
res = (Comment)EcoreUtil.create(commentClass);
setPositions(res, startPosition, endOfCommentBody + endTag.length());
currentPosition = endOfCommentBody + endTag.length();
}
final CommentBody commentBody = AcceleoPackage.eINSTANCE.getAcceleoFactory().createCommentBody();
commentBody.setValue(text.substring(startOfCommentBody, endOfCommentBody));
setPositions(commentBody, startOfCommentBody, endOfCommentBody);
res.setBody(commentBody);
return res;
}
/**
* Parses a {@link ModuleDocumentation}.
*
* @return the created {@link ModuleDocumentation} if any recognized, <code>null</code> otherwise
*/
protected ModuleDocumentation parseModuleDocumentation() {
final ModuleDocumentation res;
if (text.startsWith(DOCUMENTATION_START, currentPosition)) {
final int commentStartPositon = currentPosition;
currentPosition += DOCUMENTATION_START.length();
final int startPosition = currentPosition;
int endPosition = getNext(DOCUMENTATION_END);
if (endPosition < 0) {
endPosition = text.length();
currentPosition = endPosition;
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createErrorModuleDocumentation();
((ErrorModuleDocumentation)res).setMissingEndHeader(endPosition);
errors.add((Error)res);
} else {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createModuleDocumentation();
currentPosition = endPosition + DOCUMENTATION_END.length();
}
final String docString = text.substring(startPosition, endPosition);
final CommentBody commentBody = AcceleoPackage.eINSTANCE.getAcceleoFactory().createCommentBody();
setPositions(commentBody, startPosition, endPosition);
commentBody.setValue(docString);
res.setBody(commentBody);
final int authorPosition = docString.indexOf(AUTHOR_TAG);
if (authorPosition >= 0) {
final int authorStart = authorPosition + AUTHOR_TAG.length();
int authorEnd = docString.indexOf(NEW_LINE, authorStart);
res.setAuthor(docString.substring(authorStart, authorEnd));
}
final int versionPosition = docString.indexOf(VERSION_TAG);
if (versionPosition >= 0) {
final int versionStart = versionPosition + VERSION_TAG.length();
int versionEnd = docString.indexOf(NEW_LINE, versionStart);
res.setVersion(docString.substring(versionStart, versionEnd));
}
final int sincePosition = docString.indexOf(SINCE_TAG);
if (sincePosition >= 0) {
final int sinceStart = sincePosition + SINCE_TAG.length();
int sinceEnd = docString.indexOf(NEW_LINE, sinceStart);
if (sinceEnd >= 0) {
res.setSince(docString.substring(sinceStart, sinceEnd));
} else {
res.setSince(docString.substring(sinceStart, docString.length()));
}
}
setPositions(res, commentStartPositon, currentPosition);
} else {
res = null;
}
return res;
}
/**
* Parses a {@link ModuleElementDocumentation}.
*
* @return the created {@link ModuleElementDocumentation} if any recognized, <code>null</code> otherwise
*/
protected ModuleElementDocumentation parseModuleElementDocumentation() {
final ModuleElementDocumentation res;
if (text.startsWith(DOCUMENTATION_START, currentPosition)) {
final int commentStartPositon = currentPosition;
currentPosition += DOCUMENTATION_START.length();
final int startPosition = currentPosition;
int endPosition = getNext(DOCUMENTATION_END);
if (endPosition < 0) {
endPosition = text.length();
currentPosition = endPosition;
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createErrorModuleElementDocumentation();
((ErrorModuleElementDocumentation)res).setMissingEndHeader(endPosition);
errors.add((Error)res);
} else {
currentPosition = endPosition + DOCUMENTATION_END.length();
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createModuleElementDocumentation();
}
final String docString = text.substring(startPosition, endPosition);
final CommentBody commentBody = AcceleoPackage.eINSTANCE.getAcceleoFactory().createCommentBody();
setPositions(commentBody, currentPosition, endPosition);
commentBody.setValue(docString);
res.setBody(commentBody);
int paramPosition = docString.indexOf(PARAM_TAG);
while (paramPosition >= 0) {
final ParameterDocumentation paramDoc = AcceleoPackage.eINSTANCE.getAcceleoFactory()
.createParameterDocumentation();
final int paramStart = paramPosition + PARAM_TAG.length();
int paramEnd = docString.indexOf(NEW_LINE, paramStart);
if (paramEnd < 0) {
paramPosition = -1;
paramEnd = docString.length();
} else {
paramPosition = docString.indexOf(PARAM_TAG, paramEnd);
}
setPositions(paramDoc, paramStart + startPosition, paramEnd + startPosition);
final CommentBody paramBody = AcceleoPackage.eINSTANCE.getAcceleoFactory()
.createCommentBody();
paramBody.setValue(docString.substring(paramStart, paramEnd));
setPositions(paramBody, paramStart + startPosition, paramEnd + startPosition);
paramDoc.setBody(paramBody);
res.getParameterDocumentation().add(paramDoc);
}
setPositions(res, commentStartPositon, currentPosition);
} else {
res = null;
}
return res;
}
/**
* Parses a {@link Module}.
*
* @param comments
* the {@link List} of {@link Comment}
* @return the created {@link Module}
*/
protected Module parseModule(List<Comment> comments) {
final Module res;
if (text.startsWith(MODULE_HEADER_START, currentPosition)) {
// Start of the whole module, including comments and documentation that may be before the
// '[module' declaration.
final int startPosition = Stream.concat(Stream.of(currentPosition), comments.stream().map(
positions::getStartPositions)).min(Integer::compareTo).get();
// Start of the header of the module, i.e. where the '[module' declaration is located.
final int startHeaderPosition = currentPosition;
currentPosition += MODULE_HEADER_START.length();
skipSpaces();
final int identifierStartPosition = currentPosition;
final String name = parseIdentifier();
final int identifierEndPosition = currentPosition;
skipSpaces();
final int missingOpenParenthesis = readMissingString(OPEN_PARENTHESIS);
skipSpaces();
final List<Metamodel> metamodels = new ArrayList<Metamodel>();
Metamodel metamodel = parseMetamodel();
int missingEPackage = -1;
if (metamodel == null) {
missingEPackage = currentPosition;
}
while (metamodel != null) {
metamodels.add(metamodel);
skipSpaces();
if (readString(COMMA)) {
skipSpaces();
metamodel = parseMetamodel();
if (metamodel == null) {
missingEPackage = currentPosition;
} else {
missingEPackage = -1;
}
} else {
break;
}
}
skipSpaces();
final int missingCloseParenthesis = readMissingString(CLOSE_PARENTHESIS);
skipSpaces();
final ModuleReference extendedModule;
if (readString(EXTENDS)) {
skipSpaces();
extendedModule = parseModuleReference();
skipSpaces();
} else {
extendedModule = null;
}
final int missingEndHeader = readMissingString(MODULE_HEADER_END);
final int endHeaderPosition = currentPosition;
skipSpaces();
final List<Import> imports = new ArrayList<Import>();
Import imported = parseImport();
while (imported != null) {
imports.add(imported);
skipSpaces();
imported = parseImport();
}
final List<ModuleElement> moduleElements = parseModuleElements();
final int endPosition = currentPosition;
final boolean missingParenthesis = missingOpenParenthesis != -1 || missingCloseParenthesis != -1;
if (missingParenthesis || missingEPackage != -1 || missingEndHeader != -1) {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createErrorModule();
((ErrorModule)res).setMissingOpenParenthesis(missingOpenParenthesis);
((ErrorModule)res).setMissingEPackage(missingEPackage);
((ErrorModule)res).setMissingCloseParenthesis(missingCloseParenthesis);
((ErrorModule)res).setMissingEndHeader(missingEndHeader);
errors.add((ErrorModule)res);
} else {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createModule();
}
setIdentifierPositions(res, identifierStartPosition, identifierEndPosition);
setPositions(res, startPosition, endPosition);
res.setStartHeaderPosition(startHeaderPosition);
res.setEndHeaderPosition(endHeaderPosition);
res.getModuleElements().addAll(comments);
res.setDocumentation(getLastDocumentation(comments));
res.setName(name);
res.getMetamodels().addAll(metamodels);
res.getImports().addAll(imports);
res.setExtends(extendedModule);
res.getModuleElements().addAll(moduleElements);
} else {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createErrorModule();
errors.add((ErrorModule)res);
}
return res;
}
/**
* Parses an import {@link ModuleReference}.
*
* @return the create {@link ModuleReference} if any is recognized, <code>null</code> otherwise
*/
protected Import parseImport() {
final Import res;
if (text.startsWith(IMPORT_START, currentPosition)) {
final int startPosition = currentPosition;
currentPosition += IMPORT_START.length();
skipSpaces();
final ModuleReference moduleReference = parseModuleReference();
skipSpaces();
final int missingEnd = readMissingString(IMPORT_END);
if (missingEnd != -1) {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createErrorImport();
((ErrorImport)res).setMissingEnd(missingEnd);
errors.add((ErrorImport)res);
} else {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createImport();
}
res.setModule(moduleReference);
setPositions(res, startPosition, currentPosition);
} else {
res = null;
}
return res;
}
/**
* Parses a {@link ModuleReference}.
*
* @return the recognized {@link ModuleReference} if any, <code>null</code> otherwise
*/
protected ModuleReference parseModuleReference() {
final ModuleReference res;
final int startPosition = currentPosition;
final String moduleQualifiedName = parseModuleQualifiedName();
if (moduleQualifiedName == null) {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createErrorModuleReference();
errors.add((ErrorModuleReference)res);
} else {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createModuleReference();
}
res.setQualifiedName(moduleQualifiedName);
setPositions(res, startPosition, currentPosition);
return res;
}
/**
* Parses module qualified name.
*
* @return the recognized module qualified name if any, <code>null</code> otherwise
*/
protected String parseModuleQualifiedName() {
final StringBuilder builder = new StringBuilder();
String segment = parseIdentifier();
while (segment != null) {
builder.append(segment);
if (readString(QUALIFIER_SEPARATOR)) {
builder.append(QUALIFIER_SEPARATOR);
segment = parseIdentifier();
} else {
break;
}
}
final String res;
if (builder.length() != 0) {
res = builder.toString();
} else {
res = null;
}
return res;
}
/**
* Gets the last {@link Documentation} from the given {@link List} of {@link Comment}.
*
* @param comments
* the {@link List} of {@link Comment}
* @return the last {@link Documentation} from the given {@link List} of {@link Comment}
*/
protected Documentation getLastDocumentation(List<Comment> comments) {
for (int i = comments.size() - 1; i >= 0; i--) {
Comment current = comments.get(i);
if (current instanceof Documentation) {
return (Documentation)current;
}
}
return null;
}
/**
* Parses a {@link List} of {@link ModuleElement}.
*
* @return the created {@link List} of {@link ModuleElement}
*/
protected List<ModuleElement> parseModuleElements() {
final List<ModuleElement> res = new ArrayList<ModuleElement>();
ModuleElement moduleElement;
do {
skipSpaces();
final List<Comment> comments = parseCommentsOrModuleElementDocumentations();
res.addAll(comments);
final Documentation documentation = getLastDocumentation(comments);
final Template template = parseTemplate(documentation, hasMain(comments));
if (template != null) {
moduleElement = template;
} else {
moduleElement = parseQuery(documentation);
}
if (moduleElement != null) {
res.add(moduleElement);
}
} while (moduleElement != null);
return res;
}
/**
* Tells if the given {@link List} of {@link Comment} contains a {@value #MAIN_TAG}.
*
* @param comments
* the {@link List} of {@link Comment}
* @return <code>true</code> if the given {@link List} of {@link Comment} contains a {@value #MAIN_TAG},
* <code>false</code> otherwise
*/
private boolean hasMain(List<Comment> comments) {
for (Comment comment : comments) {
if (comment.getBody().getValue().contains(MAIN_TAG)) {
return true;
}
}
return false;
}
/**
* Parses a {@link List} of {@link Comment} and {@link ModuleElementDocumentation}.
*
* @return the created {@link List} of {@link Comment} and {@link ModuleElementDocumentation}
*/
protected List<Comment> parseCommentsOrModuleElementDocumentations() {
final List<Comment> comments = new ArrayList<Comment>();
Comment comment = parseComment();
ModuleElementDocumentation documentation = parseModuleElementDocumentation();
while (comment != null || documentation != null) {
if (comment != null) {
comments.add(comment);
}
if (documentation != null) {
comments.add(documentation);
}
skipSpaces();
comment = parseComment();
documentation = parseModuleElementDocumentation();
}
return comments;
}
/**
* Parses a {@link Query}.
*
* @param documentation
* the {@link Documentation} for the {@link Query} if any, <code>null</code> otherwise
* @return the created {@link Query} if any recognized, <code>null</code> otherwise
*/
protected Query parseQuery(Documentation documentation) {
final Query res;
if (text.startsWith(QUERY_START, currentPosition)) {
final int startPosition = currentPosition;
currentPosition += QUERY_START.length();
skipSpaces();
final VisibilityKind visibility = parseVisibility();
final int missingVisibility;
if (visibility == null) {
missingVisibility = currentPosition;
} else {
missingVisibility = -1;
}
skipSpaces();
final int identifierStartPosition = currentPosition;
final String name = parseIdentifier();
final int identifierEndPosition = currentPosition;
final int missingName;
if (name == null) {
missingName = currentPosition;
} else {
missingName = -1;
}
skipSpaces();
final int missingOpenParenthesis = readMissingString(OPEN_PARENTHESIS);
skipSpaces();
final List<Variable> parameters = parseParameters();
final int missingParameters;
if (parameters.isEmpty()) {
missingParameters = currentPosition;
} else {
missingParameters = -1;
}
skipSpaces();
final int missingCloseParenthesis = readMissingString(CLOSE_PARENTHESIS);
skipSpaces();
final int missingColon = readMissingString(COLON);
skipSpaces();
final int typeEndLimit = getAqlExpressionEndLimit(EQUAL, QUERY_END);
final AstResult type = parseWhileAqlTypeLiteral(text.substring(currentPosition, typeEndLimit));
type.addAllPositonsTo(positions, currentPosition, lines[currentPosition],
columns[currentPosition]);
currentPosition += type.getEndPosition(type.getAst());
final int missingType;
if (type.getStartPosition(type.getAst()) == type.getEndPosition(type.getAst())) {
missingType = currentPosition;
} else {
missingType = -1;
}
skipSpaces();
final int missingEqual = readMissingString(EQUAL);
skipSpaces();
final int expressionEndLimit = getAqlExpressionEndLimit(QUERY_END, QUERY_END);
final Expression body = parseExpression(expressionEndLimit);
skipSpaces();
final int missingEnd = readMissingString(QUERY_END);
final boolean missingValue = missingVisibility != -1 || missingName != -1 || missingType != -1
|| parameters.isEmpty();
final boolean missingParenthesis = missingOpenParenthesis != -1 || missingParameters != -1
|| missingCloseParenthesis != -1;
final boolean missingSymbols = missingColon != -1 || missingEqual != -1 || missingEnd != -1;
if (missingVisibility != -1 || missingValue || missingParenthesis || missingSymbols) {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createErrorQuery();
((ErrorQuery)res).setMissingVisibility(missingVisibility);
((ErrorQuery)res).setMissingName(missingName);
((ErrorQuery)res).setMissingOpenParenthesis(missingOpenParenthesis);
((ErrorQuery)res).setMissingParameters(missingParameters);
((ErrorQuery)res).setMissingCloseParenthesis(missingCloseParenthesis);
((ErrorQuery)res).setMissingColon(missingColon);
((ErrorQuery)res).setMissingType(missingType);
((ErrorQuery)res).setMissingEqual(missingEqual);
((ErrorQuery)res).setMissingEnd(missingEnd);
errors.add((ErrorQuery)res);
} else {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createQuery();
}
res.setDocumentation(documentation);
res.setVisibility(visibility);
res.setName(name);
res.getParameters().addAll(parameters);
if (type != null) {
res.setType(type);
res.setTypeAql(type.getAst());
}
res.setBody(body);
setIdentifierPositions(res, identifierStartPosition, identifierEndPosition);
setPositions(res, startPosition, currentPosition);
} else {
res = null;
}
return res;
}
/**
* Parses a {@link List} of at least one {@link Variable}.
*
* @return the {@link List} of at least one {@link Variable}
*/
protected List<Variable> parseParameters() {
final List<Variable> res = new ArrayList<Variable>();
Variable variable = parseVariable();
while (variable != null) {
res.add(variable);
skipSpaces();
if (readString(COMMA)) {
skipSpaces();
variable = parseVariable();
} else {
skipSpaces();
break;
}
}
return res;
}
/**
* Parses a {@link Variable}.
*
* @return the created {@link Variable} if any is recognized, <code>null</code> otherwise
*/
protected Variable parseVariable() {
final Variable res;
final int startPosition = currentPosition;
final int identifierStartPosition = currentPosition;
final String name = parseIdentifier();
final int identifierEndPosition = currentPosition;
final int missingName;
if (name == null) {
missingName = currentPosition;
} else {
missingName = -1;
}
skipSpaces();
final int missingColon = readMissingString(COLON);
skipSpaces();
final int typeEndLimit = getAqlExpressionEndLimit(COMMA, CLOSE_PARENTHESIS);
final AstResult type = parseWhileAqlTypeLiteral(text.substring(currentPosition, typeEndLimit));
type.addAllPositonsTo(positions, currentPosition, lines[currentPosition], columns[currentPosition]);
currentPosition += type.getEndPosition(type.getAst());
final int missingType;
if (type.getStartPosition(type.getAst()) == type.getEndPosition(type.getAst())) {
missingType = currentPosition;
} else {
missingType = -1;
}
if (missingName == -1) {
if (missingType != -1 || missingColon != -1) {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createErrorVariable();
((ErrorVariable)res).setMissingName(missingName);
((ErrorVariable)res).setMissingColon(missingColon);
((ErrorVariable)res).setMissingType(missingType);
errors.add((ErrorVariable)res);
} else {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createVariable();
}
res.setName(name);
if (type != null) {
res.setType(type);
res.setTypeAql(type.getAst());
}
setIdentifierPositions(res, identifierStartPosition, identifierEndPosition);
setPositions(res, startPosition, currentPosition);
} else {
res = null;
}
return res;
}
/**
* Parses a {@link Template}.
*
* @param documentation
* the {@link Documentation} for the {@link Template} if any, <code>null</code> otherwise
* @param isMain
* tells if the parsed {@link Template} should be a {@link Template#isMain() main}
* {@link Template}.
* @return the created {@link Template} if any recognized, <code>null</code> otherwise
*/
// CHECKSTYLE:OFF
protected Template parseTemplate(Documentation documentation, boolean isMain) {
final Template res;
if (text.startsWith(TEMPLATE_HEADER_START, currentPosition)) {
final int startPosition = currentPosition;
final int significantTextColumn = columns[startPosition] + INDENTATION;
final int headerStartLine = lines[startPosition];
currentPosition += TEMPLATE_HEADER_START.length();
skipSpaces();
final VisibilityKind visibility = parseVisibility();
final int missingVisibility;
if (visibility == null) {
missingVisibility = currentPosition;
} else {
missingVisibility = -1;
}
skipSpaces();
final int identifierStartPosition = currentPosition;
final String name = parseIdentifier();
final int identifierEndPosition = currentPosition;
final int missingName;
if (name == null) {
missingName = currentPosition;
} else {
missingName = -1;
}
skipSpaces();
final int missingOpenParenthesis = readMissingString(OPEN_PARENTHESIS);
skipSpaces();
final List<Variable> parameters = parseParameters();
final int missingParameters;
if (parameters.isEmpty()) {
missingParameters = currentPosition;
} else {
missingParameters = -1;
}
skipSpaces();
final int missingCloseParenthesis = readMissingString(CLOSE_PARENTHESIS);
skipSpaces();
final int missingGuardOpenParenthesis;
final int missingGuardCloseParenthesis;
final Expression guardExpression;
if (text.startsWith(TEMPLATE_GUARD, currentPosition)) {
currentPosition += TEMPLATE_GUARD.length();
skipSpaces();
missingGuardOpenParenthesis = readMissingString(OPEN_PARENTHESIS);
skipSpaces();
final int expressionEndLimit = getAqlExpressionEndLimit(CLOSE_PARENTHESIS,
TEMPLATE_HEADER_END);
guardExpression = parseExpression(expressionEndLimit);
skipSpaces();
missingGuardCloseParenthesis = readMissingString(CLOSE_PARENTHESIS);
skipSpaces();
} else {
guardExpression = null;
missingGuardOpenParenthesis = -1;
missingGuardCloseParenthesis = -1;
}
final int missingPostCloseParenthesis;
final Expression postExpression;
if (text.startsWith(TEMPLATE_POST, currentPosition)) {
currentPosition += TEMPLATE_POST.length();
skipSpaces();
final int expressionEndLimit = getAqlExpressionEndLimit(CLOSE_PARENTHESIS,
TEMPLATE_HEADER_END);
postExpression = parseExpression(expressionEndLimit);
skipSpaces();
missingPostCloseParenthesis = readMissingString(CLOSE_PARENTHESIS);
skipSpaces();
} else {
missingPostCloseParenthesis = -1;
postExpression = null;
}
final int missingEndHeader = readMissingString(TEMPLATE_HEADER_END);
final Block body = parseBlock(headerStartLine, significantTextColumn, TEMPLATE_END);
final int missingEnd = readMissingString(TEMPLATE_END);
final boolean missingValue = missingVisibility != -1 || missingName != -1 || parameters.isEmpty();
final boolean missingGuardParenthesis = missingGuardOpenParenthesis != -1
|| missingGuardCloseParenthesis != -1;
final boolean missingParenthesis = missingOpenParenthesis != -1 || missingParameters != -1
|| missingCloseParenthesis != -1 || missingGuardParenthesis
|| missingPostCloseParenthesis != -1;
if (missingValue || missingParenthesis || missingEndHeader != -1 || missingEnd != -1) {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createErrorTemplate();
((ErrorTemplate)res).setMissingVisibility(missingVisibility);
((ErrorTemplate)res).setMissingName(missingName);
((ErrorTemplate)res).setMissingOpenParenthesis(missingOpenParenthesis);
((ErrorTemplate)res).setMissingParameters(missingParameters);
((ErrorTemplate)res).setMissingCloseParenthesis(missingCloseParenthesis);
((ErrorTemplate)res).setMissingGuardOpenParenthesis(missingGuardOpenParenthesis);
((ErrorTemplate)res).setMissingGuardCloseParenthesis(missingGuardCloseParenthesis);
((ErrorTemplate)res).setMissingPostCloseParenthesis(missingPostCloseParenthesis);
((ErrorTemplate)res).setMissingEndHeader(missingEndHeader);
((ErrorTemplate)res).setMissingEnd(missingEnd);
errors.add((ErrorTemplate)res);
} else {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createTemplate();
}
res.setMain(isMain);
res.setDocumentation(documentation);
res.setVisibility(visibility);
res.setName(name);
res.getParameters().addAll(parameters);
res.setGuard(guardExpression);
res.setPost(postExpression);
res.setBody(body);
setIdentifierPositions(res, identifierStartPosition, identifierEndPosition);
setPositions(res, startPosition, currentPosition);
} else {
res = null;
}
return res;
}
// CHECKSTYLE:ON
/**
* Parses a {@link Block}.
*
* @param headerStartLine
* the header start line
* @param significantTextColumn
* the column where the text starts to be significant for {@link TextStatement}
* @param endBlocks
* the end of block strings
* @return the created {@link Block}
*/
protected Block parseBlock(int headerStartLine, int significantTextColumn, String... endBlocks) {
final Block res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createBlock();
final int startPosition = currentPosition;
boolean inlined = !text.startsWith(NEW_LINE, currentPosition)
&& headerStartLine == lines[currentPosition];
skipNewLine();
int beforeStatementPosition = currentPosition;
Statement statement = parseStatement(inlined, significantTextColumn);
int afterStatementPosition = currentPosition;
boolean onlyCommentsFromStart = true;
LeafStatement lastLeafStatement = null;
endOfBlock: while (beforeStatementPosition != afterStatementPosition) {
if (statement != null) {
if (statement instanceof Comment) {
if (onlyCommentsFromStart) {
inlined = !text.startsWith(NEW_LINE, currentPosition)
&& headerStartLine == lines[currentPosition];
// we skip the new line after a start of block header
skipNewLine();
}
if (lastLeafStatement != null) {
lastLeafStatement.setNewLineNeeded(lastLeafStatement.isNewLineNeeded() || (!inlined
&& text.startsWith(NEW_LINE, currentPosition)));
}
} else if (statement instanceof LeafStatement) {
lastLeafStatement = (LeafStatement)statement;
onlyCommentsFromStart = false;
} else {
lastLeafStatement = null;
onlyCommentsFromStart = false;
}
res.getStatements().add(statement);
}
for (String endOfBlock : endBlocks) {
if (text.startsWith(endOfBlock, currentPosition)) {
break endOfBlock;
}
}
beforeStatementPosition = currentPosition;
statement = parseStatement(inlined, significantTextColumn);
afterStatementPosition = currentPosition;
}
res.setInlined(inlined);
setPositions(res, startPosition, currentPosition);
return res;
}
/**
* Skips the new line at current position if any, no operation otherwise.
*/
private void skipNewLine() {
if (text.startsWith(NEW_LINE, currentPosition)) {
currentPosition += NEW_LINE.length();
}
}
/**
* Parses a {@link Statement}.
*
* @param inlined
* <code>true</code> if the current {@link Block} is inlined, <code>false</code> otherwise
* @param significantTextColumn
* the column where the text starts to be significant for {@link TextStatement}
* @return the created {@link Statement} if any is recognize, <code>null</code> otherwise
*/
protected Statement parseStatement(boolean inlined, int significantTextColumn) {
Statement res = null;
// CHECKSTYLE:OFF
final FileStatement file = parseFileStatement();
if (file != null) {
res = file;
} else {
final ForStatement forStatement = parseForStatement();
if (forStatement != null) {
res = forStatement;
} else {
final IfStatement ifStatement = parseIfStatement(IF_HEADER_START);
if (ifStatement != null) {
res = ifStatement;
} else {
final LetStatement letStatement = parseLetStatement();
if (letStatement != null) {
res = letStatement;
} else {
final ProtectedArea protectedArea = parseProtectedArea();
if (protectedArea != null) {
res = protectedArea;
} else {
final Comment comment = parseComment();
if (comment != null) {
res = comment;
} else {
final ExpressionStatement expressionStatement = parseExpressionStatement(
inlined);
if (expressionStatement != null) {
res = expressionStatement;
} else {
final TextStatement text = parseTextStatement(inlined,
significantTextColumn);
if (text != null) {
res = text;
} else {
res = null;
}
}
}
}
}
}
}
}
// CHECKSTYLE:ON
return res;
}
/**
* Parses a {@link IfStatement}.
*
* @param startTag
* the starting tag
* @return the created {@link IfStatement} if any is recognized, <code>null</code> otherwise
*/
protected IfStatement parseIfStatement(String startTag) {
final IfStatement res;
if (text.startsWith(startTag, currentPosition)) {
final int startPosition = currentPosition;
final int thenSignificantTextColumn = columns[startPosition] + INDENTATION;
final int thenHeaderStartLine = lines[startPosition];
currentPosition += startTag.length();
skipSpaces();
final int missingOpenParenthesis = readMissingString(OPEN_PARENTHESIS);
skipSpaces();
final int expressionEndLimit = getAqlExpressionEndLimit(CLOSE_PARENTHESIS, IF_HEADER_END);
final Expression condition = parseExpression(expressionEndLimit);
skipSpaces();
final int missingCloseParenthesis = readMissingString(CLOSE_PARENTHESIS);
skipSpaces();
final int missingEndHeader = readMissingString(IF_HEADER_END);
final Block thenblock = parseBlock(thenHeaderStartLine, thenSignificantTextColumn, IF_END,
IF_ELSEIF, IF_ELSE);
final IfStatement elseIf = parseIfStatement(IF_ELSEIF);
final boolean parseEndIf = elseIf == null;
final Block elseBlock;
if (elseIf != null) {
elseBlock = AcceleoPackage.eINSTANCE.getAcceleoFactory().createBlock();
elseBlock.getStatements().add(elseIf);
} else if (readString(IF_ELSE)) {
final int elseStartPosition = currentPosition - IF_ELSE.length();
final int elseSignificantTextColumn = columns[elseStartPosition] + INDENTATION;
final int headerStartLine = lines[elseStartPosition];
elseBlock = parseBlock(headerStartLine, elseSignificantTextColumn, IF_END);
} else {
elseBlock = null;
}
final int missingEnd;
if (parseEndIf) {
missingEnd = readMissingString(IF_END);
} else {
missingEnd = -1;
}
if (missingOpenParenthesis != -1 || missingCloseParenthesis != -1 || missingEndHeader != -1
|| missingEnd != -1) {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createErrorIfStatement();
((ErrorIfStatement)res).setMissingOpenParenthesis(missingOpenParenthesis);
((ErrorIfStatement)res).setMissingCloseParenthesis(missingCloseParenthesis);
((ErrorIfStatement)res).setMissingEndHeader(missingEndHeader);
((ErrorIfStatement)res).setMissingEnd(missingEnd);
errors.add((ErrorIfStatement)res);
} else {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createIfStatement();
}
res.setCondition(condition);
res.setThen(thenblock);
res.setElse(elseBlock);
setPositions(res, startPosition, currentPosition);
} else {
res = null;
}
return res;
}
/**
* Parses a {@link ForStatement}.
*
* @return the created {@link ForStatement} if any is recognized, <code>null</code> otherwise
*/
protected ForStatement parseForStatement() {
final ForStatement res;
if (text.startsWith(FOR_HEADER_START, currentPosition)) {
final int startPosition = currentPosition;
final int significantTextColumn = columns[startPosition] + INDENTATION;
final int headerStartLine = lines[startPosition];
currentPosition += FOR_HEADER_START.length();
skipSpaces();
final int missingOpenParenthesis = readMissingString(OPEN_PARENTHESIS);
skipSpaces();
final int bindingEndLimit = getAqlExpressionEndLimit(CLOSE_PARENTHESIS, FOR_HEADER_END);
final Binding binding = parseBinding(PIPE, bindingEndLimit);
final int missingBinding;
if (binding == null) {
missingBinding = currentPosition;
} else {
missingBinding = -1;
}
skipSpaces();
final int missingCloseParenthesis = readMissingString(CLOSE_PARENTHESIS);
skipSpaces();
final Expression separator;
final int missingSeparatorCloseParenthesis;
if (readString(FOR_SEPARATOR)) {
skipSpaces();
final int separatorEndLimit = getAqlExpressionEndLimit(CLOSE_PARENTHESIS, FOR_HEADER_END);
separator = parseExpression(separatorEndLimit);
skipSpaces();
missingSeparatorCloseParenthesis = readMissingString(CLOSE_PARENTHESIS);
} else {
separator = null;
missingSeparatorCloseParenthesis = -1;
}
final int missingEndHeader = readMissingString(FOR_HEADER_END);
final Block body = parseBlock(headerStartLine, significantTextColumn, FOR_END);
final int missingEnd = readMissingString(FOR_END);
final boolean missingParenthesis = missingOpenParenthesis != -1 || missingBinding != -1
|| missingCloseParenthesis != -1 || missingSeparatorCloseParenthesis != -1;
if (missingParenthesis || missingEndHeader != -1 || missingEnd != -1) {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createErrorForStatement();
((ErrorForStatement)res).setMissingOpenParenthesis(missingOpenParenthesis);
((ErrorForStatement)res).setMissingBinding(missingBinding);
((ErrorForStatement)res).setMissingCloseParenthesis(missingCloseParenthesis);
((ErrorForStatement)res).setMissingSeparatorCloseParenthesis(
missingSeparatorCloseParenthesis);
((ErrorForStatement)res).setMissingEndHeader(missingEndHeader);
((ErrorForStatement)res).setMissingEnd(missingEnd);
errors.add((ErrorForStatement)res);
} else {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createForStatement();
}
res.setBinding(binding);
res.setSeparator(separator);
res.setBody(body);
setPositions(res, startPosition, currentPosition);
} else {
res = null;
}
return res;
}
/**
* Parses a {@link FileStatement}.
*
* @return the created {@link FileStatement} if any is recognized, <code>null</code> otherwise
*/
protected FileStatement parseFileStatement() {
final FileStatement res;
if (text.startsWith(FILE_HEADER_START, currentPosition)) {
final int startPosition = currentPosition;
final int significantTextColumn = columns[startPosition] + INDENTATION;
final int headerStartLine = lines[startPosition];
currentPosition += FILE_HEADER_START.length();
skipSpaces();
final int missingOpenParenthesis = readMissingString(OPEN_PARENTHESIS);
skipSpaces();
final int urlExpressionEndLimit = getAqlExpressionEndLimit(COMMA, FILE_HEADER_END);
final Expression url = parseExpression(urlExpressionEndLimit);
skipSpaces();
final int missingComma = readMissingString(COMMA);
skipSpaces();
final OpenModeKind openMode = parseOpenModeKind();
final int missingOpenMode;
if (openMode == null) {
missingOpenMode = currentPosition;
final int position = getNext(COMMA, CLOSE_PARENTHESIS);
if (position >= 0) {
currentPosition = position;
}
} else {
missingOpenMode = -1;
}
final Expression charset;
if (readString(COMMA)) {
skipSpaces();
final int charsetExpressionEndLimit = getAqlExpressionEndLimit(CLOSE_PARENTHESIS,
FILE_HEADER_END);
charset = parseExpression(charsetExpressionEndLimit);
} else {
charset = null;
}
skipSpaces();
final int missingCloseParenthesis = readMissingString(CLOSE_PARENTHESIS);
skipSpaces();
final int missingEndHeader = readMissingString(FILE_HEADER_END);
final Block body = parseBlock(headerStartLine, significantTextColumn, FILE_END);
final int missingEnd = readMissingString(FILE_END);
final boolean missingSymbole = missingOpenParenthesis != -1 || missingCloseParenthesis != -1
|| missingComma != -1;
if (missingOpenMode != -1 || missingSymbole || missingEndHeader != -1 || missingEnd != -1) {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createErrorFileStatement();
((ErrorFileStatement)res).setMissingOpenMode(missingOpenMode);
((ErrorFileStatement)res).setMissingOpenParenthesis(missingOpenParenthesis);
((ErrorFileStatement)res).setMissingComma(missingComma);
((ErrorFileStatement)res).setMissingCloseParenthesis(missingCloseParenthesis);
((ErrorFileStatement)res).setMissingEndHeader(missingEndHeader);
((ErrorFileStatement)res).setMissingEnd(missingEnd);
errors.add((ErrorFileStatement)res);
} else {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createFileStatement();
}
res.setUrl(url);
res.setCharset(charset);
res.setMode(openMode);
res.setBody(body);
setPositions(res, startPosition, currentPosition);
} else {
res = null;
}
return res;
}
/**
* Parses {@link OpenModeKind}.
*
* @return the {@link OpenModeKind} if any is recognized, <code>null</code> otherwise
*/
protected OpenModeKind parseOpenModeKind() {
OpenModeKind res = null;
for (OpenModeKind openMode : OpenModeKind.VALUES) {
if (text.startsWith(openMode.getName(), currentPosition)) {
res = openMode;
currentPosition += openMode.getName().length();
break;
}
}
return res;
}
/**
* Parses a {@link LetStatement}.
*
* @return the created {@link LetStatement} if any is recognized, <code>null</code> otherwise
*/
protected LetStatement parseLetStatement() {
final LetStatement res;
if (text.startsWith(LET_HEADER_START, currentPosition)) {
final int startPosition = currentPosition;
final int significantTextColumn = columns[startPosition] + INDENTATION;
final int headerStartLine = lines[startPosition];
currentPosition += LET_HEADER_START.length();
skipSpaces();
final List<Binding> bindings = parseBindings(EQUAL, LET_HEADER_END);
final int missingBindings;
if (bindings.isEmpty()) {
missingBindings = currentPosition;
} else {
missingBindings = -1;
}
skipSpaces();
final int missingEndHeader = readMissingString(LET_HEADER_END);
final Block body = parseBlock(headerStartLine, significantTextColumn, LET_END);
final int missingEnd = readMissingString(LET_END);
if (missingBindings != -1 || missingEndHeader != -1 || missingEnd != -1) {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createErrorLetStatement();
((ErrorLetStatement)res).setMissingBindings(missingBindings);
((ErrorLetStatement)res).setMissingEndHeader(missingEndHeader);
((ErrorLetStatement)res).setMissingEnd(missingEnd);
errors.add((ErrorLetStatement)res);
} else {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createLetStatement();
}
res.getVariables().addAll(bindings);
res.setBody(body);
setPositions(res, startPosition, currentPosition);
} else {
res = null;
}
return res;
}
/**
* Parses a {@link Binding}.
*
* @param affectationSymbol
* the affectation symbol
* @param endLimit
* the end limit of the binding
* @return the created {@link Binding} if any is recognized, <code>null</code> otherwise
*/
protected Binding parseBinding(String affectationSymbol, int endLimit) {
final Binding res;
final int startPosition = currentPosition;
final int identifierStartPosition = currentPosition;
final String name = parseIdentifier();
final int identifierEndPosition = currentPosition;
final int missingName;
if (name == null) {
missingName = currentPosition;
} else {
missingName = -1;
}
skipSpaces();
int missingType;
AstResult type;
int missingColon = currentPosition;
if (readString(COLON)) {
skipSpaces();
final int typeEndLimit = Math.min(getAqlExpressionEndLimit(affectationSymbol, COMMA), endLimit);
type = parseWhileAqlTypeLiteral(text.substring(currentPosition, typeEndLimit));
type.addAllPositonsTo(positions, currentPosition, lines[currentPosition],
columns[currentPosition]);
currentPosition += type.getEndPosition(type.getAst());
if (type.getStartPosition(type.getAst()) == type.getEndPosition(type.getAst())) {
missingType = currentPosition;
} else {
missingType = -1;
}
missingColon = -1;
} else {
skipSpaces();
final int typeEndLimit = Math.min(getAqlExpressionEndLimit(affectationSymbol, COMMA), endLimit);
type = parseWhileAqlTypeLiteral(text.substring(currentPosition, typeEndLimit));
type.addAllPositonsTo(positions, currentPosition, lines[currentPosition],
columns[currentPosition]);
currentPosition += type.getEndPosition(type.getAst());
if (type.getStartPosition(type.getAst()) == type.getEndPosition(type.getAst())) {
missingColon = -1;
type = null;
}
missingType = -1;
}
skipSpaces();
int missingAffectationSymbol = readMissingString(affectationSymbol);
if (missingColon != -1 && missingAffectationSymbol != -1) {
final int typeEndLimit = Math.min(getAqlExpressionEndLimit(affectationSymbol, COMMA), endLimit);
type = parseWhileAqlTypeLiteral(text.substring(currentPosition, typeEndLimit));
type.addAllPositonsTo(positions, currentPosition, lines[currentPosition],
columns[currentPosition]);
currentPosition += type.getEndPosition(type.getAst());
if (type.getStartPosition(type.getAst()) == type.getEndPosition(type.getAst())) {
missingType = currentPosition;
} else {
missingType = -1;
}
skipSpaces();
missingAffectationSymbol = readMissingString(affectationSymbol);
}
skipSpaces();
final Expression expression = parseExpression(endLimit);
if (missingName == -1) {
final boolean hasErrorExpression = expression.getAst()
.getAst() instanceof org.eclipse.acceleo.query.ast.Error;
if (missingColon != -1 || missingType != -1 || missingAffectationSymbol != -1
|| hasErrorExpression) {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createErrorBinding();
((ErrorBinding)res).setMissingName(missingName);
((ErrorBinding)res).setMissingColon(missingColon);
((ErrorBinding)res).setMissingType(missingType);
if (missingAffectationSymbol != -1) {
((ErrorBinding)res).setMissingAffectationSymbole(affectationSymbol);
((ErrorBinding)res).setMissingAffectationSymbolePosition(missingAffectationSymbol);
}
errors.add((ErrorBinding)res);
} else {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createBinding();
}
res.setName(name);
if (type != null) {
res.setType(type);
res.setTypeAql(type.getAst());
}
res.setInitExpression(expression);
setIdentifierPositions(res, identifierStartPosition, identifierEndPosition);
setPositions(res, startPosition, currentPosition);
} else {
res = null;
}
return res;
}
/**
* Parses a {@link List} of at least one {@link Binding}.
*
* @param affectationSymbol
* the affectation symbol
* @param endTag
* the end tag
* @return the {@link List} of at least one {@link Binding}
*/
protected List<Binding> parseBindings(String affectationSymbol, String endTag) {
final List<Binding> res = new ArrayList<Binding>();
int bindingEndLimit = getAqlExpressionEndLimit(COMMA, endTag);
Binding binding = parseBinding(affectationSymbol, bindingEndLimit);
while (binding != null) {
res.add(binding);
skipSpaces();
if (readString(COMMA)) {
skipSpaces();
bindingEndLimit = getAqlExpressionEndLimit(COMMA, endTag);
binding = parseBinding(affectationSymbol, bindingEndLimit);
} else {
skipSpaces();
break;
}
}
return res;
}
/**
* Gets the {@link EPackage} with the given {@link EPackage#getName() name}.
*
* @param ePackageName
* the {@link EPackage#getName() name}
* @return the {@link EPackage} with the given {@link EPackage#getName() name} if any, <code>null</code>
* otherwise
*/
protected EPackage getEPackage(String ePackageName) {
EPackage res = null;
for (Object object : EPackage.Registry.INSTANCE.values()) {
if (object instanceof EPackage) {
if (ePackageName.equals(((EPackage)object).getName())) {
res = (EPackage)object;
break;
}
} else if (object instanceof EPackage.Descriptor) {
// TODO this code load all EPackages
final EPackage ePackage = ((EPackage.Descriptor)object).getEPackage();
if (ePackageName.equals(ePackage.getName())) {
res = ePackage;
}
}
}
return res;
}
/**
* Parses a {@link ProtectedArea}.
*
* @return the created {@link ProtectedArea} if any is recognized, <code>null</code> otherwise
*/
protected ProtectedArea parseProtectedArea() {
final ProtectedArea res;
if (text.startsWith(PROTECTED_AREA_HEADER_START, currentPosition)) {
final int startPosition = currentPosition;
final int significantTextColumn = columns[startPosition] + INDENTATION;
final int headerStartLine = lines[startPosition];
currentPosition += PROTECTED_AREA_HEADER_START.length();
skipSpaces();
final int missingOpenParenthesis = readMissingString(OPEN_PARENTHESIS);
skipSpaces();
final int expressionEndLimit = getAqlExpressionEndLimit(CLOSE_PARENTHESIS,
PROTECTED_AREA_HEADER_END);
final Expression id = parseExpression(expressionEndLimit);
skipSpaces();
final int missingCloseParenthesis = readMissingString(CLOSE_PARENTHESIS);
skipSpaces();
final int missingEndHeader = readMissingString(PROTECTED_AREA_HEADER_END);
final Block body = parseBlock(headerStartLine, significantTextColumn, PROTECTED_AREA_END);
final int missingEnd = readMissingString(PROTECTED_AREA_END);
if (missingOpenParenthesis != -1 || missingCloseParenthesis != -1 || missingEndHeader != -1
|| missingEnd != -1) {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createErrorProtectedArea();
((ErrorProtectedArea)res).setMissingOpenParenthesis(missingOpenParenthesis);
((ErrorProtectedArea)res).setMissingCloseParenthesis(missingCloseParenthesis);
((ErrorProtectedArea)res).setMissingEndHeader(missingEndHeader);
((ErrorProtectedArea)res).setMissingEnd(missingEnd);
errors.add((ErrorProtectedArea)res);
} else {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createProtectedArea();
}
res.setId(id);
res.setBody(body);
setPositions(res, startPosition, currentPosition);
} else {
res = null;
}
return res;
}
/**
* Parses an {@link ExpressionStatement}.
*
* @param inlined
* <code>true</code> if the current {@link Block} is inlined, <code>false</code> otherwise
* @return the created {@link ExpressionStatement} if any is recognized, <code>null</code> otherwise
*/
protected ExpressionStatement parseExpressionStatement(boolean inlined) {
final ExpressionStatement res;
if (!text.startsWith(END_BLOCK_PREFIX, currentPosition) && !text.startsWith(IF_ELSE, currentPosition)
&& text.startsWith(EXPRESSION_STATEMENT_START, currentPosition)) {
final int startPosition = currentPosition;
currentPosition += EXPRESSION_STATEMENT_START.length();
skipSpaces();
final int expressionEndLimit = getAqlExpressionEndLimit(EXPRESSION_STATEMENT_END,
EXPRESSION_STATEMENT_END);
final Expression expression = parseExpression(expressionEndLimit);
skipSpaces();
final int missingEndHeader = readMissingString(EXPRESSION_STATEMENT_END);
if (missingEndHeader != -1) {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createErrorExpressionStatement();
((ErrorExpressionStatement)res).setMissingEndHeader(missingEndHeader);
errors.add((ErrorExpressionStatement)res);
} else {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createExpressionStatement();
res.setNewLineNeeded(!inlined && text.startsWith(NEW_LINE, currentPosition));
}
res.setExpression(expression);
if (res.isNewLineNeeded()) {
currentPosition += NEW_LINE.length();
}
setPositions(res, startPosition, currentPosition);
} else {
res = null;
}
return res;
}
/**
* Parses an {@link Expression}.
*
* @param endLimit
* the end limit of the expression
* @return the created {@link Expression}
*/
protected Expression parseExpression(int endLimit) {
final Expression res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createExpression();
final AstResult astResult = parseWhileAqlExpression(text.substring(currentPosition, endLimit));
final int startPosition = currentPosition;
res.setAst(astResult);
res.setAql(astResult.getAst());
final int endPosition = currentPosition + astResult.getEndPosition(astResult.getAst());
setPositions(res, startPosition, endPosition);
currentPosition = endPosition;
astResult.addAllPositonsTo(positions, startPosition, lines[startPosition], columns[startPosition]);
return res;
}
/**
* Parses {@link VisibilityKind}.
*
* @return the {@link VisibilityKind} if any is recognized, <code>null</code> otherwise
*/
protected VisibilityKind parseVisibility() {
VisibilityKind res = null;
for (VisibilityKind visibility : VisibilityKind.VALUES) {
if (text.startsWith(visibility.getName(), currentPosition)) {
res = visibility;
currentPosition += visibility.getName().length();
break;
}
}
return res;
}
/**
* Parses {@link Metamodel}.
*
* @return the {@link Metamodel} if any recognized, <code>null</code> otherwise
*/
protected Metamodel parseMetamodel() {
final Metamodel res;
if (readString(QUOTE)) {
final int startPosition = currentPosition;
int nextQuote = getNext(QUOTE, CLOSE_PARENTHESIS, MODULE_HEADER_END);
if (nextQuote < 0) {
nextQuote = text.length();
}
final String nsURI = text.substring(currentPosition, nextQuote);
currentPosition = nextQuote;
final EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(nsURI);
final int missingEndQuote = readMissingString(QUOTE);
if (ePackage == null || missingEndQuote != -1) {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createErrorMetamodel();
((ErrorMetamodel)res).setMissingEndQuote(missingEndQuote);
errors.add((ErrorMetamodel)res);
} else {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createMetamodel();
}
res.setReferencedPackage(ePackage);
setPositions(res, startPosition, currentPosition);
} else {
res = null;
}
return res;
}
/**
* Gets the position of the next token.
*
* @param tokens
* tokens
* @return the position of the next token if any found, <code>-1</code> otherwise
*/
protected int getNext(String... tokens) {
int res = -1;
for (String token : tokens) {
final int position = text.indexOf(token, currentPosition);
if (position >= 0) {
res = position;
break;
}
}
return res;
}
/**
* Parses {@link TextStatement}.
*
* @param inlined
* <code>true</code> if the current {@link Block} is inlined, <code>false</code> otherwise
* @param significantTextColumn
* the column where the text starts to be significant for {@link TextStatement}
* @return the created {@link TextStatement} if any, <code>null</code> otherwise
*/
protected TextStatement parseTextStatement(boolean inlined, int significantTextColumn) {
final TextStatement res;
int endOfText = getNext(TEXT_END);
if (endOfText < 0) {
endOfText = text.length();
}
if (currentPosition != endOfText) {
if (inlined) {
// raw copy of the text
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createTextStatement();
setPositions(res, currentPosition, endOfText);
res.setValue(text.substring(currentPosition, endOfText));
res.setNewLineNeeded(false);
currentPosition = endOfText;
} else {
res = getSignificantTextStatement(significantTextColumn, endOfText);
}
} else {
res = null;
}
return res;
}
/**
* Gets the significant {@link TextStatement} at the given column and until the end of text.
*
* @param significantTextColumn
* the significant text column
* @param endOfText
* the end of text
* @return the significant {@link TextStatement} at the given column and until the end of text.
*/
private TextStatement getSignificantTextStatement(int significantTextColumn, int endOfText) {
final TextStatement res;
int localStartOfText = currentPosition;
if (columns[localStartOfText] == 0 && text.startsWith(NEW_LINE, localStartOfText)) {
final NewLineStatement newLineStatement = AcceleoPackage.eINSTANCE.getAcceleoFactory()
.createNewLineStatement();
newLineStatement.setIndentationNeeded(false);
newLineStatement.setNewLineNeeded(true);
newLineStatement.setValue("");
currentPosition += NEW_LINE.length();
setPositions(newLineStatement, localStartOfText, currentPosition);
res = newLineStatement;
} else {
res = getNonEmptyLineTextStatement(significantTextColumn, endOfText);
}
return res;
}
/**
* Gets the significant {@link TextStatement} for indented text.
*
* @param significantTextColumn
* the significant text column
* @param endOfText
* the end of text
* @return the significant {@link TextStatement} for indented text
*/
private TextStatement getNonEmptyLineTextStatement(int significantTextColumn, int endOfText) {
final TextStatement res;
int localStartOfText = currentPosition;
if (text.startsWith(NEW_LINE, localStartOfText)) {
if (columns[localStartOfText] > significantTextColumn) {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createTextStatement();
res.setNewLineNeeded(true);
res.setValue("");
currentPosition += NEW_LINE.length();
setPositions(res, localStartOfText, currentPosition);
} else {
currentPosition = currentPosition + NEW_LINE.length(); // skip the new line
res = getNonEmptyLineNonEmptyTextTextStatement(significantTextColumn, endOfText);
}
} else {
res = getNonEmptyLineNonEmptyTextTextStatement(significantTextColumn, endOfText);
}
return res;
}
/**
* Gets the non empty line {@link TextStatement}.
*
* @param significantTextColumn
* the significant text column
* @param endOfText
* the end of text
* @return the non empty line {@link TextStatement}
*/
private TextStatement getNonEmptyLineNonEmptyTextTextStatement(int significantTextColumn, int endOfText) {
final TextStatement res;
int localStartOfText = currentPosition;
while (localStartOfText < endOfText && columns[localStartOfText] < significantTextColumn) {
localStartOfText++;
}
if (text.startsWith(NEW_LINE, localStartOfText)
&& columns[localStartOfText] == significantTextColumn) {
final NewLineStatement newLineStatement = AcceleoPackage.eINSTANCE.getAcceleoFactory()
.createNewLineStatement();
newLineStatement.setIndentationNeeded(true);
newLineStatement.setNewLineNeeded(true);
newLineStatement.setValue("");
currentPosition = localStartOfText + NEW_LINE.length();
setPositions(newLineStatement, localStartOfText, currentPosition);
res = newLineStatement;
} else if (localStartOfText < endOfText) {
int localEndOfText = localStartOfText;
while (localEndOfText < endOfText && columns[localEndOfText] >= significantTextColumn) {
localEndOfText++;
}
final boolean needNewLine;
if (columns[localEndOfText] == 0) {
localEndOfText = localEndOfText - NEW_LINE.length(); // remove the new line
needNewLine = true;
} else {
needNewLine = false;
}
final boolean isEmptyLine = columns[localStartOfText] == significantTextColumn && needNewLine;
if (localStartOfText < localEndOfText || isEmptyLine) {
res = AcceleoPackage.eINSTANCE.getAcceleoFactory().createTextStatement();
res.setValue(text.substring(localStartOfText, localEndOfText));
res.setNewLineNeeded(needNewLine);
if (needNewLine) {
localEndOfText += NEW_LINE.length();
}
setPositions(res, localStartOfText, localEndOfText);
currentPosition = localEndOfText;
} else {
res = null;
currentPosition = localEndOfText;
}
} else {
res = null;
currentPosition = endOfText;
}
return res;
}
/**
* Parses while matching an AQL expression.
*
* @param expression
* the expression to parse
* @return the corresponding {@link AstResult}
*/
protected AstResult parseWhileAqlExpression(String expression) {
final AstResult result;
if (expression != null && expression.length() > 0) {
AstBuilderListener astBuilder = new AstBuilderListener();
CharStream input = new UnbufferedCharStream(new StringReader(expression), expression.length());
QueryLexer lexer = new QueryLexer(input);
lexer.setTokenFactory(new CommonTokenFactory(true));
lexer.removeErrorListeners();
lexer.addErrorListener(astBuilder.getErrorListener());
TokenStream tokens = new UnbufferedTokenStream<CommonToken>(lexer);
QueryParser parser = new QueryParser(tokens);
parser.addParseListener(astBuilder);
parser.removeErrorListeners();
parser.addErrorListener(astBuilder.getErrorListener());
// parser.setTrace(true);
parser.expression();
result = astBuilder.getAstResult();
} else {
org.eclipse.acceleo.query.ast.ErrorExpression errorExpression = (org.eclipse.acceleo.query.ast.ErrorExpression)EcoreUtil
.create(AstPackage.eINSTANCE.getErrorExpression());
List<org.eclipse.acceleo.query.ast.Error> aqlErrors = new ArrayList<org.eclipse.acceleo.query.ast.Error>(
1);
aqlErrors.add(errorExpression);
final Positions aqlPositions = new Positions();
if (expression != null) {
aqlPositions.setIdentifierStartPositions(errorExpression, Integer.valueOf(0));
aqlPositions.setIdentifierStartLines(errorExpression, Integer.valueOf(0));
aqlPositions.setIdentifierStartColumns(errorExpression, Integer.valueOf(0));
aqlPositions.setIdentifierEndPositions(errorExpression, Integer.valueOf(0));
aqlPositions.setIdentifierEndLines(errorExpression, Integer.valueOf(0));
aqlPositions.setIdentifierEndColumns(errorExpression, Integer.valueOf(0));
aqlPositions.setStartPositions(errorExpression, Integer.valueOf(0));
aqlPositions.setStartLines(errorExpression, Integer.valueOf(0));
aqlPositions.setStartColumns(errorExpression, Integer.valueOf(0));
aqlPositions.setEndPositions(errorExpression, Integer.valueOf(0));
aqlPositions.setEndLines(errorExpression, Integer.valueOf(0));
aqlPositions.setEndColumns(errorExpression, Integer.valueOf(0));
}
final BasicDiagnostic diagnostic = new BasicDiagnostic();
diagnostic.add(new BasicDiagnostic(Diagnostic.ERROR, AstBuilderListener.PLUGIN_ID, 0,
"missing expression", new Object[] {errorExpression }));
result = new AstResult(errorExpression, aqlPositions, aqlErrors, diagnostic);
}
return result;
}
/**
* Parses while matching an AQL expression.
*
* @param expression
* the expression to parse
* @return the corresponding {@link AstResult}
*/
protected AstResult parseWhileAqlTypeLiteral(String expression) {
final AstResult result;
if (expression != null && expression.length() > 0) {
AstBuilderListener astBuilder = new AstBuilderListener();
CharStream input = new UnbufferedCharStream(new StringReader(expression), expression.length());
QueryLexer lexer = new QueryLexer(input);
lexer.setTokenFactory(new CommonTokenFactory(true));
lexer.removeErrorListeners();
lexer.addErrorListener(astBuilder.getErrorListener());
TokenStream tokens = new UnbufferedTokenStream<CommonToken>(lexer);
QueryParser parser = new QueryParser(tokens);
parser.addParseListener(astBuilder);
parser.removeErrorListeners();
parser.addErrorListener(astBuilder.getErrorListener());
// parser.setTrace(true);
parser.typeLiteral();
result = astBuilder.getAstResult();
} else {
ErrorTypeLiteral errorTypeLiteral = (ErrorTypeLiteral)EcoreUtil.create(AstPackage.eINSTANCE
.getErrorTypeLiteral());
List<org.eclipse.acceleo.query.ast.Error> errs = new ArrayList<org.eclipse.acceleo.query.ast.Error>(
1);
errs.add(errorTypeLiteral);
final Positions aqlPositions = new Positions();
if (expression != null) {
aqlPositions.setIdentifierStartPositions(errorTypeLiteral, Integer.valueOf(0));
aqlPositions.setIdentifierStartLines(errorTypeLiteral, Integer.valueOf(0));
aqlPositions.setIdentifierStartColumns(errorTypeLiteral, Integer.valueOf(0));
aqlPositions.setIdentifierEndPositions(errorTypeLiteral, Integer.valueOf(0));
aqlPositions.setIdentifierEndLines(errorTypeLiteral, Integer.valueOf(0));
aqlPositions.setIdentifierEndColumns(errorTypeLiteral, Integer.valueOf(0));
aqlPositions.setStartPositions(errorTypeLiteral, Integer.valueOf(0));
aqlPositions.setStartLines(errorTypeLiteral, Integer.valueOf(0));
aqlPositions.setStartColumns(errorTypeLiteral, Integer.valueOf(0));
aqlPositions.setEndPositions(errorTypeLiteral, Integer.valueOf(0));
aqlPositions.setEndLines(errorTypeLiteral, Integer.valueOf(0));
aqlPositions.setEndColumns(errorTypeLiteral, Integer.valueOf(0));
}
final BasicDiagnostic diagnostic = new BasicDiagnostic();
diagnostic.add(new BasicDiagnostic(Diagnostic.ERROR, AstBuilderListener.PLUGIN_ID, 0,
"missing type literal", new Object[] {errorTypeLiteral }));
result = new AstResult(errorTypeLiteral, aqlPositions, errs, diagnostic);
}
return result;
}
/**
* Gets the end AQL expression limit according to the given end delimiter and the given end tag.
*
* @param endDelimiter
* the end delimiter
* @param endTag
* the end tag
* @return the end AQL expression limit according to the given end delimiter and the given end tag if any
* found, <code>-1</code> otherwise
*/
protected int getAqlExpressionEndLimit(String endDelimiter, String endTag) {
int res = currentPosition;
int parenthesisDepth = 0;
int curlyBracketDepth = 0;
while (res < text.length()) {
if ((text.startsWith(endTag, res) || endLimitReached(endDelimiter, res)) && isProperlyParenthesed(
parenthesisDepth, curlyBracketDepth)) {
break;
}
switch (text.charAt(res)) {
case '\'':
// skip string literal
boolean isEscaped = false;
res++;
endStrinLiteral: while (res < text.length()) {
switch (text.charAt(res)) {
case '\\':
isEscaped = !isEscaped;
res++;
break;
case '\'':
if (!isEscaped) {
res++;
break endStrinLiteral;
}
res++;
isEscaped = false;
break;
default:
res++;
isEscaped = false;
break;
}
}
break;
case '{':
curlyBracketDepth++;
res++;
break;
case '}':
curlyBracketDepth--;
res++;
break;
case '(':
parenthesisDepth++;
res++;
break;
case ')':
parenthesisDepth--;
res++;
break;
default:
res++;
break;
}
}
return res;
}
/**
* Tells if the end limit for the search has been reached.
*
* @param endDelimiter
* the end delimiter
* @param position
* the current position
* @return <code>true</code> if the end limit for the search has been reached, <code>false</code>
* otherwise
*/
private boolean endLimitReached(String endDelimiter, int position) {
return text.startsWith(endDelimiter, position) || text.startsWith(TEXT_END, position);
}
/**
* Tells if the expression is properly parenthesed so far.
*
* @param parenthesisDepth
* the parenthesis depth
* @param curlyBracketDepth
* the curly bracket depth
* @return <code>true</code> if the expression is properly parenthesed so far, <code>false</code>
* otherwise
*/
private boolean isProperlyParenthesed(int parenthesisDepth, int curlyBracketDepth) {
return parenthesisDepth == 0 && curlyBracketDepth == 0;
}
}