package de.gaalop.latex; import de.gaalop.cfg.*; import de.gaalop.dfg.*; import java.util.regex.Pattern; import java.util.regex.Matcher; import static java.lang.Double.compare; /** * This class implements the CFG and DFG visitor that generate LaTeX code. */ public class LatexVisitor implements ControlFlowVisitor, ExpressionVisitor { private Pattern INDEXED_NUMBER = Pattern.compile("^(\\w+)(\\d+)$"); private StringBuilder code = new StringBuilder(); private final static FloatConstant HALF = new FloatConstant(0.5f); private final static Negation MINUS_HALF = new Negation(HALF); private final static Division ONE_HALF = new Division(new FloatConstant(1.0f), new FloatConstant(2.0f)); private final static Negation MINUS_ONE_HALF = new Negation(ONE_HALF); public String getCode() { return code.toString(); } @Override public void visit(StartNode node) { code.append("\\begin{align*}\n"); node.getSuccessor().accept(this); } @Override public void visit(AssignmentNode node) { node.getVariable().accept(this); code.append("&= "); node.getValue().accept(this); code.append("\\\\\n"); node.getSuccessor().accept(this); } @Override public void visit(ExpressionStatement node) { node.getExpression().accept(this); code.append("\\\\\n"); node.getSuccessor().accept(this); } @Override public void visit(StoreResultNode node) { code.append('?'); code.append(node.getValue()); code.append("\\\\\n"); node.getSuccessor().accept(this); } @Override public void visit(IfThenElseNode node) { code.append("\\text{IF } ("); node.getCondition().accept(this); code.append(") \\\\\n"); node.getPositive().accept(this); if (!(node.getNegative() instanceof BlockEndNode)) { code.append("\\text{ELSE} \\\\\n"); node.getNegative().accept(this); } code.append("\\text{END IF} \\\\\n"); node.getSuccessor().accept(this); } @Override public void visit(LoopNode node) { code.append("\\text{LOOP} \\\\\n"); node.getBody().accept(this); code.append("\\text{END LOOP} \\\\\n"); node.getSuccessor().accept(this); } @Override public void visit(BreakNode breakNode) { code.append("\\text{break}\\\\"); breakNode.getSuccessor().accept(this); } @Override public void visit(BlockEndNode node) { } @Override public void visit(EndNode node) { code.append("\\end{align*}\n"); } private void addBinaryInfix(BinaryOperation op, String operator) { op.getLeft().accept(this); code.append(operator); op.getRight().accept(this); } @Override public void visit(Subtraction subtraction) { addBinaryInfix(subtraction, "-"); } @Override public void visit(Addition addition) { addBinaryInfix(addition, "+"); } @Override public void visit(Division division) { code.append("\\cfrac{"); division.getLeft().accept(this); code.append("}{"); division.getRight().accept(this); code.append("}"); } @Override public void visit(InnerProduct innerProduct) { addBinaryInfix(innerProduct, "\\cdot"); } @Override public void visit(Multiplication multiplication) { if (multiplication.getLeft().equals(HALF)) { ONE_HALF.accept(this); multiplication.getRight().accept(this); } else if (multiplication.getRight().equals(HALF)) { ONE_HALF.accept(this); multiplication.getLeft().accept(this); } else if (multiplication.getLeft().equals(MINUS_HALF)) { MINUS_ONE_HALF.accept(this); multiplication.getRight().accept(this); } else if (multiplication.getRight().equals(MINUS_HALF)) { MINUS_ONE_HALF.accept(this); multiplication.getLeft().accept(this); } else { addBinaryInfix(multiplication, "*"); } } @Override public void visit(MathFunctionCall mathFunctionCall) { code.append(mathFunctionCall.getFunction().toString()); code.append('('); mathFunctionCall.getOperand().accept(this); code.append(')'); } @Override public void visit(Variable variable) { String name = variable.getName(); code.append(name.replace("_", "\\_")); } private void addIdentifier(String name) { Matcher matcher = INDEXED_NUMBER.matcher(name); if (matcher.matches()) { code.append(matcher.group(1).replace("_", "\\_")); code.append("_{"); code.append(matcher.group(2)); code.append("}"); } else { code.append(name.replace("_", "\\_")); } } @Override public void visit(MultivectorComponent component) { addIdentifier(component.getName().replace("_opt", "")); code.append("_{"); code.append(component.getBladeIndex()); code.append('}'); } @Override public void visit(Exponentiation exponentiation) { exponentiation.getLeft().accept(this); code.append("^{"); exponentiation.getRight().accept(this); code.append('}'); } @Override public void visit(FloatConstant floatConstant) { if (Double.isNaN(floatConstant.getValue())) code.append("undefined"); else if (compare(floatConstant.getValue(), Math.floor(floatConstant.getValue())) == 0) { code.append((int) floatConstant.getValue()); } else { code.append(Double.toString(floatConstant.getValue())); } } @Override public void visit(OuterProduct outerProduct) { addBinaryInfix(outerProduct, "\\wedge"); } @Override public void visit(BaseVector baseVector) { code.append("e_{"); code.append(baseVector.getIndex()); code.append('}'); } @Override public void visit(Negation negation) { code.append('-'); negation.getOperand().accept(this); } @Override public void visit(Reverse node) { code.append('~'); node.getOperand().accept(this); } @Override public void visit(LogicalOr node) { addBinaryInfix(node, " \\vee "); } @Override public void visit(LogicalAnd node) { addBinaryInfix(node, " \\wedge "); } @Override public void visit(LogicalNegation node) { code.append("!"); node.getOperand().accept(this); } @Override public void visit(Equality node) { addBinaryInfix(node, " == "); } @Override public void visit(Inequality node) { addBinaryInfix(node, " \neq "); } @Override public void visit(Relation relation) { addBinaryInfix(relation, relation.getTypeString()); } @Override public void visit(Macro node) { throw new IllegalArgumentException("Macros should have been inlined."); } @Override public void visit(FunctionArgument node) { throw new IllegalArgumentException("Macros should have been inlined."); } @Override public void visit(MacroCall node) { throw new IllegalArgumentException("Macros should have been inlined."); } @Override public void visit(ColorNode node) { node.getSuccessor().accept(this); } }