aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java
diff options
context:
space:
mode:
authorGravatar Han-Wen Nienhuys <hanwen@google.com>2015-02-25 16:45:20 +0100
committerGravatar Han-Wen Nienhuys <hanwen@google.com>2015-02-25 16:45:20 +0100
commitd08b27fa9701fecfdb69e1b0d1ac2459efc2129b (patch)
tree5d50963026239ca5aebfb47ea5b8db7e814e57c8 /src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java
Update from Google.
-- MOE_MIGRATED_REVID=85702957
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java244
1 files changed, 244 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java b/src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java
new file mode 100644
index 0000000000..6c85ab121b
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java
@@ -0,0 +1,244 @@
+// Copyright 2014 Google Inc. 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.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.events.EventHandler;
+import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.CachingPackageLocator;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+/**
+ * Abstract syntax node for an entire BUILD file.
+ */
+public class BuildFileAST extends ASTNode {
+
+ private final ImmutableList<Statement> stmts;
+
+ private final ImmutableList<Comment> comments;
+
+ private final ImmutableSet<PathFragment> imports;
+
+ /**
+ * Whether any errors were encountered during scanning or parsing.
+ */
+ private final boolean containsErrors;
+
+ private final String contentHashCode;
+
+ private BuildFileAST(Lexer lexer, List<Statement> preludeStatements, Parser.ParseResult result) {
+ this(lexer, preludeStatements, result, null);
+ }
+
+ private BuildFileAST(Lexer lexer, List<Statement> preludeStatements,
+ Parser.ParseResult result, String contentHashCode) {
+ this.stmts = ImmutableList.<Statement>builder()
+ .addAll(preludeStatements)
+ .addAll(result.statements)
+ .build();
+ this.comments = ImmutableList.copyOf(result.comments);
+ this.containsErrors = result.containsErrors;
+ this.contentHashCode = contentHashCode;
+ this.imports = fetchImports(this.stmts);
+ if (result.statements.size() > 0) {
+ setLocation(lexer.createLocation(
+ result.statements.get(0).getLocation().getStartOffset(),
+ result.statements.get(result.statements.size() - 1).getLocation().getEndOffset()));
+ } else {
+ setLocation(Location.fromFile(lexer.getFilename()));
+ }
+ }
+
+ private ImmutableSet<PathFragment> fetchImports(List<Statement> stmts) {
+ Set<PathFragment> imports = new HashSet<>();
+ for (Statement stmt : stmts) {
+ if (stmt instanceof LoadStatement) {
+ LoadStatement imp = (LoadStatement) stmt;
+ imports.add(imp.getImportPath());
+ }
+ }
+ return ImmutableSet.copyOf(imports);
+ }
+
+ /**
+ * Returns true if any errors were encountered during scanning or parsing. If
+ * set, clients should not rely on the correctness of the AST for builds or
+ * BUILD-file editing.
+ */
+ public boolean containsErrors() {
+ return containsErrors;
+ }
+
+ /**
+ * Returns an (immutable, ordered) list of statements in this BUILD file.
+ */
+ public ImmutableList<Statement> getStatements() {
+ return stmts;
+ }
+
+ /**
+ * Returns an (immutable, ordered) list of comments in this BUILD file.
+ */
+ public ImmutableList<Comment> getComments() {
+ return comments;
+ }
+
+ /**
+ * Returns an (immutable) set of imports in this BUILD file.
+ */
+ public ImmutableCollection<PathFragment> getImports() {
+ return imports;
+ }
+
+ /**
+ * Executes this build file in a given Environment.
+ *
+ * <p>If, for any reason, execution of a statement cannot be completed, an
+ * {@link EvalException} is thrown by {@link Statement#exec(Environment)}.
+ * This exception is caught here and reported through reporter and execution
+ * continues on the next statement. In effect, there is a "try/except" block
+ * around every top level statement. Such exceptions are not ignored, though:
+ * they are visible via the return value. Rules declared in a package
+ * containing any error (including loading-phase semantical errors that
+ * cannot be checked here) must also be considered "in error".
+ *
+ * <p>Note that this method will not affect the value of {@link
+ * #containsErrors()}; that refers only to lexer/parser errors.
+ *
+ * @return true if no error occurred during execution.
+ */
+ public boolean exec(Environment env, EventHandler eventHandler) throws InterruptedException {
+ boolean ok = true;
+ for (Statement stmt : stmts) {
+ try {
+ stmt.exec(env);
+ } catch (EvalException e) {
+ ok = false;
+ // Do not report errors caused by a previous parsing error, as it has already been
+ // reported.
+ if (e.isDueToIncompleteAST()) {
+ continue;
+ }
+ // When the exception is raised from another file, report first the location in the
+ // BUILD file (as it is the most probable cause for the error).
+ Location exnLoc = e.getLocation();
+ Location nodeLoc = stmt.getLocation();
+ if (exnLoc == null || !nodeLoc.getPath().equals(exnLoc.getPath())) {
+ eventHandler.handle(Event.error(nodeLoc,
+ e.getMessage() + " (raised from " + exnLoc + ")"));
+ } else {
+ eventHandler.handle(Event.error(exnLoc, e.getMessage()));
+ }
+ }
+ }
+ return ok;
+ }
+
+ @Override
+ public String toString() {
+ return "BuildFileAST" + getStatements();
+ }
+
+ @Override
+ public void accept(SyntaxTreeVisitor visitor) {
+ visitor.visit(this);
+ }
+
+ /**
+ * Parse the specified build file, returning its AST. All errors during
+ * scanning or parsing will be reported to the reporter.
+ *
+ * @throws IOException if the file cannot not be read.
+ */
+ public static BuildFileAST parseBuildFile(Path buildFile, EventHandler eventHandler,
+ CachingPackageLocator locator, boolean parsePython)
+ throws IOException {
+ ParserInputSource inputSource = ParserInputSource.create(buildFile);
+ return parseBuildFile(inputSource, eventHandler, locator, parsePython);
+ }
+
+ /**
+ * Parse the specified build file, returning its AST. All errors during
+ * scanning or parsing will be reported to the reporter.
+ */
+ public static BuildFileAST parseBuildFile(ParserInputSource input,
+ List<Statement> preludeStatements,
+ EventHandler eventHandler,
+ CachingPackageLocator locator,
+ boolean parsePython) {
+ Lexer lexer = new Lexer(input, eventHandler, parsePython);
+ Parser.ParseResult result = Parser.parseFile(lexer, eventHandler, locator, parsePython);
+ return new BuildFileAST(lexer, preludeStatements, result);
+ }
+
+ public static BuildFileAST parseBuildFile(ParserInputSource input, EventHandler eventHandler,
+ CachingPackageLocator locator, boolean parsePython) {
+ Lexer lexer = new Lexer(input, eventHandler, parsePython);
+ Parser.ParseResult result = Parser.parseFile(lexer, eventHandler, locator, parsePython);
+ return new BuildFileAST(lexer, ImmutableList.<Statement>of(), result);
+ }
+
+ /**
+ * Parse the specified build file, returning its AST. All errors during
+ * scanning or parsing will be reported to the reporter.
+ */
+ public static BuildFileAST parseBuildFile(Lexer lexer, EventHandler eventHandler) {
+ Parser.ParseResult result = Parser.parseFile(lexer, eventHandler, null, false);
+ return new BuildFileAST(lexer, ImmutableList.<Statement>of(), result);
+ }
+
+ /**
+ * Parse the specified Skylark file, returning its AST. All errors during
+ * scanning or parsing will be reported to the reporter.
+ *
+ * @throws IOException if the file cannot not be read.
+ */
+ public static BuildFileAST parseSkylarkFile(Path file, EventHandler eventHandler,
+ CachingPackageLocator locator, ValidationEnvironment validationEnvironment)
+ throws IOException {
+ ParserInputSource input = ParserInputSource.create(file);
+ Lexer lexer = new Lexer(input, eventHandler, false);
+ Parser.ParseResult result =
+ Parser.parseFileForSkylark(lexer, eventHandler, locator, validationEnvironment);
+ return new BuildFileAST(lexer, ImmutableList.<Statement>of(), result, input.contentHashCode());
+ }
+
+ /**
+ * Parse the specified build file, without building the AST.
+ *
+ * @return true if the input file is syntactically valid
+ */
+ public static boolean checkSyntax(ParserInputSource input,
+ EventHandler eventHandler, boolean parsePython) {
+ return !parseBuildFile(input, eventHandler, null, parsePython).containsErrors();
+ }
+
+ /**
+ * Returns a hash code calculated from the string content of the source file of this AST.
+ */
+ @Nullable public String getContentHashCode() {
+ return contentHashCode;
+ }
+}