Add a pretty printer for Skylark ASTs

This can be used to canonically compare ASTs for equality, e.g. in tests.

RELNOTES: None
PiperOrigin-RevId: 160283160
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Parameter.java b/src/main/java/com/google/devtools/build/lib/syntax/Parameter.java
index 1b74dcd..5e16ea3 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Parameter.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Parameter.java
@@ -13,6 +13,7 @@
 // limitations under the License.
 package com.google.devtools.build.lib.syntax;
 
+import java.io.IOException;
 import javax.annotation.Nullable;
 
 /**
@@ -44,27 +45,37 @@
   public boolean isMandatory() {
     return false;
   }
+
   public boolean isOptional() {
     return false;
   }
+
   @Override
   public boolean isStar() {
     return false;
   }
+
   @Override
   public boolean isStarStar() {
     return false;
   }
-  @Nullable public String getName() {
+
+  @Nullable
+  public String getName() {
     return name;
   }
+
   public boolean hasName() {
     return true;
   }
-  @Nullable public T getType() {
+
+  @Nullable
+  public T getType() {
     return type;
   }
-  @Nullable public V getDefaultValue() {
+
+  @Nullable
+  public V getDefaultValue() {
     return null;
   }
 
@@ -79,18 +90,20 @@
       super(name, type);
     }
 
-    @Override public boolean isMandatory() {
+    @Override
+    public boolean isMandatory() {
       return true;
     }
 
     @Override
-    public String toString() {
-      return name;
+    public void prettyPrint(Appendable buffer) throws IOException {
+      buffer.append(name);
     }
   }
 
   /** optional parameter (positional or key-only depending on position): Ident = Value */
   public static final class Optional<V, T> extends Parameter<V, T> {
+
     public final V defaultValue;
 
     public Optional(String name, @Nullable V defaultValue) {
@@ -103,15 +116,29 @@
       this.defaultValue = defaultValue;
     }
 
-    @Override @Nullable public V getDefaultValue() {
+    @Override
+    @Nullable
+    public V getDefaultValue() {
       return defaultValue;
     }
 
-    @Override public boolean isOptional() {
+    @Override
+    public boolean isOptional() {
       return true;
     }
 
     @Override
+    public void prettyPrint(Appendable buffer) throws IOException {
+      buffer.append(name);
+      buffer.append('=');
+      // This should only ever be used on a parameter representing static information, i.e. with V
+      // and T instantiated as Expression.
+      ((Expression) defaultValue).prettyPrint(buffer);
+    }
+
+    // Keep this as a separate method so that it can be used regardless of what V and T are
+    // parameterized with.
+    @Override
     public String toString() {
       return name + "=" + defaultValue;
     }
@@ -119,6 +146,7 @@
 
   /** extra positionals parameter (star): *identifier */
   public static final class Star<V, T> extends Parameter<V, T> {
+
     public Star(@Nullable String name, @Nullable T type) {
       super(name, type);
     }
@@ -132,21 +160,23 @@
       return name != null;
     }
 
-    @Override public boolean isStar() {
+    @Override
+    public boolean isStar() {
       return true;
     }
 
-    @Override public String toString() {
-      if (name == null) {
-        return "*";
-      } else {
-        return "*" + name;
+    @Override
+    public void prettyPrint(Appendable buffer) throws IOException {
+      buffer.append('*');
+      if (name != null) {
+        buffer.append(name);
       }
     }
   }
 
   /** extra keywords parameter (star_star): **identifier */
   public static final class StarStar<V, T> extends Parameter<V, T> {
+
     public StarStar(String name, @Nullable T type) {
       super(name, type);
     }
@@ -155,13 +185,15 @@
       super(name);
     }
 
-    @Override public boolean isStarStar() {
+    @Override
+    public boolean isStarStar() {
       return true;
     }
 
     @Override
-    public String toString() {
-      return "**" + name;
+    public void prettyPrint(Appendable buffer) throws IOException {
+      buffer.append("**");
+      buffer.append(name);
     }
   }