Index: tools/testing/dart/status_expressions_lib.dart |
diff --git a/tools/testing/dart/status_expressions_lib.dart b/tools/testing/dart/status_expressions_lib.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0bd31d2191e82807ad4f657d65c08fa402ce9897 |
--- /dev/null |
+++ b/tools/testing/dart/status_expressions_lib.dart |
@@ -0,0 +1,264 @@ |
+// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+#library("status_expressions"); |
+ |
+/** |
+ * Parse and evaluate expressions in a .status file for Dart and V8. |
ngeoffray
2011/10/26 12:01:37
Extra space.
|
+ * There are set expressions and Boolean expressions in a .status file. |
+ * The grammar is: |
+ * BooleanExpression := $variable_name == value | |
+ * (BooleanExpression) | |
+ * BooleanExpression && BooleanExpression | |
+ * BooleanExpression || BooleanExpression |
+ * |
+ * SetExpression := value | (SetExpression) | |
+ * SetExpression || SetExpression | |
+ * SetExpression if BooleanExpression | |
+ * SetExpression , SetExpression |
+ * |
+ * 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
|
+ * both evaluate to set union, but with different precedence. |
+ * |
+ * Values and names are non-empty strings of word characters, matching |
+ * the RegExp \w+. |
+ * |
+ * Expressions evaluate as expected, with values of variables found in |
+ * an environment passed to the evaluator. The SetExpression "value" |
+ * evaluates to a singleton set containing that value. "A if B" evaluates |
+ * to A if B is true, and to the empty set if B is false. |
+ */ |
+ |
+class Tokenizer { |
+ String expression; |
+ List<String> tokens; |
+ |
+ Tokenizer(String this.expression) |
Søren Gjesse
2011/10/26 13:36:24
Fit on one line?
|
+ : tokens = new List<String>(); |
+ |
+ /** |
+ * 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
|
+ */ |
+ static final test_regexp = |
+ const RegExp(@"^([()$\w\s,]|(\&\&)|(\|\|)|(\=\=))+$"); |
+ static final regexp = const RegExp(@"[()$,]|(\&\&)|(\|\|)|(\=\=)|\w+"); |
+ |
+ void tokenize() { |
+ if (!test_regexp.hasMatch(expression)) { |
+ throw new ExpectException("Syntax error in '" + expression + '"'); |
+ } |
+ for (Match match in regexp.allMatches(expression)) tokens.add(match[0]); |
+ } |
+} |
+ |
+ |
+interface BooleanExpression { |
+ bool evaluate(Map<String, String> environment); |
+} |
+ |
+ |
+interface SetExpression { |
+ // A set of test outcomes. |
Mads Ager (google)
2011/10/26 12:37:47
Please update the comment here to actually describ
|
+ Set<String> evaluate(Map<String, String> environment); |
+} |
+ |
+ |
+class Comparison implements BooleanExpression { |
+ Variable left; |
+ TermConstant right; |
+ |
+ Comparison(this.left, this.right); |
+ |
+ bool evaluate(environment) => left.termValue() == right.termValue(); |
+ String toString() => "==(\$${left.name}, ${right.value})"; |
Mads Ager (google)
2011/10/26 12:37:47
Why not just "(\$${left.name} == ${right.value})"?
|
+} |
+ |
+class Variable { |
+ String name; |
+ Variable(String this.name); |
+ |
+ String termValue(environment) => environment[name]; |
+} |
+ |
+class TermConstant { |
+ String value; |
+ TermConstant(String this.value); |
+ |
+ String termValue(environment) => value; |
+} |
+ |
+ |
+class BooleanOperation implements BooleanExpression { |
+ String op; |
+ BooleanExpression left; |
+ BooleanExpression right; |
+ |
+ BooleanOperation(this.op, this.left, this.right); |
+ |
+ bool evaluate(environment) => (op == "&&") ? |
+ left.evaluate(environment) && right.evaluate(environment) : |
+ left.evaluate(environment) || right.evaluate(environment); |
+ 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.
|
+} |
+ |
+class SetUnion implements SetExpression { |
+ SetExpression left; |
+ SetExpression right; |
+ |
+ SetUnion(this.left, this.right); |
+ |
+ // Overwrites left.evaluate(env). |
+ Set<String> evaluate(environment) => |
+ left.evaluate(environment).addAll(right.evaluate(environment)); |
+ String toString() => "||(${left.toString()},${right.toString()})"; |
+} |
+ |
+class SetIf implements SetExpression { |
+ SetExpression left; |
+ BooleanExpression right; |
+ |
+ SetIf(this.left, this.right); |
+ |
+ Set<String> evaluate(environment) => right.evaluate(environment) ? |
+ left.evaluate(environment) : new Set<String>(); |
+ String toString() => "if(${left.toString()},${right.toString()})"; |
+} |
+ |
+ |
+class SetConstant implements SetExpression { |
+ String value; |
+ |
+ SetConstant(this.value); |
+ |
+ Set<String> evaluate(environment) => new Set<string>().add(value); |
+ String toString() => value; |
+} |
+ |
+ |
+/* 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.
|
+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.
|
+ List<String> tokens; |
+ Iterator token_iterator; |
+ String current; |
+ |
+ Scanner(this.tokens) { |
+ //print(tokens[0] + tokens[1]); |
Mads Ager (google)
2011/10/26 12:37:47
remove.
William Hesse
2011/10/26 15:15:03
Done.
|
+ token_iterator = tokens.iterator(); |
+ 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.
|
+ //print(current); |
Mads Ager (google)
2011/10/26 12:37:47
remove.
William Hesse
2011/10/26 15:15:03
Done.
|
+ } |
+ |
+ bool hasMore() => current != null; |
+ |
+ void advance() { |
+ current = token_iterator.hasNext() ? token_iterator.next() : null; |
+ print(current); |
Mads Ager (google)
2011/10/26 12:37:47
remove
William Hesse
2011/10/26 15:15:03
Done.
|
+ } |
+} |
+ |
+class ExpressionParser { |
+ 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.
|
+ ExpressionParser(this.scanner); |
+ |
+ SetExpression parseSetExpression() => parseSetUnion(); |
+ |
+ SetExpression parseSetUnion() { |
+ SetExpression left = parseSetIf(); |
+ while (scanner.hasMore() && scanner.current == ","){ |
+ scanner.advance(); |
+ SetExpression right = parseSetIf(); |
+ left = new SetUnion(left, right); |
+ } |
+ return left; |
+ } |
+ |
+ SetExpression parseSetIf() { |
+ SetExpression left = parseSetOr(); |
+ while (scanner.hasMore() && scanner.current == "if") { |
+ scanner.advance(); |
+ BooleanExpression right = parseBooleanExpression(); |
+ left = new SetIf(left, right); |
+ } |
+ return left; |
+ } |
+ |
+ SetExpression parseSetOr() { |
+ SetExpression left = parseSetAtomic(); |
+ while (scanner.hasMore() && scanner.current == "||"){ |
+ scanner.advance(); |
+ SetExpression right = parseSetIf(); |
+ left = new SetUnion(left, right); |
+ } |
+ return left; |
+ } |
+ |
+ |
+ SetExpression parseSetAtomic() { |
+ if (scanner.current == "(") { |
+ scanner.advance(); |
+ SetExpression value = parseSetUnion(); |
+ Expect.equals(scanner.current, ")", |
+ "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.
|
+ scanner.advance(); |
+ return value; |
+ } |
+ Expect.isTrue(const RegExp(@"^\w+$").hasMatch(scanner.current), |
+ "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.
|
+ SetExpression value = new SetConstant(scanner.current); |
+ scanner.advance(); |
+ return value; |
+ } |
+ |
+ BooleanExpression parseBooleanExpression() => parseBooleanOr(); |
+ |
+ BooleanExpression parseBooleanOr() { |
+ BooleanExpression left = parseBooleanAnd(); |
+ while (scanner.hasMore() && scanner.current == "||") { |
+ scanner.advance(); |
+ BooleanExpression right = parseBooleanAnd(); |
+ left = new BooleanOperation("||", left, right); |
+ } |
+ return left; |
+ } |
+ |
+ |
+ BooleanExpression parseBooleanAnd() { |
+ BooleanExpression left = parseBooleanAtomic(); |
+ while (scanner.hasMore() && scanner.current == "&&") { |
+ scanner.advance(); |
+ BooleanExpression right = parseBooleanAtomic(); |
+ left = new BooleanOperation("&&", left, right); |
+ } |
+ return left; |
+ } |
+ |
+ BooleanExpression parseBooleanAtomic() { |
+ if (scanner.current == "(") { |
+ scanner.advance(); |
+ SetExpression value = parseBooleanExpression(); |
+ Expect.equals(scanner.current, ")", |
+ "Missing right parenthesis in expression"); |
Mads Ager (google)
2011/10/26 12:37:47
align
William Hesse
2011/10/26 15:15:03
Done.
|
+ scanner.advance(); |
+ return value; |
+ } |
+ |
+ // The only atomic booleans are of the form $variable == value. |
+ Expect.equals(scanner.current, @"$", |
+ @"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.
|
+ scanner.advance(); |
+ Expect.isTrue(const RegExp(@"^\w+$").hasMatch(scanner.current), |
+ @"Expected $identifier in expression, got " + scanner.current); |
+ Variable left = new Variable(scanner.current); |
+ scanner.advance(); |
+ Expect.equals(scanner.current, "==", |
+ "Expected == in expression, got " + scanner.current); |
+ scanner.advance(); |
+ Expect.isTrue(const RegExp(@"^\w+$").hasMatch(scanner.current), |
+ @"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.
|
+ TermConstant right = new TermConstant(scanner.current); |
+ scanner.advance(); |
+ return new Comparison(left, right); |
+ } |
+} |
+ |