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

Side by Side Diff: tools/testing/dart/status_expression.dart

Issue 2913963002: Add negation to single-identifier tests in status files. (Closed)
Patch Set: Address comments Created 3 years, 6 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 unified diff | Download patch
« no previous file with comments | « tests/standalone/status_expression_test.dart ('k') | tools/testing/dart/status_file.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 import 'environment.dart'; 5 import 'environment.dart';
6 6
7 /// A parsed Boolean expression AST. 7 /// A parsed Boolean expression AST.
8 abstract class Expression { 8 abstract class Expression {
9 /// Parses Boolean expressions in a .status file for Dart. 9 /// Parses Boolean expressions in a .status file for Dart.
10 /// 10 ///
11 /// The grammar is: 11 /// The grammar is:
12 /// 12 ///
13 /// expression := or 13 /// expression := or
14 /// or := and ( "||" and )* 14 /// or := and ( "||" and )*
15 /// and := primary ( "&&" primary )* 15 /// and := primary ( "&&" primary )*
16 /// primary := "$" identifier ( ( "==" | "!=" ) identifier )? | 16 /// primary := "$" identifier ( "==" | "!=" ) identifier |
17 /// "!"? "$" identifier |
17 /// "(" expression ")" 18 /// "(" expression ")"
18 /// identifier := regex "\w+" 19 /// identifier := regex "\w+"
19 /// 20 ///
20 /// Expressions evaluate as expected, with values of variables found in an 21 /// Expressions evaluate as expected, with values of variables found in an
21 /// environment passed to the evaluator. 22 /// environment passed to the evaluator.
22 static Expression parse(String expression) => 23 static Expression parse(String expression) =>
23 new _ExpressionParser(expression).parse(); 24 new _ExpressionParser(expression).parse();
24 25
25 /// Validates that this expression does not contain any invalid uses of 26 /// Validates that this expression does not contain any invalid uses of
26 /// variables. 27 /// variables.
(...skipping 10 matching lines...) Expand all
37 38
38 /// Keyword token strings. 39 /// Keyword token strings.
39 class _Token { 40 class _Token {
40 static const leftParen = "("; 41 static const leftParen = "(";
41 static const rightParen = ")"; 42 static const rightParen = ")";
42 static const dollar = r"$"; 43 static const dollar = r"$";
43 static const equals = "=="; 44 static const equals = "==";
44 static const notEqual = "!="; 45 static const notEqual = "!=";
45 static const and = "&&"; 46 static const and = "&&";
46 static const or = "||"; 47 static const or = "||";
48 static const not = "!";
47 } 49 }
48 50
49 /// A reference to a variable. 51 /// A reference to a variable.
50 class _Variable { 52 class _Variable {
51 final String name; 53 final String name;
52 54
53 _Variable(this.name); 55 _Variable(this.name);
54 56
55 String lookup(Environment environment) { 57 String lookup(Environment environment) {
56 var value = environment.lookUp(name); 58 var value = environment.lookUp(name);
57 if (value == null) { 59 if (value == null) {
58 throw new Exception("Could not find '$name' in environment " 60 throw new Exception("Could not find '$name' in environment "
59 "while evaluating status file expression."); 61 "while evaluating status file expression.");
60 } 62 }
61 63
62 // Explicitly stringify all values so that things like: 64 // Explicitly stringify all values so that things like:
63 // 65 //
64 // $strong == true 66 // $strong == true
65 // 67 //
66 // work correctly even though "true" is treated as a string here. 68 // work correctly even though "true" is treated as a string here.
67 // TODO(rnystrom): Is there a cleaner/safer way to do this? 69 // TODO(rnystrom): Is there a cleaner/safer way to do this?
68 return value.toString(); 70 return value.toString();
69 } 71 }
70 } 72 }
71 73
72 /// Tests whether a given variable is or is not equal some literal value, as in: 74 /// Tests whether a given variable is or is not equal some literal value, as in:
73 /// 75 /// ```
74 /// $variable == someValue 76 /// $variable == someValue
77 /// ```
78 /// Negate the result if [negate] is true.
75 class _ComparisonExpression implements Expression { 79 class _ComparisonExpression implements Expression {
76 final _Variable left; 80 final _Variable left;
77 final String right; 81 final String right;
78 final bool negate; 82 final bool negate;
79 83
80 _ComparisonExpression(this.left, this.right, this.negate); 84 _ComparisonExpression(this.left, this.right, this.negate);
81 85
82 void validate(List<String> errors) { 86 void validate(List<String> errors) {
83 Environment.validate(left.name, right, errors); 87 Environment.validate(left.name, right, errors);
84 } 88 }
85 89
86 bool evaluate(Environment environment) { 90 bool evaluate(Environment environment) {
87 return negate != (left.lookup(environment) == right); 91 return negate != (left.lookup(environment) == right);
88 } 92 }
89 93
90 String toString() => "(\$${left.name} ${negate ? '!=' : '=='} $right)"; 94 String toString() => "(\$${left.name} ${negate ? '!=' : '=='} $right)";
91 } 95 }
92 96
93 /// A reference to a variable defined in the environment. The expression 97 /// A reference to a variable defined in the environment. The expression
94 /// evaluates to true if the variable's stringified value is "true". 98 /// evaluates to true if the variable's stringified value is "true".
95 /// 99 /// ```
96 /// $variable 100 /// $variable
101 /// ```
102 /// is equivalent to
103 /// ```
104 /// $variable == true
105 /// ```
106 /// Negates result if [negate] is true, so
107 /// ```
108 /// !$variable
109 /// ```
110 /// is equivalent to
111 /// ```
112 /// $variable != true
113 /// ```
97 class _VariableExpression implements Expression { 114 class _VariableExpression implements Expression {
98 final _Variable variable; 115 final _Variable variable;
116 final bool negate;
99 117
100 _VariableExpression(this.variable); 118 _VariableExpression(this.variable, {this.negate = false});
101 119
102 void validate(List<String> errors) { 120 void validate(List<String> errors) {
103 // It must be a Boolean, so it should allow either Boolean value. 121 // It must be a Boolean, so it should allow either Boolean value.
104 Environment.validate(variable.name, "true", errors); 122 Environment.validate(variable.name, "true", errors);
105 } 123 }
106 124
107 bool evaluate(Environment environment) => 125 bool evaluate(Environment environment) =>
108 variable.lookup(environment) == "true"; 126 negate != (variable.lookup(environment) == "true");
109 127
110 String toString() => "(bool \$${variable.name})"; 128 String toString() => "(bool ${negate ? "! " : ""}\$${variable.name})";
111 } 129 }
112 130
113 /// A logical `||` or `&&` expression. 131 /// A logical `||` or `&&` expression.
114 class _LogicExpression implements Expression { 132 class _LogicExpression implements Expression {
115 /// The operator, `||` or `&&`. 133 /// The operator, `||` or `&&`.
116 final String op; 134 final String op;
117 135
118 final Expression left; 136 final Expression left;
119 final Expression right; 137 final Expression right;
120 138
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
172 Expression _parsePrimary() { 190 Expression _parsePrimary() {
173 if (_scanner.match(_Token.leftParen)) { 191 if (_scanner.match(_Token.leftParen)) {
174 var value = _parseOr(); 192 var value = _parseOr();
175 if (!_scanner.match(_Token.rightParen)) { 193 if (!_scanner.match(_Token.rightParen)) {
176 throw new FormatException("Missing right parenthesis in expression"); 194 throw new FormatException("Missing right parenthesis in expression");
177 } 195 }
178 196
179 return value; 197 return value;
180 } 198 }
181 199
200 var negate = false;
201 if (_scanner.match(_Token.not)) {
202 negate = true;
203 }
204
182 // The only atomic booleans are of the form $variable == value or 205 // The only atomic booleans are of the form $variable == value or
183 // of the form $variable. 206 // of the form $variable.
184 if (!_scanner.match(_Token.dollar)) { 207 if (!_scanner.match(_Token.dollar)) {
185 throw new FormatException( 208 throw new FormatException(
186 "Expected \$ in expression, got ${_scanner.current}"); 209 "Expected \$ in expression, got ${_scanner.current}");
187 } 210 }
188 211
189 if (!_scanner.isIdentifier) { 212 if (!_scanner.isIdentifier) {
190 throw new FormatException( 213 throw new FormatException(
191 "Expected identifier in expression, got ${_scanner.current}"); 214 "Expected identifier in expression, got ${_scanner.current}");
192 } 215 }
193 216
194 var left = new _Variable(_scanner.current); 217 var left = new _Variable(_scanner.current);
195 _scanner.advance(); 218 _scanner.advance();
196 219
197 if (_scanner.current == _Token.equals || 220 if (!negate &&
198 _scanner.current == _Token.notEqual) { 221 (_scanner.current == _Token.equals ||
199 var negate = _scanner.advance() == _Token.notEqual; 222 _scanner.current == _Token.notEqual)) {
223 var isNotEquals = _scanner.advance() == _Token.notEqual;
200 224
201 if (!_scanner.isIdentifier) { 225 if (!_scanner.isIdentifier) {
202 throw new FormatException( 226 throw new FormatException(
203 "Expected value in expression, got ${_scanner.current}"); 227 "Expected value in expression, got ${_scanner.current}");
204 } 228 }
205 229
206 var right = _scanner.advance(); 230 var right = _scanner.advance();
207 return new _ComparisonExpression(left, right, negate); 231 return new _ComparisonExpression(left, right, isNotEquals);
208 } else { 232 } else {
209 return new _VariableExpression(left); 233 return new _VariableExpression(left, negate: negate);
210 } 234 }
211 } 235 }
212 } 236 }
213 237
214 /// An iterator that allows peeking at the current token. 238 /// An iterator that allows peeking at the current token.
215 class _Scanner { 239 class _Scanner {
216 /// Tokens are "(", ")", "$", "&&", "||", "==", "!=", and (maximal) \w+. 240 /// Tokens are "(", ")", "$", "&&", "||", "!", ==", "!=", and (maximal) \w+.
217 static final _testPattern = 241 static final _testPattern = new RegExp(r"^(?:[()$\w\s]|&&|\|\||==|!=?)+$");
218 new RegExp(r"^([()$\w\s]|(\&\&)|(\|\|)|(\=\=)|(\!\=))+$"); 242 static final _tokenPattern = new RegExp(r"[()$]|&&|\|\||==|!=?|\w+");
219 static final _tokenPattern =
220 new RegExp(r"[()$]|(\&\&)|(\|\|)|(\=\=)|(\!\=)|\w+");
221 243
222 static final _identifierPattern = new RegExp(r"^\w+$"); 244 /// Pattern that recognizes identifier tokens.
245 ///
246 /// Only checks the first character, since no non-identifier token can start
247 /// with a word character.
248 static final _identifierPattern = new RegExp(r"^\w");
223 249
224 /// The token strings being iterated. 250 /// The token strings being iterated.
225 final Iterator<String> tokenIterator; 251 final Iterator<String> tokenIterator;
226 252
227 String current; 253 String current;
228 254
229 _Scanner(String expression) : tokenIterator = tokenize(expression).iterator { 255 _Scanner(String expression) : tokenIterator = tokenize(expression).iterator {
230 advance(); 256 advance();
231 } 257 }
232 258
233 static List<String> tokenize(String expression) { 259 static List<String> tokenize(String expression) {
234 if (!_testPattern.hasMatch(expression)) { 260 if (!_testPattern.hasMatch(expression)) {
235 throw new FormatException("Syntax error in '$expression'"); 261 throw new FormatException("Syntax error in '$expression'");
236 } 262 }
237 263
238 return _tokenPattern 264 return _tokenPattern
239 .allMatches(expression) 265 .allMatches(expression)
240 .map((match) => match[0]) 266 .map((match) => match[0])
241 .toList(); 267 .toList();
242 } 268 }
243 269
244 bool get hasMore => current != null; 270 bool get hasMore => current != null;
245 271
246 /// Returns `true` if the current token is an identifier. 272 /// Returns `true` if the current token is an identifier.
247 bool get isIdentifier => _identifierPattern.hasMatch(current); 273 // All non-identifier tokens are one or two characters,
274 // so a longer token must be an identifier.
275 bool get isIdentifier =>
276 current.length > 2 || _identifierPattern.hasMatch(current);
248 277
249 /// If the current token is [token], consumes it and returns `true`. 278 /// If the current token is [token], consumes it and returns `true`.
250 bool match(String token) { 279 bool match(String token) {
251 if (!hasMore || current != token) return false; 280 if (!hasMore || current != token) return false;
252 281
253 advance(); 282 advance();
254 return true; 283 return true;
255 } 284 }
256 285
257 /// Consumes the current token and returns it. 286 /// Consumes the current token and returns it.
258 String advance() { 287 String advance() {
259 var previous = current; 288 var previous = current;
260 current = tokenIterator.moveNext() ? tokenIterator.current : null; 289 current = tokenIterator.moveNext() ? tokenIterator.current : null;
261 return previous; 290 return previous;
262 } 291 }
263 } 292 }
OLDNEW
« no previous file with comments | « tests/standalone/status_expression_test.dart ('k') | tools/testing/dart/status_file.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698