| // Copyright (c) 2016, 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. |
| |
| import 'package:source_span/source_span.dart'; |
| |
| import 'ast.dart'; |
| import 'scanner.dart'; |
| import 'token.dart'; |
| |
| /// A class for parsing a boolean selector. |
| /// |
| /// Boolean 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 = 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 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 SourceSpanFormatException('Expected ":".', _scanner.peek().span); |
| } |
| |
| var whenFalse = _conditional(); |
| return ConditionalNode(condition, whenTrue, whenFalse); |
| } |
| |
| /// Parses a logical or: |
| /// |
| /// logicalOrExpression: |
| /// logicalAndExpression ("||" logicalOrExpression)? |
| Node _or() { |
| var left = _and(); |
| if (!_scanner.scan(TokenType.or)) return left; |
| return OrNode(left, _or()); |
| } |
| |
| /// Parses a logical and: |
| /// |
| /// logicalAndExpression: |
| /// simpleExpression ("&&" logicalAndExpression)? |
| Node _and() { |
| var left = _simpleExpression(); |
| if (!_scanner.scan(TokenType.and)) return left; |
| return 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 NotNode(child, token.span.expand(child.span!)); |
| |
| case TokenType.leftParen: |
| var child = _conditional(); |
| if (!_scanner.scan(TokenType.rightParen)) { |
| throw SourceSpanFormatException( |
| 'Expected ")".', _scanner.peek().span); |
| } |
| return child; |
| |
| case TokenType.identifier: |
| return VariableNode((token as IdentifierToken).name, token.span); |
| |
| default: |
| throw SourceSpanFormatException('Expected expression.', token.span); |
| } |
| } |
| } |