==== Patch <unparse> level 1
Source: [No source]
Target: eb4544d6-894b-0410-9319-a9612837a279:/trunk/janino:338
        (http://svn.codehaus.org/janino)
Log:
Add support and tests for precedence handling in UnparseVisitor

=== tests/src/UnparseTests.java
==================================================================
--- tests/src/UnparseTests.java	(revision 338)
+++ tests/src/UnparseTests.java	(patch unparse level 1)
@@ -0,0 +1,332 @@
+
+/*
+ * Janino - An embedded Java[TM] compiler
+ *
+ * Copyright (c) 2001-2007, Arno Unkrig
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *    3. The name of the author may not be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.StringWriter;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.codehaus.janino.Java;
+import org.codehaus.janino.Parser;
+import org.codehaus.janino.Scanner;
+import org.codehaus.janino.UnparseVisitor;
+import org.codehaus.janino.Visitor;
+import org.codehaus.janino.Java.AmbiguousName;
+import org.codehaus.janino.Java.ArrayAccessExpression;
+import org.codehaus.janino.Java.ArrayLength;
+import org.codehaus.janino.Java.Assignment;
+import org.codehaus.janino.Java.Atom;
+import org.codehaus.janino.Java.BinaryOperation;
+import org.codehaus.janino.Java.Cast;
+import org.codehaus.janino.Java.ClassLiteral;
+import org.codehaus.janino.Java.ConditionalExpression;
+import org.codehaus.janino.Java.ConstantValue;
+import org.codehaus.janino.Java.Crement;
+import org.codehaus.janino.Java.FieldAccess;
+import org.codehaus.janino.Java.FieldAccessExpression;
+import org.codehaus.janino.Java.Instanceof;
+import org.codehaus.janino.Java.Literal;
+import org.codehaus.janino.Java.LocalVariableAccess;
+import org.codehaus.janino.Java.MethodInvocation;
+import org.codehaus.janino.Java.NewAnonymousClassInstance;
+import org.codehaus.janino.Java.NewArray;
+import org.codehaus.janino.Java.NewClassInstance;
+import org.codehaus.janino.Java.NewInitializedArray;
+import org.codehaus.janino.Java.ParameterAccess;
+import org.codehaus.janino.Java.ParenthesizedExpression;
+import org.codehaus.janino.Java.QualifiedThisReference;
+import org.codehaus.janino.Java.SuperclassFieldAccessExpression;
+import org.codehaus.janino.Java.SuperclassMethodInvocation;
+import org.codehaus.janino.Java.ThisReference;
+import org.codehaus.janino.Java.UnaryOperation;
+
+public class UnparseTests extends TestCase {
+    public static Test suite() {
+        TestSuite s = new TestSuite(UnparseTests.class.getName());
+        s.addTest(new UnparseTests("testSimple"));
+        s.addTest(new UnparseTests("testParens"));
+        s.addTest(new UnparseTests("testMany"));
+        return s;
+    }
+
+    public UnparseTests(String name) { super(name); }
+    
+    private static void helpTestExpr(String input, String expect, boolean simplify) throws Exception {
+        Parser p = new Parser(new Scanner(
+                null, new ByteArrayInputStream(input.getBytes()
+        )));
+        Atom expr = p.parseExpression();
+        if(simplify) {
+            expr = stripUnnecessaryParenExprs(expr);
+        }
+        
+        StringWriter sw = new StringWriter();
+        UnparseVisitor uv = new UnparseVisitor(sw);
+        expr.accept(uv);
+        assertEquals(expect, sw.toString());    
+    }
+    
+    private static Java.Rvalue[] stripUnnecessaryParenExprs(Java.Rvalue[] rvalues) {
+        Java.Rvalue[] res = new Java.Rvalue[rvalues.length];
+        for(int i = 0; i < res.length; ++i) {
+            res[i] = stripUnnecessaryParenExprs(rvalues[i]);
+        }
+        return res;
+    }
+    
+    private static Java.Atom stripUnnecessaryParenExprs(Java.Atom atom) {
+        if(atom instanceof Java.Rvalue) {
+            return stripUnnecessaryParenExprs((Java.Rvalue)atom);
+        }
+        return atom;
+    }
+    
+    private static Java.Lvalue stripUnnecessaryParenExprs(Java.Lvalue lvalue) {
+        return (Java.Lvalue)stripUnnecessaryParenExprs((Java.Rvalue)lvalue);
+    }
+    
+    private static Java.Rvalue stripUnnecessaryParenExprs(Java.Rvalue rvalue) {
+        if(rvalue == null) { return null; }
+        final Java.Rvalue[] res = new Java.Rvalue[1];
+        Visitor.RvalueVisitor rv = new Visitor.RvalueVisitor() {
+            public void visitArrayLength(ArrayLength al) {
+                res[0] = new Java.ArrayLength(al.getLocation(),
+                        stripUnnecessaryParenExprs(al.lhs));
+            }
+
+            public void visitAssignment(Assignment a) {
+                res[0] = new Java.Assignment(a.getLocation(),
+                        stripUnnecessaryParenExprs(a.lhs),
+                        a.operator,
+                        stripUnnecessaryParenExprs(a.rhs)
+                );
+            }
+
+            public void visitBinaryOperation(BinaryOperation bo) {
+                res[0] = new Java.BinaryOperation(bo.getLocation(),
+                        stripUnnecessaryParenExprs(bo.lhs),
+                        bo.op,
+                        stripUnnecessaryParenExprs(bo.rhs)
+                );
+            }
+
+            public void visitCast(Cast c) {
+                res[0] = new Java.Cast(c.getLocation(),
+                        c.targetType,
+                        stripUnnecessaryParenExprs(c.value));
+            }
+
+            public void visitClassLiteral(ClassLiteral cl) {
+                res[0] = cl; //too much effort
+            }
+
+            public void visitConditionalExpression(ConditionalExpression ce) {
+                res[0] = new Java.ConditionalExpression(ce.getLocation(),
+                        stripUnnecessaryParenExprs(ce.lhs),
+                        stripUnnecessaryParenExprs(ce.mhs),
+                        stripUnnecessaryParenExprs(ce.rhs));
+            }
+
+            public void visitConstantValue(ConstantValue cv) {
+                res[0] = cv;
+            }
+
+            public void visitCrement(Crement c) {
+                if(c.pre) {
+                    res[0] = new Java.Crement(c.getLocation(),
+                            c.operator,
+                            stripUnnecessaryParenExprs(c.operand));
+                } else {
+                    res[0] = new Java.Crement(c.getLocation(),
+                            stripUnnecessaryParenExprs(c.operand),
+                            c.operator);
+                }
+            }
+
+            public void visitInstanceof(Instanceof io) {
+                res[0] = new Java.Instanceof(io.getLocation(),
+                        stripUnnecessaryParenExprs(io.lhs),
+                        io.rhs);
+            }
+
+            public void visitLiteral(Literal l) {
+                res[0] = l;
+            }
+
+            public void visitMethodInvocation(MethodInvocation mi) {
+                res[0] = new Java.MethodInvocation(mi.getLocation(),
+                        stripUnnecessaryParenExprs(mi.optionalTarget),
+                        mi.methodName,
+                        stripUnnecessaryParenExprs(mi.arguments));
+            }
+
+            public void visitNewAnonymousClassInstance(
+                    NewAnonymousClassInstance naci) {
+                res[0] = naci; //too much effort
+            }
+
+            public void visitNewArray(NewArray na) {
+                res[0] = new Java.NewArray(na.getLocation(),
+                        na.type,
+                        stripUnnecessaryParenExprs(na.dimExprs),
+                        na.dims);
+            }
+
+            public void visitNewClassInstance(NewClassInstance nci) {
+                res[0] = new Java.NewClassInstance(nci.getLocation(),
+                        stripUnnecessaryParenExprs(nci.optionalQualification),
+                        nci.type,
+                        stripUnnecessaryParenExprs(nci.arguments));
+            }
+
+            public void visitNewInitializedArray(NewInitializedArray nia) {
+                res[0] = nia; //too much effort
+            }
+
+            public void visitParameterAccess(ParameterAccess pa) {
+                res[0] = pa;
+            }
+
+            public void visitQualifiedThisReference(QualifiedThisReference qtr) {
+                res[0] = qtr;
+            }
+
+            public void visitSuperclassMethodInvocation(
+                    SuperclassMethodInvocation smi) {
+                res[0] = new Java.SuperclassMethodInvocation(smi.getLocation(),
+                        smi.methodName,
+                        stripUnnecessaryParenExprs(smi.arguments));
+            }
+
+            public void visitThisReference(ThisReference tr) {
+                res[0] = tr;
+            }
+
+            public void visitUnaryOperation(UnaryOperation uo) {
+                res[0] = new Java.UnaryOperation(uo.getLocation(),
+                        uo.operator,
+                        stripUnnecessaryParenExprs(uo.operand));
+            }
+
+            public void visitAmbiguousName(AmbiguousName an) {
+                res[0] = an;
+            }
+
+            public void visitArrayAccessExpression(ArrayAccessExpression aae) {
+                res[0] = new Java.ArrayAccessExpression(aae.getLocation(),
+                        stripUnnecessaryParenExprs(aae.lhs),
+                        stripUnnecessaryParenExprs(aae.index));
+            }
+
+            public void visitFieldAccess(FieldAccess fa) {
+                res[0] = new Java.FieldAccess(fa.getLocation(),
+                        stripUnnecessaryParenExprs(fa.lhs),
+                        fa.field);
+            }
+
+            public void visitFieldAccessExpression(FieldAccessExpression fae) {
+                res[0] = new Java.FieldAccessExpression(fae.getLocation(),
+                        stripUnnecessaryParenExprs(fae.lhs),
+                        fae.fieldName);
+            }
+
+            public void visitLocalVariableAccess(LocalVariableAccess lva) {
+                res[0] = lva;
+            }
+
+            public void visitParenthesizedExpression(ParenthesizedExpression pe) {
+                res[0] = stripUnnecessaryParenExprs(pe.value);
+            }
+
+            public void visitSuperclassFieldAccessExpression(
+                    SuperclassFieldAccessExpression scfae) {
+                res[0] = scfae;
+            }
+            
+        };
+        rvalue.accept(rv);
+        return res[0];
+    }
+    
+    public void testSimple() throws Exception {
+        helpTestExpr("1 + 2*3", "1 + 2 * 3", false);
+        helpTestExpr("1 + 2*3", "1 + 2 * 3", true);
+    }
+    
+    public void testParens() throws Exception {
+        helpTestExpr("(1 + 2)*3", "(1 + 2) * 3", false);
+        helpTestExpr("(1 + 2)*3", "(1 + 2) * 3", true);
+    }
+    
+    public void testMany() throws Exception {
+        final String[][] exprs = new String[][] {
+              //input                                  expected simplified                    expect non-simplified
+            { "((1)+2)",                               "1 + 2",                               "((1) + 2)"           },
+            { "1 + 2 * 3",                             null,                                  null                  },
+            { "1 + (2 * 3)",                           "1 + 2 * 3",                           null                  },
+            { "3 - (2 - 1)",                           null,                                  null                  },
+            { "true ? 1 : 2",                          null,                                  null                  },
+            { "(true ? false : true) ? 1 : 2",         null,                                  null                  },
+            { "true ? false : (true ? false : true)",  "true ? false : true ? false : true",  null                  },
+            { "-(-(2))",                               "-(-2)",                               null                  },
+            { "- - 2",                                 "-(-2)",                               "-(-2)"               },
+            { "x && (y || z)",                         null,                                  null                  },
+            { "(x && y) || z",                         "x && y || z",                         null                  },
+            { "x = (y = z)",                           "x = y = z",                           null                  },
+            { "(--x) + 3",                             "--x + 3",                             null                  },
+            { "(baz.bar).foo(x, (3 + 4) * 5)",         "baz.bar.foo(x, (3 + 4) * 5)",         null                  },
+            { "!(bar instanceof Integer)",             null,                                  null                  },
+            { "(true ? foo : bar).baz()",              null,                                  null                  },
+        };
+        
+        for(int i = 0; i < exprs.length; ++i) {
+            String input = exprs[i][0];
+            String expectSimplify = exprs[i][1];
+            if(expectSimplify == null) {
+                expectSimplify = input;
+            }
+            
+            String expectNoSimplify = exprs[i][2];
+            if(expectNoSimplify == null) { 
+                expectNoSimplify = input; 
+            }
+            
+            helpTestExpr(input, expectSimplify, true);
+            helpTestExpr(input, expectNoSimplify, false);
+        }
+    }
+
+}
=== tests/src/AllTests.java
==================================================================
--- tests/src/AllTests.java	(revision 338)
+++ tests/src/AllTests.java	(patch unparse level 1)
@@ -82,5 +82,6 @@
         this.addTest(ReportedBugs.suite());
         this.addTest(SandboxTests.suite());
         this.addTest(EvaluatorTests.suite());
+        this.addTest(UnparseTests.suite());
     }
 }
=== src/org/codehaus/janino/UnparseVisitor.java
==================================================================
--- src/org/codehaus/janino/UnparseVisitor.java	(revision 338)
+++ src/org/codehaus/janino/UnparseVisitor.java	(patch unparse level 1)
@@ -47,6 +47,101 @@
 public class UnparseVisitor implements Visitor.ComprehensiveVisitor {
     private final AutoIndentWriter aiw;
     private final PrintWriter      pw;
+    
+    private final Stack curPrecedence;
+    
+    /**
+     * Install op as the next op's precedence level
+     * @param op a string that it is the precedenceMap
+     */
+    private void pushPrecedence(String op) {
+        Object o = precedenceMap.get(op);
+        if(o == null) {
+            throw new RuntimeException("Illegal operator for precedence: " + op);
+        }
+        curPrecedence.push(o);
+    }
+    
+    /**
+     * Remove the current precedence setting
+     */
+    private void popPrecedence() {
+        curPrecedence.pop();
+    }
+    
+    /**
+     * Give the precedence value a slight nudge to account for associativity of operators
+     */
+    private void adjustPrecedenceForAssociativity(boolean higher) {
+        double cur = ((Double)curPrecedence.pop()).doubleValue();
+        cur += higher ? 0.5 : -0.5;
+        curPrecedence.push(Double.valueOf(cur));
+    }
+    
+    private boolean needsParens(String op) {
+        //return false;
+        if(curPrecedence.isEmpty()) { return false; }
+        double cur = ((Double)curPrecedence.peek()).doubleValue();
+        double nxt = ((Double)precedenceMap.get(op)).doubleValue();
+        return cur > nxt;
+    }
+    
+    // this provides a mapping from operators to precedence levels
+    // - higher numbers are more tightly binding
+    // - ambiguous cases are just given special tokens
+    // - "reset" is a special token for bracketing operations
+    private static final Map precedenceMap = new HashMap(); // Map<String, Double>
+    static {
+        precedenceMap.put("[]",         Double.valueOf(15));
+        precedenceMap.put(".",          Double.valueOf(15));
+        precedenceMap.put("()",         Double.valueOf(15));
+        precedenceMap.put("methodcall", Double.valueOf(15));
+        // ++ and -- are ambiguous as they are both post and prefix
+        // so instead we are just putting the names 
+        precedenceMap.put("postfix",    Double.valueOf(15));
+        precedenceMap.put("prefix",     Double.valueOf(14));
+        //unary - is ambiguous just use prefix
+        //precedenceMap.put("-",          Double.valueOf(14));
+        precedenceMap.put("~",          Double.valueOf(14));
+        precedenceMap.put("!",          Double.valueOf(14));
+        precedenceMap.put("cast",       Double.valueOf(13));
+        precedenceMap.put("new",        Double.valueOf(13));
+        precedenceMap.put("*",          Double.valueOf(12));
+        precedenceMap.put("/",          Double.valueOf(12));
+        precedenceMap.put("%",          Double.valueOf(12));
+        precedenceMap.put("+",          Double.valueOf(11));
+        precedenceMap.put("-",          Double.valueOf(11));
+        precedenceMap.put("<<",         Double.valueOf(10));
+        precedenceMap.put(">>",         Double.valueOf(10));
+        precedenceMap.put(">>>",        Double.valueOf(10));
+        precedenceMap.put(">=",         Double.valueOf(9));
+        precedenceMap.put("<=",         Double.valueOf(9));
+        precedenceMap.put("<",          Double.valueOf(9));
+        precedenceMap.put(">",          Double.valueOf(9));
+        precedenceMap.put("instanceof", Double.valueOf(9));
+        precedenceMap.put("==",         Double.valueOf(8));
+        precedenceMap.put("!=",         Double.valueOf(8));
+        precedenceMap.put("&",          Double.valueOf(7));
+        precedenceMap.put("^",          Double.valueOf(6));
+        precedenceMap.put("|",          Double.valueOf(5));
+        precedenceMap.put("&&",         Double.valueOf(4));
+        precedenceMap.put("||",         Double.valueOf(4));
+        precedenceMap.put("?:",         Double.valueOf(3));
+        precedenceMap.put("=",          Double.valueOf(2));
+        precedenceMap.put("+=",         Double.valueOf(2));
+        precedenceMap.put("-=",         Double.valueOf(2));
+        precedenceMap.put("*=",         Double.valueOf(2));
+        precedenceMap.put("/=",         Double.valueOf(2));
+        precedenceMap.put("%=",         Double.valueOf(2));
+        precedenceMap.put("&=",         Double.valueOf(2));
+        precedenceMap.put("^=",         Double.valueOf(2));
+        precedenceMap.put("|=",         Double.valueOf(2));
+        precedenceMap.put("<<=",        Double.valueOf(2));
+        precedenceMap.put(">>=",        Double.valueOf(2));
+        precedenceMap.put(">>>=",       Double.valueOf(2));
+        precedenceMap.put(">>>=",       Double.valueOf(2));
+        precedenceMap.put("reset",      Double.valueOf(0));
+    }
 
     /**
      * Testing of parsing/unparsing.
@@ -84,6 +179,7 @@
     public UnparseVisitor(Writer w) {
         this.aiw = new AutoIndentWriter(w);
         this.pw = new PrintWriter(this.aiw, true);
+        this.curPrecedence = new Stack();
     }
 
     public void unparseCompilationUnit(Java.CompilationUnit cu) {
@@ -204,8 +300,10 @@
         this.pw.print(';');
     }
     public void visitExpressionStatement(Java.ExpressionStatement es) {
+        pushPrecedence("reset"); //this might be an expression in a nested class body or something
         ((Java.Atom) es.rvalue).accept(this);
         this.pw.print(';');
+        popPrecedence();
     }
     public void visitForStatement(Java.ForStatement fs) {
         this.pw.print("for (");
@@ -334,12 +432,16 @@
         this.pw.print(' ' + fp.name);
     }
     public void visitMethodInvocation(Java.MethodInvocation mi) {
+        if(needsParens("methodcall")) { this.pw.print('('); }
         if (mi.optionalTarget != null) {
+            pushPrecedence(".");
             mi.optionalTarget.accept(this);
             this.pw.print('.');
+            popPrecedence();
         }
         this.pw.print(mi.methodName);
         this.unparseFunctionInvocationArguments(mi.arguments);
+        if(needsParens("methodcall")) { this.pw.print(')'); }
     }
     public void visitAlternateConstructorInvocation(Java.AlternateConstructorInvocation aci) {
         this.pw.print("this");
@@ -354,28 +456,47 @@
         this.unparseFunctionInvocationArguments(sci.arguments);
     }
     public void visitNewClassInstance(Java.NewClassInstance nci) {
+        if(needsParens("new")) { this.pw.print('('); }
         if (nci.optionalQualification != null) {
+            pushPrecedence(".");
             ((Java.Atom) nci.optionalQualification).accept(this);
             this.pw.print('.');
+            popPrecedence();
         }
         this.pw.print("new " + nci.type.toString());
         this.unparseFunctionInvocationArguments(nci.arguments);
+        if(needsParens("new")) { this.pw.print(')'); }
     }
     public void visitAssignment(Java.Assignment a) {
+        if(needsParens(a.operator)) { this.pw.print('('); }
+        pushPrecedence(a.operator);
         ((Java.Atom) a.lhs).accept(this);
         this.pw.print(' ' + a.operator + ' ');
         ((Java.Atom) a.rhs).accept(this);
+        popPrecedence();
+        if(needsParens(a.operator)) { this.pw.print(')'); }
     }
     public void visitAmbiguousName(Java.AmbiguousName an) { this.pw.print(an.toString()); }
     public void visitArrayAccessExpression(Java.ArrayAccessExpression aae) {
+        if(needsParens("[]")) { this.pw.print('('); }
+        pushPrecedence("[]");
         ((Java.Atom) aae.lhs).accept(this);
+        popPrecedence();
+        
         this.pw.print('[');
+        pushPrecedence("reset");
         ((Java.Atom) aae.index).accept(this);
+        popPrecedence();
         this.pw.print(']');
+        if(needsParens("[]")) { this.pw.print(')'); }
     }
     public void visitArrayLength(Java.ArrayLength al) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         ((Java.Atom) al.lhs).accept(this);
         this.pw.print(".length");
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitArrayType(Java.ArrayType at) {
         ((Java.Atom) at.componentType).accept(this);
@@ -385,44 +506,80 @@
         this.pw.print(bt.toString());
     }
     public void visitBinaryOperation(Java.BinaryOperation bo) {
+        if(needsParens(bo.op)) { this.pw.print('('); }
+        pushPrecedence(bo.op);
         ((Java.Atom) bo.lhs).accept(this);
         this.pw.print(' ' + bo.op + ' ');
+        adjustPrecedenceForAssociativity(true); //binary ops are all left -> right associative
         ((Java.Atom) bo.rhs).accept(this);
+        popPrecedence();
+        if(needsParens(bo.op)) { this.pw.print(')'); }
     }
     public void visitCast(Java.Cast c) {
+        if(needsParens("cast")) { this.pw.print('('); }
+        pushPrecedence("cast");
         this.pw.print('(');
         ((Java.Atom) c.targetType).accept(this);
         this.pw.print(") ");
         ((Java.Atom) c.value).accept(this);
+        popPrecedence();
+        if(needsParens("cast")) { this.pw.print(')'); }
     }
     public void visitClassLiteral(Java.ClassLiteral cl) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         ((Java.Atom) cl.type).accept(this);
         this.pw.print(".class");
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitConditionalExpression(Java.ConditionalExpression ce) {
+        if(needsParens("?:")) { this.pw.print('('); }
+        pushPrecedence("?:");
+        adjustPrecedenceForAssociativity(true); //ternary is right -> left associative
         ((Java.Atom) ce.lhs).accept(this);
+        adjustPrecedenceForAssociativity(false); //back to normal
         this.pw.print(" ? ");
         ((Java.Atom) ce.mhs).accept(this);
         this.pw.print(" : ");
         ((Java.Atom) ce.rhs).accept(this);
+        popPrecedence();
+        if(needsParens("?:")) { this.pw.print(')'); }
     }
     public void visitConstantValue(Java.ConstantValue cv) { this.pw.print(cv.toString()); }
     public void visitCrement(Java.Crement c) {
-        this.pw.print(
-            c.pre ?
-            c.operator + c.operand :
-            c.operand + c.operator
-        );
+        String prec = c.pre ? "prefix" : "postfix";
+        if(needsParens(prec)) { this.pw.print('('); }
+        pushPrecedence(prec);
+        if(c.pre) {
+            this.pw.print(c.operator);
+            this.pw.print(c.operand);
+        } else {
+            this.pw.print(c.operand);
+            this.pw.print(c.operator);
+        }
+        popPrecedence();
+        if(needsParens(prec)) { this.pw.print(')'); }
     }
     public void visitFieldAccess(Java.FieldAccess fa) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         fa.lhs.accept(this);
         this.pw.print('.' + fa.field.getName());
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitFieldAccessExpression(Java.FieldAccessExpression fae) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         fae.lhs.accept(this);
         this.pw.print('.' + fae.fieldName);
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         if (scfae.optionalQualification != null) {
             scfae.optionalQualification.accept((Visitor.TypeVisitor) this);
             this.pw.print(".super." + scfae.fieldName);
@@ -430,37 +587,57 @@
         {
             this.pw.print("super." + scfae.fieldName);
         }
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitInstanceof(Java.Instanceof io) {
+        if(needsParens("instanceof")) { this.pw.print('('); }
+        pushPrecedence("instanceof");
         ((Java.Atom) io.lhs).accept(this);
         this.pw.print(" instanceof ");
         ((Java.Atom) io.rhs).accept(this);
+        popPrecedence();
+        if(needsParens("instanceof")) { this.pw.print(')'); }
     }
     public void visitLiteral(Java.Literal l) { this.pw.print(l.toString()); }
     public void visitLocalVariableAccess(Java.LocalVariableAccess lva) { this.pw.print(lva.toString()); }
     public void visitNewArray(Java.NewArray na) {
+        if(needsParens("new")) { this.pw.print('('); }
+        pushPrecedence("new");
         this.pw.print("new ");
         ((Java.Atom) na.type).accept(this);
         for (int i = 0; i < na.dimExprs.length; ++i) {
             this.pw.print('[');
+            pushPrecedence("reset");
             ((Java.Atom) na.dimExprs[i]).accept(this);
+            popPrecedence();
             this.pw.print(']');
         }
         for (int i = 0; i < na.dims; ++i) {
             this.pw.print("[]");
         }
+        popPrecedence();
+        if(needsParens("new")) { this.pw.print(')'); }
     }
     public void visitNewInitializedArray(Java.NewInitializedArray nai) {
+        if(needsParens("new")) { this.pw.print('('); }
+        pushPrecedence("new");
         this.pw.print("new ");
         nai.arrayType.accept(this);
         this.pw.print(" ");
         this.unparseArrayInitializerOrRvalue(nai.arrayInitializer);
+        popPrecedence();
+        if(needsParens("new")) { this.pw.print(')'); }
     }
     public void visitPackage(Java.Package p) { this.pw.print(p.toString()); }
     public void visitParameterAccess(Java.ParameterAccess pa) { this.pw.print(pa.toString()); }
     public void visitQualifiedThisReference(Java.QualifiedThisReference qtr) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         ((Java.Atom) qtr.qualification).accept(this);
         this.pw.print(".this");
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitReferenceType(Java.ReferenceType rt) { this.pw.print(rt.toString()); }
     public void visitRvalueMemberType(Java.RvalueMemberType rmt) { this.pw.print(rmt.toString()); }
@@ -473,12 +650,19 @@
         this.pw.print("this");
     }
     public void visitUnaryOperation(Java.UnaryOperation uo) {
+        if(needsParens("prefix")) { this.pw.print('('); }
+        pushPrecedence("prefix");
         this.pw.print(uo.operator);
+        this.adjustPrecedenceForAssociativity(true); //handle cases like "- - 3" as -(-3)
         ((Java.Atom) uo.operand).accept(this);
+        popPrecedence();
+        if(needsParens("prefix")) { this.pw.print(')'); }
     }
     public void visitParenthesizedExpression(Java.ParenthesizedExpression pe) {
         this.pw.print('(');
+        pushPrecedence("reset");
         ((Java.Atom) pe.value).accept(this);
+        popPrecedence();
         this.pw.print(')');
     }
 
@@ -508,11 +692,13 @@
             } else
             {
                 this.pw.print("{ ");
+                pushPrecedence("reset");
                 this.unparseArrayInitializerOrRvalue(ai.values[0]);
                 for (int i = 1; i < ai.values.length; ++i) {
                     this.pw.print(", ");
                     this.unparseArrayInitializerOrRvalue(ai.values[i]);
                 }
+                popPrecedence();
                 this.pw.print(" }");
             }
         } else
@@ -524,22 +710,32 @@
     public void visitAnonymousClassDeclaration(Java.AnonymousClassDeclaration acd) {
         ((Java.Atom) acd.baseType).accept(this);
         this.pw.println(" {");
+        pushPrecedence("reset");
         this.unparseClassDeclarationBody(acd);
+        popPrecedence();
         this.pw.print('}');
     }
     public void visitNewAnonymousClassInstance(Java.NewAnonymousClassInstance naci) {
+        if(needsParens("new")) { this.pw.print('('); }
+        pushPrecedence("new");
         if (naci.optionalQualification != null) {
+            pushPrecedence(".");
             ((Java.Atom) naci.optionalQualification).accept(this);
+            popPrecedence();
             this.pw.print('.');
         }
         this.pw.print("new " + naci.anonymousClassDeclaration.baseType.toString() + '(');
+        pushPrecedence("reset");
         for (int i = 0; i < naci.arguments.length; ++i) {
             if (i > 0) this.pw.print(", ");
             ((Java.Atom) naci.arguments[i]).accept(this);
         }
+        popPrecedence(); //pop the reset
         this.pw.println(") {");
         this.unparseClassDeclarationBody(naci.anonymousClassDeclaration);
         this.pw.print('}');
+        popPrecedence();
+        if(needsParens("new")) { this.pw.print(')'); }
     }
     // Multi-line!
     private void unparseClassDeclarationBody(Java.ClassDeclaration cd) {
@@ -607,10 +803,12 @@
     }
     private void unparseFunctionInvocationArguments(Java.Rvalue[] arguments) {
         this.pw.print('(');
+        pushPrecedence("reset");
         for (int i = 0; i < arguments.length; ++i) {
             if (i > 0) this.pw.print(", ");
             ((Java.Atom) arguments[i]).accept(this);
         }
+        popPrecedence();
         this.pw.print(')');
     }
 }

==== BEGIN SVK PATCH BLOCK ====
Version: svk v2.0.2 (linux)

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
4mB+YbGpML/cbCrPZ8otpiKyd7iotPz/ASRlkNk=
==== END SVK PATCH BLOCK ====

