Damien Martin-Guillerez | f88f4d8 | 2015-09-25 13:56:55 +0000 | [diff] [blame] | 1 | // Copyright 2014 The Bazel Authors. All rights reserved. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | package com.google.devtools.build.lib.syntax; |
| 16 | |
| 17 | import com.google.common.annotations.VisibleForTesting; |
| 18 | import com.google.devtools.build.lib.events.Location; |
brandjon | e2ffd5d | 2017-06-27 18:14:54 +0200 | [diff] [blame] | 19 | import java.io.IOException; |
brandjon | e2ffd5d | 2017-06-27 18:14:54 +0200 | [diff] [blame] | 20 | import java.util.List; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 21 | |
Googler | 4ace465 | 2019-09-16 07:47:08 -0700 | [diff] [blame] | 22 | /** An Node is a node in a Starlark syntax tree. */ |
| 23 | public abstract class Node { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 24 | |
| 25 | private Location location; |
| 26 | |
Googler | 4ace465 | 2019-09-16 07:47:08 -0700 | [diff] [blame] | 27 | protected Node() {} |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 28 | |
Florian Weikert | 90a1596 | 2015-09-11 13:43:10 +0000 | [diff] [blame] | 29 | /** |
| 30 | * Returns whether this node represents a new scope, e.g. a function call. |
| 31 | */ |
| 32 | protected boolean isNewScope() { |
| 33 | return false; |
| 34 | } |
| 35 | |
Janak Ramakrishnan | 3745c90 | 2016-05-11 08:12:10 +0000 | [diff] [blame] | 36 | /** Returns an exception which should be thrown instead of the original one. */ |
| 37 | protected final EvalException maybeTransformException(EvalException original) { |
Florian Weikert | 90a1596 | 2015-09-11 13:43:10 +0000 | [diff] [blame] | 38 | // If there is already a non-empty stack trace, we only add this node iff it describes a |
| 39 | // new scope (e.g. FuncallExpression). |
Florian Weikert | 4b67d4f | 2015-09-14 13:35:34 +0000 | [diff] [blame] | 40 | if (original instanceof EvalExceptionWithStackTrace) { |
Florian Weikert | 90a1596 | 2015-09-11 13:43:10 +0000 | [diff] [blame] | 41 | EvalExceptionWithStackTrace real = (EvalExceptionWithStackTrace) original; |
Florian Weikert | 4b67d4f | 2015-09-14 13:35:34 +0000 | [diff] [blame] | 42 | if (isNewScope()) { |
| 43 | real.registerNode(this); |
| 44 | } |
Florian Weikert | 90a1596 | 2015-09-11 13:43:10 +0000 | [diff] [blame] | 45 | return real; |
| 46 | } |
| 47 | |
Janak Ramakrishnan | 3745c90 | 2016-05-11 08:12:10 +0000 | [diff] [blame] | 48 | if (original.canBeAddedToStackTrace()) { |
| 49 | return new EvalExceptionWithStackTrace(original, this); |
| 50 | } else { |
| 51 | return original; |
Florian Weikert | 90a1596 | 2015-09-11 13:43:10 +0000 | [diff] [blame] | 52 | } |
Florian Weikert | 90a1596 | 2015-09-11 13:43:10 +0000 | [diff] [blame] | 53 | } |
| 54 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 55 | @VisibleForTesting // productionVisibility = Visibility.PACKAGE_PRIVATE |
| 56 | public void setLocation(Location location) { |
| 57 | this.location = location; |
| 58 | } |
| 59 | |
Francois-Rene Rideau | edf7bdb | 2015-03-02 17:12:45 +0000 | [diff] [blame] | 60 | /** @return the same node with its location set, in a slightly more fluent style */ |
Googler | 4ace465 | 2019-09-16 07:47:08 -0700 | [diff] [blame] | 61 | public static <NodeT extends Node> NodeT setLocation(Location location, NodeT node) { |
Francois-Rene Rideau | edf7bdb | 2015-03-02 17:12:45 +0000 | [diff] [blame] | 62 | node.setLocation(location); |
| 63 | return node; |
| 64 | } |
| 65 | |
Googler | 99cbc17 | 2018-11-08 07:43:00 -0800 | [diff] [blame] | 66 | public Location getLocation() { |
| 67 | return location; |
| 68 | } |
| 69 | |
brandjon | e2ffd5d | 2017-06-27 18:14:54 +0200 | [diff] [blame] | 70 | /** Number of spaces that each indentation level expands to when pretty-printing. */ |
| 71 | public static final int INDENT_WIDTH = 2; |
| 72 | |
| 73 | /** Writes out the indentation prefix for a line. */ |
| 74 | protected void printIndent(Appendable buffer, int indentLevel) throws IOException { |
| 75 | for (int i = 0; i < indentLevel * INDENT_WIDTH; i++) { |
| 76 | buffer.append(' '); |
| 77 | } |
| 78 | } |
| 79 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 80 | /** |
brandjon | e2ffd5d | 2017-06-27 18:14:54 +0200 | [diff] [blame] | 81 | * Writes out a suite of statements. The statements are indented one more level than given, i.e., |
| 82 | * the {@code indentLevel} parameter should be the same as the parent node's. |
| 83 | * |
| 84 | * <p>This also prints out a {@code pass} line if the suite is empty. |
| 85 | */ |
| 86 | protected void printSuite(Appendable buffer, List<Statement> statements, int parentIndentLevel) |
| 87 | throws IOException { |
| 88 | if (statements.isEmpty()) { |
| 89 | printIndent(buffer, parentIndentLevel + 1); |
| 90 | buffer.append("pass\n"); |
| 91 | } else { |
| 92 | for (Statement stmt : statements) { |
| 93 | stmt.prettyPrint(buffer, parentIndentLevel + 1); |
| 94 | } |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | /** |
| 99 | * Writes a pretty-printed representation of this node to a buffer, assuming the given starting |
| 100 | * indentation level. |
| 101 | * |
| 102 | * <p>For expressions, the indentation level is ignored. For statements, the indentation is |
| 103 | * written, then the statement contents (which may include multiple lines with their own |
| 104 | * indentation), then a newline character. |
| 105 | * |
| 106 | * <p>Indentation expands to {@code INDENT_WIDTH} many spaces per indent. |
| 107 | * |
| 108 | * <p>Pretty printing returns the canonical source code corresponding to an AST. Generally, the |
| 109 | * output can be round-tripped: Pretty printing an AST and then parsing the result should give you |
| 110 | * back an equivalent AST. |
| 111 | * |
| 112 | * <p>Pretty printing can also be used as a proxy for comparing for equality between two ASTs. |
| 113 | * This can be very useful in tests. However, it is still possible for two different trees to have |
| 114 | * the same pretty printing. In particular, {@link BuildFileAST} includes import metadata and |
| 115 | * comment information that is not reflected in the string. |
| 116 | */ |
| 117 | public abstract void prettyPrint(Appendable buffer, int indentLevel) throws IOException; |
| 118 | |
| 119 | /** Same as {@link #prettyPrint(Appendable, int)}, except with no indent. */ |
| 120 | public void prettyPrint(Appendable buffer) throws IOException { |
| 121 | prettyPrint(buffer, 0); |
| 122 | } |
| 123 | |
| 124 | /** Returns a pretty-printed representation of this node. */ |
| 125 | public String prettyPrint() { |
| 126 | StringBuilder builder = new StringBuilder(); |
| 127 | try { |
| 128 | prettyPrint(builder); |
| 129 | } catch (IOException e) { |
| 130 | // Not possible for StringBuilder. |
| 131 | throw new AssertionError(e); |
| 132 | } |
| 133 | return builder.toString(); |
| 134 | } |
| 135 | |
| 136 | /** |
| 137 | * Print the syntax node in a form useful for debugging. |
| 138 | * |
| 139 | * <p>The output is not precisely specified; use {@link #prettyPrint()} if you need more stable |
| 140 | * and complete information. For instance, this function may omit child statements of compound |
| 141 | * statements, or parentheses around some expressions. It may also abbreviate large list literals. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 142 | */ |
| 143 | @Override |
brandjon | e2ffd5d | 2017-06-27 18:14:54 +0200 | [diff] [blame] | 144 | public String toString() { |
| 145 | return prettyPrint(); |
| 146 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 147 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 148 | /** |
Googler | 4ace465 | 2019-09-16 07:47:08 -0700 | [diff] [blame] | 149 | * Implements the double dispatch by calling into the node specific <code>visit</code> method of |
| 150 | * the {@link NodeVisitor} |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 151 | * |
Googler | 4ace465 | 2019-09-16 07:47:08 -0700 | [diff] [blame] | 152 | * @param visitor the {@link NodeVisitor} instance to dispatch to. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 153 | */ |
Googler | 4ace465 | 2019-09-16 07:47:08 -0700 | [diff] [blame] | 154 | public abstract void accept(NodeVisitor visitor); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 155 | } |