// Copyright 2014 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package net.starlark.java.syntax;

import com.google.common.base.Preconditions;

/**
 * A Node is a node in a Starlark syntax tree.
 *
 * <p>Nodes may be constructed only by the parser.
 *
 * <p>The syntax tree records the offsets within the file of all salient tokens, such as brackets
 * that mark the beginning or end of an expression, or operators whose position may be needed in a
 * run-time error message. The start and end offsets of each Node are computed inductively from
 * their tokens and subexpressions. Offsets are converted to Locations on demand in methods such as
 * {@link #getStartLocation}.
 */
public abstract class Node {

  // Use these typical node distributions in Bazel files
  // as a rough guide for optimization decisions.
  // BUILD files are much more numerous than .bzl files,
  // and typically larger.
  //
  // Large BUILD file:
  //   49  % StringLiteral
  //   17  % Identifier
  //   12  % Argument.Keyword
  //    9  % ListExpression
  //    4  % CallExpression
  //    3.5% ExpressionStatement
  //    3.1% Comment
  //    1.2% Argument.Positional
  //    1.8% all others
  //
  // Large .bzl logic file:
  //   42 % Identifier
  //   12 % DotExpression
  //   7.1% StringLiteral
  //   6.7% Argument.Keyword
  //   6.7% CallExpression
  //   4.6% Argument.Positional
  //   3.1% Comment
  //   2.4% ListExpression
  //   2.4% ExpressionStatement
  //   2.2% AssignmentStatement
  //   1.9% DictExpression.Entry
  //   1.9% BinaryOperatorExpression
  //   1.0% Comprehension
  //   6  % all others

  // The FileLocations table holds the file name and a compressed
  // mapping from token char offsets to Locations.
  // It is shared by all nodes from the same file.
  final FileLocations locs;

  Node(FileLocations locs) {
    this.locs = Preconditions.checkNotNull(locs);
  }

  /**
   * Returns the node's start offset, as a char index (zero-based count of UTF-16 codes) from the
   * start of the file.
   */
  public abstract int getStartOffset();

  /** Returns the location of the start of this syntax node. */
  public final Location getStartLocation() {
    return locs.getLocation(getStartOffset());
  }

  /** Returns the char offset of the source position immediately after this syntax node. */
  public abstract int getEndOffset();

  /** Returns the location of the source position immediately after this syntax node. */
  public final Location getEndLocation() {
    return locs.getLocation(getEndOffset());
  }

  /**
   * Returns a pretty-printed representation of this syntax tree.
   *
   * <p>This function returns the canonical source code corresponding to a syntax tree. Generally,
   * the output can be round-tripped: pretty-printing a syntax tree then parsing the result should
   * yield an equivalent syntax tree.
   *
   * <p>The pretty-printed form of a syntax tree may be used as a proxy for equality in tests.
   * However, different trees may have the same printed form. In particular, {@link StarlarkFile}
   * includes comments that are not reflected in the string.
   */
  public final String prettyPrint() {
    StringBuilder buf = new StringBuilder();
    new NodePrinter(buf).printNode(this);
    return buf.toString();
  }

  /**
   * Print the syntax node in a form useful for debugging.
   *
   * <p>The output is not precisely specified; use {@link #prettyPrint()} if you need more stable
   * and complete information. For instance, this function may omit child statements of compound
   * statements, or parentheses around some expressions. It may also abbreviate large list literals.
   */
  @Override
  public String toString() {
    return prettyPrint(); // default behavior, overridden in several subclasses
  }

  /**
   * Implements the double dispatch by calling into the node specific <code>visit</code> method of
   * the {@link NodeVisitor}
   *
   * @param visitor the {@link NodeVisitor} instance to dispatch to.
   */
  // TODO(bazel-team): Should this be package-private? Seems like the only legitimate call site is
  // NodeVisitor#visit, and all other clients (including tests) should prefer NodeVisitor#visit.
  public abstract void accept(NodeVisitor visitor);
}
