Line data Source code
1 : // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 2 : // for details. All rights reserved. Use of this source code is governed by a 3 : // BSD-style license that can be found in the LICENSE file. 4 : 5 : import 'package:source_span/source_span.dart'; 6 : 7 : import 'ast.dart'; 8 : import 'scanner.dart'; 9 : import 'token.dart'; 10 : 11 : /// A class for parsing a boolean selector. 12 : /// 13 : /// Boolean selectors use a stripped-down version of the Dart expression syntax 14 : /// that only contains variables, parentheses, and boolean operators. Variables 15 : /// may also contain dashes, contrary to Dart's syntax; this allows consistency 16 : /// with command-line arguments. 17 : class Parser { 18 : /// The scanner that tokenizes the selector. 19 : final Scanner _scanner; 20 : 21 10 : Parser(String selector) : _scanner = Scanner(selector); 22 : 23 : /// Parses the selector. 24 : /// 25 : /// This must only be called once per parser. 26 5 : Node parse() { 27 5 : var selector = _conditional(); 28 : 29 20 : if (_scanner.peek().type != TokenType.endOfFile) { 30 0 : throw SourceSpanFormatException( 31 0 : 'Expected end of input.', _scanner.peek().span); 32 : } 33 : 34 : return selector; 35 : } 36 : 37 : /// Parses a conditional: 38 : /// 39 : /// conditionalExpression: 40 : /// logicalOrExpression ("?" conditionalExpression ":" 41 : /// conditionalExpression)? 42 5 : Node _conditional() { 43 5 : var condition = _or(); 44 10 : if (!_scanner.scan(TokenType.questionMark)) return condition; 45 : 46 0 : var whenTrue = _conditional(); 47 0 : if (!_scanner.scan(TokenType.colon)) { 48 0 : throw SourceSpanFormatException('Expected ":".', _scanner.peek().span); 49 : } 50 : 51 0 : var whenFalse = _conditional(); 52 0 : return ConditionalNode(condition, whenTrue, whenFalse); 53 : } 54 : 55 : /// Parses a logical or: 56 : /// 57 : /// logicalOrExpression: 58 : /// logicalAndExpression ("||" logicalOrExpression)? 59 5 : Node _or() { 60 5 : var left = _and(); 61 10 : if (!_scanner.scan(TokenType.or)) return left; 62 0 : return OrNode(left, _or()); 63 : } 64 : 65 : /// Parses a logical and: 66 : /// 67 : /// logicalAndExpression: 68 : /// simpleExpression ("&&" logicalAndExpression)? 69 5 : Node _and() { 70 5 : var left = _simpleExpression(); 71 10 : if (!_scanner.scan(TokenType.and)) return left; 72 0 : return AndNode(left, _and()); 73 : } 74 : 75 : /// Parses a simple expression: 76 : /// 77 : /// simpleExpression: 78 : /// "!" simpleExpression | 79 : /// "(" conditionalExpression ")" | 80 : /// IDENTIFIER 81 5 : Node _simpleExpression() { 82 10 : var token = _scanner.next(); 83 5 : switch (token.type) { 84 5 : case TokenType.not: 85 0 : var child = _simpleExpression(); 86 0 : return NotNode(child, token.span.expand(child.span!)); 87 : 88 5 : case TokenType.leftParen: 89 0 : var child = _conditional(); 90 0 : if (!_scanner.scan(TokenType.rightParen)) { 91 0 : throw SourceSpanFormatException( 92 0 : 'Expected ")".', _scanner.peek().span); 93 : } 94 : return child; 95 : 96 5 : case TokenType.identifier: 97 15 : return VariableNode((token as IdentifierToken).name, token.span); 98 : 99 : default: 100 0 : throw SourceSpanFormatException('Expected expression.', token.span); 101 : } 102 : } 103 : }