LCOV - code coverage report
Current view: top level - string_scanner-1.1.0/lib/src - string_scanner.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 22 64 34.4 %
Date: 2021-11-28 14:37:50 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright (c) 2014, 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:charcode/charcode.dart';
       6             : import 'package:source_span/source_span.dart';
       7             : 
       8             : import 'exception.dart';
       9             : import 'utils.dart';
      10             : 
      11             : /// A class that scans through a string using [Pattern]s.
      12             : class StringScanner {
      13             :   /// The URL of the source of the string being scanned.
      14             :   ///
      15             :   /// This is used for error reporting. It may be `null`, indicating that the
      16             :   /// source URL is unknown or unavailable.
      17             :   final Uri? sourceUrl;
      18             : 
      19             :   /// The string being scanned through.
      20             :   final String string;
      21             : 
      22             :   /// The current position of the scanner in the string, in characters.
      23          16 :   int get position => _position;
      24           0 :   set position(int position) {
      25           0 :     if (position < 0 || position > string.length) {
      26           0 :       throw ArgumentError('Invalid position $position');
      27             :     }
      28             : 
      29           0 :     _position = position;
      30           0 :     _lastMatch = null;
      31             :   }
      32             : 
      33             :   int _position = 0;
      34             : 
      35             :   /// The data about the previous match made by the scanner.
      36             :   ///
      37             :   /// If the last match failed, this will be `null`.
      38           8 :   Match? get lastMatch {
      39             :     // Lazily unset [_lastMatch] so that we avoid extra assignments in
      40             :     // character-by-character methods that are used in core loops.
      41          24 :     if (_position != _lastMatchPosition) _lastMatch = null;
      42           8 :     return _lastMatch;
      43             :   }
      44             : 
      45             :   Match? _lastMatch;
      46             :   int? _lastMatchPosition;
      47             : 
      48             :   /// The portion of the string that hasn't yet been scanned.
      49           0 :   String get rest => string.substring(position);
      50             : 
      51             :   /// Whether the scanner has completely consumed [string].
      52          40 :   bool get isDone => position == string.length;
      53             : 
      54             :   /// Creates a new [StringScanner] that starts scanning from [position].
      55             :   ///
      56             :   /// [position] defaults to 0, the beginning of the string. [sourceUrl] is the
      57             :   /// URL of the source of the string being scanned, if available. It can be
      58             :   /// a [String], a [Uri], or `null`.
      59           8 :   StringScanner(this.string, {sourceUrl, int? position})
      60             :       : sourceUrl = sourceUrl == null
      61             :             ? null
      62           0 :             : sourceUrl is String
      63           0 :                 ? Uri.parse(sourceUrl)
      64             :                 : sourceUrl as Uri {
      65           0 :     if (position != null) this.position = position;
      66             :   }
      67             : 
      68             :   /// Consumes a single character and returns its character code.
      69             :   ///
      70             :   /// This throws a [FormatException] if the string has been fully consumed. It
      71             :   /// doesn't affect [lastMatch].
      72           0 :   int readChar() {
      73           0 :     if (isDone) _fail('more input');
      74           0 :     return string.codeUnitAt(_position++);
      75             :   }
      76             : 
      77             :   /// Returns the character code of the character [offset] away from [position].
      78             :   ///
      79             :   /// [offset] defaults to zero, and may be negative to inspect already-consumed
      80             :   /// characters.
      81             :   ///
      82             :   /// This returns `null` if [offset] points outside the string. It doesn't
      83             :   /// affect [lastMatch].
      84           5 :   int? peekChar([int? offset]) {
      85             :     offset ??= 0;
      86          10 :     final index = position + offset;
      87          20 :     if (index < 0 || index >= string.length) return null;
      88          10 :     return string.codeUnitAt(index);
      89             :   }
      90             : 
      91             :   /// If the next character in the string is [character], consumes it.
      92             :   ///
      93             :   /// Returns whether or not [character] was consumed.
      94           0 :   bool scanChar(int character) {
      95           0 :     if (isDone) return false;
      96           0 :     if (string.codeUnitAt(_position) != character) return false;
      97           0 :     _position++;
      98             :     return true;
      99             :   }
     100             : 
     101             :   /// If the next character in the string is [character], consumes it.
     102             :   ///
     103             :   /// If [character] could not be consumed, throws a [FormatException]
     104             :   /// describing the position of the failure. [name] is used in this error as
     105             :   /// the expected name of the character being matched; if it's `null`, the
     106             :   /// character itself is used instead.
     107           0 :   void expectChar(int character, {String? name}) {
     108           0 :     if (scanChar(character)) return;
     109             : 
     110             :     if (name == null) {
     111           0 :       if (character == $backslash) {
     112             :         name = r'"\"';
     113           0 :       } else if (character == $double_quote) {
     114             :         name = r'"\""';
     115             :       } else {
     116           0 :         name = '"${String.fromCharCode(character)}"';
     117             :       }
     118             :     }
     119             : 
     120           0 :     _fail(name);
     121             :   }
     122             : 
     123             :   /// If [pattern] matches at the current position of the string, scans forward
     124             :   /// until the end of the match.
     125             :   ///
     126             :   /// Returns whether or not [pattern] matched.
     127           8 :   bool scan(Pattern pattern) {
     128           8 :     final success = matches(pattern);
     129             :     if (success) {
     130          24 :       _position = _lastMatch!.end;
     131          16 :       _lastMatchPosition = _position;
     132             :     }
     133             :     return success;
     134             :   }
     135             : 
     136             :   /// If [pattern] matches at the current position of the string, scans forward
     137             :   /// until the end of the match.
     138             :   ///
     139             :   /// If [pattern] did not match, throws a [FormatException] describing the
     140             :   /// position of the failure. [name] is used in this error as the expected name
     141             :   /// of the pattern being matched; if it's `null`, the pattern itself is used
     142             :   /// instead.
     143           8 :   void expect(Pattern pattern, {String? name}) {
     144           8 :     if (scan(pattern)) return;
     145             : 
     146             :     if (name == null) {
     147           0 :       if (pattern is RegExp) {
     148           0 :         final source = pattern.pattern;
     149           0 :         name = '/$source/';
     150             :       } else {
     151             :         name =
     152           0 :             pattern.toString().replaceAll('\\', '\\\\').replaceAll('"', '\\"');
     153           0 :         name = '"$name"';
     154             :       }
     155             :     }
     156           0 :     _fail(name);
     157             :   }
     158             : 
     159             :   /// If the string has not been fully consumed, this throws a
     160             :   /// [FormatException].
     161           5 :   void expectDone() {
     162           5 :     if (isDone) return;
     163           0 :     _fail('no more input');
     164             :   }
     165             : 
     166             :   /// Returns whether or not [pattern] matches at the current position of the
     167             :   /// string.
     168             :   ///
     169             :   /// This doesn't move the scan pointer forward.
     170           8 :   bool matches(Pattern pattern) {
     171          32 :     _lastMatch = pattern.matchAsPrefix(string, position);
     172          16 :     _lastMatchPosition = _position;
     173           8 :     return _lastMatch != null;
     174             :   }
     175             : 
     176             :   /// Returns the substring of [string] between [start] and [end].
     177             :   ///
     178             :   /// Unlike [String.substring], [end] defaults to [position] rather than the
     179             :   /// end of the string.
     180           0 :   String substring(int start, [int? end]) {
     181           0 :     end ??= position;
     182           0 :     return string.substring(start, end);
     183             :   }
     184             : 
     185             :   /// Throws a [FormatException] with [message] as well as a detailed
     186             :   /// description of the location of the error in the string.
     187             :   ///
     188             :   /// [match] is the match information for the span of the string with which the
     189             :   /// error is associated. This should be a match returned by this scanner's
     190             :   /// [lastMatch] property. By default, the error is associated with the last
     191             :   /// match.
     192             :   ///
     193             :   /// If [position] and/or [length] are passed, they are used as the error span
     194             :   /// instead. If only [length] is passed, [position] defaults to the current
     195             :   /// position; if only [position] is passed, [length] defaults to 0.
     196             :   ///
     197             :   /// It's an error to pass [match] at the same time as [position] or [length].
     198           0 :   Never error(String message, {Match? match, int? position, int? length}) {
     199           0 :     validateErrorArgs(string, match, position, length);
     200             : 
     201           0 :     if (match == null && position == null && length == null) match = lastMatch;
     202           0 :     position ??= match == null ? this.position : match.start;
     203           0 :     length ??= match == null ? 0 : match.end - match.start;
     204             : 
     205           0 :     final sourceFile = SourceFile.fromString(string, url: sourceUrl);
     206           0 :     final span = sourceFile.span(position, position + length);
     207           0 :     throw StringScannerException(message, span, string);
     208             :   }
     209             : 
     210             :   // TODO(nweiz): Make this handle long lines more gracefully.
     211             :   /// Throws a [FormatException] describing that [name] is expected at the
     212             :   /// current position in the string.
     213           0 :   Never _fail(String name) {
     214           0 :     error('expected $name.', position: position, length: 0);
     215             :   }
     216             : }

Generated by: LCOV version 1.14