LCOV - code coverage report
Current view: top level - string_scanner-1.0.2/lib/src - string_scanner.dart (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 0 50 0.0 %
Date: 2017-10-10 20:17:03 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             : /// When compiled to JS, forward slashes are always escaped in [RegExp.pattern].
      12             : ///
      13             : /// See issue 17998.
      14             : final _slashAutoEscape = new RegExp("/").pattern == "\\/";
      15             : 
      16             : /// A class that scans through a string using [Pattern]s.
      17             : class StringScanner {
      18             :   /// The URL of the source of the string being scanned.
      19             :   ///
      20             :   /// This is used for error reporting. It may be `null`, indicating that the
      21             :   /// source URL is unknown or unavailable.
      22             :   final Uri sourceUrl;
      23             : 
      24             :   /// The string being scanned through.
      25             :   final String string;
      26             : 
      27             :   /// The current position of the scanner in the string, in characters.
      28           0 :   int get position => _position;
      29             :   set position(int position) {
      30           0 :     if (position < 0 || position > string.length) {
      31           0 :       throw new ArgumentError("Invalid position $position");
      32             :     }
      33             : 
      34           0 :     _position = position;
      35           0 :     _lastMatch = null;
      36             :   }
      37             :   int _position = 0;
      38             : 
      39             :   /// The data about the previous match made by the scanner.
      40             :   ///
      41             :   /// If the last match failed, this will be `null`.
      42             :   Match get lastMatch {
      43             :     // Lazily unset [_lastMatch] so that we avoid extra assignments in
      44             :     // character-by-character methods that are used in core loops.
      45           0 :     if (_position != _lastMatchPosition) _lastMatch = null;
      46           0 :     return _lastMatch;
      47             :   }
      48             :   Match _lastMatch;
      49             :   int _lastMatchPosition;
      50             : 
      51             :   /// The portion of the string that hasn't yet been scanned.
      52           0 :   String get rest => string.substring(position);
      53             : 
      54             :   /// Whether the scanner has completely consumed [string].
      55           0 :   bool get isDone => position == string.length;
      56             : 
      57             :   /// Creates a new [StringScanner] that starts scanning from [position].
      58             :   ///
      59             :   /// [position] defaults to 0, the beginning of the string. [sourceUrl] is the
      60             :   /// URL of the source of the string being scanned, if available. It can be
      61             :   /// a [String], a [Uri], or `null`.
      62             :   StringScanner(this.string, {sourceUrl, int position})
      63           0 :       : sourceUrl = sourceUrl is String ? Uri.parse(sourceUrl) : sourceUrl {
      64           0 :     if (position != null) this.position = position;
      65             :   }
      66             : 
      67             :   /// Consumes a single character and returns its character code.
      68             :   ///
      69             :   /// This throws a [FormatException] if the string has been fully consumed. It
      70             :   /// doesn't affect [lastMatch].
      71             :   int readChar() {
      72           0 :     if (isDone) _fail("more input");
      73           0 :     return string.codeUnitAt(_position++);
      74             :   }
      75             : 
      76             :   /// Returns the character code of the character [offset] away from [position].
      77             :   ///
      78             :   /// [offset] defaults to zero, and may be negative to inspect already-consumed
      79             :   /// characters.
      80             :   ///
      81             :   /// This returns `null` if [offset] points outside the string. It doesn't
      82             :   /// affect [lastMatch].
      83             :   int peekChar([int offset]) {
      84             :     if (offset == null) offset = 0;
      85           0 :     var index = position + offset;
      86           0 :     if (index < 0 || index >= string.length) return null;
      87           0 :     return string.codeUnitAt(index);
      88             :   }
      89             : 
      90             :   /// If the next character in the string is [character], consumes it.
      91             :   ///
      92             :   /// Returns whether or not [character] was consumed.
      93             :   bool scanChar(int character) {
      94           0 :     if (isDone) return false;
      95           0 :     if (string.codeUnitAt(_position) != character) return false;
      96           0 :     _position++;
      97             :     return true;
      98             :   }
      99             : 
     100             :   /// If the next character in the string is [character], consumes it.
     101             :   ///
     102             :   /// If [character] could not be consumed, throws a [FormatException]
     103             :   /// describing the position of the failure. [name] is used in this error as
     104             :   /// the expected name of the character being matched; if it's `null`, the
     105             :   /// character itself is used instead.
     106             :   void expectChar(int character, {String name}) {
     107           0 :     if (scanChar(character)) return;
     108             : 
     109             :     if (name == null) {
     110           0 :       if (character == $backslash) {
     111             :         name = r'"\"';
     112           0 :       } else if (character == $double_quote) {
     113             :         name = r'"\""';
     114             :       } else {
     115           0 :         name = '"${new String.fromCharCode(character)}"';
     116             :       }
     117             :     }
     118             : 
     119           0 :     _fail(name);
     120             :   }
     121             : 
     122             :   /// If [pattern] matches at the current position of the string, scans forward
     123             :   /// until the end of the match.
     124             :   ///
     125             :   /// Returns whether or not [pattern] matched.
     126             :   bool scan(Pattern pattern) {
     127           0 :     var success = matches(pattern);
     128             :     if (success) {
     129           0 :       _position = _lastMatch.end;
     130           0 :       _lastMatchPosition = _position;
     131             :     }
     132             :     return success;
     133             :   }
     134             : 
     135             :   /// If [pattern] matches at the current position of the string, scans forward
     136             :   /// until the end of the match.
     137             :   ///
     138             :   /// If [pattern] did not match, throws a [FormatException] describing the
     139             :   /// position of the failure. [name] is used in this error as the expected name
     140             :   /// of the pattern being matched; if it's `null`, the pattern itself is used
     141             :   /// instead.
     142             :   void expect(Pattern pattern, {String name}) {
     143           0 :     if (scan(pattern)) return;
     144             : 
     145             :     if (name == null) {
     146           0 :       if (pattern is RegExp) {
     147           0 :         var source = pattern.pattern;
     148           0 :         if (!_slashAutoEscape) source = source.replaceAll("/", "\\/");
     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             :   void expectDone() {
     162           0 :     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             :   bool matches(Pattern pattern) {
     171           0 :     _lastMatch = pattern.matchAsPrefix(string, position);
     172           0 :     _lastMatchPosition = _position;
     173           0 :     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             :   String substring(int start, [int end]) {
     181           0 :     if (end == null) 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             :   void 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             :     if (position == null) {
     203           0 :       position = match == null ? this.position : match.start;
     204             :     }
     205           0 :     if (length == null) length = match == null ? 0 : match.end - match.start;
     206             : 
     207           0 :     var sourceFile = new SourceFile.fromString(string, url: sourceUrl);
     208           0 :     var span = sourceFile.span(position, position + length);
     209           0 :     throw new StringScannerException(message, span, string);
     210             :   }
     211             : 
     212             :   // TODO(nweiz): Make this handle long lines more gracefully.
     213             :   /// Throws a [FormatException] describing that [name] is expected at the
     214             :   /// current position in the string.
     215             :   void _fail(String name) {
     216           0 :     error("expected $name.", position: this.position, length: 0);
     217             :   }
     218             : }

Generated by: LCOV version 1.13