662c2a8262
The RuntimeError has been used to report cyclic initialization errors in
dart2js.
It has also been used for other, unrelated, uses, where it makes
little to no sense.
The RuntimeError type has been removed, but added in js_helper.dart,
where dart2js uses it.
Uses of new RuntimeError("some message") have been converted to
other errors (Argument/State/Unsupported). In some cases, just
throwing a String might have been just as good.
BUG=http://dartbug.com/11040
R=johnniwinther@google.com
Review URL: https://codereview.chromium.org//16154017
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@23584 260f80e4-7a28-3924-810f-c04153c831b5
316 lines
8.6 KiB
Dart
316 lines
8.6 KiB
Dart
// 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_expression;
|
|
|
|
/**
|
|
* 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 := $variableName == value | $variableName != value |
|
|
* $variableName | (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 variableNames 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 const String LEFT_PAREN = "(";
|
|
static const String RIGHT_PAREN = ")";
|
|
static const String DOLLAR_SYMBOL = r"$";
|
|
static const String UNION = ",";
|
|
static const String EQUALS = "==";
|
|
static const String NOT_EQUALS = "!=";
|
|
static const String AND = "&&";
|
|
static const String OR = "||";
|
|
}
|
|
|
|
|
|
class Tokenizer {
|
|
String expression;
|
|
List<String> tokens;
|
|
|
|
Tokenizer(String this.expression)
|
|
: tokens = new List<String>();
|
|
|
|
// Tokens are : "(", ")", "$", ",", "&&", "||", "==", "!=", and (maximal) \w+.
|
|
static final testRegexp =
|
|
new RegExp(r"^([()$\w\s,]|(\&\&)|(\|\|)|(\=\=)|(\!\=))+$");
|
|
static final regexp = new RegExp(r"[()$,]|(\&\&)|(\|\|)|(\=\=)|(\!\=)|\w+");
|
|
|
|
List<String> tokenize() {
|
|
if (!testRegexp.hasMatch(expression)) {
|
|
throw new FormatException("Syntax error in '$expression'");
|
|
}
|
|
for (Match match in regexp.allMatches(expression)) tokens.add(match[0]);
|
|
return tokens;
|
|
}
|
|
}
|
|
|
|
|
|
abstract class BooleanExpression {
|
|
bool evaluate(Map<String, String> environment);
|
|
}
|
|
|
|
|
|
abstract class SetExpression {
|
|
Set<String> evaluate(Map<String, String> environment);
|
|
}
|
|
|
|
|
|
class Comparison implements BooleanExpression {
|
|
TermVariable left;
|
|
TermConstant right;
|
|
bool negate;
|
|
|
|
Comparison(this.left, this.right, this.negate);
|
|
|
|
bool evaluate(environment) {
|
|
return
|
|
negate != (left.termValue(environment) == right.termValue(environment));
|
|
}
|
|
|
|
String toString() =>
|
|
"(\$${left.name} ${negate ? '!=' : '=='} ${right.value})";
|
|
}
|
|
|
|
|
|
class TermVariable {
|
|
String name;
|
|
|
|
TermVariable(this.name);
|
|
|
|
String termValue(environment) => environment[name].toString();
|
|
}
|
|
|
|
|
|
class TermConstant {
|
|
String value;
|
|
|
|
TermConstant(String this.value);
|
|
|
|
String termValue(environment) => value;
|
|
}
|
|
|
|
|
|
class BooleanVariable implements BooleanExpression {
|
|
TermVariable 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.addAll does not return this.
|
|
Set<String> evaluate(environment) {
|
|
Set<String> result = left.evaluate(environment);
|
|
result.addAll(right.evaluate(environment));
|
|
return result;
|
|
}
|
|
|
|
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(String v) : value = v.toLowerCase();
|
|
|
|
Set<String> evaluate(environment) => new Set<String>.from([value]);
|
|
String toString() => value;
|
|
}
|
|
|
|
|
|
// An iterator that allows peeking at the current token.
|
|
class Scanner {
|
|
List<String> tokens;
|
|
Iterator tokenIterator;
|
|
String current;
|
|
|
|
Scanner(this.tokens) {
|
|
tokenIterator = tokens.iterator;
|
|
advance();
|
|
}
|
|
|
|
bool hasMore() => current != null;
|
|
|
|
void advance() {
|
|
current = tokenIterator.moveNext() ? tokenIterator.current : 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 == Token.LEFT_PAREN) {
|
|
scanner.advance();
|
|
SetExpression value = parseSetExpression();
|
|
if (scanner.current != Token.RIGHT_PAREN) {
|
|
throw new FormatException("Missing right parenthesis in expression");
|
|
}
|
|
scanner.advance();
|
|
return value;
|
|
}
|
|
if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) {
|
|
throw new FormatException(
|
|
"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 == Token.LEFT_PAREN) {
|
|
scanner.advance();
|
|
BooleanExpression value = parseBooleanExpression();
|
|
if (scanner.current != Token.RIGHT_PAREN) {
|
|
throw new FormatException("Missing right parenthesis in expression");
|
|
}
|
|
scanner.advance();
|
|
return value;
|
|
}
|
|
|
|
// The only atomic booleans are of the form $variable == value or the
|
|
// form $variable.
|
|
if (scanner.current != Token.DOLLAR_SYMBOL) {
|
|
throw new FormatException(
|
|
"Expected \$ in expression, got ${scanner.current}");
|
|
}
|
|
scanner.advance();
|
|
if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) {
|
|
throw new FormatException(
|
|
"Expected identifier in expression, got ${scanner.current}");
|
|
}
|
|
TermVariable left = new TermVariable(scanner.current);
|
|
scanner.advance();
|
|
if (scanner.current == Token.EQUALS ||
|
|
scanner.current == Token.NOT_EQUALS) {
|
|
bool negate = scanner.current == Token.NOT_EQUALS;
|
|
scanner.advance();
|
|
if (!new RegExp(r"^\w+$").hasMatch(scanner.current)) {
|
|
throw new FormatException(
|
|
"Expected identifier in expression, got ${scanner.current}");
|
|
}
|
|
TermConstant right = new TermConstant(scanner.current);
|
|
scanner.advance();
|
|
return new Comparison(left, right, negate);
|
|
} else {
|
|
return new BooleanVariable(left);
|
|
}
|
|
}
|
|
}
|
|
|