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

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

Issue 2888213002: Revert "Re-apply status file parser changes from 0b7728da1bef08c1c1e092005d9fd8c8bff5fa6c." (Closed)
Patch Set: 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 /// A parsed Boolean expression AST. 5 library status_expression;
6 abstract class Expression { 6
7 /// Parses Boolean expressions in a .status file for Dart. 7 /**
8 /// 8 * Parse and evaluate expressions in a .status file for Dart and V8.
9 /// The grammar is: 9 * There are set expressions and Boolean expressions in a .status file.
10 /// 10 * The grammar is:
11 /// expression := or 11 * BooleanExpression := $variableName == value | $variableName != value |
12 /// or := and ( "||" and )* 12 * $variableName | (BooleanExpression) |
13 /// and := primary ( "&&" primary )* 13 * BooleanExpression && BooleanExpression |
14 /// primary := "$" identifier ( ( "==" | "!=" ) identifier )? | 14 * BooleanExpression || BooleanExpression
15 /// "(" expression ")" 15 *
16 /// identifier := regex "\w+" 16 * SetExpression := value | (SetExpression) |
17 /// 17 * SetExpression || SetExpression |
18 /// Expressions evaluate as expected, with values of variables found in an 18 * SetExpression if BooleanExpression |
19 /// environment passed to the evaluator. 19 * SetExpression , SetExpression
20 static Expression parse(String expression) => 20 *
21 new _ExpressionParser(expression).parse(); 21 * Productions are listed in order of precedence, and the || and , operators
22 22 * both evaluate to set union, but with different precedence.
23 /// Evaluates the expression where all variables are defined by the given 23 *
24 /// [environment]. 24 * Values and variableNames are non-empty strings of word characters, matching
25 bool evaluate(Map<String, dynamic> environment); 25 * the RegExp \w+.
26 } 26 *
27 27 * Expressions evaluate as expected, with values of variables found in
28 /// Keyword token strings. 28 * an environment passed to the evaluator. The SetExpression "value"
29 class _Token { 29 * evaluates to a singleton set containing that value. "A if B" evaluates
30 static const leftParen = "("; 30 * to A if B is true, and to the empty set if B is false.
31 static const rightParen = ")"; 31 */
32 static const dollar = r"$"; 32
33 static const equals = "=="; 33 class ExprEvaluationException {
34 static const notEqual = "!="; 34 String error;
35 static const and = "&&"; 35
36 static const or = "||"; 36 ExprEvaluationException(this.error);
37 } 37
38 38 toString() => error;
39 /// A reference to a variable. 39 }
40 class _Variable { 40
41 final String name; 41 class Token {
42 42 static const String LEFT_PAREN = "(";
43 _Variable(this.name); 43 static const String RIGHT_PAREN = ")";
44 44 static const String DOLLAR_SYMBOL = r"$";
45 String lookup(Map<String, dynamic> environment) { 45 static const String UNION = ",";
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) {
46 var value = environment[name]; 102 var value = environment[name];
47 if (value == null) { 103 if (value == null) {
48 throw new Exception("Could not find '$name' in environment " 104 throw new ExprEvaluationException("Could not find '$name' in environment "
49 "while evaluating status file expression."); 105 "while evaluating status file expression.");
50 } 106 }
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?
58 return value.toString(); 107 return value.toString();
59 } 108 }
60 } 109 }
61 110
62 /// Tests whether a given variable is or is not equal some literal value, as in: 111 class TermConstant {
63 /// 112 String value;
64 /// $variable == someValue 113
65 class _ComparisonExpression implements Expression { 114 TermConstant(String this.value);
66 final _Variable left; 115
67 final String right; 116 String termValue(environment) => value;
68 final bool negate; 117 }
69 118
70 _ComparisonExpression(this.left, this.right, this.negate); 119 class BooleanVariable implements BooleanExpression {
71 120 TermVariable variable;
72 bool evaluate(Map<String, dynamic> environment) { 121
73 return negate != (left.lookup(environment) == right); 122 BooleanVariable(this.variable);
74 } 123
75 124 bool evaluate(environment) => variable.termValue(environment) == 'true';
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
91 String toString() => "(bool \$${variable.name})"; 125 String toString() => "(bool \$${variable.name})";
92 } 126 }
93 127
94 /// A logical `||` or `&&` expression. 128 class BooleanOperation implements BooleanExpression {
95 class _LogicExpression implements Expression { 129 String op;
96 /// The operator, `||` or `&&`. 130 BooleanExpression left;
97 final String op; 131 BooleanExpression right;
98 132
99 final Expression left; 133 BooleanOperation(this.op, this.left, this.right);
100 final Expression right; 134
101 135 bool evaluate(environment) => (op == Token.AND)
102 _LogicExpression(this.op, this.left, this.right);
103
104 bool evaluate(Map<String, dynamic> environment) => (op == _Token.and)
105 ? left.evaluate(environment) && right.evaluate(environment) 136 ? left.evaluate(environment) && right.evaluate(environment)
106 : left.evaluate(environment) || right.evaluate(environment); 137 : left.evaluate(environment) || right.evaluate(environment);
107
108 String toString() => "($left $op $right)"; 138 String toString() => "($left $op $right)";
109 } 139 }
110 140
111 /// Parser for Boolean expressions in a .status file for Dart. 141 class SetUnion implements SetExpression {
112 class _ExpressionParser { 142 SetExpression left;
113 final _Scanner _scanner; 143 SetExpression right;
114 144
115 _ExpressionParser(String expression) : _scanner = new _Scanner(expression); 145 SetUnion(this.left, this.right);
116 146
117 Expression parse() { 147 // Overwrites left.evaluate(env).
118 var expression = _parseOr(); 148 // Set.addAll does not return this.
119 149 Set<String> evaluate(environment) {
120 // Should consume entire string. 150 Set<String> result = left.evaluate(environment);
121 if (_scanner.hasMore) { 151 result.addAll(right.evaluate(environment));
122 throw new FormatException("Unexpected input after expression"); 152 return result;
123 } 153 }
124 154
125 return expression; 155 String toString() => "($left || $right)";
126 } 156 }
127 157
128 Expression _parseOr() { 158 class SetIf implements SetExpression {
129 var left = _parseAnd(); 159 SetExpression left;
130 while (_scanner.match(_Token.or)) { 160 BooleanExpression right;
131 var right = _parseAnd(); 161
132 left = new _LogicExpression(_Token.or, left, right); 162 SetIf(this.left, this.right);
133 } 163
134 164 Set<String> evaluate(environment) => right.evaluate(environment)
135 return left; 165 ? left.evaluate(environment)
136 } 166 : new Set<String>();
137 167 String toString() => "($left if $right)";
138 Expression _parseAnd() { 168 }
139 var left = _parsePrimary(); 169
140 while (_scanner.match(_Token.and)) { 170 class SetConstant implements SetExpression {
141 var right = _parsePrimary(); 171 String value;
142 left = new _LogicExpression(_Token.and, left, right); 172
143 } 173 SetConstant(String v) : value = v.toLowerCase();
144 174
145 return left; 175 Set<String> evaluate(environment) => [value].toSet();
146 } 176 String toString() => value;
147 177 }
148 Expression _parsePrimary() { 178
149 if (_scanner.match(_Token.leftParen)) { 179 // An iterator that allows peeking at the current token.
150 var value = _parseOr(); 180 class Scanner {
151 if (!_scanner.match(_Token.rightParen)) { 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;
212 }
213
214 SetExpression parseSetIf() {
215 SetExpression left = parseSetOr();
216 while (scanner.hasMore() && scanner.current == "if") {
217 scanner.advance();
218 BooleanExpression right = parseBooleanExpression();
219 left = new SetIf(left, right);
220 }
221 return left;
222 }
223
224 SetExpression parseSetOr() {
225 SetExpression left = parseSetAtomic();
226 while (scanner.hasMore() && scanner.current == Token.OR) {
227 scanner.advance();
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) {
152 throw new FormatException("Missing right parenthesis in expression"); 239 throw new FormatException("Missing right parenthesis in expression");
153 } 240 }
154 241 scanner.advance();
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();
155 return value; 283 return value;
156 } 284 }
157 285
158 // The only atomic booleans are of the form $variable == value or 286 // The only atomic booleans are of the form $variable == value or
159 // of the form $variable. 287 // of the form $variable.
160 if (!_scanner.match(_Token.dollar)) { 288 if (scanner.current != Token.DOLLAR_SYMBOL) {
161 throw new FormatException( 289 throw new FormatException(
162 "Expected \$ in expression, got ${_scanner.current}"); 290 "Expected \$ in expression, got ${scanner.current}");
163 } 291 }
164 292 scanner.advance();
165 if (!_scanner.isIdentifier) { 293 if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) {
166 throw new FormatException( 294 throw new FormatException(
167 "Expected identifier in expression, got ${_scanner.current}"); 295 "Expected identifier in expression, got ${scanner.current}");
168 } 296 }
169 297 TermVariable left = new TermVariable(scanner.current);
170 var left = new _Variable(_scanner.current); 298 scanner.advance();
171 _scanner.advance(); 299 if (scanner.current == Token.EQUALS ||
172 300 scanner.current == Token.NOT_EQUALS) {
173 if (_scanner.current == _Token.equals || 301 bool negate = scanner.current == Token.NOT_EQUALS;
174 _scanner.current == _Token.notEqual) { 302 scanner.advance();
175 var negate = _scanner.advance() == _Token.notEqual; 303 if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) {
176
177 if (!_scanner.isIdentifier) {
178 throw new FormatException( 304 throw new FormatException(
179 "Expected value in expression, got ${_scanner.current}"); 305 "Expected value in expression, got ${scanner.current}");
180 } 306 }
181 307 TermConstant right = new TermConstant(scanner.current);
182 var right = _scanner.advance(); 308 scanner.advance();
183 return new _ComparisonExpression(left, right, negate); 309 return new Comparison(left, right, negate);
184 } else { 310 } else {
185 return new _VariableExpression(left); 311 return new BooleanVariable(left);
186 } 312 }
187 } 313 }
188 } 314 }
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