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

Side by Side Diff: pkg/testing/lib/src/test_dart/status_expression.dart

Issue 2624373003: Move package:testing to SDK. (Closed)
Patch Set: Created 3 years, 11 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
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library status_expression;
6
7 /**
8 * Parse and evaluate expressions in a .status file for Dart and V8.
9 * There are set expressions and Boolean expressions in a .status file.
10 * The grammar is:
11 * BooleanExpression := $variableName == value | $variableName != value |
12 * $variableName | (BooleanExpression) |
13 * BooleanExpression && BooleanExpression |
14 * BooleanExpression || BooleanExpression
15 *
16 * SetExpression := value | (SetExpression) |
17 * SetExpression || SetExpression |
18 * SetExpression if BooleanExpression |
19 * SetExpression , SetExpression
20 *
21 * Productions are listed in order of precedence, and the || and , operators
22 * both evaluate to set union, but with different precedence.
23 *
24 * Values and variableNames are non-empty strings of word characters, matching
25 * the RegExp \w+.
26 *
27 * Expressions evaluate as expected, with values of variables found in
28 * an environment passed to the evaluator. The SetExpression "value"
29 * evaluates to a singleton set containing that value. "A if B" evaluates
30 * to A if B is true, and to the empty set if B is false.
31 */
32
33 class ExprEvaluationException {
34 String error;
35
36 ExprEvaluationException(this.error);
37
38 toString() => error;
39 }
40
41 class Token {
42 static const String LEFT_PAREN = "(";
43 static const String RIGHT_PAREN = ")";
44 static const String DOLLAR_SYMBOL = r"$";
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) {
102 var value = environment[name];
103 if (value == null) {
104 throw new ExprEvaluationException("Could not find '$name' in environment "
105 "while evaluating status file expression.");
106 }
107 return value.toString();
108 }
109 }
110
111 class TermConstant {
112 String value;
113
114 TermConstant(String this.value);
115
116 String termValue(environment) => value;
117 }
118
119 class BooleanVariable implements BooleanExpression {
120 TermVariable variable;
121
122 BooleanVariable(this.variable);
123
124 bool evaluate(environment) => variable.termValue(environment) == 'true';
125 String toString() => "(bool \$${variable.name})";
126 }
127
128 class BooleanOperation implements BooleanExpression {
129 String op;
130 BooleanExpression left;
131 BooleanExpression right;
132
133 BooleanOperation(this.op, this.left, this.right);
134
135 bool evaluate(environment) => (op == Token.AND)
136 ? left.evaluate(environment) && right.evaluate(environment)
137 : left.evaluate(environment) || right.evaluate(environment);
138 String toString() => "($left $op $right)";
139 }
140
141 class SetUnion implements SetExpression {
142 SetExpression left;
143 SetExpression right;
144
145 SetUnion(this.left, this.right);
146
147 // Overwrites left.evaluate(env).
148 // Set.addAll does not return this.
149 Set<String> evaluate(environment) {
150 Set<String> result = left.evaluate(environment);
151 result.addAll(right.evaluate(environment));
152 return result;
153 }
154
155 String toString() => "($left || $right)";
156 }
157
158 class SetIf implements SetExpression {
159 SetExpression left;
160 BooleanExpression right;
161
162 SetIf(this.left, this.right);
163
164 Set<String> evaluate(environment) => right.evaluate(environment)
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) => new Set<String>.from([value]);
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;
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) {
239 throw new FormatException("Missing right parenthesis in expression");
240 }
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();
283 return value;
284 }
285
286 // The only atomic booleans are of the form $variable == value or
287 // of the form $variable.
288 if (scanner.current != Token.DOLLAR_SYMBOL) {
289 throw new FormatException(
290 "Expected \$ in expression, got ${scanner.current}");
291 }
292 scanner.advance();
293 if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) {
294 throw new FormatException(
295 "Expected identifier in expression, got ${scanner.current}");
296 }
297 TermVariable left = new TermVariable(scanner.current);
298 scanner.advance();
299 if (scanner.current == Token.EQUALS ||
300 scanner.current == Token.NOT_EQUALS) {
301 bool negate = scanner.current == Token.NOT_EQUALS;
302 scanner.advance();
303 if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) {
304 throw new FormatException(
305 "Expected value in expression, got ${scanner.current}");
306 }
307 TermConstant right = new TermConstant(scanner.current);
308 scanner.advance();
309 return new Comparison(left, right, negate);
310 } else {
311 return new BooleanVariable(left);
312 }
313 }
314 }
OLDNEW
« no previous file with comments | « pkg/testing/lib/src/test_dart/path.dart ('k') | pkg/testing/lib/src/test_dart/status_file_parser.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698