package de.gaalop.maple;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import de.gaalop.Notifications;
import de.gaalop.cfg.AssignmentNode;
import de.gaalop.cfg.BlockEndNode;
import de.gaalop.cfg.BreakNode;
import de.gaalop.cfg.ColorNode;
import de.gaalop.cfg.ControlFlowGraph;
import de.gaalop.cfg.ControlFlowVisitor;
import de.gaalop.cfg.EndNode;
import de.gaalop.cfg.ExpressionStatement;
import de.gaalop.cfg.IfThenElseNode;
import de.gaalop.cfg.LoopNode;
import de.gaalop.cfg.Macro;
import de.gaalop.cfg.Node;
import de.gaalop.cfg.SequentialNode;
import de.gaalop.cfg.SetCallerVisitor;
import de.gaalop.cfg.StartNode;
import de.gaalop.cfg.StoreResultNode;
import de.gaalop.dfg.EmptyExpressionVisitor;
import de.gaalop.dfg.Expression;
import de.gaalop.dfg.FunctionArgument;
import de.gaalop.dfg.MacroCall;
import de.gaalop.dfg.UpdateMacroCallVisitor;
import de.gaalop.dfg.Variable;
/**
* This class is responsible for inlining user-defined macros at places where macros are called.
*
* @author Christian Schwinn
*
*/
public class InlineMacrosVisitor extends EmptyExpressionVisitor implements ControlFlowVisitor {
private class ReplaceExpressionVisitor implements ControlFlowVisitor {
private final String macroName;
private final Map<String, String> newNames;
public ReplaceExpressionVisitor(String macroName, Map<String, String> newNames) {
this.macroName = macroName;
this.newNames = newNames;
}
Expression replaceInExpression(Expression e) {
UsedVariablesVisitor visitor = new UsedVariablesVisitor();
e.accept(visitor);
Expression newExpression = e;
for (Variable v : visitor.getVariables()) {
if (graph.containsLocalVariable(v.getName()) && !v.globalAccess()) {
String unique = newNames.get(v.getName());
usedNames.add(unique);
Variable newVariable = new Variable(unique);
e.replaceExpression(v, newVariable);
newExpression = e;
graph.addLocalVariable(newVariable);
} else if (v instanceof FunctionArgument) {
FunctionArgument argument = (FunctionArgument) v;
Expression newArgument = currentArguments.get(macroName).get(argument.getIndex() - 1);
e.replaceExpression(argument, newArgument);
if (e instanceof FunctionArgument) {
newExpression = newArgument;
} else {
newExpression = e;
}
}
}
return newExpression;
}
private void replaceSubtree(Node root) {
if (root instanceof BlockEndNode) {
return;
}
if (root instanceof SequentialNode) {
SequentialNode node = (SequentialNode) root;
node.accept(this);
replaceSubtree(node.getSuccessor());
}
}
@Override
public void visit(StartNode node) {
}
@Override
public void visit(AssignmentNode node) {
Variable variable = node.getVariable();
newVariables.add(variable);
replaceInExpression(variable);
Expression newExpression = replaceInExpression(node.getValue());
node.replaceExpression(node.getValue(), newExpression);
}
@Override
public void visit(StoreResultNode node) {
replaceInExpression(node.getValue());
}
@Override
public void visit(IfThenElseNode node) {
replaceInExpression(node.getCondition());
replaceSubtree(node.getPositive());
replaceSubtree(node.getNegative());
}
@Override
public void visit(BlockEndNode node) {
}
@Override
public void visit(LoopNode node) {
replaceSubtree(node.getBody());
}
@Override
public void visit(BreakNode node) {
}
@Override
public void visit(Macro node) {
}
@Override
public void visit(ExpressionStatement node) {
replaceInExpression(node.getExpression());
}
@Override
public void visit(EndNode node) {
}
@Override
public void visit(ColorNode node) {
node.getSuccessor().accept(this);
}
}
private Set<Node> visitedNodes = new HashSet<Node>();
Set<String> usedNames = new HashSet<String>();
Map<String, List<Expression>> currentArguments = new HashMap<String, List<Expression>>();
List<Variable> newVariables = new ArrayList<Variable>();
ControlFlowGraph graph;
private String generateUniqueName(String original) {
String unique = original;
int i = 1;
while (graph.containsLocalVariable(unique) || usedNames.contains(unique)) {
// attention: must not collide with names of variables for multivector components in MapleCfgVisitor
unique = original + "_" + i + "_";
i++;
}
return unique;
}
/**
* Prevents visited nodes from being visited again and causing a potential infinite loop.
*
* @param node node from which to continue visiting
*/
private void continueVisitFrom(SequentialNode node) {
boolean visitSuccessor = true;
Node successor = node.getSuccessor();
for (Node visited : visitedNodes) {
if (visited == successor) {
visitSuccessor = false;
}
}
if (visitSuccessor) {
visitedNodes.add(successor);
successor.accept(this);
}
}
@Override
public void visit(StartNode node) {
visitedNodes.add(node);
this.graph = node.getGraph();
continueVisitFrom(node);
}
@Override
public void visit(AssignmentNode node) {
visitedNodes.add(node);
node.getValue().accept(this);
continueVisitFrom(node);
}
@Override
public void visit(ExpressionStatement node) {
visitedNodes.add(node);
if (node.getExpression() instanceof MacroCall) {
((MacroCall) node.getExpression()).setSingleLine();
} else {
Notifications.addWarning("Found an expression without assignment: '" + node
+ "' (Side effects are not supported.)");
}
node.getExpression().accept(this);
continueVisitFrom(node);
}
@Override
public void visit(StoreResultNode node) {
visitedNodes.add(node);
node.getValue().accept(this);
continueVisitFrom(node);
}
@Override
public void visit(IfThenElseNode node) {
visitedNodes.add(node);
node.getCondition().accept(this);
node.getPositive().accept(this);
node.getNegative().accept(this);
continueVisitFrom(node);
}
@Override
public void visit(BlockEndNode node) {
}
@Override
public void visit(LoopNode node) {
visitedNodes.add(node);
node.getBody().accept(this);
continueVisitFrom(node);
}
@Override
public void visit(BreakNode node) {
visitedNodes.add(node);
continueVisitFrom(node);
}
@Override
public void visit(Macro node) {
visitedNodes.add(node);
node.getGraph().removeNode(node);
continueVisitFrom(node);
}
@Override
public void visit(EndNode node) {
}
@Override
public void visit(FunctionArgument node) {
throw new IllegalStateException("This method should not be reachable...");
}
@Override
public void visit(MacroCall node) {
newVariables.clear();
SequentialNode caller = node.getCaller();
Macro macro = graph.getMacro(node.getName());
String macroName = macro.getName();
List<Expression> arguments = node.getArguments();
for (Expression arg : arguments) {
arg.accept(this);
}
currentArguments.put(macroName, arguments);
// generate unique names for each variable in current scope (to be used for each statement in this scope)
Map<String, String> newNames = new HashMap<String, String>();
for (Variable v : graph.getLocalVariables()) {
newNames.put(v.getName(), generateUniqueName(v.getName()));
}
Node oldPredecessor = macro;
SetCallerVisitor setCaller = new SetCallerVisitor();
ReplaceExpressionVisitor replacer = new ReplaceExpressionVisitor(macroName, newNames);
for (SequentialNode statement : macro.getBody()) {
SequentialNode newStatement = statement.copy();
newStatement.removePredecessor(oldPredecessor);
newStatement.accept(setCaller);
newStatement.accept(replacer);
caller.insertBefore(newStatement);
newStatement.accept(this);
oldPredecessor = statement;
}
if (!node.isSingleLine()) {
if (macro.getReturnValue() == null) {
throw new IllegalArgumentException(
"Cannot inline a macro without return value into the following statement:\n" + caller);
}
Expression returnValue = macro.getReturnValue().copy();
replacer.replaceInExpression(returnValue);
String unique = generateUniqueName("rslt");
Variable retVal = new Variable(unique);
usedNames.add(unique);
AssignmentNode result = new AssignmentNode(graph, retVal, returnValue);
UpdateMacroCallVisitor updater = new UpdateMacroCallVisitor(result);
result.getValue().accept(updater);
caller.insertBefore(result);
caller.replaceExpression(node, retVal);
result.accept(this);
} else {
graph.removeNode(caller);
}
// add new local variables to graph
for (Variable v : newVariables) {
graph.removeInputVariable(v);
graph.addLocalVariable(v);
}
}
@Override
public void visit(ColorNode node) {
node.getSuccessor().accept(this);
}
}