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

Side by Side 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 unified diff | 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 »
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 library status_expression; 5 /// A parsed Boolean expression AST.
6 6 abstract class Expression {
7 /** 7 /// Parses Boolean expressions in a .status file for Dart.
8 * Parse and evaluate expressions in a .status file for Dart and V8. 8 ///
9 * There are set expressions and Boolean expressions in a .status file. 9 /// The grammar is:
10 * The grammar is: 10 ///
11 * BooleanExpression := $variableName == value | $variableName != value | 11 /// expression := or
12 * $variableName | (BooleanExpression) | 12 /// or := and ( "||" and )*
13 * BooleanExpression && BooleanExpression | 13 /// and := primary ( "&&" primary )*
14 * BooleanExpression || BooleanExpression 14 /// primary := "$" identifier ( ( "==" | "!=" ) identifier )? |
15 * 15 /// "(" expression ")"
16 * SetExpression := value | (SetExpression) | 16 /// identifier := regex "\w+"
17 * SetExpression || SetExpression | 17 ///
18 * SetExpression if BooleanExpression | 18 /// Expressions evaluate as expected, with values of variables found in an
19 * SetExpression , SetExpression 19 /// environment passed to the evaluator.
20 * 20 static Expression parse(String expression) =>
21 * Productions are listed in order of precedence, and the || and , operators 21 new _ExpressionParser(expression).parse();
22 * both evaluate to set union, but with different precedence. 22
23 * 23 /// Evaluates the expression where all variables are defined by the given
24 * Values and variableNames are non-empty strings of word characters, matching 24 /// [environment].
25 * the RegExp \w+. 25 bool evaluate(Map<String, dynamic> environment);
26 * 26 }
27 * Expressions evaluate as expected, with values of variables found in 27
28 * an environment passed to the evaluator. The SetExpression "value" 28 /// Keyword token strings.
29 * evaluates to a singleton set containing that value. "A if B" evaluates 29 class _Token {
30 * to A if B is true, and to the empty set if B is false. 30 static const leftParen = "(";
31 */ 31 static const rightParen = ")";
32 32 static const dollar = r"$";
33 class ExprEvaluationException { 33 static const equals = "==";
34 String error; 34 static const notEqual = "!=";
35 35 static const and = "&&";
36 ExprEvaluationException(this.error); 36 static const or = "||";
37 37 }
38 toString() => error; 38
39 } 39 /// A reference to a variable.
40 40 class _Variable {
41 class Token { 41 final String name;
42 static const String LEFT_PAREN = "("; 42
43 static const String RIGHT_PAREN = ")"; 43 _Variable(this.name);
44 static const String DOLLAR_SYMBOL = r"$"; 44
45 static const String UNION = ","; 45 String lookup(Map<String, dynamic> environment) {
46 static const String EQUALS = "==";
47 static const String NOT_EQUALS = "!=";
48 static const String AND = "&&";
49 static const String OR = "||";
50 }
51
52 class Tokenizer {
53 String expression;
54 List<String> tokens;
55
56 Tokenizer(String this.expression) : tokens = new List<String>();
57
58 // Tokens are : "(", ")", "$", ",", "&&", "||", "==", "!=", and (maximal) \w+.
59 static final testRegexp =
60 new RegExp(r"^([()$\w\s,]|(\&\&)|(\|\|)|(\=\=)|(\!\=))+$");
61 static final regexp = new RegExp(r"[()$,]|(\&\&)|(\|\|)|(\=\=)|(\!\=)|\w+");
62
63 List<String> tokenize() {
64 if (!testRegexp.hasMatch(expression)) {
65 throw new FormatException("Syntax error in '$expression'");
66 }
67 for (Match match in regexp.allMatches(expression)) tokens.add(match[0]);
68 return tokens;
69 }
70 }
71
72 abstract class BooleanExpression {
73 bool evaluate(Map<String, String> environment);
74 }
75
76 abstract class SetExpression {
77 Set<String> evaluate(Map<String, String> environment);
78 }
79
80 class Comparison implements BooleanExpression {
81 TermVariable left;
82 TermConstant right;
83 bool negate;
84
85 Comparison(this.left, this.right, this.negate);
86
87 bool evaluate(environment) {
88 return negate !=
89 (left.termValue(environment) == right.termValue(environment));
90 }
91
92 String toString() =>
93 "(\$${left.name} ${negate ? '!=' : '=='} ${right.value})";
94 }
95
96 class TermVariable {
97 String name;
98
99 TermVariable(this.name);
100
101 String termValue(environment) {
102 var value = environment[name]; 46 var value = environment[name];
103 if (value == null) { 47 if (value == null) {
104 throw new ExprEvaluationException("Could not find '$name' in environment " 48 throw new Exception("Could not find '$name' in environment "
105 "while evaluating status file expression."); 49 "while evaluating status file expression.");
106 } 50 }
51
52 // Explicitly stringify all values so that things like:
53 //
54 // $strong == true
55 //
56 // work correctly even though "true" is treated as a string here.
57 // TODO(rnystrom): Is there a cleaner/safer way to do this?
107 return value.toString(); 58 return value.toString();
108 } 59 }
109 } 60 }
110 61
111 class TermConstant { 62 /// Tests whether a given variable is or is not equal some literal value, as in:
112 String value; 63 ///
113 64 /// $variable == someValue
114 TermConstant(String this.value); 65 class _ComparisonExpression implements Expression {
115 66 final _Variable left;
116 String termValue(environment) => value; 67 final String right;
117 } 68 final bool negate;
118 69
119 class BooleanVariable implements BooleanExpression { 70 _ComparisonExpression(this.left, this.right, this.negate);
120 TermVariable variable; 71
121 72 bool evaluate(Map<String, dynamic> environment) {
122 BooleanVariable(this.variable); 73 return negate != (left.lookup(environment) == right);
123 74 }
124 bool evaluate(environment) => variable.termValue(environment) == 'true'; 75
76 String toString() => "(\$${left.name} ${negate ? '!=' : '=='} $right)";
77 }
78
79 /// A reference to a variable defined in the environment. The expression
80 /// evaluates to true if the variable's stringified value is "true".
81 ///
82 /// $variable
83 class _VariableExpression implements Expression {
84 final _Variable variable;
85
86 _VariableExpression(this.variable);
87
88 bool evaluate(Map<String, dynamic> environment) =>
89 variable.lookup(environment) == "true";
90
125 String toString() => "(bool \$${variable.name})"; 91 String toString() => "(bool \$${variable.name})";
126 } 92 }
127 93
128 class BooleanOperation implements BooleanExpression { 94 /// A logical `||` or `&&` expression.
129 String op; 95 class _LogicExpression implements Expression {
130 BooleanExpression left; 96 /// The operator, `||` or `&&`.
131 BooleanExpression right; 97 final String op;
132 98
133 BooleanOperation(this.op, this.left, this.right); 99 final Expression left;
134 100 final Expression right;
135 bool evaluate(environment) => (op == Token.AND) 101
102 _LogicExpression(this.op, this.left, this.right);
103
104 bool evaluate(Map<String, dynamic> environment) => (op == _Token.and)
136 ? left.evaluate(environment) && right.evaluate(environment) 105 ? left.evaluate(environment) && right.evaluate(environment)
137 : left.evaluate(environment) || right.evaluate(environment); 106 : left.evaluate(environment) || right.evaluate(environment);
107
138 String toString() => "($left $op $right)"; 108 String toString() => "($left $op $right)";
139 } 109 }
140 110
141 class SetUnion implements SetExpression { 111 /// Parser for Boolean expressions in a .status file for Dart.
142 SetExpression left; 112 class _ExpressionParser {
143 SetExpression right; 113 final _Scanner _scanner;
144 114
145 SetUnion(this.left, this.right); 115 _ExpressionParser(String expression) : _scanner = new _Scanner(expression);
146 116
147 // Overwrites left.evaluate(env). 117 Expression parse() {
148 // Set.addAll does not return this. 118 var expression = _parseOr();
149 Set<String> evaluate(environment) { 119
150 Set<String> result = left.evaluate(environment); 120 // Should consume entire string.
151 result.addAll(right.evaluate(environment)); 121 if (_scanner.hasMore) {
152 return result; 122 throw new FormatException("Unexpected input after expression");
153 } 123 }
154 124
155 String toString() => "($left || $right)"; 125 return expression;
156 } 126 }
157 127
158 class SetIf implements SetExpression { 128 Expression _parseOr() {
159 SetExpression left; 129 var left = _parseAnd();
160 BooleanExpression right; 130 while (_scanner.match(_Token.or)) {
161 131 var right = _parseAnd();
162 SetIf(this.left, this.right); 132 left = new _LogicExpression(_Token.or, left, right);
163 133 }
164 Set<String> evaluate(environment) => right.evaluate(environment) 134
165 ? left.evaluate(environment)
166 : new Set<String>();
167 String toString() => "($left if $right)";
168 }
169
170 class SetConstant implements SetExpression {
171 String value;
172
173 SetConstant(String v) : value = v.toLowerCase();
174
175 Set<String> evaluate(environment) => [value].toSet();
176 String toString() => value;
177 }
178
179 // An iterator that allows peeking at the current token.
180 class Scanner {
181 List<String> tokens;
182 Iterator tokenIterator;
183 String current;
184
185 Scanner(this.tokens) {
186 tokenIterator = tokens.iterator;
187 advance();
188 }
189
190 bool hasMore() => current != null;
191
192 void advance() {
193 current = tokenIterator.moveNext() ? tokenIterator.current : null;
194 }
195 }
196
197 class ExpressionParser {
198 Scanner scanner;
199
200 ExpressionParser(this.scanner);
201
202 SetExpression parseSetExpression() => parseSetUnion();
203
204 SetExpression parseSetUnion() {
205 SetExpression left = parseSetIf();
206 while (scanner.hasMore() && scanner.current == Token.UNION) {
207 scanner.advance();
208 SetExpression right = parseSetIf();
209 left = new SetUnion(left, right);
210 }
211 return left; 135 return left;
212 } 136 }
213 137
214 SetExpression parseSetIf() { 138 Expression _parseAnd() {
215 SetExpression left = parseSetOr(); 139 var left = _parsePrimary();
216 while (scanner.hasMore() && scanner.current == "if") { 140 while (_scanner.match(_Token.and)) {
217 scanner.advance(); 141 var right = _parsePrimary();
218 BooleanExpression right = parseBooleanExpression(); 142 left = new _LogicExpression(_Token.and, left, right);
219 left = new SetIf(left, right); 143 }
220 } 144
221 return left; 145 return left;
222 } 146 }
223 147
224 SetExpression parseSetOr() { 148 Expression _parsePrimary() {
225 SetExpression left = parseSetAtomic(); 149 if (_scanner.match(_Token.leftParen)) {
226 while (scanner.hasMore() && scanner.current == Token.OR) { 150 var value = _parseOr();
227 scanner.advance(); 151 if (!_scanner.match(_Token.rightParen)) {
228 SetExpression right = parseSetAtomic();
229 left = new SetUnion(left, right);
230 }
231 return left;
232 }
233
234 SetExpression parseSetAtomic() {
235 if (scanner.current == Token.LEFT_PAREN) {
236 scanner.advance();
237 SetExpression value = parseSetExpression();
238 if (scanner.current != Token.RIGHT_PAREN) {
239 throw new FormatException("Missing right parenthesis in expression"); 152 throw new FormatException("Missing right parenthesis in expression");
240 } 153 }
241 scanner.advance(); 154
242 return value;
243 }
244 if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) {
245 throw new FormatException(
246 "Expected identifier in expression, got ${scanner.current}");
247 }
248 SetExpression value = new SetConstant(scanner.current);
249 scanner.advance();
250 return value;
251 }
252
253 BooleanExpression parseBooleanExpression() => parseBooleanOr();
254
255 BooleanExpression parseBooleanOr() {
256 BooleanExpression left = parseBooleanAnd();
257 while (scanner.hasMore() && scanner.current == Token.OR) {
258 scanner.advance();
259 BooleanExpression right = parseBooleanAnd();
260 left = new BooleanOperation(Token.OR, left, right);
261 }
262 return left;
263 }
264
265 BooleanExpression parseBooleanAnd() {
266 BooleanExpression left = parseBooleanAtomic();
267 while (scanner.hasMore() && scanner.current == Token.AND) {
268 scanner.advance();
269 BooleanExpression right = parseBooleanAtomic();
270 left = new BooleanOperation(Token.AND, left, right);
271 }
272 return left;
273 }
274
275 BooleanExpression parseBooleanAtomic() {
276 if (scanner.current == Token.LEFT_PAREN) {
277 scanner.advance();
278 BooleanExpression value = parseBooleanExpression();
279 if (scanner.current != Token.RIGHT_PAREN) {
280 throw new FormatException("Missing right parenthesis in expression");
281 }
282 scanner.advance();
283 return value; 155 return value;
284 } 156 }
285 157
286 // The only atomic booleans are of the form $variable == value or 158 // The only atomic booleans are of the form $variable == value or
287 // of the form $variable. 159 // of the form $variable.
288 if (scanner.current != Token.DOLLAR_SYMBOL) { 160 if (!_scanner.match(_Token.dollar)) {
289 throw new FormatException( 161 throw new FormatException(
290 "Expected \$ in expression, got ${scanner.current}"); 162 "Expected \$ in expression, got ${_scanner.current}");
291 } 163 }
292 scanner.advance(); 164
293 if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) { 165 if (!_scanner.isIdentifier) {
294 throw new FormatException( 166 throw new FormatException(
295 "Expected identifier in expression, got ${scanner.current}"); 167 "Expected identifier in expression, got ${_scanner.current}");
296 } 168 }
297 TermVariable left = new TermVariable(scanner.current); 169
298 scanner.advance(); 170 var left = new _Variable(_scanner.current);
299 if (scanner.current == Token.EQUALS || 171 _scanner.advance();
300 scanner.current == Token.NOT_EQUALS) { 172
301 bool negate = scanner.current == Token.NOT_EQUALS; 173 if (_scanner.current == _Token.equals ||
302 scanner.advance(); 174 _scanner.current == _Token.notEqual) {
303 if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) { 175 var negate = _scanner.advance() == _Token.notEqual;
176
177 if (!_scanner.isIdentifier) {
304 throw new FormatException( 178 throw new FormatException(
305 "Expected value in expression, got ${scanner.current}"); 179 "Expected value in expression, got ${_scanner.current}");
306 } 180 }
307 TermConstant right = new TermConstant(scanner.current); 181
308 scanner.advance(); 182 var right = _scanner.advance();
309 return new Comparison(left, right, negate); 183 return new _ComparisonExpression(left, right, negate);
310 } else { 184 } else {
311 return new BooleanVariable(left); 185 return new _VariableExpression(left);
312 } 186 }
313 } 187 }
314 } 188 }
189
190 /// An iterator that allows peeking at the current token.
191 class _Scanner {
192 /// Tokens are "(", ")", "$", "&&", "||", "==", "!=", and (maximal) \w+.
193 static final _testPattern =
194 new RegExp(r"^([()$\w\s]|(\&\&)|(\|\|)|(\=\=)|(\!\=))+$");
195 static final _tokenPattern =
196 new RegExp(r"[()$]|(\&\&)|(\|\|)|(\=\=)|(\!\=)|\w+");
197
198 static final _identifierPattern = new RegExp(r"^\w+$");
199
200 /// The token strings being iterated.
201 final Iterator<String> tokenIterator;
202
203 String current;
204
205 _Scanner(String expression) : tokenIterator = tokenize(expression).iterator {
206 advance();
207 }
208
209 static List<String> tokenize(String expression) {
210 if (!_testPattern.hasMatch(expression)) {
211 throw new FormatException("Syntax error in '$expression'");
212 }
213
214 return _tokenPattern
215 .allMatches(expression)
216 .map((match) => match[0])
217 .toList();
218 }
219
220 bool get hasMore => current != null;
221
222 /// Returns `true` if the current token is an identifier.
223 bool get isIdentifier => _identifierPattern.hasMatch(current);
224
225 /// If the current token is [token], consumes it and returns `true`.
226 bool match(String token) {
227 if (!hasMore || current != token) return false;
228
229 advance();
230 return true;
231 }
232
233 /// Consumes the current token and returns it.
234 String advance() {
235 var previous = current;
236 current = tokenIterator.moveNext() ? tokenIterator.current : null;
237 return previous;
238 }
239 }
OLDNEW
« 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