diff options
Diffstat (limited to 'src/main/java/com/google')
6 files changed, 253 insertions, 15 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkModules.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkModules.java index 7d1e17647b..500edcc4cb 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkModules.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkModules.java @@ -22,6 +22,7 @@ import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.packages.MethodLibrary; import com.google.devtools.build.lib.packages.SkylarkNativeModule; import com.google.devtools.build.lib.syntax.Environment; +import com.google.devtools.build.lib.syntax.EvaluationContext; import com.google.devtools.build.lib.syntax.Function; import com.google.devtools.build.lib.syntax.SkylarkBuiltin; import com.google.devtools.build.lib.syntax.SkylarkEnvironment; @@ -38,7 +39,7 @@ import java.util.Map; /** * A class to handle all Skylark modules, to create and setup Validation and regular Environments. */ -// TODO(bazel-team): move that to syntax/ and +// TODO(bazel-team): move that to the syntax package and // let each extension register itself in a static { } statement. public class SkylarkModules { @@ -138,6 +139,11 @@ public class SkylarkModules { return new ValidationEnvironment(CollectionUtils.toImmutable(builtIn)); } + public static EvaluationContext newEvaluationContext(EventHandler eventHandler) { + return EvaluationContext.newSkylarkContext( + getNewEnvironment(eventHandler), getValidationEnvironment()); + } + /** * Collects the SkylarkFunctions from the fields of the class of the object parameter * and adds them into the builder. diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java b/src/main/java/com/google/devtools/build/lib/syntax/Environment.java index 456007f9ba..bd595127fc 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/Environment.java @@ -135,14 +135,18 @@ public class Environment { this.eventHandler = Preconditions.checkNotNull(eventHandler); } + public EventHandler getEventHandler() { + return eventHandler; + } + // Sets up the global environment private void setupGlobal() { // In Python 2.x, True and False are global values and can be redefined by the user. // In Python 3.x, they are keywords. We implement them as values, for the sake of // simplicity. We define them as Boolean objects. - env.put("False", FALSE); - env.put("True", TRUE); - env.put("None", NONE); + update("False", FALSE); + update("True", TRUE); + update("None", NONE); } public boolean isSkylarkEnabled() { diff --git a/src/main/java/com/google/devtools/build/lib/syntax/EvaluationContext.java b/src/main/java/com/google/devtools/build/lib/syntax/EvaluationContext.java new file mode 100644 index 0000000000..c361bb3fca --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/syntax/EvaluationContext.java @@ -0,0 +1,223 @@ +// Copyright 2015 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.annotations.VisibleForTesting; +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.devtools.build.lib.events.Event; +import com.google.devtools.build.lib.events.EventHandler; +import com.google.devtools.build.lib.events.EventKind; +import com.google.devtools.build.lib.packages.CachingPackageLocator; +import com.google.devtools.build.lib.syntax.Environment.NoSuchVariableException; +import com.google.devtools.build.lib.vfs.Path; + +import java.util.List; + +import javax.annotation.Nullable; + +/** + * Context for the evaluation of programs. + */ +public final class EvaluationContext { + + @Nullable private EventHandler eventHandler; + private Environment env; + @Nullable private ValidationEnvironment validationEnv; + private boolean parsePython; + + private EvaluationContext(EventHandler eventHandler, Environment env, + @Nullable ValidationEnvironment validationEnv, boolean parsePython) { + this.eventHandler = eventHandler; + this.env = env; + this.validationEnv = validationEnv; + this.parsePython = parsePython; + } + + /** + * The fail fast handler, which throws a runtime exception whenever we encounter an error event. + */ + public static final EventHandler FAIL_FAST_HANDLER = new EventHandler() { + @Override + public void handle(Event event) { + Preconditions.checkArgument( + !EventKind.ERRORS_AND_WARNINGS.contains(event.getKind()), event); + } + }; + + public static final EventHandler PRINT_HANDLER = new EventHandler() { + @Override + public void handle(Event event) { + System.out.print(event.getMessage()); + } + }; + + public static EvaluationContext newBuildContext(EventHandler eventHandler, Environment env, + boolean parsePython) { + return new EvaluationContext(eventHandler, env, null, parsePython); + } + + public static EvaluationContext newBuildContext(EventHandler eventHandler, Environment env) { + return newBuildContext(eventHandler, env, false); + } + + public static EvaluationContext newBuildContext(EventHandler eventHandler) { + return newBuildContext(eventHandler, new Environment()); + } + + public static EvaluationContext newSkylarkContext( + Environment env, ValidationEnvironment validationEnv) { + return new EvaluationContext(env.getEventHandler(), env, validationEnv, false); + } + + public static EvaluationContext newSkylarkContext(EventHandler eventHandler) { + return newSkylarkContext(new SkylarkEnvironment(eventHandler), new ValidationEnvironment()); + } + + public Environment getEnvironment() { + return env; + } + + public EventHandler getEventHandler() { + return eventHandler; + } + + public ValidationEnvironment getValidationEnvironment() { + return validationEnv; + } + + /** Base context for Skylark evaluation for internal use only, while initializing builtins */ + static final EvaluationContext SKYLARK_INITIALIZATION = newSkylarkContext(FAIL_FAST_HANDLER); + + /** Mock package locator */ + private static final class EmptyPackageLocator implements CachingPackageLocator { + @Override + public Path getBuildFileForPackage(String packageName) { + return null; + } + } + + /** An empty package locator */ + private static final CachingPackageLocator EMPTY_PACKAGE_LOCATOR = new EmptyPackageLocator(); + + /** Create a Lexer without a supporting file */ + @VisibleForTesting + Lexer createLexer(String... input) { + return new Lexer(ParserInputSource.create(Joiner.on("\n").join(input), null), + eventHandler); + } + + /** Is this a Skylark evaluation context? */ + public boolean isSkylark() { + return env.isSkylarkEnabled(); + } + + /** Parse a string without a supporting file, returning statements and comments */ + @VisibleForTesting + Parser.ParseResult parseFileWithComments(String... input) { + return isSkylark() + ? Parser.parseFileForSkylark(createLexer(input), eventHandler, null, validationEnv) + : Parser.parseFile(createLexer(input), eventHandler, EMPTY_PACKAGE_LOCATOR, parsePython); + } + + /** Parse a string without a supporting file, returning statements only */ + @VisibleForTesting + List<Statement> parseFile(String... input) { + return parseFileWithComments(input).statements; + } + + /** Parse an Expression from string without a supporting file */ + @VisibleForTesting + Expression parseExpression(String... input) { + return Parser.parseExpression(createLexer(input), eventHandler); + } + + /** Evaluate an Expression */ + @VisibleForTesting + Object evalExpression(Expression expression) throws EvalException, InterruptedException { + return expression.eval(env); + } + + /** Evaluate an Expression as parsed from String-s */ + Object evalExpression(String... input) throws EvalException, InterruptedException { + return evalExpression(parseExpression(input)); + } + + /** Parse a build (not Skylark) Statement from string without a supporting file */ + @VisibleForTesting + Statement parseStatement(String... input) { + return Parser.parseStatement(createLexer(input), eventHandler); + } + + /** + * Evaluate a Statement + * @param statement the Statement + * @return the value of the evaluation, if it's an Expression, or else null + */ + @Nullable private Object eval(Statement statement) throws EvalException, InterruptedException { + if (statement instanceof ExpressionStatement) { + return evalExpression(((ExpressionStatement) statement).getExpression()); + } + statement.exec(env); + return null; + } + + /** + * Evaluate a list of Statement-s + * @return the value of the last statement if it's an Expression or else null + */ + @Nullable private Object eval(List<Statement> statements) + throws EvalException, InterruptedException { + Object last = null; + for (Statement statement : statements) { + last = eval(statement); + } + return last; + } + + /** Update a variable in the environment, in fluent style */ + public EvaluationContext update(String varname, Object value) throws EvalException { + env.update(varname, value); + if (validationEnv != null) { + validationEnv.update( + varname, SkylarkType.typeForInference(SkylarkType.of(value.getClass())), null); + } + return this; + } + + /** Lookup a variable in the environment */ + public Object lookup(String varname) throws NoSuchVariableException { + return env.lookup(varname); + } + + /** Print a String in this context */ + public void print(String msg) { + if (msg != null) { + eventHandler.handle(new Event(EventKind.STDOUT, null, msg)); + } + } + + /** Print a String in this context */ + public void println(String msg) { + if (msg != null) { + print(msg + "\n"); + } + } + + /** Evaluate a series of statements */ + public Object eval(String... input) throws EvalException, InterruptedException { + return eval(parseFile(input)); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Lexer.java b/src/main/java/com/google/devtools/build/lib/syntax/Lexer.java index fd68bdea09..86b8de7b1d 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/Lexer.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/Lexer.java @@ -164,7 +164,8 @@ public final class Lexer { @Override public PathFragment getPath() { - return lineNumberTable.getPath(getStartOffset()).asFragment(); + Path path = lineNumberTable.getPath(getStartOffset()); + return path != null ? path.asFragment() : null; } @Override diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Parser.java b/src/main/java/com/google/devtools/build/lib/syntax/Parser.java index e8c30c393f..75b8db8b4c 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/Parser.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/Parser.java @@ -212,7 +212,13 @@ class Parser { @VisibleForTesting public static Statement parseStatement( Lexer lexer, EventHandler eventHandler) { - return new Parser(lexer, eventHandler, null).parseSmallStatement(); + Parser parser = new Parser(lexer, eventHandler, null); + Statement result = parser.parseSmallStatement(); + while (parser.token.kind == TokenKind.NEWLINE) { + parser.nextToken(); + } + parser.expect(TokenKind.EOF); + return result; } /** diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java index c772038276..6d7b1dbb1f 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java @@ -82,15 +82,15 @@ public class SkylarkSignatureProcessor { paramList.add(getParameter(name, starParam, enforcedTypes, doc, undocumented, /*mandatory=*/false, /*star=*/true, /*starStar=*/false, /*defaultValue=*/null)); } + for (Param param : annotation.mandatoryNamedOnly()) { + paramList.add(getParameter(name, param, enforcedTypes, doc, undocumented, + /*mandatory=*/true, /*star=*/false, /*starStar=*/false, /*defaultValue=*/null)); + } for (Param param : annotation.optionalNamedOnly()) { paramList.add(getParameter(name, param, enforcedTypes, doc, undocumented, /*mandatory=*/false, /*star=*/false, /*starStar=*/false, /*defaultValue=*/getDefaultValue(param, defaultValuesIterator))); } - for (Param param : annotation.mandatoryNamedOnly()) { - paramList.add(getParameter(name, param, enforcedTypes, doc, undocumented, - /*mandatory=*/true, /*star=*/false, /*starStar=*/false, /*defaultValue=*/null)); - } if (annotation.extraKeywords().length > 0) { Preconditions.checkArgument(annotation.extraKeywords().length == 1); paramList.add( @@ -190,13 +190,11 @@ public class SkylarkSignatureProcessor { return Environment.NONE; } else { try { - // TODO(bazel-team): when we have Evaluation, remove the throw and uncomment the return. - throw new RuntimeException("Not Implemented Yet!"); - // return new Evaluation ().eval(param.defaultValue()); + return EvaluationContext.SKYLARK_INITIALIZATION.evalExpression(param.defaultValue()); } catch (Exception e) { throw new RuntimeException(String.format( - "Exception while processing @SkylarkSignature.Param %s, default value %s: %s", - param.name(), param.defaultValue(), e.getMessage()), e); + "Exception while processing @SkylarkSignature.Param %s, default value %s", + param.name(), param.defaultValue()), e); } } } |