| Index: pkg/string_scanner/lib/src/string_scanner.dart
|
| diff --git a/pkg/string_scanner/lib/string_scanner.dart b/pkg/string_scanner/lib/src/string_scanner.dart
|
| similarity index 69%
|
| copy from pkg/string_scanner/lib/string_scanner.dart
|
| copy to pkg/string_scanner/lib/src/string_scanner.dart
|
| index 3e3913b50169ba5796102c6314aba658341457b8..c9e74594a6f5a7ee08f53e0c59adaba7e82c21c4 100644
|
| --- a/pkg/string_scanner/lib/string_scanner.dart
|
| +++ b/pkg/string_scanner/lib/src/string_scanner.dart
|
| @@ -2,19 +2,26 @@
|
| // 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.
|
|
|
| -/// A library for parsing strings using a sequence of patterns.
|
| -library string_scanner;
|
| +library string_scanner.string_scanner;
|
|
|
| -import 'dart:math' as math;
|
| +import 'package:source_maps/source_maps.dart';
|
| +
|
| +import 'exception.dart';
|
| +import 'utils.dart';
|
|
|
| /// When compiled to JS, forward slashes are always escaped in [RegExp.pattern].
|
| ///
|
| /// See issue 17998.
|
| final _slashAutoEscape = new RegExp("/").pattern == "\\/";
|
|
|
| -// TODO(nweiz): Add some integration between this and source maps.
|
| /// A class that scans through a string using [Pattern]s.
|
| class StringScanner {
|
| + /// The URL of the source of the string being scanned.
|
| + ///
|
| + /// This is used for error reporting. It may be `null`, indicating that the
|
| + /// source URL is unknown or unavailable.
|
| + final Uri sourceUrl;
|
| +
|
| /// The string being scanned through.
|
| final String string;
|
|
|
| @@ -43,11 +50,37 @@ class StringScanner {
|
|
|
| /// Creates a new [StringScanner] that starts scanning from [position].
|
| ///
|
| - /// [position] defaults to 0, the beginning of the string.
|
| - StringScanner(this.string, {int position}) {
|
| + /// [position] defaults to 0, the beginning of the string. [sourceUrl] is the
|
| + /// URL of the source of the string being scanned, if available. It can be
|
| + /// either a [String] or a [Uri].
|
| + StringScanner(this.string, {sourceUrl, int position})
|
| + : sourceUrl = sourceUrl is String ? Uri.parse(sourceUrl) : sourceUrl {
|
| if (position != null) this.position = position;
|
| }
|
|
|
| + /// Consumes a single character and returns its character code.
|
| + ///
|
| + /// This throws a [FormatException] if the string has been fully consumed. It
|
| + /// doesn't affect [lastMatch].
|
| + int readChar() {
|
| + if (isDone) _fail("more input");
|
| + return string.codeUnitAt(_position++);
|
| + }
|
| +
|
| + /// Returns the character code of the character [offset] away from [position].
|
| + ///
|
| + /// [offset] defaults to zero, and may be negative to inspect already-consumed
|
| + /// characters.
|
| + ///
|
| + /// This returns `null` if [offset] points outside the string. It doesn't
|
| + /// affect [lastMatch].
|
| + int peekChar([int offset]) {
|
| + if (offset == null) offset = 0;
|
| + var index = position + offset;
|
| + if (index < 0 || index >= string.length) return null;
|
| + return string.codeUnitAt(index);
|
| + }
|
| +
|
| /// If [pattern] matches at the current position of the string, scans forward
|
| /// until the end of the match.
|
| ///
|
| @@ -112,17 +145,7 @@ class StringScanner {
|
| ///
|
| /// 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}) {
|
| - if (match != null && (position != null || length != null)) {
|
| - throw new ArgumentError("Can't pass both match and position/length.");
|
| - }
|
| -
|
| - if (position != null && position < 0) {
|
| - throw new RangeError("position must be greater than or equal to 0.");
|
| - }
|
| -
|
| - if (length != null && length < 1) {
|
| - throw new RangeError("length must be greater than or equal to 0.");
|
| - }
|
| + validateErrorArgs(string, match, position, length);
|
|
|
| if (match == null && position == null && length == null) match = lastMatch;
|
| if (position == null) {
|
| @@ -130,40 +153,16 @@ class StringScanner {
|
| }
|
| if (length == null) length = match == null ? 1 : match.end - match.start;
|
|
|
| - var newlines = "\n".allMatches(string.substring(0, position)).toList();
|
| - var line = newlines.length + 1;
|
| - var column;
|
| - var lastLine;
|
| - if (newlines.isEmpty) {
|
| - column = position + 1;
|
| - lastLine = string.substring(0, position);
|
| - } else {
|
| - column = position - newlines.last.end + 1;
|
| - lastLine = string.substring(newlines.last.end, position);
|
| - }
|
| -
|
| - var remaining = string.substring(position);
|
| - var nextNewline = remaining.indexOf("\n");
|
| - if (nextNewline == -1) {
|
| - lastLine += remaining;
|
| - } else {
|
| - length = math.min(length, nextNewline);
|
| - lastLine += remaining.substring(0, nextNewline);
|
| - }
|
| -
|
| - var spaces = new List.filled(column - 1, ' ').join();
|
| - var underline = new List.filled(length, '^').join();
|
| -
|
| - throw new FormatException(
|
| - "Error on line $line, column $column: $message\n"
|
| - "$lastLine\n"
|
| - "$spaces$underline");
|
| + var url = sourceUrl == null ? null : sourceUrl.toString();
|
| + var sourceFile = new SourceFile.text(url, string);
|
| + var span = sourceFile.span(position, position + length);
|
| + throw new StringScannerException(message, string, sourceUrl, span);
|
| }
|
|
|
| // TODO(nweiz): Make this handle long lines more gracefully.
|
| /// Throws a [FormatException] describing that [name] is expected at the
|
| /// current position in the string.
|
| void _fail(String name) {
|
| - error("expected $name.", position: this.position, length: 1);
|
| + error("expected $name.", position: this.position, length: 0);
|
| }
|
| }
|
|
|