blob: 8fc20177d5b95402ff01e293fcd8e807945167cf [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008-2013 The University of York, Antonio Garcia-Dominguez.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* Dimitrios Kolovos - initial API and implementation
* Antonio Garcia-Dominguez - refactor parse(...) methods
******************************************************************************/
package org.eclipse.epsilon.eol;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import org.antlr.runtime.ANTLRInputStream;
import org.antlr.runtime.CommonToken;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.Lexer;
import org.antlr.runtime.ParserRuleReturnScope;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.Token;
import org.antlr.runtime.TokenStream;
import org.eclipse.epsilon.common.module.AbstractModuleElement;
import org.eclipse.epsilon.common.module.IModule;
import org.eclipse.epsilon.common.module.ModuleElement;
import org.eclipse.epsilon.common.parse.AST;
import org.eclipse.epsilon.common.parse.EpsilonParseProblemManager;
import org.eclipse.epsilon.common.parse.EpsilonParser;
import org.eclipse.epsilon.common.parse.EpsilonTreeAdaptor;
import org.eclipse.epsilon.common.parse.Position;
import org.eclipse.epsilon.common.parse.problem.ParseProblem;
import org.eclipse.epsilon.common.util.AstUtil;
import org.eclipse.epsilon.eol.parse.EolLexer;
import org.eclipse.epsilon.eol.parse.EolParser;
import org.eclipse.epsilon.eol.util.ReflectionUtil;
public abstract class AbstractModule extends AbstractModuleElement implements IModule {
protected EpsilonParser parser;
protected ArrayList<ParseProblem> parseProblems = new ArrayList<ParseProblem>();
public abstract String getMainRule();
protected abstract Lexer createLexer(ANTLRInputStream inputStream);
public abstract EpsilonParser createParser(TokenStream tokenStream);
protected File sourceFile;
protected URI sourceUri;
public File getSourceFile() {
return sourceFile;
}
public URI getSourceUri() {
return sourceUri;
}
public List<ParseProblem> getParseProblems() {
return parseProblems;
}
public boolean parse(String code) throws Exception {
return parse(code, null);
}
public boolean parse(String code, File file) throws Exception {
this.sourceFile = file;
if (file != null) {
this.sourceUri = file.toURI();
}
return parse(sourceUri, new ByteArrayInputStream(code.getBytes()));
}
public boolean parse(File file) throws Exception {
return parse(file.toURI());
}
public boolean parse(URI uri) throws Exception {
this.sourceUri = uri;
final String uriScheme = uri.getScheme();
if ("file".equals(uriScheme)) {
this.sourceFile = new File(uri);
}
return parse(uri, uri.toURL().openStream());
}
protected boolean invokeMainRule(List<CommonToken> multilineComments) throws Exception {
EpsilonParseProblemManager.INSTANCE.reset();
AST cst = null;
try {
cst = (AST)((ParserRuleReturnScope) ReflectionUtil.executeMethod(parser,getMainRule(), new Object[]{})).getTree();
}
catch (RecognitionException ex){
ParseProblem problem = new ParseProblem();
problem.setLine(ex.line);
problem.setColumn(ex.charPositionInLine);
problem.setReason(ex.getMessage());
getParseProblems().add(problem);
ex.printStackTrace();
}
catch (Throwable ex) {
ParseProblem problem = new ParseProblem();
Token next = parser.input.LT(1);
problem.setLine(next.getLine());
problem.setColumn(next.getCharPositionInLine());
problem.setReason("mismatched input: '" + next.getText() + "'");
getParseProblems().add(problem);
}
parseProblems.addAll(EpsilonParseProblemManager.INSTANCE.getParseProblems());
EpsilonParseProblemManager.INSTANCE.reset();
if (getParseProblems().size() == 0){
assignAnnotations(cst);
assignComments(cst, multilineComments);
//createAst(cst, null);
//assignAnnotations(ast);
//buildModel();
createAst(cst, null);
return true;
}
else {
return false;
}
}
public ModuleElement createAst(AST cst, ModuleElement parentAst) {
if (cst == null) return null;
ModuleElement moduleElement = adapt(cst, parentAst);
if (moduleElement != null) {
moduleElement.setUri(cst.getUri());
//moduleElement.setFile(cst.getFile());
moduleElement.setModule(cst.getModule());
//try {
// moduleElement.setBasename(cst.getBasename());
//}
//catch (Exception ex) {}
moduleElement.setRegion(cst.getRegion());
moduleElement.build(cst, this);
if (parentAst != null) {
moduleElement.setParent(parentAst);
parentAst.getChildren().add(moduleElement);
}
}
return moduleElement;
}
public abstract ModuleElement adapt(AST cst, ModuleElement parentAst);
protected List<CommonToken> extractComments(CommonTokenStream stream) {
List<CommonToken> comments = new ArrayList<CommonToken>();
for (Object t : stream.getTokens()) {
CommonToken token = (CommonToken) t;
if (token.getType() == EolLexer.COMMENT || token.getType() == EolParser.LINE_COMMENT) {
comments.add(token);
}
}
return comments;
}
private boolean parse(URI uri, final InputStream iStream) throws Exception {
parseProblems.clear();
Scanner s = new java.util.Scanner(iStream);
try {
// Replace tabs with spaces to get consistent column numbers in ASTs
s.useDelimiter("\\A");
String contents = s.hasNext() ? s.next() : "";
ByteArrayInputStream noTabsStream = new ByteArrayInputStream(contents.replaceAll("\t", " ").getBytes());
final Lexer lexer = createLexer(new ANTLRInputStream(noTabsStream));
final CommonTokenStream stream = new CommonTokenStream(lexer);
List<CommonToken> multilineComments = extractComments(stream);
final EpsilonTreeAdaptor adaptor = new EpsilonTreeAdaptor(uri, this);
parser = createParser(stream);
parser.setDeepTreeAdaptor(adaptor);
return invokeMainRule(multilineComments);
}
catch (Exception ex) {
parseProblems.add(new ParseProblem("Exception during parsing: " + ex.getLocalizedMessage(), ParseProblem.ERROR));
throw ex;
}
finally {
s.close();
}
}
protected void assignComments(AST root, List<CommonToken> comments) {
for (CommonToken comment : comments) {
assignComment(root, comment);
}
}
protected void assignComment(AST root, CommonToken comment) {
for (AST ast : root.getDescendants()) {
Position start = ast.getRegion().getStart();
if (!ast.isImaginary() && comment.getLine() <= start.getLine() && comment.getCharPositionInLine() <= start.getColumn()) {
ast.getCommentTokens().add(comment);
return;
}
}
}
protected void assignAnnotations(AST ast) {
List<AST> children = AstUtil.getChildren(ast);
for (AST child : children) {
if (child.getType() == EolParser.ANNOTATIONBLOCK) {
AST target = null;
//HACK to support main-less EOL programs
if (child.getNextSibling() != null) {
if (child.getNextSibling().getType() != EolParser.BLOCK) {
target = child.getNextSibling();
}
else if (child.getNextSibling().getNextSibling() != null) {
target = child.getNextSibling().getNextSibling();
}
}
if (target != null) {
target.setAnnotationsAst(child);
}
} else {
assignAnnotations(child);
}
}
}
}