Cleanup in the parser
- Move break/continue check from ValidationEnvironment to the Parser
- Remove some differences between BUILD / Skylark parsing mode
- Fix location off-by-one error in the break/continue tokens
- Remove duplicated error message ('for loops are not allowed on top-level')
--
MOS_MIGRATED_REVID=137259929
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/FlowStatement.java b/src/main/java/com/google/devtools/build/lib/syntax/FlowStatement.java
index e498b9a..15f1ee2 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/FlowStatement.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/FlowStatement.java
@@ -58,11 +58,7 @@
}
@Override
- void validate(ValidationEnvironment env) throws EvalException {
- if (!env.isInsideLoop()) {
- throw new EvalException(getLocation(), kind.name + " statement must be inside a for loop");
- }
- }
+ void validate(ValidationEnvironment env) throws EvalException {}
@Override
public String toString() {
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/ForStatement.java b/src/main/java/com/google/devtools/build/lib/syntax/ForStatement.java
index b641be3..97ac506 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/ForStatement.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/ForStatement.java
@@ -30,16 +30,14 @@
import com.google.devtools.build.lib.syntax.compiler.Variable.InternalVariable;
import com.google.devtools.build.lib.syntax.compiler.VariableScope;
import com.google.devtools.build.lib.util.Preconditions;
-
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
import net.bytebuddy.implementation.bytecode.Duplication;
import net.bytebuddy.implementation.bytecode.constant.IntegerConstant;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
/**
* Syntax node for a for loop statement.
*/
@@ -111,21 +109,12 @@
@Override
void validate(ValidationEnvironment env) throws EvalException {
- if (env.isTopLevel()) {
- throw new EvalException(getLocation(), "'For' is not allowed as a top level statement");
- }
- env.enterLoop();
+ // TODO(bazel-team): validate variable. Maybe make it temporarily readonly.
+ collection.validate(env);
+ variable.validate(env, getLocation());
- try {
- // TODO(bazel-team): validate variable. Maybe make it temporarily readonly.
- collection.validate(env);
- variable.validate(env, getLocation());
-
- for (Statement stmt : block) {
- stmt.validate(env);
- }
- } finally {
- env.exitLoop(getLocation());
+ for (Statement stmt : block) {
+ stmt.validate(env);
}
}
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 4101062..8b28b3e 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
@@ -135,6 +135,7 @@
private Token token; // current lookahead token
private Token pushedToken = null; // used to implement LL(2)
+ private int loopCount; // break/continue keywords can be used only inside a loop
private static final boolean DEBUGGING = false;
@@ -1171,8 +1172,7 @@
int start = token.left;
if (token.kind == TokenKind.RETURN) {
return parseReturnStatement();
- } else if ((parsingMode == SKYLARK)
- && (token.kind == TokenKind.BREAK || token.kind == TokenKind.CONTINUE)) {
+ } else if (token.kind == TokenKind.BREAK || token.kind == TokenKind.CONTINUE) {
return parseFlowStatement(token.kind);
}
Expression expression = parseExpression();
@@ -1232,9 +1232,14 @@
expect(TokenKind.IN);
Expression collection = parseExpression();
expect(TokenKind.COLON);
- List<Statement> block = parseSuite();
- Statement stmt = new ForStatement(loopVar, collection, block);
- list.add(setLocation(stmt, start, token.right));
+ enterLoop();
+ try {
+ List<Statement> block = parseSuite();
+ Statement stmt = new ForStatement(loopVar, collection, block);
+ list.add(setLocation(stmt, start, token.right));
+ } finally {
+ exitLoop();
+ }
}
// def foo(bar1, bar2):
@@ -1401,10 +1406,16 @@
// flow_stmt ::= break_stmt | continue_stmt
private FlowStatement parseFlowStatement(TokenKind kind) {
int start = token.left;
+ int end = token.right;
expect(kind);
+ if (loopCount == 0) {
+ reportError(
+ lexer.createLocation(start, end),
+ kind.getPrettyName() + " statement must be inside a for loop");
+ }
FlowStatement.Kind flowKind =
kind == TokenKind.BREAK ? FlowStatement.Kind.BREAK : FlowStatement.Kind.CONTINUE;
- return setLocation(new FlowStatement(flowKind), start, token.right);
+ return setLocation(new FlowStatement(flowKind), start, end);
}
// return_stmt ::= RETURN [expr]
@@ -1429,7 +1440,7 @@
int start = token.left;
Token blockToken = token;
syncTo(EnumSet.of(TokenKind.COLON, TokenKind.EOF)); // skip over expression or name
- if (blockToken.kind == TokenKind.ELSE && parsingMode == SKYLARK) {
+ if (blockToken.kind == TokenKind.ELSE) {
reportError(
lexer.createLocation(blockToken.left, blockToken.right),
"syntax error at 'else': not allowed here.");
@@ -1450,4 +1461,13 @@
private void makeComment(Token token) {
comments.add(setLocation(new Comment((String) token.value), token.left, token.right));
}
+
+ private void enterLoop() {
+ loopCount++;
+ }
+
+ private void exitLoop() {
+ Preconditions.checkState(loopCount > 0);
+ loopCount--;
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java b/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java
index 095b46f..c1a94c5 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java
@@ -46,12 +46,6 @@
private final Stack<Set<String>> futureReadOnlyVariables = new Stack<>();
/**
- * Tracks the number of nested for loops that contain the statement that is currently being
- * validated
- */
- private int loopCount = 0;
-
- /**
* Create a ValidationEnvironment for a given global Environment.
*/
public ValidationEnvironment(Environment env) {
@@ -169,33 +163,4 @@
return false;
}
}
-
- /**
- * Returns whether the current statement is inside a for loop (either in this environment or one
- * of its parents)
- *
- * @return True if the current statement is inside a for loop
- */
- public boolean isInsideLoop() {
- return (loopCount > 0);
- }
-
- /**
- * Signals that the block of a for loop was entered
- */
- public void enterLoop() {
- ++loopCount;
- }
-
- /**
- * Signals that the block of a for loop was left
- *
- * @param location The current location
- * @throws EvalException If there was no corresponding call to
- * {@code ValidationEnvironment#enterLoop}
- */
- public void exitLoop(Location location) throws EvalException {
- Preconditions.checkState(loopCount > 0);
- --loopCount;
- }
}