Chromium Code Reviews| 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..1a7e815a1b9bea17346cc6c1add387e8e43eec60 |
| --- /dev/null |
| +++ b/tools/testing/dart/status_expressions_lib.dart |
| @@ -0,0 +1,280 @@ |
| +// 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. |
| + * There are set expressions and Boolean expressions in a .status file. |
| + * The grammar is: |
| + * BooleanExpression := $variable_name == value | |
|
kasperl
2011/10/27 10:43:31
camelCase
|
| + * (BooleanExpression) | |
| + * BooleanExpression && BooleanExpression | |
| + * BooleanExpression || BooleanExpression |
| + * |
| + * SetExpression := value | (SetExpression) | |
| + * SetExpression || SetExpression | |
| + * SetExpression if BooleanExpression | |
| + * SetExpression , SetExpression |
| + * |
| + * Productions are listed in order of precedence, and the || and , operators |
| + * 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 Token { |
| + static final String AND = "&&"; |
| + static final String OR = "||"; |
| + static final String UNION = ","; |
| +} |
| + |
| +class Tokenizer { |
| + String expression; |
| + List<String> tokens; |
| + |
| + Tokenizer(String this.expression) |
| + : tokens = new List<String>(); |
| + |
| + // Tokens are : "(", ")", "$", ",", "&&", "||", "==", and (maximal) \w+. |
| + static final testRegexp = |
| + const RegExp(@"^([()$\w\s,]|(\&\&)|(\|\|)|(\=\=))+$"); |
| + static final regexp = const RegExp(@"[()$,]|(\&\&)|(\|\|)|(\=\=)|\w+"); |
| + |
| + void tokenize() { |
| + if (!testRegexp.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 { |
| + 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/27 07:07:47
Two blank lines?
|
| +class Variable { |
| + String name; |
| + |
| + Variable(this.name); |
| + |
| + String termValue(environment) => environment[name]; |
| +} |
| + |
|
Mads Ager (google)
2011/10/27 07:07:47
Two blank lines?
|
| +class TermConstant { |
|
Mads Ager (google)
2011/10/27 07:07:47
Should this just be named Constant or should Varia
|
| + String value; |
| + |
| + TermConstant(String this.value); |
| + |
| + String termValue(environment) => value; |
| +} |
| + |
| + |
| +class BooleanVariable implements BooleanExpression { |
| + Variable variable; |
| + |
| + BooleanVariable(this.variable); |
| + |
| + bool evaluate(environment) => variable.termValue(environment) == "true"; |
| + String toString() => "(bool \$${variable.name})"; |
| +} |
| + |
| + |
| +class BooleanOperation implements BooleanExpression { |
| + String op; |
| + BooleanExpression left; |
| + BooleanExpression right; |
| + |
| + BooleanOperation(this.op, this.left, this.right); |
| + |
| + bool evaluate(environment) => (op == Token.AND) ? |
| + left.evaluate(environment) && right.evaluate(environment) : |
| + left.evaluate(environment) || right.evaluate(environment); |
| + String toString() => "($left $op $right)"; |
| +} |
| + |
| +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 || $right)"; |
| +} |
| + |
| +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() => "($left if $right)"; |
| +} |
| + |
| + |
| +class SetConstant implements SetExpression { |
| + String value; |
| + |
| + SetConstant(this.value); |
| + |
| + Set<String> evaluate(environment) => new Set<string>().add(value); |
| + String toString() => value; |
| +} |
| + |
| + |
| +// An iterator that allows peeking at the current token. |
| +class Scanner { |
| + List<String> tokens; |
| + Iterator token_iterator; |
|
kasperl
2011/10/27 10:43:31
camelCase
|
| + String current; |
| + |
| + Scanner(this.tokens) { |
| + token_iterator = tokens.iterator(); |
| + advance(); |
| + } |
| + |
| + bool hasMore() => current != null; |
| + |
| + void advance() { |
| + current = token_iterator.hasNext() ? token_iterator.next() : null; |
| + } |
| +} |
| + |
| +class ExpressionParser { |
| + Scanner scanner; |
| + |
| + ExpressionParser(this.scanner); |
| + |
| + SetExpression parseSetExpression() => parseSetUnion(); |
| + |
| + SetExpression parseSetUnion() { |
| + SetExpression left = parseSetIf(); |
| + while (scanner.hasMore() && scanner.current == Token.UNION){ |
| + 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 == Token.OR){ |
| + scanner.advance(); |
| + SetExpression right = parseSetAtomic(); |
| + left = new SetUnion(left, right); |
| + } |
| + return left; |
| + } |
| + |
| + |
| + SetExpression parseSetAtomic() { |
| + if (scanner.current == "(") { |
|
Søren Gjesse
2011/10/27 10:22:20
Token for (.
|
| + scanner.advance(); |
| + SetExpression value = parseSetExpression(); |
| + Expect.equals(scanner.current, ")", |
|
Søren Gjesse
2011/10/27 10:22:20
and ).
|
| + "Missing right parenthesis in expression"); |
| + scanner.advance(); |
| + return value; |
| + } |
| + Expect.isTrue(const RegExp(@"^\w+$").hasMatch(scanner.current), |
| + "Expected identifier in expression, got ${scanner.current}"); |
| + SetExpression value = new SetConstant(scanner.current); |
| + scanner.advance(); |
| + return value; |
| + } |
| + |
| + BooleanExpression parseBooleanExpression() => parseBooleanOr(); |
| + |
| + BooleanExpression parseBooleanOr() { |
| + BooleanExpression left = parseBooleanAnd(); |
| + while (scanner.hasMore() && scanner.current == Token.OR) { |
| + scanner.advance(); |
| + BooleanExpression right = parseBooleanAnd(); |
| + left = new BooleanOperation(Token.OR, left, right); |
| + } |
| + return left; |
| + } |
| + |
| + |
| + BooleanExpression parseBooleanAnd() { |
| + BooleanExpression left = parseBooleanAtomic(); |
| + while (scanner.hasMore() && scanner.current == Token.AND) { |
| + scanner.advance(); |
| + BooleanExpression right = parseBooleanAtomic(); |
| + left = new BooleanOperation(Token.AND, left, right); |
| + } |
| + return left; |
| + } |
| + |
| + BooleanExpression parseBooleanAtomic() { |
| + if (scanner.current == "(") { |
| + scanner.advance(); |
| + SetExpression value = parseBooleanExpression(); |
| + Expect.equals(scanner.current, ")", |
| + "Missing right parenthesis in expression"); |
| + scanner.advance(); |
| + return value; |
| + } |
| + |
| + // The only atomic booleans are of the form $variable == value or the |
| + // form $variable. |
| + 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}"); |
| + Variable left = new Variable(scanner.current); |
| + scanner.advance(); |
| + if (scanner.current == "==") { |
|
Søren Gjesse
2011/10/27 10:22:20
Token for ==
|
| + scanner.advance(); |
| + Expect.isTrue(const RegExp(@"^\w+$").hasMatch(scanner.current), |
| + "Expected identifier in expression, got ${scanner.current}"); |
| + TermConstant right = new TermConstant(scanner.current); |
| + scanner.advance(); |
| + return new Comparison(left, right); |
| + } else { |
| + return new BooleanVariable(left); |
| + } |
| + } |
| +} |
| + |