blob: 3d439dd4fbb123e77e9ca83673dcb5bd17a1411a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 University of Illinois at Urbana-Champaign and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* UIUC - Initial API and implementation
*******************************************************************************/
package org.eclipse.photran.internal.core.model;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.IContributedModelBuilder;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.internal.core.model.Parent;
import org.eclipse.cdt.internal.core.model.TranslationUnit;
import org.eclipse.core.resources.IFile;
import org.eclipse.photran.internal.core.lexer.FixedFormLexerPhase2;
import org.eclipse.photran.internal.core.lexer.FreeFormLexerPhase1;
import org.eclipse.photran.internal.core.lexer.FreeFormLexerPhase2;
import org.eclipse.photran.internal.core.lexer.ILexer;
import org.eclipse.photran.internal.core.lexer.IToken;
import org.eclipse.photran.internal.core.lexer.SimpleToken;
import org.eclipse.photran.internal.core.lexer.SimpleTokenFactory;
import org.eclipse.photran.internal.core.lexer.Terminal;
/**
* This is a minimal Fortran model builder based only on lexical analysis. See
* {@link IFortranModelBuilder}.
* <p>
* <i>This is probably <b>not</b> the model builder you are looking for.</i> Generally, Photran
* uses a parser-based model builder from the cdtinterface.vpg plug-in instead. This one is used
* only if the VPG plug-ins are not available, or if the user specifically chooses to use this
* model builder instead (in the workspace preferences).
* <p>
* Editors can force the model builder to use fixed or free format for a given file by calling
* {@link #setIsFixedForm(boolean)}. Otherwise, the format is determined by content type (i.e., by
* the filename extension and the user's workspace preferences).
*
* @author Jeff Overbey
*
* @see IFortranModelBuilder
* @see IContributedModelBuilder
*/
@SuppressWarnings("restriction")
public class SimpleFortranModelBuilder implements IFortranModelBuilder
{
private TranslationUnit translationUnit = null;
private Map newElements = null;
private boolean isFixedForm = false;
public void setTranslationUnit(ITranslationUnit tu)
{
if (!(tu instanceof ITranslationUnit)) throw new Error("Unexpected subclass of ITranslationUnit");
this.translationUnit = (TranslationUnit)tu;
}
public void setIsFixedForm(boolean isFixedForm)
{
this.isFixedForm = isFixedForm;
}
public void parse(boolean quickParseMode) throws Exception
{
this.newElements = new HashMap();
InputStream in = new ByteArrayInputStream(translationUnit.getBuffer().getContents().getBytes());
String filename = translationUnit.getElementName();
boolean wasSuccessful = true;
try
{
// FortranElement note = new FortranElement.UnknownNode(translationUnit, isFixedForm ? "<Fixed Form Source>" : "<Free Form Source>");
// this.addF90Element(note);
IFile file = translationUnit.getFile();
ILexer lexer =
isFixedForm ? (ILexer)new FixedFormLexerPhase2(in, file, filename, SimpleTokenFactory.getInstance())
: (ILexer)new FreeFormLexerPhase2(new FreeFormLexerPhase1(in, file, filename, SimpleTokenFactory.getInstance(), false));
createSourceFormNode();
buildModel(lexer);
/* For whatever reason, we have to only set the *parent* while we
* parse, then go back through (now) and traverse the model in
* preorder, executing addChild(). This doesn't make any sense,
* but the "obvious" way of doing it all while parsing (in
* BuildModelParserAction.java) doesn't work.
*/
//newElements.putAll(newElts);
//this.addF90Elements(newElements);
}
catch (Exception e)
{
createParseFailureNode(translationUnit, e.getMessage());
wasSuccessful = false;
}
// From CDT: important to know if the unit has parse errors or not
setIsStructureKnown(wasSuccessful);
}
public void setIsStructureKnown(boolean isStructureKnown)
{
translationUnit.setIsStructureKnown(isStructureKnown);
}
private FortranElement createSourceFormNode() throws CModelException
{
String sourceForm = isFixedForm ? "<Fixed Form Source>" : "<Free Form Source>";
FortranElement element = new FortranElement.UnknownNode(translationUnit, sourceForm);
translationUnit.addChild(element);
this.newElements.put(element, element.getElementInfo());
return element;
}
private FortranElement createParseFailureNode(Parent parent, String errorMessage)
throws CModelException
{
FortranElement element = new FortranElement.ErrorNode(parent, errorMessage);
parent.addChild(element);
this.newElements.put(element, element.getElementInfo());
return element;
}
// private void addF90Elements(Map elts) throws CModelException
// {
// // Add all top-level elements (those with no parent) and recurse
// addF90ElementsFor(null, elts);
// }
//
// private void addF90ElementsFor(Parent parent, Map elts) throws CModelException
// {
// Iterator it = elts.keySet().iterator();
// while (it.hasNext())
// {
// FortranElement e = (FortranElement)it.next();
// if (e.getParent() == parent)
// {
// e.setIdentifier(e.getIdentifier()); // It seems to forget position info
// addF90ElementsFor(e, elts);
// }
// }
// }
// --NODE HIERARCHY--------------------------------------------------
private Stack/*<Parent>*/ stack = new Stack/*<Parent>*/();
private Parent currentParent()
{
return stack.isEmpty() ? translationUnit : (Parent)stack.peek();
}
private void startNesting(FortranElement element) throws CModelException
{
//System.out.println("Begin " + element.getElementName());
currentParent().addChild(element);
newElements.put(element, element.getElementInfo());
stack.add(element);
}
private void doneNesting()
{
//System.out.println("End");
if (!stack.isEmpty()) stack.pop();
}
// --STAREMENT ANALYSIS----------------------------------------------
private ArrayList/*<IToken>*/ statement = new ArrayList/*<IToken>*/(64);
private boolean readStatement(ILexer lexer) throws Exception
{
statement.clear();
IToken tok;
do
{
tok = lexer.yylex();
statement.add(tok);
} while (tok.getTerminal() != Terminal.T_EOS && tok.getTerminal() != Terminal.END_OF_INPUT);
return tok.getTerminal() != Terminal.END_OF_INPUT;
}
private boolean statementStartsWith(Terminal t)
{
boolean startsWithLabel = ((IToken)statement.get(0)).getTerminal() == Terminal.T_ICON;
int firstTokenIndex = startsWithLabel ? 1 : 0;
return (((IToken)statement.get(firstTokenIndex)).getTerminal() == t);
}
private boolean statementContains(Terminal t)
{
for (int i = 0, length = statement.size(); i < length; i++)
if (((IToken)statement.get(i)).getTerminal() == t)
return true;
return false;
}
private IToken tokenFollowing(Terminal afterTerminal)
{
boolean foundToken = (afterTerminal == null);
for (int i = 0, length = statement.size(); i < length; i++)
{
IToken thisToken = (IToken)statement.get(i);
if (thisToken.getTerminal() == afterTerminal)
foundToken = true;
else if (foundToken)
return thisToken;
}
return new SimpleToken(Terminal.T_IDENT, "???");
}
private IToken firstIdentifierAfter(Terminal afterTerminal)
{
return firstIdentifierAfter(afterTerminal, "(anonymous)");
}
private IToken firstIdentifierAfter(Terminal afterTerminal, String textIfNotFound)
{
boolean foundToken = (afterTerminal == null);
for (int i = 0, length = statement.size(); i < length; i++)
{
IToken thisToken = (IToken)statement.get(i);
if (thisToken.getTerminal() == afterTerminal)
foundToken = true;
else if (foundToken && thisToken.getTerminal() == Terminal.T_IDENT)
return thisToken;
}
return new SimpleToken(Terminal.T_IDENT, textIfNotFound);
}
private boolean isEndStatement()
{
if (statementStartsWith(Terminal.T_ENDBLOCK)
|| statementStartsWith(Terminal.T_ENDBLOCKDATA)
|| statementStartsWith(Terminal.T_ENDFUNCTION)
|| statementStartsWith(Terminal.T_ENDINTERFACE)
|| statementStartsWith(Terminal.T_ENDMODULE)
|| statementStartsWith(Terminal.T_ENDPROGRAM)
|| statementStartsWith(Terminal.T_ENDSUBROUTINE))
//|| statementStartsWith(Terminal.T_ENDTYPE)
{
return true;
}
else if (statementStartsWith(Terminal.T_END))
{
Terminal next = tokenFollowing(Terminal.T_END).getTerminal();
return next == Terminal.T_BLOCK
|| next == Terminal.T_BLOCKDATA
|| next == Terminal.T_FUNCTION
|| next == Terminal.T_INTERFACE
|| next == Terminal.T_MODULE
|| next == Terminal.T_PROGRAM
|| next == Terminal.T_SUBROUTINE;
//|| next == Terminal.T_TYPE;
}
else return false;
}
// --NODE CREATION METHODS-------------------------------------------
private void buildModel(ILexer lexer) throws Exception
{
while (readStatement(lexer))
processStatement();
}
private void processStatement() throws CModelException
{
if (isEndStatement()) // Make sure this is checked before the check for T_FUNCTION below
doneNesting();
else if (statementStartsWith(Terminal.T_PROGRAM))
startNesting(new FortranElement.MainProgram(currentParent(), firstIdentifierAfter(Terminal.T_PROGRAM)));
else if (statementStartsWith(Terminal.T_MODULE))
startNesting(new FortranElement.Module(currentParent(), firstIdentifierAfter(Terminal.T_MODULE)));
// else if (statementStartsWith(Terminal.T_TYPE))
// startNesting(new FortranElement.Module(currentParent(), firstIdentifierAfter(Terminal.T_MODULE)));
else if (statementContains(Terminal.T_FUNCTION))
startNesting(new FortranElement.Function(currentParent(), firstIdentifierAfter(Terminal.T_FUNCTION)));
else if (statementStartsWith(Terminal.T_SUBROUTINE))
startNesting(new FortranElement.Subroutine(currentParent(), firstIdentifierAfter(Terminal.T_SUBROUTINE)));
else if (statementStartsWith(Terminal.T_INTERFACE))
startNesting(new FortranElement.Interface(currentParent(), firstIdentifierAfter(Terminal.T_INTERFACE, "(Interface Block)")));
else if (statementStartsWith(Terminal.T_BLOCK))
startNesting(new FortranElement.BlockData(currentParent(), firstIdentifierAfter(Terminal.T_BLOCK)));
else if (statementStartsWith(Terminal.T_BLOCKDATA))
startNesting(new FortranElement.BlockData(currentParent(), firstIdentifierAfter(Terminal.T_BLOCKDATA)));
}
}