Index: lib/src/backend/platform_selector/parser.dart |
diff --git a/lib/src/backend/platform_selector/parser.dart b/lib/src/backend/platform_selector/parser.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..83468d6af14d0d08b89ae9524ec23f70ba05d7e2 |
--- /dev/null |
+++ b/lib/src/backend/platform_selector/parser.dart |
@@ -0,0 +1,110 @@ |
+// Copyright (c) 2015, 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 unittest.backend.platform_selector.parser; |
+ |
+import 'package:source_span/source_span.dart'; |
+ |
+import 'ast.dart'; |
+import 'scanner.dart'; |
+import 'token.dart'; |
+ |
+/// A class for parsing a platform selector. |
+/// |
+/// Platform selectors use a stripped-down version of the Dart expression |
+/// syntax that only contains variables, parentheses, and boolean operators. |
+/// Variables may also contain dashes, contrary to Dart's syntax; this allows |
+/// consistency with command-line arguments. |
Bob Nystrom
2015/03/11 20:05:52
Does this add enough value to be worth the possibl
nweiz
2015/03/12 19:48:57
I think so. A lot of the identifiers that people w
|
+class Parser { |
+ /// The scanner that tokenizes the selector. |
+ final Scanner _scanner; |
+ |
+ Parser(String selector) |
+ : _scanner = new Scanner(selector); |
+ |
+ /// Parses the selector. |
+ /// |
+ /// This must only be called once per parser. |
+ Node parse() { |
+ var selector = _conditional(); |
+ |
+ if (_scanner.peek().type != TokenType.endOfFile) { |
+ throw new SourceSpanFormatException( |
+ "Expected end of input.", _scanner.peek().span); |
+ } |
+ |
+ return selector; |
+ } |
+ |
+ /// Parses a conditional: |
+ /// |
+ /// conditionalExpression: |
+ /// logicalOrExpression ("?" conditionalExpression ":" |
+ /// conditionalExpression)? |
+ Node _conditional() { |
+ var condition = _or(); |
+ if (_scanner.peek().type != TokenType.questionMark) return condition; |
+ |
+ _scanner.next(); |
Bob Nystrom
2015/03/11 20:05:52
You could simplify this (and a bunch below) by add
nweiz
2015/03/12 19:48:57
Done.
|
+ var branch1 = _conditional(); |
+ var token = _scanner.next(); |
+ if (token.type != TokenType.colon) { |
+ throw new SourceSpanFormatException('Expected ":".', token.span); |
+ } |
+ |
+ var branch2 = _conditional(); |
+ return new ConditionalNode(condition, branch1, branch2); |
+ } |
+ |
+ /// Parses a logical or: |
+ /// |
+ /// logicalOrExpression: |
+ /// logicalAndExpression ("||" logicalOrExpression)? |
+ Node _or() { |
+ var branch1 = _and(); |
+ if (_scanner.peek().type != TokenType.or) return branch1; |
+ _scanner.next(); |
+ return new OrNode(branch1, _or()); |
+ } |
+ |
+ /// Parses a logical and: |
+ /// |
+ /// logicalAndExpression: |
+ /// simpleExpression ("&&" logicalAndExpression)? |
+ Node _and() { |
+ var branch1 = _simpleExpression(); |
+ if (_scanner.peek().type != TokenType.and) return branch1; |
+ _scanner.next(); |
+ return new AndNode(branch1, _and()); |
+ } |
+ |
+ /// Parses a simple expression: |
+ /// |
+ /// simpleExpression: |
+ /// "!" simpleExpression | |
+ /// "(" conditionalExpression ")" | |
+ /// IDENTIFIER |
+ Node _simpleExpression() { |
+ var token = _scanner.next(); |
+ switch (token.type) { |
+ case TokenType.not: |
+ var child = _simpleExpression(); |
+ return new NotNode(child, token.span.expand(child.span)); |
+ |
+ case TokenType.leftParen: |
+ var child = _conditional(); |
+ var next = _scanner.next(); |
+ if (next.type != TokenType.rightParen) { |
+ throw new SourceSpanFormatException('Expected ")".', next.span); |
+ } |
+ return child; |
+ |
+ case TokenType.identifier: |
+ return new VariableNode(token.name, token.span); |
+ |
+ default: |
+ throw new SourceSpanFormatException("Expected expression.", token.span); |
+ } |
+ } |
+} |