package de.gaalop.java;
import de.gaalop.cfg.*;
import de.gaalop.dfg.*;
import java.util.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* This visitor traverses the control and data flow graphs and generates Java code.
*/
public class JavaVisitor implements ControlFlowVisitor, ExpressionVisitor {
public String filename;
protected Log log = LogFactory.getLog(JavaVisitor.class);
protected StringBuilder codePre = new StringBuilder();
protected StringBuilder codeCalc = new StringBuilder();
protected StringBuilder codePost = new StringBuilder();
protected ControlFlowGraph graph;
protected int indentation = 0;
protected Set<String> declaredLocal = new HashSet<String>();
protected Set<String> outputtedMultivectors = new HashSet<String>();
private final int JAVALIMIT = 65500; // let space for indentation!
private byte curSection = 0;
private boolean implementFactorial = false;
/**
* Appends a character to the result string
* @param character The character to append
*/
private void append(char character) {
append(character + "");
}
/**
* Appends a string to the current code section
* The code section is definied by the curSection member
* @param string The string to append to the code section
*/
private void append(String string) {
switch (curSection) {
case 0:
codePre.append(string);
break;
case 1:
codeCalc.append(string);
break;
case 2:
codePost.append(string);
break;
}
}
/**
* Returns the code, created by this code generator
* @return The generated code
*/
public String getCode() {
StringBuilder result = new StringBuilder();
result.append(codePre);
if (codeCalc.length() > JAVALIMIT) {
result.append(splitUpCalcMethods());
} else {
result.append(codeCalc);
}
result.append(codePost);
return result.toString();
}
/**
* Appends the indentation to the result string
*/
protected void appendIndentation() {
for (int i = 0; i < indentation; ++i) {
append('\t');
}
}
@Override
public void visit(StartNode node) {
graph = node.getGraph();
//process all members
curSection = 0;
implementFactorial = false;
append("import java.util.HashMap;\n\n");
int lastDotIndex = filename.lastIndexOf('.');
if (lastDotIndex != -1)
filename = filename.substring(0, lastDotIndex);
append("public class " + filename + " implements GAProgram {\n");
indentation++;
indentation++;
appendIndentation();
curSection = 1;
node.getSuccessor().accept(this);
}
/**
* Sorts a set of variables by name to make the order deterministic.
*
* @param inputVariables
* @return
*/
protected List<Variable> sortVariables(Set<Variable> inputVariables) {
List<Variable> variables = new ArrayList<Variable>(inputVariables);
Comparator<Variable> comparator = new Comparator<Variable>() {
@Override
public int compare(Variable o1, Variable o2) {
return o1.getName().compareToIgnoreCase(o2.getName());
}
};
Collections.sort(variables, comparator);
return variables;
}
private String getVarName(Variable var) {
if (!(var instanceof MultivectorComponent)) {
return var.getName() + "$0";
} else {
return var.getName() + "$" + ((MultivectorComponent) var).getBladeIndex();
}
}
@Override
public void visit(AssignmentNode node) {
appendIndentation();
declaredLocal.add(getVarName(node.getVariable()));
node.getVariable().accept(this);
append(" = ");
node.getValue().accept(this);
if (node.getVariable() instanceof MultivectorComponent) {
append(';');
append(" // ");
MultivectorComponent component = (MultivectorComponent) node.getVariable();
append(node.getGraph().getAlgebraDefinitionFile().getBladeString(component.getBladeIndex()));
}
append(";\n");
node.getSuccessor().accept(this);
}
@Override
public void visit(ExpressionStatement node) {
appendIndentation();
node.getExpression().accept(this);
append(";\n");
node.getSuccessor().accept(this);
}
@Override
public void visit(StoreResultNode node) {
outputtedMultivectors.add(node.getValue().getName());
node.getSuccessor().accept(this);
}
@Override
public void visit(IfThenElseNode node) {
Expression condition = node.getCondition();
appendIndentation();
append("if (");
condition.accept(this);
append(") {\n");
indentation++;
node.getPositive().accept(this);
indentation--;
appendIndentation();
append("}");
if (node.getNegative() instanceof BlockEndNode) {
append("\n");
} else {
append(" else ");
boolean isElseIf = false;
if (node.getNegative() instanceof IfThenElseNode) {
IfThenElseNode ifthenelse = (IfThenElseNode) node.getNegative();
isElseIf = ifthenelse.isElseIf();
}
if (!isElseIf) {
append("{\n");
indentation++;
}
node.getNegative().accept(this);
if (!isElseIf) {
indentation--;
appendIndentation();
append("}\n");
}
}
node.getSuccessor().accept(this);
}
@Override
public void visit(LoopNode node) {
appendIndentation();
append("while(true) {\n");
indentation++;
node.getBody().accept(this);
indentation--;
appendIndentation();
append("}\n");
node.getSuccessor().accept(this);
}
@Override
public void visit(BreakNode breakNode) {
appendIndentation();
append("break;\n");
}
@Override
public void visit(BlockEndNode node) {
// nothing to do
}
@Override
public void visit(EndNode node) {
indentation--;
appendIndentation();
append("}\n\n"); // close procedure calculate
curSection = 2;
indentation--;
if (implementFactorial) {
appendIndentation();
append("private double fact(int n) {\n");
indentation++;
appendIndentation();
append("double result = 1;\n");
appendIndentation();
append("for (int i=2;i<=n;i++)\n");
indentation++;
appendIndentation();
append("result *= i;\n");
indentation--;
appendIndentation();
append("return result;\n");
indentation--;
appendIndentation();
append("}\n");
}
append("\n");
indentation--;
appendIndentation();
append("}\n"); // close class
//declare variables
curSection = 0;
append("\n");
indentation++;
indentation++;
appendIndentation();
append("// input variables\n");
for (Variable inputVar : graph.getInputVariables()) {
appendIndentation();
append("private double " + getVarName(inputVar) + ";\n");
}
append("\n");
//outputtedMultivectors
//declaredLocal
LinkedList<String> locals = new LinkedList<String>();
LinkedList<String> outputs = new LinkedList<String>();
for (String l: declaredLocal) {
String name = l.split("\\$")[0];
if (outputtedMultivectors.contains(name)) {
boolean outputComponentExist = false;
for (String outputVarStr : graph.getPragmaOutputVariables())
if (outputVarStr.split("\\$")[0].equals(name))
outputComponentExist = true;
if (outputComponentExist) {
if (graph.getPragmaOutputVariables().contains(l))
outputs.add(l);
else
locals.add(l);
} else {
outputs.add(l);
}
} else
locals.add(l);
}
String[] localsArr = locals.toArray(new String[0]);
String[] outputsArr = outputs.toArray(new String[0]);
Arrays.sort(localsArr);
Arrays.sort(outputsArr);
appendIndentation();
append("// local variables\n");
for (String curLocal : localsArr) {
appendIndentation();
append("private double " + curLocal + ";\n");
}
append("\n");
appendIndentation();
append("// output variables\n");
for (String curOutput : outputsArr) {
appendIndentation();
append("private double " + curOutput + ";\n");
}
append("\n");
// getValue for output variables
appendIndentation();
append("@Override\n");
appendIndentation();
append("public double getValue(String varName) {\n");
indentation++;
for (String curOutput : outputs) {
appendIndentation();
append("if (varName.equals(\"" + curOutput + "\")) return " + curOutput + ";\n");
}
appendIndentation();
append("return 0.0d;\n");
indentation--;
appendIndentation();
append("}\n"); // close procedure getValue
append("\n");
//getValues for output variables
appendIndentation();
append("@Override\n");
appendIndentation();
append("public HashMap<String,Double> getValues() {\n");
indentation++;
appendIndentation();
append("HashMap<String,Double> result = new HashMap<String,Double>();\n");
for (String curOutput : outputs) {
appendIndentation();
append("result.put(\"" + curOutput + "\"," + curOutput + ");\n");
}
appendIndentation();
append("return result;\n");
indentation--;
appendIndentation();
append("}\n"); // close procedure getValues
// setValue for input variables
appendIndentation();
append("@Override\n");
appendIndentation();
append("public boolean setValue(String varName, double value) {\n");
indentation++;
for (Variable inputVar : graph.getInputVariables()) {
appendIndentation();
append("if (varName.equals(\"" + getVarName(inputVar) + "\")) { " + getVarName(inputVar) + " = value; return true; }\n");
}
appendIndentation();
append("return false;\n");
indentation--;
appendIndentation();
append("}\n"); // close procedure setValue
appendIndentation();
append("\n");
appendIndentation();
append("@Override\n");
appendIndentation();
append("public void calculate() {\n");
indentation++;
curSection = 2;
}
@Override
public void visit(ColorNode node) {
node.getSuccessor().accept(this);
}
protected void addBinaryInfix(BinaryOperation op, String operator) {
append("(");
addChild(op, op.getLeft());
append(operator);
addChild(op, op.getRight());
append(")");
}
protected void addChild(Expression parent, Expression child) {
if (OperatorPriority.hasLowerPriority(parent, child)) {
append('(');
child.accept(this);
append(')');
} else {
child.accept(this);
}
}
@Override
public void visit(Subtraction subtraction) {
addBinaryInfix(subtraction, " - ");
}
@Override
public void visit(Addition addition) {
addBinaryInfix(addition, " + ");
}
@Override
public void visit(Division division) {
addBinaryInfix(division, " / ");
}
@Override
public void visit(InnerProduct innerProduct) {
throw new UnsupportedOperationException("The Java backend does not support the inner product.");
}
@Override
public void visit(Multiplication multiplication) {
addBinaryInfix(multiplication, " * ");
}
@Override
public void visit(MathFunctionCall mathFunctionCall) {
String funcName = "(double) Math." + mathFunctionCall.getFunction().toString().toLowerCase();
if (mathFunctionCall.getFunction() == MathFunction.FACT) {
funcName = "fact";
implementFactorial = true;
}
append(funcName);
append('(');
mathFunctionCall.getOperand().accept(this);
append(')');
}
@Override
public void visit(Variable variable) {
append(getVarName(variable));
}
@Override
public void visit(MultivectorComponent component) {
append(getVarName(component));
}
@Override
public void visit(Exponentiation exponentiation) {
if (isSquare(exponentiation)) {
Multiplication m = new Multiplication(exponentiation.getLeft(), exponentiation.getLeft());
m.accept(this);
} else {
append("Math.pow(");
exponentiation.getLeft().accept(this);
append(',');
exponentiation.getRight().accept(this);
append(')');
}
}
/**
* Returns if the exponentiation has an exponent which is equal to 2
* @param exponentiation The exponentiation
* @return <value>true</value> if the exponent is equal to 2, <value>false</value> otherwise
*/
protected boolean isSquare(Exponentiation exponentiation) {
final FloatConstant two = new FloatConstant(2.0f);
return two.equals(exponentiation.getRight());
}
@Override
public void visit(FloatConstant floatConstant) {
append(Double.toString(floatConstant.getValue()));
append('d');
}
@Override
public void visit(OuterProduct outerProduct) {
throw new UnsupportedOperationException("The Java backend does not support the outer product.");
}
@Override
public void visit(BaseVector baseVector) {
throw new UnsupportedOperationException("The Java backend does not support base vectors.");
}
@Override
public void visit(Negation negation) {
append("(-");
addChild(negation, negation.getOperand());
append(")");
}
@Override
public void visit(Reverse node) {
throw new UnsupportedOperationException("The Java backend does not support the reverse operation.");
}
@Override
public void visit(LogicalOr node) {
addBinaryInfix(node, " || ");
}
@Override
public void visit(LogicalAnd node) {
addBinaryInfix(node, " && ");
}
@Override
public void visit(LogicalNegation node) {
append('!');
addChild(node, node.getOperand());
}
@Override
public void visit(Equality node) {
addBinaryInfix(node, " == ");
}
@Override
public void visit(Inequality node) {
addBinaryInfix(node, " != ");
}
@Override
public void visit(Relation relation) {
addBinaryInfix(relation, relation.getTypeString());
}
@Override
public void visit(Macro node) {
throw new IllegalStateException("Macros should have been inlined and removed from the graph.");
}
@Override
public void visit(FunctionArgument node) {
throw new IllegalStateException("Macros should have been inlined and no function arguments should be the graph.");
}
@Override
public void visit(MacroCall node) {
throw new IllegalStateException("Macros should have been inlined and no macro calls should be in the graph.");
}
//Calc method code can be exceeded about 65535 chars,
//catch this hard limit by splitting up the calc method
/**
* Splits the calculation method in parts, so that each one don't exceeds the java code hard limit of
* 65535 chars
* @return The splitted methods
*/
private String splitUpCalcMethods() {
StringBuilder result = new StringBuilder();
int backupIndentation = indentation;
int length = codeCalc.length();
int curPosition = 0;
int curProcCounter = 1;
LOOP:
while (length - curPosition > JAVALIMIT) {
// Divide in two parts:
String part = codeCalc.substring(curPosition, curPosition + JAVALIMIT);
int lastSemicolon = part.lastIndexOf('\n');
if (lastSemicolon > 0) {
curPosition += lastSemicolon + 1;
result.append(part.substring(0, lastSemicolon + 1));
for (int i = 0; i < indentation; ++i) {
result.append('\t');
}
result.append("calculate" + curProcCounter + "();\n");
//end procedure and begin new procedure
indentation--;
for (int i = 0; i < indentation; ++i) {
result.append('\t');
}
result.append("}\n\n");
for (int i = 0; i < indentation; ++i) {
result.append('\t');
}
result.append("public void calculate" + curProcCounter + "() {\n");
indentation++;
curProcCounter++;
} else {
System.err.println("Expression is more than " + JAVALIMIT + " chars long. The outputted file is not compilable with code limit of java! Try to split the calculate method manually!");
break LOOP; // catch endless loop
}
}
result.append(codeCalc.substring(curPosition));
indentation = backupIndentation;
return result.toString();
}
}