Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Unified Diff: tools/testing/dart/status_expression.dart

Issue 2880203004: Re-apply status file parser changes from 0b7728da1bef08c1c1e092005d9fd8c8bff5fa6c. (Closed)
Patch Set: Explicitly stringify variable in status expressions. Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/testing/dart/runtime_configuration.dart ('k') | tools/testing/dart/status_file.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/testing/dart/status_expression.dart
diff --git a/tools/testing/dart/status_expression.dart b/tools/testing/dart/status_expression.dart
index 3804d1daecb47f829a67aab2364539b2685bd7dc..c0e35c2017d050fe45c419e60f34b796df9d8d6f 100644
--- a/tools/testing/dart/status_expression.dart
+++ b/tools/testing/dart/status_expression.dart
@@ -2,313 +2,238 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-library status_expression;
-
-/**
- * Parse and evaluate expressions in a .status file for Dart and V8.
- * There are set expressions and Boolean expressions in a .status file.
- * The grammar is:
- * BooleanExpression := $variableName == value | $variableName != value |
- * $variableName | (BooleanExpression) |
- * BooleanExpression && BooleanExpression |
- * BooleanExpression || BooleanExpression
- *
- * SetExpression := value | (SetExpression) |
- * SetExpression || SetExpression |
- * SetExpression if BooleanExpression |
- * SetExpression , SetExpression
- *
- * Productions are listed in order of precedence, and the || and , operators
- * both evaluate to set union, but with different precedence.
- *
- * Values and variableNames are non-empty strings of word characters, matching
- * the RegExp \w+.
- *
- * Expressions evaluate as expected, with values of variables found in
- * an environment passed to the evaluator. The SetExpression "value"
- * evaluates to a singleton set containing that value. "A if B" evaluates
- * to A if B is true, and to the empty set if B is false.
- */
-
-class ExprEvaluationException {
- String error;
-
- ExprEvaluationException(this.error);
-
- toString() => error;
+/// A parsed Boolean expression AST.
+abstract class Expression {
+ /// Parses Boolean expressions in a .status file for Dart.
+ ///
+ /// The grammar is:
+ ///
+ /// expression := or
+ /// or := and ( "||" and )*
+ /// and := primary ( "&&" primary )*
+ /// primary := "$" identifier ( ( "==" | "!=" ) identifier )? |
+ /// "(" expression ")"
+ /// identifier := regex "\w+"
+ ///
+ /// Expressions evaluate as expected, with values of variables found in an
+ /// environment passed to the evaluator.
+ static Expression parse(String expression) =>
+ new _ExpressionParser(expression).parse();
+
+ /// Evaluates the expression where all variables are defined by the given
+ /// [environment].
+ bool evaluate(Map<String, dynamic> environment);
}
-class Token {
- static const String LEFT_PAREN = "(";
- static const String RIGHT_PAREN = ")";
- static const String DOLLAR_SYMBOL = r"$";
- static const String UNION = ",";
- static const String EQUALS = "==";
- static const String NOT_EQUALS = "!=";
- static const String AND = "&&";
- static const String OR = "||";
+/// Keyword token strings.
+class _Token {
+ static const leftParen = "(";
+ static const rightParen = ")";
+ static const dollar = r"$";
+ static const equals = "==";
+ static const notEqual = "!=";
+ static const and = "&&";
+ static const or = "||";
}
-class Tokenizer {
- String expression;
- List<String> tokens;
+/// A reference to a variable.
+class _Variable {
+ final String name;
- Tokenizer(String this.expression) : tokens = new List<String>();
+ _Variable(this.name);
- // Tokens are : "(", ")", "$", ",", "&&", "||", "==", "!=", and (maximal) \w+.
- static final testRegexp =
- new RegExp(r"^([()$\w\s,]|(\&\&)|(\|\|)|(\=\=)|(\!\=))+$");
- static final regexp = new RegExp(r"[()$,]|(\&\&)|(\|\|)|(\=\=)|(\!\=)|\w+");
-
- List<String> tokenize() {
- if (!testRegexp.hasMatch(expression)) {
- throw new FormatException("Syntax error in '$expression'");
- }
- for (Match match in regexp.allMatches(expression)) tokens.add(match[0]);
- return tokens;
- }
-}
-
-abstract class BooleanExpression {
- bool evaluate(Map<String, String> environment);
-}
-
-abstract class SetExpression {
- Set<String> evaluate(Map<String, String> environment);
-}
-
-class Comparison implements BooleanExpression {
- TermVariable left;
- TermConstant right;
- bool negate;
-
- Comparison(this.left, this.right, this.negate);
-
- bool evaluate(environment) {
- return negate !=
- (left.termValue(environment) == right.termValue(environment));
- }
-
- String toString() =>
- "(\$${left.name} ${negate ? '!=' : '=='} ${right.value})";
-}
-
-class TermVariable {
- String name;
-
- TermVariable(this.name);
-
- String termValue(environment) {
+ String lookup(Map<String, dynamic> environment) {
var value = environment[name];
if (value == null) {
- throw new ExprEvaluationException("Could not find '$name' in environment "
+ throw new Exception("Could not find '$name' in environment "
"while evaluating status file expression.");
}
+
+ // Explicitly stringify all values so that things like:
+ //
+ // $strong == true
+ //
+ // work correctly even though "true" is treated as a string here.
+ // TODO(rnystrom): Is there a cleaner/safer way to do this?
return value.toString();
}
}
-class TermConstant {
- String value;
-
- TermConstant(String this.value);
-
- String termValue(environment) => value;
-}
-
-class BooleanVariable implements BooleanExpression {
- TermVariable variable;
+/// Tests whether a given variable is or is not equal some literal value, as in:
+///
+/// $variable == someValue
+class _ComparisonExpression implements Expression {
+ final _Variable left;
+ final String right;
+ final bool negate;
- BooleanVariable(this.variable);
+ _ComparisonExpression(this.left, this.right, this.negate);
- bool evaluate(environment) => variable.termValue(environment) == 'true';
- String toString() => "(bool \$${variable.name})";
-}
-
-class BooleanOperation implements BooleanExpression {
- String op;
- BooleanExpression left;
- BooleanExpression right;
-
- BooleanOperation(this.op, this.left, this.right);
-
- bool evaluate(environment) => (op == Token.AND)
- ? left.evaluate(environment) && right.evaluate(environment)
- : left.evaluate(environment) || right.evaluate(environment);
- String toString() => "($left $op $right)";
-}
-
-class SetUnion implements SetExpression {
- SetExpression left;
- SetExpression right;
-
- SetUnion(this.left, this.right);
-
- // Overwrites left.evaluate(env).
- // Set.addAll does not return this.
- Set<String> evaluate(environment) {
- Set<String> result = left.evaluate(environment);
- result.addAll(right.evaluate(environment));
- return result;
+ bool evaluate(Map<String, dynamic> environment) {
+ return negate != (left.lookup(environment) == right);
}
- String toString() => "($left || $right)";
+ String toString() => "(\$${left.name} ${negate ? '!=' : '=='} $right)";
}
-class SetIf implements SetExpression {
- SetExpression left;
- BooleanExpression right;
+/// A reference to a variable defined in the environment. The expression
+/// evaluates to true if the variable's stringified value is "true".
+///
+/// $variable
+class _VariableExpression implements Expression {
+ final _Variable variable;
- SetIf(this.left, this.right);
+ _VariableExpression(this.variable);
- Set<String> evaluate(environment) => right.evaluate(environment)
- ? left.evaluate(environment)
- : new Set<String>();
- String toString() => "($left if $right)";
-}
+ bool evaluate(Map<String, dynamic> environment) =>
+ variable.lookup(environment) == "true";
-class SetConstant implements SetExpression {
- String value;
-
- SetConstant(String v) : value = v.toLowerCase();
-
- Set<String> evaluate(environment) => [value].toSet();
- String toString() => value;
+ String toString() => "(bool \$${variable.name})";
}
-// An iterator that allows peeking at the current token.
-class Scanner {
- List<String> tokens;
- Iterator tokenIterator;
- String current;
+/// A logical `||` or `&&` expression.
+class _LogicExpression implements Expression {
+ /// The operator, `||` or `&&`.
+ final String op;
- Scanner(this.tokens) {
- tokenIterator = tokens.iterator;
- advance();
- }
+ final Expression left;
+ final Expression right;
- bool hasMore() => current != null;
+ _LogicExpression(this.op, this.left, this.right);
- void advance() {
- current = tokenIterator.moveNext() ? tokenIterator.current : null;
- }
-}
+ bool evaluate(Map<String, dynamic> environment) => (op == _Token.and)
+ ? left.evaluate(environment) && right.evaluate(environment)
+ : left.evaluate(environment) || right.evaluate(environment);
-class ExpressionParser {
- Scanner scanner;
+ String toString() => "($left $op $right)";
+}
- ExpressionParser(this.scanner);
+/// Parser for Boolean expressions in a .status file for Dart.
+class _ExpressionParser {
+ final _Scanner _scanner;
- SetExpression parseSetExpression() => parseSetUnion();
+ _ExpressionParser(String expression) : _scanner = new _Scanner(expression);
- SetExpression parseSetUnion() {
- SetExpression left = parseSetIf();
- while (scanner.hasMore() && scanner.current == Token.UNION) {
- scanner.advance();
- SetExpression right = parseSetIf();
- left = new SetUnion(left, right);
- }
- return left;
- }
+ Expression parse() {
+ var expression = _parseOr();
- SetExpression parseSetIf() {
- SetExpression left = parseSetOr();
- while (scanner.hasMore() && scanner.current == "if") {
- scanner.advance();
- BooleanExpression right = parseBooleanExpression();
- left = new SetIf(left, right);
+ // Should consume entire string.
+ if (_scanner.hasMore) {
+ throw new FormatException("Unexpected input after expression");
}
- return left;
- }
- SetExpression parseSetOr() {
- SetExpression left = parseSetAtomic();
- while (scanner.hasMore() && scanner.current == Token.OR) {
- scanner.advance();
- SetExpression right = parseSetAtomic();
- left = new SetUnion(left, right);
- }
- return left;
+ return expression;
}
- SetExpression parseSetAtomic() {
- if (scanner.current == Token.LEFT_PAREN) {
- scanner.advance();
- SetExpression value = parseSetExpression();
- if (scanner.current != Token.RIGHT_PAREN) {
- throw new FormatException("Missing right parenthesis in expression");
- }
- scanner.advance();
- return value;
+ Expression _parseOr() {
+ var left = _parseAnd();
+ while (_scanner.match(_Token.or)) {
+ var right = _parseAnd();
+ left = new _LogicExpression(_Token.or, left, right);
}
- if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) {
- throw new FormatException(
- "Expected identifier in expression, got ${scanner.current}");
- }
- SetExpression value = new SetConstant(scanner.current);
- scanner.advance();
- return value;
- }
- BooleanExpression parseBooleanExpression() => parseBooleanOr();
-
- BooleanExpression parseBooleanOr() {
- BooleanExpression left = parseBooleanAnd();
- while (scanner.hasMore() && scanner.current == Token.OR) {
- scanner.advance();
- BooleanExpression right = parseBooleanAnd();
- left = new BooleanOperation(Token.OR, left, right);
- }
return left;
}
- BooleanExpression parseBooleanAnd() {
- BooleanExpression left = parseBooleanAtomic();
- while (scanner.hasMore() && scanner.current == Token.AND) {
- scanner.advance();
- BooleanExpression right = parseBooleanAtomic();
- left = new BooleanOperation(Token.AND, left, right);
+ Expression _parseAnd() {
+ var left = _parsePrimary();
+ while (_scanner.match(_Token.and)) {
+ var right = _parsePrimary();
+ left = new _LogicExpression(_Token.and, left, right);
}
+
return left;
}
- BooleanExpression parseBooleanAtomic() {
- if (scanner.current == Token.LEFT_PAREN) {
- scanner.advance();
- BooleanExpression value = parseBooleanExpression();
- if (scanner.current != Token.RIGHT_PAREN) {
+ Expression _parsePrimary() {
+ if (_scanner.match(_Token.leftParen)) {
+ var value = _parseOr();
+ if (!_scanner.match(_Token.rightParen)) {
throw new FormatException("Missing right parenthesis in expression");
}
- scanner.advance();
+
return value;
}
// The only atomic booleans are of the form $variable == value or
// of the form $variable.
- if (scanner.current != Token.DOLLAR_SYMBOL) {
+ if (!_scanner.match(_Token.dollar)) {
throw new FormatException(
- "Expected \$ in expression, got ${scanner.current}");
+ "Expected \$ in expression, got ${_scanner.current}");
}
- scanner.advance();
- if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) {
+
+ if (!_scanner.isIdentifier) {
throw new FormatException(
- "Expected identifier in expression, got ${scanner.current}");
+ "Expected identifier in expression, got ${_scanner.current}");
}
- TermVariable left = new TermVariable(scanner.current);
- scanner.advance();
- if (scanner.current == Token.EQUALS ||
- scanner.current == Token.NOT_EQUALS) {
- bool negate = scanner.current == Token.NOT_EQUALS;
- scanner.advance();
- if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) {
+
+ var left = new _Variable(_scanner.current);
+ _scanner.advance();
+
+ if (_scanner.current == _Token.equals ||
+ _scanner.current == _Token.notEqual) {
+ var negate = _scanner.advance() == _Token.notEqual;
+
+ if (!_scanner.isIdentifier) {
throw new FormatException(
- "Expected value in expression, got ${scanner.current}");
+ "Expected value in expression, got ${_scanner.current}");
}
- TermConstant right = new TermConstant(scanner.current);
- scanner.advance();
- return new Comparison(left, right, negate);
+
+ var right = _scanner.advance();
+ return new _ComparisonExpression(left, right, negate);
} else {
- return new BooleanVariable(left);
+ return new _VariableExpression(left);
}
}
}
+
+/// An iterator that allows peeking at the current token.
+class _Scanner {
+ /// Tokens are "(", ")", "$", "&&", "||", "==", "!=", and (maximal) \w+.
+ static final _testPattern =
+ new RegExp(r"^([()$\w\s]|(\&\&)|(\|\|)|(\=\=)|(\!\=))+$");
+ static final _tokenPattern =
+ new RegExp(r"[()$]|(\&\&)|(\|\|)|(\=\=)|(\!\=)|\w+");
+
+ static final _identifierPattern = new RegExp(r"^\w+$");
+
+ /// The token strings being iterated.
+ final Iterator<String> tokenIterator;
+
+ String current;
+
+ _Scanner(String expression) : tokenIterator = tokenize(expression).iterator {
+ advance();
+ }
+
+ static List<String> tokenize(String expression) {
+ if (!_testPattern.hasMatch(expression)) {
+ throw new FormatException("Syntax error in '$expression'");
+ }
+
+ return _tokenPattern
+ .allMatches(expression)
+ .map((match) => match[0])
+ .toList();
+ }
+
+ bool get hasMore => current != null;
+
+ /// Returns `true` if the current token is an identifier.
+ bool get isIdentifier => _identifierPattern.hasMatch(current);
+
+ /// If the current token is [token], consumes it and returns `true`.
+ bool match(String token) {
+ if (!hasMore || current != token) return false;
+
+ advance();
+ return true;
+ }
+
+ /// Consumes the current token and returns it.
+ String advance() {
+ var previous = current;
+ current = tokenIterator.moveNext() ? tokenIterator.current : null;
+ return previous;
+ }
+}
« no previous file with comments | « tools/testing/dart/runtime_configuration.dart ('k') | tools/testing/dart/status_file.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698