package org.eclipse.iee.translator.antlr.translator; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.antlr.v4.runtime.ANTLRInputStream; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ErrorNode; import org.eclipse.iee.translator.antlr.math.MathBaseVisitor; import org.eclipse.iee.translator.antlr.math.MathLexer; import org.eclipse.iee.translator.antlr.math.MathParser; import org.eclipse.iee.translator.antlr.math.MathParser.IntervalParameterContext; import org.eclipse.iee.translator.antlr.math.MathParser.MatrixElementContext; import org.eclipse.iee.translator.antlr.math.MathParser.MatrixRowContext; import org.eclipse.iee.translator.antlr.math.MathParser.ValueParameterContext; import org.eclipse.iee.translator.antlr.math.MathParser.VectorContext; import com.google.common.base.Joiner; public class TexTranslator { private static List<String> fGreekLetters = new ArrayList<String>() { private static final long serialVersionUID = 1L; { add("alpha"); add("beta"); add("delta"); add("epsilon"); add("varepsilon"); add("zeta"); add("eta"); add("theta"); add("vartheta"); add("gamma"); add("kappa"); add("lambda"); add("pi"); add("mu"); add("nu"); add("xi"); add("varpi"); add("rho"); add("varrho"); add("sigma"); add("varsigma"); add("tau"); add("upsilon"); add("phi"); add("varphi"); add("chi"); add("psi"); add("omega"); add("Gamma"); add("Delta"); add("Theta"); add("Lambda"); add("Xi"); add("Pi"); add("Sigma"); add("Upsilon"); add("Phi"); add("Psi"); add("Omega"); } }; private static class TexMathVisitor extends MathBaseVisitor<String> { // statement rule public String visitFunctionDefinition( MathParser.FunctionDefinitionContext ctx) { return visitStandardFunction(ctx.name) + "=" + visit(ctx.value); } public String visitVariableAssignment(MathParser.VariableAssignmentContext ctx) { return visit(ctx.name) + "=" + visit(ctx.value); } public String visitLogicComparison(MathParser.LogicComparisonContext ctx) { if (ctx.sign.getText().matches(">=")) return visit(ctx.left) + "\\ge" + visit(ctx.right); if (ctx.sign.getText().matches("<=")) return visit(ctx.left) + "\\le" + visit(ctx.right); if (ctx.sign.getText().matches(">")) return visit(ctx.left) + ">" + visit(ctx.right); if (ctx.sign.getText().matches("<")) return visit(ctx.left) + "<" + visit(ctx.right); if (ctx.sign.getText().matches("!=")) return visit(ctx.left) + "\\ne" + visit(ctx.right); if (ctx.sign.getText().matches("==")) return visit(ctx.left) + "==" + visit(ctx.right); return visitChildren(ctx); } public String visitFunction(MathParser.FunctionContext ctx) { return visitChildren(ctx); } public String visitStandardFunction( MathParser.StandardFunctionContext ctx) { String function = ""; function += translateName(ctx.name.getText()); function += "("; for (int i = 0; i < ctx.params.size(); i++) { function += visit(ctx.params.get(i)); if (i != ctx.params.size() - 1) function += ","; } function += ")"; return function; } public String visitInternalFunction( MathParser.InternalFunctionContext ctx) { String function = ""; switch (ctx.name.getText()) { case "Integrate": for (int i = ctx.params.size() - 1; i >= 0; i--) { function += "\\int_"; IntervalParameterContext paramCtx = (IntervalParameterContext) ctx.params .get(i); function += "{" + visit(paramCtx.min) + "}"; function += "^{" + visit(paramCtx.max) + "}"; } function += "(" + visit(ctx.func) + ")" + "\\,"; for (int i = 0; i < ctx.params.size(); i++) { function += "d"; IntervalParameterContext paramCtx = (IntervalParameterContext) ctx.params .get(i); function += paramCtx.variable.getText(); if (i < ctx.params.size() - 1) function += "\\,"; } break; case "Sum": for (int i = ctx.params.size() - 1; i >= 0; i--) { function += "\\sum_"; IntervalParameterContext paramCtx = (IntervalParameterContext) ctx.params .get(i); function += "{" + paramCtx.variable.getText() + "=" + visit(paramCtx.min) + "}"; function += "^{" + visit(paramCtx.max) + "}"; } function += "(" + visit(ctx.func) + ")"; break; case "Diff": ValueParameterContext valueParamCtx = (ValueParameterContext) ctx.params .get(0); function += "\\frac{d}{d" + valueParamCtx.variable.getText() + "}"; function += "(" + visit(ctx.func) + ")"; break; case "Product": for (int i = ctx.params.size() - 1; i >= 0; i--) { function += "\\prod_"; IntervalParameterContext paramCtx = (IntervalParameterContext) ctx.params .get(i); function += "{" + paramCtx.variable.getText() + "=" + visit(paramCtx.min) + "}"; function += "^{" + visit(paramCtx.max) + "}"; } function += "(" + visit(ctx.func) + ")"; break; case "Sqrt": function += "\\sqrt{" + visit(ctx.func) + "}"; break; } return function; } public String visitAdd(MathParser.AddContext ctx) { return visit(ctx.left) + ctx.sign.getText() + visit(ctx.right); } public String visitShift(MathParser.ShiftContext ctx) { return visit(ctx.left) + ctx.sign.getText() + visit(ctx.right); } public String visitBitwiseAdd(MathParser.BitwiseAddContext ctx) { return visit(ctx.left) + "\\&" + visit(ctx.right); } public String visitBitwiseOr(MathParser.BitwiseOrContext ctx) { return visit(ctx.left) + "|" + visit(ctx.right); } public String visitXor(MathParser.XorContext ctx) { return visit(ctx.left) + "\\oplus " + visit(ctx.right); } public String visitMult(MathParser.MultContext ctx) { if (ctx.sign.getText().matches("\\*")) return visit(ctx.left) + "*" + visit(ctx.right); if (ctx.sign.getText().matches("/")) { return "\\frac{" + removeRoundBrackets(visit(ctx.left)) + "}{" + removeRoundBrackets(visit(ctx.right)) + "}"; } if (ctx.sign.getText().matches("%")) return visit(ctx.left) + " \\mod " + visit(ctx.right); return visitChildren(ctx); } public String visitPrimaryExpr(MathParser.PrimaryExprContext ctx) { return visitChildren(ctx); } public String visitPower(MathParser.PowerContext ctx) { return visit(ctx.left) + "^{" + visit(ctx.right) + "}"; } public String visitMatrix(MathParser.MatrixContext ctx) { String matrix = ""; int i; matrix += "$$\\left(\\begin{array}{"; int rowsCount = ctx.rows.size(); for (i = 0; i < rowsCount; i++) matrix += "c"; matrix += "}"; for (i = 0; i < rowsCount; i++) { matrix += visitVector(ctx.rows.get(i)); if (i != rowsCount - 1) matrix += "\\\\"; } matrix += "\\end{array}\\right)$$"; return matrix; } public String visitLogicMult(MathParser.LogicMultContext ctx) { return visit(ctx.left) + " \\wedge " + visit(ctx.right); } public String visitLogicBrackets(MathParser.LogicBracketsContext ctx) { return '(' + visit(ctx.expr) + ')'; } public String visitLogicAdd(MathParser.LogicAddContext ctx) { return visit(ctx.left) + " \\vee " + visit(ctx.right); } public String visitUnary(MathParser.UnaryContext ctx) { return ctx.sign.getText() + visit(ctx.unaryExpr); } public String visitExprBrackets(MathParser.ExprBracketsContext ctx) { return '(' + visit(ctx.bracketedExpr) + ')'; } public String visitVector(VectorContext ctx) { String row = ""; for (int i = 0; i < ctx.elements.size(); i++) { row += visit(ctx.elements.get(i)); if (i != ctx.elements.size() - 1) row += "&"; } return row; } // primary rule public String visitVariable(MathParser.VariableContext ctx) { return translateName(ctx.getText()); } public String visitFloatNumber(MathParser.FloatNumberContext ctx) { return ctx.getText(); } public String visitIntNumber(MathParser.IntNumberContext ctx) { return ctx.getText(); } public String visitMatrixDefinition( MathParser.MatrixDefinitionContext ctx) { return visitChildren(ctx); } @Override public String visitMatrixRow(MatrixRowContext ctx) { StringBuilder sb = new StringBuilder(translateName(ctx.container.getText())); sb.append("_{").append(visit(ctx.rowId)).append("}"); return sb.toString(); } @Override public String visitMatrixElement(MatrixElementContext ctx) { StringBuilder sb = new StringBuilder(translateName(ctx.container.getText())); sb.append("_{").append(visit(ctx.rowId)).append(",").append(visit(ctx.columnId)).append("}"); return sb.toString(); } public String visitMethodCall(MathParser.MethodCallContext ctx) { return translateName(ctx.container.getText()) + "." + visitStandardFunction(ctx.func); } public String visitProperty(MathParser.PropertyContext ctx) { return translateName(ctx.container.getText()) + "." + translateName(ctx.property.getText()); } @Override public String visitErrorNode(ErrorNode node) { return node.getText(); } @Override protected String aggregateResult(String aggregate, String nextResult) { return Joiner.on("").skipNulls().join(aggregate, nextResult); } } public static String translate(String expression) { String result = ""; ANTLRInputStream input = new ANTLRInputStream(expression); MathLexer lexer = new MathLexer(input); CommonTokenStream tokens = new CommonTokenStream(lexer); MathParser parser = new MathParser(tokens); parser.setBuildParseTree(true); ParserRuleContext tree = parser.statement(); TexMathVisitor mathVisitor = new TexMathVisitor(); result = mathVisitor.visit(tree); return result; } private static String translateName(String name) { String translatedName = name; for (int i = 0; i < fGreekLetters.size(); i++) { translatedName = replaceGreekLetter(translatedName, fGreekLetters.get(i)); } return translatedName; } private static String replaceGreekLetter(String name, String letter) { String translatedName = ""; int beginIndex = 0; int start = 0; int end = 0; int length = name.length(); Pattern p = Pattern.compile(letter); Matcher m = p.matcher(name); while (m.find()) { start = m.start(); end = m.end(); translatedName += name.substring(beginIndex, start); if (start == 0) { if (end == length) { translatedName += "\\"; } else { if (name.substring(end, end + 1).matches("[_]")) translatedName += "\\"; } } else { if (end == length) { if (name.substring(start - 1, start).matches("[_]")) translatedName += "\\"; } else { if (name.substring(start - 1, start).matches("[_]") && name.substring(end, end + 1).matches("[_]")) translatedName += "\\"; } } translatedName += letter; beginIndex = end; } if (end != length) translatedName += name.substring(end, length); return translatedName; } private static String removeRoundBrackets(String input) { String result = input.trim(); if (result.charAt(0) == '(' && result.charAt(result.length() - 1) == ')') { result = result.substring(1, result.length() - 1); } return result; } }