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

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

Issue 2885623002: Revert "Refactor and clean up the status file parsing code." (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 dynamic 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 107 return value.toString();
52 return value; 108 }
53 } 109 }
54 } 110
55 111 class TermConstant {
56 /// Tests whether a given variable is or is not equal some literal value, as in: 112 String value;
57 /// 113
58 /// $variable == someValue 114 TermConstant(String this.value);
59 class _ComparisonExpression implements Expression { 115
60 final _Variable left; 116 String termValue(environment) => value;
61 final String right; 117 }
62 final bool negate; 118
63 119 class BooleanVariable implements BooleanExpression {
64 _ComparisonExpression(this.left, this.right, this.negate); 120 TermVariable variable;
65 121
66 bool evaluate(Map<String, dynamic> environment) { 122 BooleanVariable(this.variable);
67 return negate != (left.lookup(environment) == right); 123
68 } 124 bool evaluate(environment) => variable.termValue(environment) == 'true';
69
70 String toString() => "(\$${left.name} ${negate ? '!=' : '=='} $right)";
71 }
72
73 /// A reference to a variable defined in the environment. The variable's value
74 /// should be Boolean and the expression is true when the value is "true".
75 ///
76 /// $variable
77 class _VariableExpression implements Expression {
78 final _Variable variable;
79
80 _VariableExpression(this.variable);
81
82 bool evaluate(Map<String, dynamic> environment) =>
83 variable.lookup(environment) == 'true';
84
85 String toString() => "(bool \$${variable.name})"; 125 String toString() => "(bool \$${variable.name})";
86 } 126 }
87 127
88 /// A logical `||` or `&&` expression. 128 class BooleanOperation implements BooleanExpression {
89 class _LogicExpression implements Expression { 129 String op;
90 /// The operator, `||` or `&&`. 130 BooleanExpression left;
91 final String op; 131 BooleanExpression right;
92 132
93 final Expression left; 133 BooleanOperation(this.op, this.left, this.right);
94 final Expression right; 134
95 135 bool evaluate(environment) => (op == Token.AND)
96 _LogicExpression(this.op, this.left, this.right);
97
98 bool evaluate(Map<String, dynamic> environment) => (op == _Token.and)
99 ? left.evaluate(environment) && right.evaluate(environment) 136 ? left.evaluate(environment) && right.evaluate(environment)
100 : left.evaluate(environment) || right.evaluate(environment); 137 : left.evaluate(environment) || right.evaluate(environment);
101
102 String toString() => "($left $op $right)"; 138 String toString() => "($left $op $right)";
103 } 139 }
104 140
105 /// Parser for Boolean expressions in a .status file for Dart. 141 class SetUnion implements SetExpression {
106 class _ExpressionParser { 142 SetExpression left;
107 final _Scanner _scanner; 143 SetExpression right;
108 144
109 _ExpressionParser(String expression) : _scanner = new _Scanner(expression); 145 SetUnion(this.left, this.right);
110 146
111 Expression parse() { 147 // Overwrites left.evaluate(env).
112 var expression = _parseOr(); 148 // Set.addAll does not return this.
113 149 Set<String> evaluate(environment) {
114 // Should consume entire string. 150 Set<String> result = left.evaluate(environment);
115 if (_scanner.hasMore) { 151 result.addAll(right.evaluate(environment));
116 throw new FormatException("Unexpected input after expression"); 152 return result;
117 } 153 }
118 154
119 return expression; 155 String toString() => "($left || $right)";
120 } 156 }
121 157
122 Expression _parseOr() { 158 class SetIf implements SetExpression {
123 var left = _parseAnd(); 159 SetExpression left;
124 while (_scanner.match(_Token.or)) { 160 BooleanExpression right;
125 var right = _parseAnd(); 161
126 left = new _LogicExpression(_Token.or, left, right); 162 SetIf(this.left, this.right);
127 } 163
128 164 Set<String> evaluate(environment) => right.evaluate(environment)
129 return left; 165 ? left.evaluate(environment)
130 } 166 : new Set<String>();
131 167 String toString() => "($left if $right)";
132 Expression _parseAnd() { 168 }
133 var left = _parsePrimary(); 169
134 while (_scanner.match(_Token.and)) { 170 class SetConstant implements SetExpression {
135 var right = _parsePrimary(); 171 String value;
136 left = new _LogicExpression(_Token.and, left, right); 172
137 } 173 SetConstant(String v) : value = v.toLowerCase();
138 174
139 return left; 175 Set<String> evaluate(environment) => [value].toSet();
140 } 176 String toString() => value;
141 177 }
142 Expression _parsePrimary() { 178
143 if (_scanner.match(_Token.leftParen)) { 179 // An iterator that allows peeking at the current token.
144 var value = _parseOr(); 180 class Scanner {
145 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) {
146 throw new FormatException("Missing right parenthesis in expression"); 239 throw new FormatException("Missing right parenthesis in expression");
147 } 240 }
148 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();
149 return value; 283 return value;
150 } 284 }
151 285
152 // The only atomic booleans are of the form $variable == value or 286 // The only atomic booleans are of the form $variable == value or
153 // of the form $variable. 287 // of the form $variable.
154 if (!_scanner.match(_Token.dollar)) { 288 if (scanner.current != Token.DOLLAR_SYMBOL) {
155 throw new FormatException( 289 throw new FormatException(
156 "Expected \$ in expression, got ${_scanner.current}"); 290 "Expected \$ in expression, got ${scanner.current}");
157 } 291 }
158 292 scanner.advance();
159 if (!_scanner.isIdentifier) { 293 if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) {
160 throw new FormatException( 294 throw new FormatException(
161 "Expected identifier in expression, got ${_scanner.current}"); 295 "Expected identifier in expression, got ${scanner.current}");
162 } 296 }
163 297 TermVariable left = new TermVariable(scanner.current);
164 var left = new _Variable(_scanner.current); 298 scanner.advance();
165 _scanner.advance(); 299 if (scanner.current == Token.EQUALS ||
166 300 scanner.current == Token.NOT_EQUALS) {
167 if (_scanner.current == _Token.equals || 301 bool negate = scanner.current == Token.NOT_EQUALS;
168 _scanner.current == _Token.notEqual) { 302 scanner.advance();
169 var negate = _scanner.advance() == _Token.notEqual; 303 if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) {
170
171 if (!_scanner.isIdentifier) {
172 throw new FormatException( 304 throw new FormatException(
173 "Expected value in expression, got ${_scanner.current}"); 305 "Expected value in expression, got ${scanner.current}");
174 } 306 }
175 307 TermConstant right = new TermConstant(scanner.current);
176 var right = _scanner.advance(); 308 scanner.advance();
177 return new _ComparisonExpression(left, right, negate); 309 return new Comparison(left, right, negate);
178 } else { 310 } else {
179 return new _VariableExpression(left); 311 return new BooleanVariable(left);
180 } 312 }
181 } 313 }
182 } 314 }
183
184 /// An iterator that allows peeking at the current token.
185 class _Scanner {
186 /// Tokens are "(", ")", "$", "&&", "||", "==", "!=", and (maximal) \w+.
187 static final _testPattern =
188 new RegExp(r"^([()$\w\s]|(\&\&)|(\|\|)|(\=\=)|(\!\=))+$");
189 static final _tokenPattern =
190 new RegExp(r"[()$]|(\&\&)|(\|\|)|(\=\=)|(\!\=)|\w+");
191
192 static final _identifierPattern = new RegExp(r"^\w+$");
193
194 /// The token strings being iterated.
195 final Iterator<String> tokenIterator;
196
197 String current;
198
199 _Scanner(String expression) : tokenIterator = tokenize(expression).iterator {
200 advance();
201 }
202
203 static List<String> tokenize(String expression) {
204 if (!_testPattern.hasMatch(expression)) {
205 throw new FormatException("Syntax error in '$expression'");
206 }
207
208 return _tokenPattern
209 .allMatches(expression)
210 .map((match) => match[0])
211 .toList();
212 }
213
214 bool get hasMore => current != null;
215
216 /// Returns `true` if the current token is an identifier.
217 bool get isIdentifier => _identifierPattern.hasMatch(current);
218
219 /// If the current token is [token], consumes it and returns `true`.
220 bool match(String token) {
221 if (!hasMore || current != token) return false;
222
223 advance();
224 return true;
225 }
226
227 /// Consumes the current token and returns it.
228 String advance() {
229 var previous = current;
230 current = tokenIterator.moveNext() ? tokenIterator.current : null;
231 return previous;
232 }
233 }
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