| Index: packages/string_scanner/lib/src/string_scanner.dart
|
| diff --git a/packages/string_scanner/lib/src/string_scanner.dart b/packages/string_scanner/lib/src/string_scanner.dart
|
| index d9c1c2fd3c109c692671508a2e514a6bd5e9ec16..712292c60a6e42c99e21a20f550fc9c517880fa7 100644
|
| --- a/packages/string_scanner/lib/src/string_scanner.dart
|
| +++ b/packages/string_scanner/lib/src/string_scanner.dart
|
| @@ -2,8 +2,7 @@
|
| // 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.
|
|
|
| -library string_scanner.string_scanner;
|
| -
|
| +import 'package:charcode/charcode.dart';
|
| import 'package:source_span/source_span.dart';
|
|
|
| import 'exception.dart';
|
| @@ -33,14 +32,21 @@ class StringScanner {
|
| }
|
|
|
| _position = position;
|
| + _lastMatch = null;
|
| }
|
| int _position = 0;
|
|
|
| /// The data about the previous match made by the scanner.
|
| ///
|
| /// If the last match failed, this will be `null`.
|
| - Match get lastMatch => _lastMatch;
|
| + Match get lastMatch {
|
| + // Lazily unset [_lastMatch] so that we avoid extra assignments in
|
| + // character-by-character methods that are used in core loops.
|
| + if (_position != _lastMatchPosition) _lastMatch = null;
|
| + return _lastMatch;
|
| + }
|
| Match _lastMatch;
|
| + int _lastMatchPosition;
|
|
|
| /// The portion of the string that hasn't yet been scanned.
|
| String get rest => string.substring(position);
|
| @@ -81,13 +87,48 @@ class StringScanner {
|
| return string.codeUnitAt(index);
|
| }
|
|
|
| + /// If the next character in the string is [character], consumes it.
|
| + ///
|
| + /// Returns whether or not [character] was consumed.
|
| + bool scanChar(int character) {
|
| + if (isDone) return false;
|
| + if (string.codeUnitAt(_position) != character) return false;
|
| + _position++;
|
| + return true;
|
| + }
|
| +
|
| + /// If the next character in the string is [character], consumes it.
|
| + ///
|
| + /// If [character] could not be consumed, throws a [FormatException]
|
| + /// describing the position of the failure. [name] is used in this error as
|
| + /// the expected name of the character being matched; if it's `null`, the
|
| + /// character itself is used instead.
|
| + void expectChar(int character, {String name}) {
|
| + if (scanChar(character)) return;
|
| +
|
| + if (name == null) {
|
| + if (character == $backslash) {
|
| + name = r'"\"';
|
| + } else if (character == $double_quote) {
|
| + name = r'"\""';
|
| + } else {
|
| + name = '"${new String.fromCharCode(character)}"';
|
| + }
|
| + }
|
| +
|
| + _fail(name);
|
| + }
|
| +
|
| /// If [pattern] matches at the current position of the string, scans forward
|
| /// until the end of the match.
|
| ///
|
| /// Returns whether or not [pattern] matched.
|
| bool scan(Pattern pattern) {
|
| var success = matches(pattern);
|
| - if (success) _position = _lastMatch.end;
|
| + if (success) {
|
| + _position = _lastMatch.end;
|
| + _lastMatchPosition = _position;
|
| + }
|
| return success;
|
| }
|
|
|
| @@ -128,6 +169,7 @@ class StringScanner {
|
| /// This doesn't move the scan pointer forward.
|
| bool matches(Pattern pattern) {
|
| _lastMatch = pattern.matchAsPrefix(string, position);
|
| + _lastMatchPosition = _position;
|
| return _lastMatch != null;
|
| }
|
|
|
| @@ -150,7 +192,7 @@ class StringScanner {
|
| ///
|
| /// If [position] and/or [length] are passed, they are used as the error span
|
| /// instead. If only [length] is passed, [position] defaults to the current
|
| - /// position; if only [position] is passed, [length] defaults to 1.
|
| + /// position; if only [position] is passed, [length] defaults to 0.
|
| ///
|
| /// It's an error to pass [match] at the same time as [position] or [length].
|
| void error(String message, {Match match, int position, int length}) {
|
| @@ -160,9 +202,9 @@ class StringScanner {
|
| if (position == null) {
|
| position = match == null ? this.position : match.start;
|
| }
|
| - if (length == null) length = match == null ? 1 : match.end - match.start;
|
| + if (length == null) length = match == null ? 0 : match.end - match.start;
|
|
|
| - var sourceFile = new SourceFile(string, url: sourceUrl);
|
| + var sourceFile = new SourceFile.fromString(string, url: sourceUrl);
|
| var span = sourceFile.span(position, position + length);
|
| throw new StringScannerException(message, span, string);
|
| }
|
|
|