OLD | NEW |
---|---|
(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_expressions"); | |
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 := $variable_name == value | | |
kasperl
2011/10/27 10:43:31
camelCase
| |
12 * (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 names 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 Token { | |
34 static final String AND = "&&"; | |
35 static final String OR = "||"; | |
36 static final String UNION = ","; | |
37 } | |
38 | |
39 class Tokenizer { | |
40 String expression; | |
41 List<String> tokens; | |
42 | |
43 Tokenizer(String this.expression) | |
44 : tokens = new List<String>(); | |
45 | |
46 // Tokens are : "(", ")", "$", ",", "&&", "||", "==", and (maximal) \w+. | |
47 static final testRegexp = | |
48 const RegExp(@"^([()$\w\s,]|(\&\&)|(\|\|)|(\=\=))+$"); | |
49 static final regexp = const RegExp(@"[()$,]|(\&\&)|(\|\|)|(\=\=)|\w+"); | |
50 | |
51 void tokenize() { | |
52 if (!testRegexp.hasMatch(expression)) { | |
53 throw new ExpectException("Syntax error in '$expression'"); | |
54 } | |
55 for (Match match in regexp.allMatches(expression)) tokens.add(match[0]); | |
56 } | |
57 } | |
58 | |
59 | |
60 interface BooleanExpression { | |
61 bool evaluate(Map<String, String> environment); | |
62 } | |
63 | |
64 | |
65 interface SetExpression { | |
66 Set<String> evaluate(Map<String, String> environment); | |
67 } | |
68 | |
69 | |
70 class Comparison implements BooleanExpression { | |
71 Variable left; | |
72 TermConstant right; | |
73 | |
74 Comparison(this.left, this.right); | |
75 | |
76 bool evaluate(environment) => left.termValue() == right.termValue(); | |
77 String toString() => "(\$${left.name} == ${right.value})"; | |
78 } | |
79 | |
Mads Ager (google)
2011/10/27 07:07:47
Two blank lines?
| |
80 class Variable { | |
81 String name; | |
82 | |
83 Variable(this.name); | |
84 | |
85 String termValue(environment) => environment[name]; | |
86 } | |
87 | |
Mads Ager (google)
2011/10/27 07:07:47
Two blank lines?
| |
88 class TermConstant { | |
Mads Ager (google)
2011/10/27 07:07:47
Should this just be named Constant or should Varia
| |
89 String value; | |
90 | |
91 TermConstant(String this.value); | |
92 | |
93 String termValue(environment) => value; | |
94 } | |
95 | |
96 | |
97 class BooleanVariable implements BooleanExpression { | |
98 Variable variable; | |
99 | |
100 BooleanVariable(this.variable); | |
101 | |
102 bool evaluate(environment) => variable.termValue(environment) == "true"; | |
103 String toString() => "(bool \$${variable.name})"; | |
104 } | |
105 | |
106 | |
107 class BooleanOperation implements BooleanExpression { | |
108 String op; | |
109 BooleanExpression left; | |
110 BooleanExpression right; | |
111 | |
112 BooleanOperation(this.op, this.left, this.right); | |
113 | |
114 bool evaluate(environment) => (op == Token.AND) ? | |
115 left.evaluate(environment) && right.evaluate(environment) : | |
116 left.evaluate(environment) || right.evaluate(environment); | |
117 String toString() => "($left $op $right)"; | |
118 } | |
119 | |
120 class SetUnion implements SetExpression { | |
121 SetExpression left; | |
122 SetExpression right; | |
123 | |
124 SetUnion(this.left, this.right); | |
125 | |
126 // Overwrites left.evaluate(env). | |
127 Set<String> evaluate(environment) => | |
128 left.evaluate(environment).addAll(right.evaluate(environment)); | |
129 String toString() => "($left || $right)"; | |
130 } | |
131 | |
132 class SetIf implements SetExpression { | |
133 SetExpression left; | |
134 BooleanExpression right; | |
135 | |
136 SetIf(this.left, this.right); | |
137 | |
138 Set<String> evaluate(environment) => right.evaluate(environment) ? | |
139 left.evaluate(environment) : new Set<String>(); | |
140 String toString() => "($left if $right)"; | |
141 } | |
142 | |
143 | |
144 class SetConstant implements SetExpression { | |
145 String value; | |
146 | |
147 SetConstant(this.value); | |
148 | |
149 Set<String> evaluate(environment) => new Set<string>().add(value); | |
150 String toString() => value; | |
151 } | |
152 | |
153 | |
154 // An iterator that allows peeking at the current token. | |
155 class Scanner { | |
156 List<String> tokens; | |
157 Iterator token_iterator; | |
kasperl
2011/10/27 10:43:31
camelCase
| |
158 String current; | |
159 | |
160 Scanner(this.tokens) { | |
161 token_iterator = tokens.iterator(); | |
162 advance(); | |
163 } | |
164 | |
165 bool hasMore() => current != null; | |
166 | |
167 void advance() { | |
168 current = token_iterator.hasNext() ? token_iterator.next() : null; | |
169 } | |
170 } | |
171 | |
172 class ExpressionParser { | |
173 Scanner scanner; | |
174 | |
175 ExpressionParser(this.scanner); | |
176 | |
177 SetExpression parseSetExpression() => parseSetUnion(); | |
178 | |
179 SetExpression parseSetUnion() { | |
180 SetExpression left = parseSetIf(); | |
181 while (scanner.hasMore() && scanner.current == Token.UNION){ | |
182 scanner.advance(); | |
183 SetExpression right = parseSetIf(); | |
184 left = new SetUnion(left, right); | |
185 } | |
186 return left; | |
187 } | |
188 | |
189 SetExpression parseSetIf() { | |
190 SetExpression left = parseSetOr(); | |
191 while (scanner.hasMore() && scanner.current == "if") { | |
192 scanner.advance(); | |
193 BooleanExpression right = parseBooleanExpression(); | |
194 left = new SetIf(left, right); | |
195 } | |
196 return left; | |
197 } | |
198 | |
199 SetExpression parseSetOr() { | |
200 SetExpression left = parseSetAtomic(); | |
201 while (scanner.hasMore() && scanner.current == Token.OR){ | |
202 scanner.advance(); | |
203 SetExpression right = parseSetAtomic(); | |
204 left = new SetUnion(left, right); | |
205 } | |
206 return left; | |
207 } | |
208 | |
209 | |
210 SetExpression parseSetAtomic() { | |
211 if (scanner.current == "(") { | |
Søren Gjesse
2011/10/27 10:22:20
Token for (.
| |
212 scanner.advance(); | |
213 SetExpression value = parseSetExpression(); | |
214 Expect.equals(scanner.current, ")", | |
Søren Gjesse
2011/10/27 10:22:20
and ).
| |
215 "Missing right parenthesis in expression"); | |
216 scanner.advance(); | |
217 return value; | |
218 } | |
219 Expect.isTrue(const RegExp(@"^\w+$").hasMatch(scanner.current), | |
220 "Expected identifier in expression, got ${scanner.current}"); | |
221 SetExpression value = new SetConstant(scanner.current); | |
222 scanner.advance(); | |
223 return value; | |
224 } | |
225 | |
226 BooleanExpression parseBooleanExpression() => parseBooleanOr(); | |
227 | |
228 BooleanExpression parseBooleanOr() { | |
229 BooleanExpression left = parseBooleanAnd(); | |
230 while (scanner.hasMore() && scanner.current == Token.OR) { | |
231 scanner.advance(); | |
232 BooleanExpression right = parseBooleanAnd(); | |
233 left = new BooleanOperation(Token.OR, left, right); | |
234 } | |
235 return left; | |
236 } | |
237 | |
238 | |
239 BooleanExpression parseBooleanAnd() { | |
240 BooleanExpression left = parseBooleanAtomic(); | |
241 while (scanner.hasMore() && scanner.current == Token.AND) { | |
242 scanner.advance(); | |
243 BooleanExpression right = parseBooleanAtomic(); | |
244 left = new BooleanOperation(Token.AND, left, right); | |
245 } | |
246 return left; | |
247 } | |
248 | |
249 BooleanExpression parseBooleanAtomic() { | |
250 if (scanner.current == "(") { | |
251 scanner.advance(); | |
252 SetExpression value = parseBooleanExpression(); | |
253 Expect.equals(scanner.current, ")", | |
254 "Missing right parenthesis in expression"); | |
255 scanner.advance(); | |
256 return value; | |
257 } | |
258 | |
259 // The only atomic booleans are of the form $variable == value or the | |
260 // form $variable. | |
261 Expect.equals(scanner.current, @"$", | |
262 "Expected \$ in expression, got ${scanner.current}"); | |
263 scanner.advance(); | |
264 Expect.isTrue(const RegExp(@"^\w+$").hasMatch(scanner.current), | |
265 "Expected identifier in expression, got ${scanner.current}"); | |
266 Variable left = new Variable(scanner.current); | |
267 scanner.advance(); | |
268 if (scanner.current == "==") { | |
Søren Gjesse
2011/10/27 10:22:20
Token for ==
| |
269 scanner.advance(); | |
270 Expect.isTrue(const RegExp(@"^\w+$").hasMatch(scanner.current), | |
271 "Expected identifier in expression, got ${scanner.current}"); | |
272 TermConstant right = new TermConstant(scanner.current); | |
273 scanner.advance(); | |
274 return new Comparison(left, right); | |
275 } else { | |
276 return new BooleanVariable(left); | |
277 } | |
278 } | |
279 } | |
280 | |
OLD | NEW |