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..2761e8c0b2804207aee7682938d94a8e22a807b1 |
--- /dev/null |
+++ b/lib/src/backend/platform_selector/parser.dart |
@@ -0,0 +1,107 @@ |
+// 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. |
+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.scan(TokenType.questionMark)) return condition; |
+ |
+ var whenTrue = _conditional(); |
+ if (!_scanner.scan(TokenType.colon)) { |
+ throw new SourceSpanFormatException( |
+ 'Expected ":".', _scanner.peek().span); |
+ } |
+ |
+ var whenFalse = _conditional(); |
+ return new ConditionalNode(condition, whenTrue, whenFalse); |
+ } |
+ |
+ /// Parses a logical or: |
+ /// |
+ /// logicalOrExpression: |
+ /// logicalAndExpression ("||" logicalOrExpression)? |
+ Node _or() { |
+ var left = _and(); |
+ if (!_scanner.scan(TokenType.or)) return left; |
+ return new OrNode(left, _or()); |
+ } |
+ |
+ /// Parses a logical and: |
+ /// |
+ /// logicalAndExpression: |
+ /// simpleExpression ("&&" logicalAndExpression)? |
+ Node _and() { |
+ var left = _simpleExpression(); |
+ if (!_scanner.scan(TokenType.and)) return left; |
+ return new AndNode(left, _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(); |
+ if (!_scanner.scan(TokenType.rightParen)) { |
+ throw new SourceSpanFormatException( |
+ 'Expected ")".', _scanner.peek().span); |
+ } |
+ return child; |
+ |
+ case TokenType.identifier: |
+ return new VariableNode(token.name, token.span); |
+ |
+ default: |
+ throw new SourceSpanFormatException("Expected expression.", token.span); |
+ } |
+ } |
+} |