Chromium Code Reviews| 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 |