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. | |
ngeoffray
2011/10/26 12:01:37
Extra space.
| |
9 * There are set expressions and Boolean expressions in a .status file. | |
10 * The grammar is: | |
11 * BooleanExpression := $variable_name == value | | |
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 * Production are listed in order of precedence, and the || and , operators | |
Mads Ager (google)
2011/10/26 12:37:47
Remove the extra leading space starting from here
| |
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 Tokenizer { | |
34 String expression; | |
35 List<String> tokens; | |
36 | |
37 Tokenizer(String this.expression) | |
Søren Gjesse
2011/10/26 13:36:24
Fit on one line?
| |
38 : tokens = new List<String>(); | |
39 | |
40 /** | |
41 * Tokens are : "&&", "||", "==", "$", "(", ")", ",", and (maximal) \w+. | |
Søren Gjesse
2011/10/26 13:36:24
Maybe list the tokens in the same order as they ar
| |
42 */ | |
43 static final test_regexp = | |
44 const RegExp(@"^([()$\w\s,]|(\&\&)|(\|\|)|(\=\=))+$"); | |
45 static final regexp = const RegExp(@"[()$,]|(\&\&)|(\|\|)|(\=\=)|\w+"); | |
46 | |
47 void tokenize() { | |
48 if (!test_regexp.hasMatch(expression)) { | |
49 throw new ExpectException("Syntax error in '" + expression + '"'); | |
50 } | |
51 for (Match match in regexp.allMatches(expression)) tokens.add(match[0]); | |
52 } | |
53 } | |
54 | |
55 | |
56 interface BooleanExpression { | |
57 bool evaluate(Map<String, String> environment); | |
58 } | |
59 | |
60 | |
61 interface SetExpression { | |
62 // A set of test outcomes. | |
Mads Ager (google)
2011/10/26 12:37:47
Please update the comment here to actually describ
| |
63 Set<String> evaluate(Map<String, String> environment); | |
64 } | |
65 | |
66 | |
67 class Comparison implements BooleanExpression { | |
68 Variable left; | |
69 TermConstant right; | |
70 | |
71 Comparison(this.left, this.right); | |
72 | |
73 bool evaluate(environment) => left.termValue() == right.termValue(); | |
74 String toString() => "==(\$${left.name}, ${right.value})"; | |
Mads Ager (google)
2011/10/26 12:37:47
Why not just "(\$${left.name} == ${right.value})"?
| |
75 } | |
76 | |
77 class Variable { | |
78 String name; | |
79 Variable(String this.name); | |
80 | |
81 String termValue(environment) => environment[name]; | |
82 } | |
83 | |
84 class TermConstant { | |
85 String value; | |
86 TermConstant(String this.value); | |
87 | |
88 String termValue(environment) => value; | |
89 } | |
90 | |
91 | |
92 class BooleanOperation implements BooleanExpression { | |
93 String op; | |
94 BooleanExpression left; | |
95 BooleanExpression right; | |
96 | |
97 BooleanOperation(this.op, this.left, this.right); | |
98 | |
99 bool evaluate(environment) => (op == "&&") ? | |
100 left.evaluate(environment) && right.evaluate(environment) : | |
101 left.evaluate(environment) || right.evaluate(environment); | |
102 String toString() => "$op(${left.toString()},${right.toString()})"; | |
Mads Ager (google)
2011/10/26 12:37:47
I would go for infix here and in the rest.
| |
103 } | |
104 | |
105 class SetUnion implements SetExpression { | |
106 SetExpression left; | |
107 SetExpression right; | |
108 | |
109 SetUnion(this.left, this.right); | |
110 | |
111 // Overwrites left.evaluate(env). | |
112 Set<String> evaluate(environment) => | |
113 left.evaluate(environment).addAll(right.evaluate(environment)); | |
114 String toString() => "||(${left.toString()},${right.toString()})"; | |
115 } | |
116 | |
117 class SetIf implements SetExpression { | |
118 SetExpression left; | |
119 BooleanExpression right; | |
120 | |
121 SetIf(this.left, this.right); | |
122 | |
123 Set<String> evaluate(environment) => right.evaluate(environment) ? | |
124 left.evaluate(environment) : new Set<String>(); | |
125 String toString() => "if(${left.toString()},${right.toString()})"; | |
126 } | |
127 | |
128 | |
129 class SetConstant implements SetExpression { | |
130 String value; | |
131 | |
132 SetConstant(this.value); | |
133 | |
134 Set<String> evaluate(environment) => new Set<string>().add(value); | |
135 String toString() => value; | |
136 } | |
137 | |
138 | |
139 /* An iterator that provides peeking at the current token. */ | |
Mads Ager (google)
2011/10/26 12:37:47
I think you should either make this a doc comment
William Hesse
2011/10/26 15:15:03
Done.
| |
140 class Scanner { | |
Søren Gjesse
2011/10/26 13:36:24
Should we have token constants?
class Token {
f
William Hesse
2011/10/26 15:15:03
Done.
| |
141 List<String> tokens; | |
142 Iterator token_iterator; | |
143 String current; | |
144 | |
145 Scanner(this.tokens) { | |
146 //print(tokens[0] + tokens[1]); | |
Mads Ager (google)
2011/10/26 12:37:47
remove.
William Hesse
2011/10/26 15:15:03
Done.
| |
147 token_iterator = tokens.iterator(); | |
148 current = token_iterator.next(); | |
Mads Ager (google)
2011/10/26 12:37:47
Shouldn't this just call advance?
William Hesse
2011/10/26 15:15:03
Done.
| |
149 //print(current); | |
Mads Ager (google)
2011/10/26 12:37:47
remove.
William Hesse
2011/10/26 15:15:03
Done.
| |
150 } | |
151 | |
152 bool hasMore() => current != null; | |
153 | |
154 void advance() { | |
155 current = token_iterator.hasNext() ? token_iterator.next() : null; | |
156 print(current); | |
Mads Ager (google)
2011/10/26 12:37:47
remove
William Hesse
2011/10/26 15:15:03
Done.
| |
157 } | |
158 } | |
159 | |
160 class ExpressionParser { | |
161 Scanner scanner; | |
Søren Gjesse
2011/10/26 13:36:24
Empty line before the constructor.
William Hesse
2011/10/26 15:15:03
Done.
| |
162 ExpressionParser(this.scanner); | |
163 | |
164 SetExpression parseSetExpression() => parseSetUnion(); | |
165 | |
166 SetExpression parseSetUnion() { | |
167 SetExpression left = parseSetIf(); | |
168 while (scanner.hasMore() && scanner.current == ","){ | |
169 scanner.advance(); | |
170 SetExpression right = parseSetIf(); | |
171 left = new SetUnion(left, right); | |
172 } | |
173 return left; | |
174 } | |
175 | |
176 SetExpression parseSetIf() { | |
177 SetExpression left = parseSetOr(); | |
178 while (scanner.hasMore() && scanner.current == "if") { | |
179 scanner.advance(); | |
180 BooleanExpression right = parseBooleanExpression(); | |
181 left = new SetIf(left, right); | |
182 } | |
183 return left; | |
184 } | |
185 | |
186 SetExpression parseSetOr() { | |
187 SetExpression left = parseSetAtomic(); | |
188 while (scanner.hasMore() && scanner.current == "||"){ | |
189 scanner.advance(); | |
190 SetExpression right = parseSetIf(); | |
191 left = new SetUnion(left, right); | |
192 } | |
193 return left; | |
194 } | |
195 | |
196 | |
197 SetExpression parseSetAtomic() { | |
198 if (scanner.current == "(") { | |
199 scanner.advance(); | |
200 SetExpression value = parseSetUnion(); | |
201 Expect.equals(scanner.current, ")", | |
202 "Missing right parenthesis in expression"); | |
Mads Ager (google)
2011/10/26 12:37:47
Please align with first parameter.
William Hesse
2011/10/26 15:15:03
Done.
| |
203 scanner.advance(); | |
204 return value; | |
205 } | |
206 Expect.isTrue(const RegExp(@"^\w+$").hasMatch(scanner.current), | |
207 "Expected identifier in expression, got " + scanner.current); | |
Mads Ager (google)
2011/10/26 12:37:47
Please align with first parameter or move both to
William Hesse
2011/10/26 15:15:03
Done.
| |
208 SetExpression value = new SetConstant(scanner.current); | |
209 scanner.advance(); | |
210 return value; | |
211 } | |
212 | |
213 BooleanExpression parseBooleanExpression() => parseBooleanOr(); | |
214 | |
215 BooleanExpression parseBooleanOr() { | |
216 BooleanExpression left = parseBooleanAnd(); | |
217 while (scanner.hasMore() && scanner.current == "||") { | |
218 scanner.advance(); | |
219 BooleanExpression right = parseBooleanAnd(); | |
220 left = new BooleanOperation("||", left, right); | |
221 } | |
222 return left; | |
223 } | |
224 | |
225 | |
226 BooleanExpression parseBooleanAnd() { | |
227 BooleanExpression left = parseBooleanAtomic(); | |
228 while (scanner.hasMore() && scanner.current == "&&") { | |
229 scanner.advance(); | |
230 BooleanExpression right = parseBooleanAtomic(); | |
231 left = new BooleanOperation("&&", left, right); | |
232 } | |
233 return left; | |
234 } | |
235 | |
236 BooleanExpression parseBooleanAtomic() { | |
237 if (scanner.current == "(") { | |
238 scanner.advance(); | |
239 SetExpression value = parseBooleanExpression(); | |
240 Expect.equals(scanner.current, ")", | |
241 "Missing right parenthesis in expression"); | |
Mads Ager (google)
2011/10/26 12:37:47
align
William Hesse
2011/10/26 15:15:03
Done.
| |
242 scanner.advance(); | |
243 return value; | |
244 } | |
245 | |
246 // The only atomic booleans are of the form $variable == value. | |
247 Expect.equals(scanner.current, @"$", | |
248 @"Expected $identifier in expression, got " + scanner.current); | |
Mads Ager (google)
2011/10/26 12:37:47
$identifier -> $
William Hesse
2011/10/26 15:15:03
Done.
| |
249 scanner.advance(); | |
250 Expect.isTrue(const RegExp(@"^\w+$").hasMatch(scanner.current), | |
251 @"Expected $identifier in expression, got " + scanner.current); | |
252 Variable left = new Variable(scanner.current); | |
253 scanner.advance(); | |
254 Expect.equals(scanner.current, "==", | |
255 "Expected == in expression, got " + scanner.current); | |
256 scanner.advance(); | |
257 Expect.isTrue(const RegExp(@"^\w+$").hasMatch(scanner.current), | |
258 @"Expected identifier in expression, got " + scanner.current); | |
Søren Gjesse
2011/10/26 13:36:24
No need for @ here.
William Hesse
2011/10/26 15:15:03
Done.
| |
259 TermConstant right = new TermConstant(scanner.current); | |
260 scanner.advance(); | |
261 return new Comparison(left, right); | |
262 } | |
263 } | |
264 | |
OLD | NEW |