| // 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 com.google.devtools.build.lib.syntax; | |
| import com.google.common.collect.ImmutableList; | |
| import com.google.common.hash.HashCode; | |
| import com.google.devtools.build.lib.events.Event; | |
| import java.io.IOException; | |
| import java.util.Collections; | |
| import java.util.List; | |
| import javax.annotation.Nullable; | |
| /** | |
| * Syntax tree for a Starlark file, such as a Bazel BUILD or .bzl file. | |
| * | |
| * <p>Call {@link #parse} to parse a file. Parser errors are recorded in the syntax tree (see {@link | |
| * #errors}), which may be incomplete. | |
| */ | |
| public final class StarlarkFile extends Node { | |
| private final ImmutableList<Statement> statements; | |
| private final ImmutableList<Comment> comments; | |
| final List<Event> errors; // appended to by ValidationEnvironment | |
| private final List<Event> stringEscapeEvents; | |
| @Nullable private final String contentHashCode; | |
| private StarlarkFile( | |
| ImmutableList<Statement> statements, | |
| List<Event> errors, | |
| String contentHashCode, | |
| Lexer.LexerLocation location, | |
| ImmutableList<Comment> comments, | |
| List<Event> stringEscapeEvents) { | |
| this.statements = statements; | |
| this.comments = comments; | |
| this.errors = errors; | |
| this.stringEscapeEvents = stringEscapeEvents; | |
| this.contentHashCode = contentHashCode; | |
| this.setLocation(location); | |
| } | |
| private static StarlarkFile create( | |
| List<Statement> preludeStatements, | |
| Parser.ParseResult result, | |
| String contentHashCode, | |
| boolean allowImportInternal) { | |
| ImmutableList.Builder<Statement> statementsbuilder = | |
| ImmutableList.<Statement>builder().addAll(preludeStatements); | |
| if (allowImportInternal) { | |
| for (Statement stmt : result.statements) { | |
| if (stmt instanceof LoadStatement) { | |
| statementsbuilder.add(LoadStatement.allowLoadingOfInternalSymbols((LoadStatement) stmt)); | |
| } else { | |
| statementsbuilder.add(stmt); | |
| } | |
| } | |
| } else { | |
| statementsbuilder.addAll(result.statements); | |
| } | |
| ImmutableList<Statement> statements = statementsbuilder.build(); | |
| return new StarlarkFile( | |
| statements, | |
| result.errors, | |
| contentHashCode, | |
| result.location, | |
| ImmutableList.copyOf(result.comments), | |
| result.stringEscapeEvents); | |
| } | |
| /** | |
| * Extract a subtree containing only statements from {@code firstStatement} (included) up to | |
| * {@code lastStatement} excluded. | |
| */ | |
| public StarlarkFile subTree(int firstStatement, int lastStatement) { | |
| ImmutableList<Statement> statements = this.statements.subList(firstStatement, lastStatement); | |
| return new StarlarkFile( | |
| statements, | |
| errors, | |
| null, | |
| (Lexer.LexerLocation) this.statements.get(firstStatement).getStartLocation(), | |
| ImmutableList.of(), | |
| stringEscapeEvents); | |
| } | |
| /** | |
| * Returns an unmodifiable view of the list of scanner, parser, and (perhaps) resolver errors | |
| * accumulated in this Starlark file. | |
| */ | |
| public List<Event> errors() { | |
| return Collections.unmodifiableList(errors); | |
| } | |
| /** Returns errors().isEmpty(). */ | |
| public boolean ok() { | |
| return errors.isEmpty(); | |
| } | |
| /** | |
| * Appends string escaping errors to {@code errors}. The Lexer diverts such errors into a separate | |
| * bucket as they should be selectively reported depending on a StarlarkSemantics, to which the | |
| * lexer/parser does not have access. This function is called by ValidationEnvironment, which has | |
| * access to a StarlarkSemantics and can thus decide whether to respect or ignore these events. | |
| * | |
| * <p>Naturally this function should be called at most once. | |
| */ | |
| void addStringEscapeEvents() { | |
| errors.addAll(stringEscapeEvents); | |
| } | |
| /** Returns an (immutable, ordered) list of statements in this BUILD file. */ | |
| public ImmutableList<Statement> getStatements() { | |
| return statements; | |
| } | |
| /** Returns an (immutable, ordered) list of comments in this BUILD file. */ | |
| public ImmutableList<Comment> getComments() { | |
| return comments; | |
| } | |
| @Override | |
| public String toString() { | |
| return "<StarlarkFile with " + statements.size() + " statements>"; | |
| } | |
| @Override | |
| public void accept(NodeVisitor visitor) { | |
| visitor.visit(this); | |
| } | |
| /** | |
| * Parse the specified file, returning its syntax tree with the preludeStatements inserted at the | |
| * front of its statement list. | |
| */ | |
| public static StarlarkFile parseWithPrelude( | |
| ParserInput input, List<Statement> preludeStatements) { | |
| Parser.ParseResult result = Parser.parseFile(input); | |
| return create( | |
| preludeStatements, result, /* contentHashCode= */ null, /*allowImportInternal=*/ false); | |
| } | |
| /** | |
| * Parse the specified build file, returning its AST. All load statements parsed that way will be | |
| * exempt from visibility restrictions. | |
| */ | |
| // TODO(adonovan): make LoadStatement.allowInternal publicly settable, and delete this. | |
| public static StarlarkFile parseVirtualBuildFile( | |
| ParserInput input, List<Statement> preludeStatements) { | |
| Parser.ParseResult result = Parser.parseFile(input); | |
| return create( | |
| preludeStatements, result, /* contentHashCode= */ null, /*allowImportInternal=*/ true); | |
| } | |
| // TODO(adonovan): make the digest publicly settable, and delete this. | |
| public static StarlarkFile parseWithDigest(ParserInput input, byte[] digest) throws IOException { | |
| Parser.ParseResult result = Parser.parseFile(input); | |
| return create( | |
| /* preludeStatements= */ ImmutableList.of(), | |
| result, | |
| HashCode.fromBytes(digest).toString(), | |
| /* allowImportInternal= */ false); | |
| } | |
| /** | |
| * Parse a Starlark file. | |
| * | |
| * <p>A syntax tree is always returned, even in case of error. Errors are recorded in the tree. | |
| * Example usage: | |
| * | |
| * <pre> | |
| * StarlarkFile file = StarlarkFile.parse(input); | |
| * if (!file.ok()) { | |
| * Event.replayEventsOn(handler, file.errors()); | |
| * ... | |
| * } | |
| * </pre> | |
| */ | |
| public static StarlarkFile parse(ParserInput input) { | |
| Parser.ParseResult result = Parser.parseFile(input); | |
| return create( | |
| /* preludeStatements= */ ImmutableList.of(), | |
| result, | |
| /* contentHashCode= */ null, | |
| /* allowImportInternal=*/ false); | |
| } | |
| /** | |
| * Returns a hash code calculated from the string content of the source file of this AST. | |
| */ | |
| @Nullable public String getContentHashCode() { | |
| return contentHashCode; | |
| } | |
| } |