LCOV - code coverage report
Current view: top level - boolean_selector-2.1.0/lib/src - scanner.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 32 55 58.2 %
Date: 2021-11-28 14:37:50 Functions: 0 0 -

          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:string_scanner/string_scanner.dart';
       6             : 
       7             : import 'token.dart';
       8             : 
       9             : /// A regular expression matching both whitespace and single-line comments.
      10             : ///
      11             : /// This will only match if consumes at least one character.
      12          15 : final _whitespaceAndSingleLineComments = RegExp(r'([ \t\n]+|//[^\n]*(\n|$))+');
      13             : 
      14             : /// A regular expression matching the body of a multi-line comment, after `/*`
      15             : /// but before `*/` or a nested `/*`.
      16             : ///
      17             : /// This will only match if it consumes at least one character.
      18           0 : final _multiLineCommentBody = RegExp(r'([^/*]|/[^*]|\*[^/])+');
      19             : 
      20             : /// A regular expression matching a hyphenated identifier.
      21             : ///
      22             : /// This is like a standard Dart identifier, except that it can also contain
      23             : /// hyphens.
      24          15 : final _hyphenatedIdentifier = RegExp(r'[a-zA-Z_-][a-zA-Z0-9_-]*');
      25             : 
      26             : /// A scanner that converts a boolean selector string into a stream of tokens.
      27             : class Scanner {
      28             :   /// The underlying string scanner.
      29             :   final SpanScanner _scanner;
      30             : 
      31             :   /// The next token to emit.
      32             :   Token? _next;
      33             : 
      34             :   /// Whether the scanner has emitted a [TokenType.endOfFile] token.
      35             :   bool _endOfFileEmitted = false;
      36             : 
      37          10 :   Scanner(String selector) : _scanner = SpanScanner(selector);
      38             : 
      39             :   /// Returns the next token that will be returned by [next].
      40             :   ///
      41             :   /// Throws a [StateError] if a [TokenType.endOfFile] token has already been
      42             :   /// consumed.
      43          15 :   Token peek() => _next ??= _readNext();
      44             : 
      45             :   /// Consumes and returns the next token in the stream.
      46             :   ///
      47             :   /// Throws a [StateError] if a [TokenType.endOfFile] token has already been
      48             :   /// consumed.
      49           5 :   Token next() {
      50          10 :     var token = _next ?? _readNext();
      51          15 :     _endOfFileEmitted = token.type == TokenType.endOfFile;
      52           5 :     _next = null;
      53             :     return token;
      54             :   }
      55             : 
      56             :   /// If the next token matches [type], consumes it and returns `true`;
      57             :   /// otherwise, returns `false`.
      58             :   ///
      59             :   /// Throws a [StateError] if a [TokenType.endOfFile] token has already been
      60             :   /// consumed.
      61           5 :   bool scan(TokenType type) {
      62          15 :     if (peek().type != type) return false;
      63           0 :     next();
      64             :     return true;
      65             :   }
      66             : 
      67             :   /// Scan and return the next token in the stream.
      68           5 :   Token _readNext() {
      69           5 :     if (_endOfFileEmitted) throw StateError('No more tokens.');
      70             : 
      71           5 :     _consumeWhitespace();
      72          10 :     if (_scanner.isDone) {
      73          25 :       return Token(TokenType.endOfFile, _scanner.spanFrom(_scanner.state));
      74             :     }
      75             : 
      76          10 :     switch (_scanner.peekChar()) {
      77           5 :       case 0x28 /* ( */ :
      78           0 :         return _scanOperator(TokenType.leftParen);
      79           5 :       case 0x29 /* ) */ :
      80           0 :         return _scanOperator(TokenType.rightParen);
      81           5 :       case 0x3F /* ? */ :
      82           0 :         return _scanOperator(TokenType.questionMark);
      83           5 :       case 0x3A /* : */ :
      84           0 :         return _scanOperator(TokenType.colon);
      85           5 :       case 0x21 /* ! */ :
      86           0 :         return _scanOperator(TokenType.not);
      87           5 :       case 0x7C /* | */ :
      88           0 :         return _scanOr();
      89           5 :       case 0x26 /* & */ :
      90           0 :         return _scanAnd();
      91             :       default:
      92           5 :         return _scanIdentifier();
      93             :     }
      94             :   }
      95             : 
      96             :   /// Scans a single-character operator and returns a token of type [type].
      97             :   ///
      98             :   /// This assumes that the caller has already verified that the next character
      99             :   /// is correct for the given operator.
     100           0 :   Token _scanOperator(TokenType type) {
     101           0 :     var start = _scanner.state;
     102           0 :     _scanner.readChar();
     103           0 :     return Token(type, _scanner.spanFrom(start));
     104             :   }
     105             : 
     106             :   /// Scans a `||` operator and returns the appropriate token.
     107             :   ///
     108             :   /// This validates that the next two characters are `||`.
     109           0 :   Token _scanOr() {
     110           0 :     var start = _scanner.state;
     111           0 :     _scanner.expect('||');
     112           0 :     return Token(TokenType.or, _scanner.spanFrom(start));
     113             :   }
     114             : 
     115             :   /// Scans a `&&` operator and returns the appropriate token.
     116             :   ///
     117             :   /// This validates that the next two characters are `&&`.
     118           0 :   Token _scanAnd() {
     119           0 :     var start = _scanner.state;
     120           0 :     _scanner.expect('&&');
     121           0 :     return Token(TokenType.and, _scanner.spanFrom(start));
     122             :   }
     123             : 
     124             :   /// Scans and returns an identifier token.
     125           5 :   Token _scanIdentifier() {
     126          15 :     _scanner.expect(_hyphenatedIdentifier, name: 'expression');
     127          30 :     return IdentifierToken(_scanner.lastMatch![0]!, _scanner.lastSpan!);
     128             :   }
     129             : 
     130             :   /// Consumes all whitespace and comments immediately following the cursor's
     131             :   /// current position.
     132           5 :   void _consumeWhitespace() {
     133          15 :     while (_scanner.scan(_whitespaceAndSingleLineComments) ||
     134           5 :         _multiLineComment()) {
     135             :       // Do nothing.
     136             :     }
     137             :   }
     138             : 
     139             :   /// Consumes a single multi-line comment.
     140             :   ///
     141             :   /// Returns whether or not a comment was consumed.
     142           5 :   bool _multiLineComment() {
     143          10 :     if (!_scanner.scan('/*')) return false;
     144             : 
     145           0 :     while (_scanner.scan(_multiLineCommentBody) || _multiLineComment()) {
     146             :       // Do nothing.
     147             :     }
     148           0 :     _scanner.expect('*/');
     149             : 
     150             :     return true;
     151             :   }
     152             : }

Generated by: LCOV version 1.14