blob: ebdf80f08424e01d871149378b04e1dba3152212 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 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.analysis.loops;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.photran.internal.core.analysis.binding.ScopingNode;
import org.eclipse.photran.internal.core.parser.ASTDoConstructNode;
import org.eclipse.photran.internal.core.parser.ASTEndDoStmtNode;
import org.eclipse.photran.internal.core.parser.ASTLabelDoStmtNode;
import org.eclipse.photran.internal.core.parser.ASTVisitor;
import org.eclipse.photran.internal.core.parser.IASTNode;
import org.eclipse.photran.internal.core.parser.IActionStmt;
import org.eclipse.photran.internal.core.parser.IExecutableConstruct;
import org.eclipse.photran.internal.core.parser.IExecutionPartConstruct;
import org.eclipse.photran.internal.core.parser.IObsoleteActionStmt;
/**
* Identifies DO-loops in a Fortran AST and replaces them with {@link ASTProperLoopConstructNode}s.
* <p>
* Due to a deficiency in the parser, DO-constructs are not recognized as a single construct; DO and END DO statements
* are recognized as ordinary statements on par with the statements comprising their body.
* <p>
* {@link ASTProperLoopConstructNode} provides a &quot;proper&quot; representation of DO-loops. Invoking
* {@link #replaceAllLoopsIn(ScopingNode)} will identify the loops in an AST and replace them with
* {@link ASTProperLoopConstructNode}s. After this has been done, the AST can be visited using an
* {@link IASTVisitorWithLoops}.
*
* @author Jeff Overbey
* @author Mariano Mendez
*
* @see LoopReplacer
* @see IASTVisitorWithLoops
*/
public class LoopReplacer
{
public static void replaceAllLoopsIn(ScopingNode scope)
{
new LoopReplacer().replaceLoopsFromLastToFirstIn(scope);
}
/** A list of all the loops in scope, from last to first */
private List<ASTDoConstructNode> queue = new LinkedList<ASTDoConstructNode>();
private void replaceLoopsFromLastToFirstIn(ScopingNode scope)
{
collectLoopsIn(scope);
while (!queue.isEmpty())
replaceLoop(queue.remove(0));
}
private void collectLoopsIn(ScopingNode scope)
{
scope.accept(new ASTVisitor()
{
@Override public void visitASTDoConstructNode(ASTDoConstructNode node)
{
// Collect all DoLoopsStmt
queue.add(0, node);
}
});
}
private void replaceLoop(ASTDoConstructNode loopToReplace)
{
// Save ancestor nodes, since parent pointers will be changed when we manipulate
// the AST in #buildASTProperLoopConstructNode below.
IASTNode oldParent = loopToReplace.getParent();
ScopingNode scope = loopToReplace.findNearestAncestor(ScopingNode.class);
// Now manipulate the AST
ASTProperLoopConstructNode newLoop = buildASTProperLoopConstructNode(loopToReplace, scope);
loopToReplace.replaceWith(newLoop);
newLoop.setParent(oldParent);
}
private ASTProperLoopConstructNode buildASTProperLoopConstructNode(ASTDoConstructNode loopToReplace, ScopingNode scope)
{
ASTLabelDoStmtNode lastLoopHeader = loopToReplace.getLabelDoStmt();
// First, remove siblings of lastLoopHeader that should actually be in the loop body
ASTProperLoopConstructBuilder nodeBuilder = new ASTProperLoopConstructBuilder(lastLoopHeader);
scope.accept(nodeBuilder);
// We needed to keep the loop header in the AST so that that ASTProperLoopConstructBuilder could find it
// Now that it's finished, we can move the loop header into the ASTProperLoopConstructNode
lastLoopHeader.removeFromTree();
nodeBuilder.result.setLoopHeader(lastLoopHeader);
return nodeBuilder.result;
}
private class ASTProperLoopConstructBuilder extends ASTVisitorWithLoops
{
private final ASTProperLoopConstructNode result = new ASTProperLoopConstructNode();
private final ASTLabelDoStmtNode loopHeader;
private final IASTNode doConstructNode;
private final IASTNode listEnclosingDoConstructNode;
private boolean loopHeaderFound = false;
private IASTNode oldStyleEndLoopRef=null;
// First, save ancestor nodes, since parent pointers will be changed when we
// manipulate the AST in the #visit methods below
public ASTProperLoopConstructBuilder(ASTLabelDoStmtNode loopHeader)
{
this.loopHeader = loopHeader;
this.doConstructNode = loopHeader.getParent();
this.listEnclosingDoConstructNode = doConstructNode.getParent();
this.oldStyleEndLoopRef=null;
}
// Start accumulating body statements when we find the loop header
@Override public void visitASTLabelDoStmtNode(ASTLabelDoStmtNode node)
{
if (node == loopHeader)
loopHeaderFound = true;
traverseChildren(node);
}
// Accumulate all statements between the loop header and the END DO stmt
@Override public void visitIExecutionPartConstruct(IExecutionPartConstruct node)
{
if (shouldBeInLoopBody(node))
{
node.removeFromTree();
this.result.getBody().add(node);
}
}
@Override public void visitIExecutableConstruct(IExecutableConstruct node)
{
visitIExecutionPartConstruct(node);
}
@Override public void visitIActionStmt(IActionStmt node)
{
// Obtain a reference to the end of the old Style Loop Node
visitIExecutionPartConstruct(node);
if (isOldStyleDoLoopEnd(node))
{
this.result.setEndDoStmt(null);
this.oldStyleEndLoopRef=node;
}
//traverseChildren(node);
}
@Override public void visitIObsoleteActionStmt(IObsoleteActionStmt node)
{
visitIExecutionPartConstruct(node);
}
private boolean shouldBeInLoopBody(IExecutionPartConstruct node)
{
return loopHeaderFound
&& !endDoStmtFound()
&& !oldStyleEndLoopFound()
&& !isLoopHeader(node)
&& isCurrentlySiblingOfLoopHeader(node);
}
private boolean isOldStyleDoLoopEnd( IActionStmt node )
{
if ( (node.getLabel()!=null) && (this.loopHeader.getLblRef()!=null) )
{
return loopHeaderFound
&& !endDoStmtFound()
&& !(node.getParent() == this.listEnclosingDoConstructNode)
&& this.loopHeader.getLblRef().getLabel().getText().equals(node.getLabel().getText()) ;
}
return false;
}
private boolean isCurrentlySiblingOfLoopHeader(IExecutionPartConstruct node)
{
return node.getParent() == listEnclosingDoConstructNode;
}
// Don't accumulate either the ASTLabelDoStmtNode or the ASTDoConstructNode in the body; these are the header
private boolean isLoopHeader(IExecutionPartConstruct node)
{
return node == loopHeader || node == doConstructNode;
}
// Stop accumulating body statements as soon as we find an END DO stmt
@Override public void visitASTEndDoStmtNode(ASTEndDoStmtNode node)
{
if (loopHeaderFound && !endDoStmtFound() && (node.getParent() == listEnclosingDoConstructNode) && !oldStyleEndLoopFound() )
{
node.removeFromTree();
this.result.setEndDoStmt(node);
}
traverseChildren(node);
}
private boolean endDoStmtFound()
{
return this.result.getEndDoStmt() != null;
}
private boolean oldStyleEndLoopFound()
{
return this.oldStyleEndLoopRef != null;
}
@Override public void visitASTProperLoopConstructNode(ASTProperLoopConstructNode node)
{
// Do not traverse child statements of nested loops
// Except if you are working with a Shared Do Loop Termination
// you need to know where is the ending Loop
if (node.getLoopHeader().getLblRef()==null) return;
if (this.loopHeader.getLblRef()==null) return;
String nodeLabel=node.getLoopHeader().getLblRef().getLabel().getText();
String headerLabel= this.loopHeader.getLblRef().getLabel().getText();
if ( !endDoStmtFound() && !oldStyleEndLoopFound() && nodeLabel.equals(headerLabel) )
{
visitIExecutionPartConstruct(node);
this.oldStyleEndLoopRef=node.getLoopHeader().getLblRef();
}
}
}
}