blob: 44303a58a2efd60006d8da9611a915040205ada2 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
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.
14package com.google.devtools.build.lib.syntax;
15
Laurent Le Brun8e965b82016-08-03 11:50:24 +000016import com.google.common.base.Joiner;
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +000017import com.google.common.base.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010018import com.google.common.collect.ImmutableList;
dannarkf4b9ff42018-06-12 09:28:45 -070019import com.google.common.collect.ImmutableMap;
Lukacs Berki48338222015-06-12 11:37:46 +000020import com.google.common.hash.HashCode;
dannarkf4b9ff42018-06-12 09:28:45 -070021import com.google.devtools.build.lib.cmdline.RepositoryName;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010022import com.google.devtools.build.lib.events.Event;
23import com.google.devtools.build.lib.events.EventHandler;
24import com.google.devtools.build.lib.events.Location;
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +000025import com.google.devtools.build.lib.syntax.Parser.ParseResult;
26import com.google.devtools.build.lib.syntax.SkylarkImports.SkylarkImportSyntaxException;
27import com.google.devtools.build.lib.util.Pair;
Carmi Grushko46bf88c2017-02-20 22:37:15 +000028import com.google.devtools.build.lib.vfs.PathFragment;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010029import java.io.IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010030import java.util.List;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010031import javax.annotation.Nullable;
32
33/**
34 * Abstract syntax node for an entire BUILD file.
35 */
brandjon540aac62017-06-12 23:08:09 +020036// TODO(bazel-team): Consider breaking this up into two classes: One that extends ASTNode and does
37// not include import info; and one that wraps that object with additional import info but that
38// does not itself extend ASTNode. This would help keep the AST minimalistic.
brandjona64b1c42017-05-15 21:27:33 +020039public class BuildFileAST extends ASTNode {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010040
brandjon990622b2017-07-11 19:56:45 +020041 private final ImmutableList<Statement> statements;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010042
43 private final ImmutableList<Comment> comments;
44
Laurent Le Brun7c3d6682016-10-13 14:55:19 +000045 @Nullable private final ImmutableList<SkylarkImport> imports;
Laurent Le Brun6190ffb2015-07-01 16:24:56 +000046
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010047 /**
48 * Whether any errors were encountered during scanning or parsing.
49 */
50 private final boolean containsErrors;
51
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +000052 @Nullable private final String contentHashCode;
Lukacs Berkif445ea12015-07-09 07:16:41 +000053
Damien Martin-Guillerez5e95a462016-02-05 22:32:08 +000054 private BuildFileAST(
brandjon990622b2017-07-11 19:56:45 +020055 ImmutableList<Statement> statements,
Damien Martin-Guillerez5e95a462016-02-05 22:32:08 +000056 boolean containsErrors,
57 String contentHashCode,
Laurent Le Brun8c8857d2016-08-04 10:22:16 +000058 Location location,
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +000059 ImmutableList<Comment> comments,
Laurent Le Brun7c3d6682016-10-13 14:55:19 +000060 @Nullable ImmutableList<SkylarkImport> imports) {
brandjon990622b2017-07-11 19:56:45 +020061 this.statements = statements;
Damien Martin-Guillerez5e95a462016-02-05 22:32:08 +000062 this.containsErrors = containsErrors;
63 this.contentHashCode = contentHashCode;
Laurent Le Brun8c8857d2016-08-04 10:22:16 +000064 this.comments = comments;
Damien Martin-Guillerez5e95a462016-02-05 22:32:08 +000065 this.setLocation(location);
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +000066 this.imports = imports;
67 }
68
69 private static BuildFileAST create(
70 List<Statement> preludeStatements,
71 ParseResult result,
72 String contentHashCode,
dannarkf4b9ff42018-06-12 09:28:45 -070073 ImmutableMap<RepositoryName, RepositoryName> repositoryMapping,
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +000074 EventHandler eventHandler) {
brandjon990622b2017-07-11 19:56:45 +020075 ImmutableList<Statement> statements =
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +000076 ImmutableList.<Statement>builder()
77 .addAll(preludeStatements)
78 .addAll(result.statements)
79 .build();
80
81 boolean containsErrors = result.containsErrors;
brandjon990622b2017-07-11 19:56:45 +020082 Pair<Boolean, ImmutableList<SkylarkImport>> skylarkImports =
dannarkf4b9ff42018-06-12 09:28:45 -070083 fetchLoads(statements, repositoryMapping, eventHandler);
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +000084 containsErrors |= skylarkImports.first;
85 return new BuildFileAST(
brandjon990622b2017-07-11 19:56:45 +020086 statements,
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +000087 containsErrors,
88 contentHashCode,
89 result.location,
90 ImmutableList.copyOf(result.comments),
91 skylarkImports.second);
Damien Martin-Guillerez5e95a462016-02-05 22:32:08 +000092 }
93
94 /**
95 * Extract a subtree containing only statements from {@code firstStatement} (included) up to
96 * {@code lastStatement} excluded.
97 */
98 public BuildFileAST subTree(int firstStatement, int lastStatement) {
brandjon990622b2017-07-11 19:56:45 +020099 ImmutableList<Statement> statements = this.statements.subList(firstStatement, lastStatement);
Laurent Le Brun7c3d6682016-10-13 14:55:19 +0000100 ImmutableList.Builder<SkylarkImport> imports = ImmutableList.builder();
brandjon990622b2017-07-11 19:56:45 +0200101 for (Statement stmt : statements) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100102 if (stmt instanceof LoadStatement) {
Laurent Le Brun7b1708c2016-10-13 10:05:12 +0000103 String str = ((LoadStatement) stmt).getImport().getValue();
Laurent Le Brun7c3d6682016-10-13 14:55:19 +0000104 try {
dannarkf4b9ff42018-06-12 09:28:45 -0700105 imports.add(SkylarkImports.create(str, /* repositoryMapping= */ ImmutableMap.of()));
Laurent Le Brun7c3d6682016-10-13 14:55:19 +0000106 } catch (SkylarkImportSyntaxException e) {
107 throw new IllegalStateException(
dmaclach6a4b1512018-09-04 08:51:37 -0700108 "Cannot create SkylarkImport for '" + str + "'. This is an internal error.");
Laurent Le Brun7c3d6682016-10-13 14:55:19 +0000109 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100110 }
111 }
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000112 return new BuildFileAST(
brandjon990622b2017-07-11 19:56:45 +0200113 statements,
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000114 containsErrors,
115 null,
brandjon990622b2017-07-11 19:56:45 +0200116 this.statements.get(firstStatement).getLocation(),
117 ImmutableList.of(),
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000118 imports.build());
119 }
120
121 /**
122 * Collects all load statements. Returns a pair with a boolean saying if there were errors and the
123 * imports that could be resolved.
124 */
laurentlb17d975e2017-09-01 17:49:23 +0200125 private static Pair<Boolean, ImmutableList<SkylarkImport>> fetchLoads(
dannarkf4b9ff42018-06-12 09:28:45 -0700126 List<Statement> statements,
127 ImmutableMap<RepositoryName, RepositoryName> repositoryMapping,
128 EventHandler eventHandler) {
Laurent Le Brun7c3d6682016-10-13 14:55:19 +0000129 ImmutableList.Builder<SkylarkImport> imports = ImmutableList.builder();
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000130 boolean error = false;
brandjon990622b2017-07-11 19:56:45 +0200131 for (Statement stmt : statements) {
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000132 if (stmt instanceof LoadStatement) {
Laurent Le Brun7b1708c2016-10-13 10:05:12 +0000133 String importString = ((LoadStatement) stmt).getImport().getValue();
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000134 try {
dannarkf4b9ff42018-06-12 09:28:45 -0700135 imports.add(SkylarkImports.create(importString, repositoryMapping));
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000136 } catch (SkylarkImportSyntaxException e) {
137 eventHandler.handle(Event.error(stmt.getLocation(), e.getMessage()));
138 error = true;
139 }
140 }
141 }
Laurent Le Brun7c3d6682016-10-13 14:55:19 +0000142 return Pair.of(error, imports.build());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100143 }
144
145 /**
146 * Returns true if any errors were encountered during scanning or parsing. If
147 * set, clients should not rely on the correctness of the AST for builds or
148 * BUILD-file editing.
149 */
150 public boolean containsErrors() {
151 return containsErrors;
152 }
153
154 /**
155 * Returns an (immutable, ordered) list of statements in this BUILD file.
156 */
157 public ImmutableList<Statement> getStatements() {
brandjon990622b2017-07-11 19:56:45 +0200158 return statements;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100159 }
160
161 /**
162 * Returns an (immutable, ordered) list of comments in this BUILD file.
163 */
164 public ImmutableList<Comment> getComments() {
165 return comments;
166 }
167
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000168 /** Returns a list of loads in this BUILD file. */
169 public ImmutableList<SkylarkImport> getImports() {
170 Preconditions.checkNotNull(imports, "computeImports Should be called in parse* methods");
Laurent Le Brun7c3d6682016-10-13 14:55:19 +0000171 return imports;
Laurent Le Brun6190ffb2015-07-01 16:24:56 +0000172 }
173
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000174 /** Returns a list of loads as strings in this BUILD file. */
Laurent Le Brun7b1708c2016-10-13 10:05:12 +0000175 public ImmutableList<StringLiteral> getRawImports() {
176 ImmutableList.Builder<StringLiteral> imports = ImmutableList.builder();
brandjon990622b2017-07-11 19:56:45 +0200177 for (Statement stmt : statements) {
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000178 if (stmt instanceof LoadStatement) {
179 imports.add(((LoadStatement) stmt).getImport());
180 }
181 }
182 return imports.build();
183 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100184 /**
185 * Executes this build file in a given Environment.
186 *
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000187 * <p>If, for any reason, execution of a statement cannot be completed, an {@link EvalException}
laurentlb919d1b72017-09-01 20:22:11 +0200188 * is thrown by {@link Eval#exec(Statement)}. This exception is caught here and reported
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000189 * through reporter and execution continues on the next statement. In effect, there is a
190 * "try/except" block around every top level statement. Such exceptions are not ignored, though:
191 * they are visible via the return value. Rules declared in a package containing any error
192 * (including loading-phase semantical errors that cannot be checked here) must also be considered
193 * "in error".
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100194 *
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000195 * <p>Note that this method will not affect the value of {@link #containsErrors()}; that refers
196 * only to lexer/parser errors.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100197 *
198 * @return true if no error occurred during execution.
199 */
200 public boolean exec(Environment env, EventHandler eventHandler) throws InterruptedException {
201 boolean ok = true;
brandjon990622b2017-07-11 19:56:45 +0200202 for (Statement stmt : statements) {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000203 if (!execTopLevelStatement(stmt, env, eventHandler)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100204 ok = false;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100205 }
206 }
207 return ok;
208 }
209
Dmitry Lomov950310f2017-03-01 17:45:12 +0000210 /**
211 * Executes tol-level statement of this build file in a given Environment.
212 *
213 * <p>If, for any reason, execution of a statement cannot be completed, an {@link EvalException}
laurentlb919d1b72017-09-01 20:22:11 +0200214 * is thrown by {@link Eval#exec(Statement)}. This exception is caught here and reported
Dmitry Lomov950310f2017-03-01 17:45:12 +0000215 * through reporter. In effect, there is a
216 * "try/except" block around every top level statement. Such exceptions are not ignored, though:
217 * they are visible via the return value. Rules declared in a package containing any error
218 * (including loading-phase semantical errors that cannot be checked here) must also be considered
219 * "in error".
220 *
221 * <p>Note that this method will not affect the value of {@link #containsErrors()}; that refers
222 * only to lexer/parser errors.
223 *
224 * @return true if no error occurred during execution.
225 */
226
227 public boolean execTopLevelStatement(Statement stmt, Environment env,
228 EventHandler eventHandler) throws InterruptedException {
229 try {
Googler29eafdf2018-05-23 12:32:07 -0700230 Eval.fromEnvironment(env).exec(stmt);
Dmitry Lomov950310f2017-03-01 17:45:12 +0000231 return true;
232 } catch (EvalException e) {
233 // Do not report errors caused by a previous parsing error, as it has already been
234 // reported.
235 if (e.isDueToIncompleteAST()) {
236 return false;
237 }
238 // When the exception is raised from another file, report first the location in the
239 // BUILD file (as it is the most probable cause for the error).
240 Location exnLoc = e.getLocation();
241 Location nodeLoc = stmt.getLocation();
242 eventHandler.handle(Event.error(
243 (exnLoc == null || !nodeLoc.getPath().equals(exnLoc.getPath())) ? nodeLoc : exnLoc,
244 e.getMessage()));
245 return false;
246 }
247 }
248
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100249 @Override
brandjone2ffd5d2017-06-27 18:14:54 +0200250 public void prettyPrint(Appendable buffer, int indentLevel) throws IOException {
251 // Only statements are printed, not comments and processed import data.
brandjon990622b2017-07-11 19:56:45 +0200252 for (Statement stmt : statements) {
brandjone2ffd5d2017-06-27 18:14:54 +0200253 stmt.prettyPrint(buffer, indentLevel);
254 }
255 }
256
257 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100258 public String toString() {
brandjon990622b2017-07-11 19:56:45 +0200259 return "<BuildFileAST with " + statements.size() + " statements>";
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100260 }
261
262 @Override
263 public void accept(SyntaxTreeVisitor visitor) {
264 visitor.visit(this);
265 }
266
267 /**
dannarkf4b9ff42018-06-12 09:28:45 -0700268 * Parse the specified build file, returning its AST. All errors during scanning or parsing will
269 * be reported to the reporter.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100270 */
dannarkf4b9ff42018-06-12 09:28:45 -0700271 public static BuildFileAST parseBuildFile(
272 ParserInputSource input,
273 List<Statement> preludeStatements,
274 ImmutableMap<RepositoryName, RepositoryName> repositoryMapping,
275 EventHandler eventHandler) {
laurentlb17d975e2017-09-01 17:49:23 +0200276 Parser.ParseResult result = Parser.parseFile(input, eventHandler);
dannarkf4b9ff42018-06-12 09:28:45 -0700277 return create(
278 preludeStatements, result, /* contentHashCode= */ null, repositoryMapping, eventHandler)
laurentlb17d975e2017-09-01 17:49:23 +0200279 .validateBuildFile(eventHandler);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100280 }
281
Laurent Le Brunf1112b32016-08-03 13:16:02 +0000282 public static BuildFileAST parseBuildFile(ParserInputSource input, EventHandler eventHandler) {
laurentlb17d975e2017-09-01 17:49:23 +0200283 Parser.ParseResult result = Parser.parseFile(input, eventHandler);
dannarkf4b9ff42018-06-12 09:28:45 -0700284 return create(
285 /* preludeStatements= */ ImmutableList.<Statement>of(),
286 result,
287 /* contentHashCode= */ null,
288 /* repositoryMapping= */ ImmutableMap.of(),
289 eventHandler)
laurentlb17d975e2017-09-01 17:49:23 +0200290 .validateBuildFile(eventHandler);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100291 }
292
tomlu67c84b12017-11-06 19:49:16 +0100293 public static BuildFileAST parseSkylarkFile(
294 byte[] bytes, byte[] digest, PathFragment path, EventHandler eventHandler)
Laurent Le Brun8c8857d2016-08-04 10:22:16 +0000295 throws IOException {
tomlu67c84b12017-11-06 19:49:16 +0100296 ParserInputSource input = ParserInputSource.create(bytes, path);
laurentlb17d975e2017-09-01 17:49:23 +0200297 Parser.ParseResult result = Parser.parseFile(input, eventHandler);
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000298 return create(
dannarkf4b9ff42018-06-12 09:28:45 -0700299 /* preludeStatements= */ ImmutableList.of(),
300 result,
301 HashCode.fromBytes(digest).toString(),
302 /* repositoryMapping= */ ImmutableMap.of(),
303 eventHandler);
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000304 }
305
laurentlb04b4c662017-08-07 20:03:40 +0200306 public static BuildFileAST parseSkylarkFile(ParserInputSource input, EventHandler eventHandler) {
laurentlb17d975e2017-09-01 17:49:23 +0200307 Parser.ParseResult result = Parser.parseFile(input, eventHandler);
dannarkf4b9ff42018-06-12 09:28:45 -0700308 return create(
309 /* preludeStatements= */ ImmutableList.<Statement>of(),
310 result,
311 /* contentHashCode= */ null,
312 /* repositoryMapping= */ ImmutableMap.of(),
313 eventHandler);
laurentlb04b4c662017-08-07 20:03:40 +0200314 }
315
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000316 /**
317 * Parse the specified non-build Skylark file but avoid the validation of the imports, returning
318 * its AST. All errors during scanning or parsing will be reported to the reporter.
319 *
320 * <p>This method should not be used in Bazel code, since it doesn't validate that the imports are
321 * syntactically valid.
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000322 */
323 public static BuildFileAST parseSkylarkFileWithoutImports(
brandjon540aac62017-06-12 23:08:09 +0200324 ParserInputSource input, EventHandler eventHandler) {
laurentlb17d975e2017-09-01 17:49:23 +0200325 ParseResult result = Parser.parseFile(input, eventHandler);
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000326 return new BuildFileAST(
327 ImmutableList.<Statement>builder()
328 .addAll(ImmutableList.<Statement>of())
329 .addAll(result.statements)
330 .build(),
brandjon540aac62017-06-12 23:08:09 +0200331 result.containsErrors,
dannarkf4b9ff42018-06-12 09:28:45 -0700332 /* contentHashCode= */null,
Miguel Alcon Pinto927f3b22016-08-22 14:21:30 +0000333 result.location,
brandjon540aac62017-06-12 23:08:09 +0200334 ImmutableList.copyOf(result.comments),
dannarkf4b9ff42018-06-12 09:28:45 -0700335 /* imports= */null);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100336 }
337
Laurent Le Brun8c8857d2016-08-04 10:22:16 +0000338 /**
339 * Run static checks on the AST.
340 *
341 * @return a new AST (or the same), with the containsErrors flag updated.
342 */
laurentlbbde7c412017-06-12 15:22:37 +0200343 public BuildFileAST validate(Environment env, EventHandler eventHandler) {
brandjon990622b2017-07-11 19:56:45 +0200344 boolean valid = ValidationEnvironment.validateAst(env, statements, eventHandler);
Laurent Le Brun8c8857d2016-08-04 10:22:16 +0000345 if (valid || containsErrors) {
346 return this;
347 }
brandjon990622b2017-07-11 19:56:45 +0200348 return new BuildFileAST(statements, true, contentHashCode, getLocation(), comments, imports);
Laurent Le Brun8c8857d2016-08-04 10:22:16 +0000349 }
350
laurentlb17d975e2017-09-01 17:49:23 +0200351 /**
352 * Run static checks for a BUILD file.
353 *
354 * @return a new AST (or the same), with the containsErrors flag updated.
355 */
356 public BuildFileAST validateBuildFile(EventHandler eventHandler) {
357 boolean valid = ValidationEnvironment.checkBuildSyntax(statements, eventHandler);
358 if (valid || containsErrors) {
359 return this;
360 }
361 return new BuildFileAST(statements, true, contentHashCode, getLocation(), comments, imports);
362 }
363
364 public static BuildFileAST parseString(EventHandler eventHandler, String... content) {
Laurent Le Brun8e965b82016-08-03 11:50:24 +0000365 String str = Joiner.on("\n").join(content);
Carmi Grushko46bf88c2017-02-20 22:37:15 +0000366 ParserInputSource input = ParserInputSource.create(str, PathFragment.EMPTY_FRAGMENT);
laurentlb17d975e2017-09-01 17:49:23 +0200367 Parser.ParseResult result = Parser.parseFile(input, eventHandler);
dannarkf4b9ff42018-06-12 09:28:45 -0700368 return create(
369 /* preludeStatements= */ ImmutableList.of(),
370 result,
371 /* contentHashCode= */ null,
372 /* repositoryMapping= */ ImmutableMap.of(),
373 eventHandler);
Laurent Le Brun8e965b82016-08-03 11:50:24 +0000374 }
375
brandjon733a97d2017-06-27 17:11:27 +0200376 public static BuildFileAST parseBuildString(EventHandler eventHandler, String... content) {
laurentlb17d975e2017-09-01 17:49:23 +0200377 return parseString(eventHandler, content).validateBuildFile(eventHandler);
Laurent Le Brun8e965b82016-08-03 11:50:24 +0000378 }
379
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100380 /**
381 * Parse the specified build file, without building the AST.
382 *
383 * @return true if the input file is syntactically valid
384 */
Laurent Le Brunb566c7d2016-10-07 16:31:03 +0000385 public static boolean checkSyntax(ParserInputSource input, EventHandler eventHandler) {
laurentlb17d975e2017-09-01 17:49:23 +0200386 Parser.ParseResult result = Parser.parseFile(input, eventHandler);
Laurent Le Brunf1112b32016-08-03 13:16:02 +0000387 return !result.containsErrors;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100388 }
389
390 /**
Laurent Le Brun8e965b82016-08-03 11:50:24 +0000391 * Evaluates the code and return the value of the last statement if it's an
392 * Expression or else null.
393 */
394 @Nullable public Object eval(Environment env) throws EvalException, InterruptedException {
395 Object last = null;
Googler29eafdf2018-05-23 12:32:07 -0700396 Eval evaluator = Eval.fromEnvironment(env);
brandjon990622b2017-07-11 19:56:45 +0200397 for (Statement statement : statements) {
Laurent Le Brun8e965b82016-08-03 11:50:24 +0000398 if (statement instanceof ExpressionStatement) {
399 last = ((ExpressionStatement) statement).getExpression().eval(env);
400 } else {
laurentlb919d1b72017-09-01 20:22:11 +0200401 evaluator.exec(statement);
Laurent Le Brun8e965b82016-08-03 11:50:24 +0000402 last = null;
403 }
404 }
405 return last;
406 }
407
Laurent Le Brund4d7fca2017-02-14 19:12:02 +0000408 /**
409 * Evaluates the lines from input and return the value of the last statement if it's an
410 * Expression or else null. In case of error (either during validation or evaluation), it
411 * throws an EvalException.
412 */
413 @Nullable
Laurent Le Bruna6d8fe42016-11-28 18:14:54 +0000414 public static Object eval(Environment env, String... input)
415 throws EvalException, InterruptedException {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000416 BuildFileAST ast = parseAndValidateSkylarkString(env, input);
417 return ast.eval(env);
418 }
419
420 /**
421 * Parses and validates the lines from input and return the the AST
422 * In case of error during validation, it throws an EvalException.
423 */
424 public static BuildFileAST parseAndValidateSkylarkString(Environment env, String[] input)
425 throws EvalException {
laurentlb17d975e2017-09-01 17:49:23 +0200426 BuildFileAST ast = parseString(env.getEventHandler(), input);
laurentlbbde7c412017-06-12 15:22:37 +0200427 ValidationEnvironment.validateAst(env, ast.getStatements());
Dmitry Lomov950310f2017-03-01 17:45:12 +0000428 return ast;
Laurent Le Bruna6d8fe42016-11-28 18:14:54 +0000429 }
430
Laurent Le Brun8e965b82016-08-03 11:50:24 +0000431 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100432 * Returns a hash code calculated from the string content of the source file of this AST.
433 */
434 @Nullable public String getContentHashCode() {
435 return contentHashCode;
436 }
437}