OLD | NEW |
---|---|
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 library status_expression; |
6 | 6 |
7 /** | 7 /** |
8 * Parse and evaluate expressions in a .status file for Dart and V8. | 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. | 9 * There are set expressions and Boolean expressions in a .status file. |
10 * The grammar is: | 10 * The grammar is: |
11 * BooleanExpression := $variableName == value | $variableName | | 11 * BooleanExpression := $variableName == value | $variableName != value | |
12 * (BooleanExpression) | | 12 * $variableName | (BooleanExpression) | |
13 * BooleanExpression && BooleanExpression | | 13 * BooleanExpression && BooleanExpression | |
14 * BooleanExpression || BooleanExpression | 14 * BooleanExpression || BooleanExpression |
15 * | 15 * |
16 * SetExpression := value | (SetExpression) | | 16 * SetExpression := value | (SetExpression) | |
17 * SetExpression || SetExpression | | 17 * SetExpression || SetExpression | |
18 * SetExpression if BooleanExpression | | 18 * SetExpression if BooleanExpression | |
19 * SetExpression , SetExpression | 19 * SetExpression , SetExpression |
20 * | 20 * |
21 * Productions are listed in order of precedence, and the || and , operators | 21 * Productions are listed in order of precedence, and the || and , operators |
22 * both evaluate to set union, but with different precedence. | 22 * both evaluate to set union, but with different precedence. |
23 * | 23 * |
24 * Values and variableNames are non-empty strings of word characters, matching | 24 * Values and variableNames are non-empty strings of word characters, matching |
25 * the RegExp \w+. | 25 * the RegExp \w+. |
26 * | 26 * |
27 * Expressions evaluate as expected, with values of variables found in | 27 * Expressions evaluate as expected, with values of variables found in |
28 * an environment passed to the evaluator. The SetExpression "value" | 28 * an environment passed to the evaluator. The SetExpression "value" |
29 * evaluates to a singleton set containing that value. "A if B" evaluates | 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. | 30 * to A if B is true, and to the empty set if B is false. |
31 */ | 31 */ |
32 | 32 |
33 | 33 |
34 class Token { | 34 class Token { |
35 static const String LEFT_PAREN = "("; | 35 static const String LEFT_PAREN = "("; |
36 static const String RIGHT_PAREN = ")"; | 36 static const String RIGHT_PAREN = ")"; |
37 static const String DOLLAR_SYMBOL = r"$"; | 37 static const String DOLLAR_SYMBOL = r"$"; |
38 static const String UNION = ","; | 38 static const String UNION = ","; |
39 static const String EQUALS = "=="; | 39 static const String EQUALS = "=="; |
40 static const String NOT_EQUALS = "!="; | |
40 static const String AND = "&&"; | 41 static const String AND = "&&"; |
41 static const String OR = "||"; | 42 static const String OR = "||"; |
42 } | 43 } |
43 | 44 |
44 | 45 |
45 class Tokenizer { | 46 class Tokenizer { |
46 String expression; | 47 String expression; |
47 List<String> tokens; | 48 List<String> tokens; |
48 | 49 |
49 Tokenizer(String this.expression) | 50 Tokenizer(String this.expression) |
50 : tokens = new List<String>(); | 51 : tokens = new List<String>(); |
51 | 52 |
52 // Tokens are : "(", ")", "$", ",", "&&", "||", "==", and (maximal) \w+. | 53 // Tokens are : "(", ")", "$", ",", "&&", "||", "==", "!=", and (maximal) \w+ . |
ricow1
2013/05/24 10:54:38
Long line
kustermann
2013/05/24 11:00:34
Done.
| |
53 static final testRegexp = | 54 static final testRegexp = |
54 new RegExp(r"^([()$\w\s,]|(\&\&)|(\|\|)|(\=\=))+$"); | 55 new RegExp(r"^([()$\w\s,]|(\&\&)|(\|\|)|(\=\=)|(\!\=))+$"); |
55 static final regexp = new RegExp(r"[()$,]|(\&\&)|(\|\|)|(\=\=)|\w+"); | 56 static final regexp = new RegExp(r"[()$,]|(\&\&)|(\|\|)|(\=\=)|(\!\=)|\w+"); |
56 | 57 |
57 List<String> tokenize() { | 58 List<String> tokenize() { |
58 if (!testRegexp.hasMatch(expression)) { | 59 if (!testRegexp.hasMatch(expression)) { |
59 throw new FormatException("Syntax error in '$expression'"); | 60 throw new FormatException("Syntax error in '$expression'"); |
60 } | 61 } |
61 for (Match match in regexp.allMatches(expression)) tokens.add(match[0]); | 62 for (Match match in regexp.allMatches(expression)) tokens.add(match[0]); |
62 return tokens; | 63 return tokens; |
63 } | 64 } |
64 } | 65 } |
65 | 66 |
66 | 67 |
67 abstract class BooleanExpression { | 68 abstract class BooleanExpression { |
68 bool evaluate(Map<String, String> environment); | 69 bool evaluate(Map<String, String> environment); |
69 } | 70 } |
70 | 71 |
71 | 72 |
72 abstract class SetExpression { | 73 abstract class SetExpression { |
73 Set<String> evaluate(Map<String, String> environment); | 74 Set<String> evaluate(Map<String, String> environment); |
74 } | 75 } |
75 | 76 |
76 | 77 |
77 class Comparison implements BooleanExpression { | 78 class Comparison implements BooleanExpression { |
78 TermVariable left; | 79 TermVariable left; |
79 TermConstant right; | 80 TermConstant right; |
81 bool negate; | |
80 | 82 |
81 Comparison(this.left, this.right); | 83 Comparison(this.left, this.right, this.negate); |
82 | 84 |
83 bool evaluate(environment) => | 85 bool evaluate(environment) { |
84 left.termValue(environment) == right.termValue(environment); | 86 var equals = left.termValue(environment) == right.termValue(environment); |
85 String toString() => "(\$${left.name} == ${right.value})"; | 87 return negate ? !equals : equals; |
Bill Hesse
2013/05/24 10:53:23
How about
return negate != (left... == right....);
kustermann
2013/05/24 11:00:34
Done.
| |
88 } | |
89 | |
90 String toString() => | |
91 "(\$${left.name} ${negate ? '!=' : '=='} ${right.value})"; | |
86 } | 92 } |
87 | 93 |
88 | 94 |
89 class TermVariable { | 95 class TermVariable { |
90 String name; | 96 String name; |
91 | 97 |
92 TermVariable(this.name); | 98 TermVariable(this.name); |
93 | 99 |
94 String termValue(environment) => environment[name].toString(); | 100 String termValue(environment) => environment[name].toString(); |
95 } | 101 } |
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
283 throw new RuntimeError( | 289 throw new RuntimeError( |
284 "Expected \$ in expression, got ${scanner.current}"); | 290 "Expected \$ in expression, got ${scanner.current}"); |
285 } | 291 } |
286 scanner.advance(); | 292 scanner.advance(); |
287 if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) { | 293 if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) { |
288 throw new RuntimeError( | 294 throw new RuntimeError( |
289 "Expected identifier in expression, got ${scanner.current}"); | 295 "Expected identifier in expression, got ${scanner.current}"); |
290 } | 296 } |
291 TermVariable left = new TermVariable(scanner.current); | 297 TermVariable left = new TermVariable(scanner.current); |
292 scanner.advance(); | 298 scanner.advance(); |
293 if (scanner.current == Token.EQUALS) { | 299 if (scanner.current == Token.EQUALS || |
300 scanner.current == Token.NOT_EQUALS) { | |
301 bool negate = scanner.current == Token.NOT_EQUALS; | |
294 scanner.advance(); | 302 scanner.advance(); |
295 if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) { | 303 if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) { |
296 throw new RuntimeError( | 304 throw new RuntimeError( |
297 "Expected identifier in expression, got ${scanner.current}"); | 305 "Expected identifier in expression, got ${scanner.current}"); |
298 } | 306 } |
299 TermConstant right = new TermConstant(scanner.current); | 307 TermConstant right = new TermConstant(scanner.current); |
300 scanner.advance(); | 308 scanner.advance(); |
301 return new Comparison(left, right); | 309 return new Comparison(left, right, negate); |
302 } else { | 310 } else { |
303 return new BooleanVariable(left); | 311 return new BooleanVariable(left); |
304 } | 312 } |
305 } | 313 } |
306 } | 314 } |
307 | 315 |
OLD | NEW |