Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1750)

Side by Side Diff: pkg/string_scanner/lib/src/string_scanner.dart

Issue 299973002: Add LineScanner and SpanScanner classes to string_scanner. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: code review Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/string_scanner/lib/src/span_scanner.dart ('k') | pkg/string_scanner/lib/src/utils.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /// A library for parsing strings using a sequence of patterns. 5 library string_scanner.string_scanner;
6 library string_scanner;
7 6
8 import 'dart:math' as math; 7 import 'package:source_maps/source_maps.dart';
8
9 import 'exception.dart';
10 import 'utils.dart';
9 11
10 /// When compiled to JS, forward slashes are always escaped in [RegExp.pattern]. 12 /// When compiled to JS, forward slashes are always escaped in [RegExp.pattern].
11 /// 13 ///
12 /// See issue 17998. 14 /// See issue 17998.
13 final _slashAutoEscape = new RegExp("/").pattern == "\\/"; 15 final _slashAutoEscape = new RegExp("/").pattern == "\\/";
14 16
15 // TODO(nweiz): Add some integration between this and source maps.
16 /// A class that scans through a string using [Pattern]s. 17 /// A class that scans through a string using [Pattern]s.
17 class StringScanner { 18 class StringScanner {
19 /// The URL of the source of the string being scanned.
20 ///
21 /// This is used for error reporting. It may be `null`, indicating that the
22 /// source URL is unknown or unavailable.
23 final Uri sourceUrl;
24
18 /// The string being scanned through. 25 /// The string being scanned through.
19 final String string; 26 final String string;
20 27
21 /// The current position of the scanner in the string, in characters. 28 /// The current position of the scanner in the string, in characters.
22 int get position => _position; 29 int get position => _position;
23 set position(int position) { 30 set position(int position) {
24 if (position < 0 || position > string.length) { 31 if (position < 0 || position > string.length) {
25 throw new ArgumentError("Invalid position $position"); 32 throw new ArgumentError("Invalid position $position");
26 } 33 }
27 34
28 _position = position; 35 _position = position;
29 } 36 }
30 int _position = 0; 37 int _position = 0;
31 38
32 /// The data about the previous match made by the scanner. 39 /// The data about the previous match made by the scanner.
33 /// 40 ///
34 /// If the last match failed, this will be `null`. 41 /// If the last match failed, this will be `null`.
35 Match get lastMatch => _lastMatch; 42 Match get lastMatch => _lastMatch;
36 Match _lastMatch; 43 Match _lastMatch;
37 44
38 /// The portion of the string that hasn't yet been scanned. 45 /// The portion of the string that hasn't yet been scanned.
39 String get rest => string.substring(position); 46 String get rest => string.substring(position);
40 47
41 /// Whether the scanner has completely consumed [string]. 48 /// Whether the scanner has completely consumed [string].
42 bool get isDone => position == string.length; 49 bool get isDone => position == string.length;
43 50
44 /// Creates a new [StringScanner] that starts scanning from [position]. 51 /// Creates a new [StringScanner] that starts scanning from [position].
45 /// 52 ///
46 /// [position] defaults to 0, the beginning of the string. 53 /// [position] defaults to 0, the beginning of the string. [sourceUrl] is the
47 StringScanner(this.string, {int position}) { 54 /// URL of the source of the string being scanned, if available. It can be
55 /// either a [String] or a [Uri].
56 StringScanner(this.string, {sourceUrl, int position})
57 : sourceUrl = sourceUrl is String ? Uri.parse(sourceUrl) : sourceUrl {
48 if (position != null) this.position = position; 58 if (position != null) this.position = position;
49 } 59 }
50 60
61 /// Consumes a single character and returns its character code.
62 ///
63 /// This throws a [FormatException] if the string has been fully consumed. It
64 /// doesn't affect [lastMatch].
65 int readChar() {
66 if (isDone) _fail("more input");
67 return string.codeUnitAt(_position++);
68 }
69
70 /// Returns the character code of the character [offset] away from [position].
71 ///
72 /// [offset] defaults to zero, and may be negative to inspect already-consumed
73 /// characters.
74 ///
75 /// This returns `null` if [offset] points outside the string. It doesn't
76 /// affect [lastMatch].
77 int peekChar([int offset]) {
78 if (offset == null) offset = 0;
79 var index = position + offset;
80 if (index < 0 || index >= string.length) return null;
81 return string.codeUnitAt(index);
82 }
83
51 /// If [pattern] matches at the current position of the string, scans forward 84 /// If [pattern] matches at the current position of the string, scans forward
52 /// until the end of the match. 85 /// until the end of the match.
53 /// 86 ///
54 /// Returns whether or not [pattern] matched. 87 /// Returns whether or not [pattern] matched.
55 bool scan(Pattern pattern) { 88 bool scan(Pattern pattern) {
56 var success = matches(pattern); 89 var success = matches(pattern);
57 if (success) _position = _lastMatch.end; 90 if (success) _position = _lastMatch.end;
58 return success; 91 return success;
59 } 92 }
60 93
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
105 /// error is associated. This should be a match returned by this scanner's 138 /// error is associated. This should be a match returned by this scanner's
106 /// [lastMatch] property. By default, the error is associated with the last 139 /// [lastMatch] property. By default, the error is associated with the last
107 /// match. 140 /// match.
108 /// 141 ///
109 /// If [position] and/or [length] are passed, they are used as the error span 142 /// If [position] and/or [length] are passed, they are used as the error span
110 /// instead. If only [length] is passed, [position] defaults to the current 143 /// instead. If only [length] is passed, [position] defaults to the current
111 /// position; if only [position] is passed, [length] defaults to 1. 144 /// position; if only [position] is passed, [length] defaults to 1.
112 /// 145 ///
113 /// It's an error to pass [match] at the same time as [position] or [length]. 146 /// It's an error to pass [match] at the same time as [position] or [length].
114 void error(String message, {Match match, int position, int length}) { 147 void error(String message, {Match match, int position, int length}) {
115 if (match != null && (position != null || length != null)) { 148 validateErrorArgs(string, match, position, length);
116 throw new ArgumentError("Can't pass both match and position/length.");
117 }
118
119 if (position != null && position < 0) {
120 throw new RangeError("position must be greater than or equal to 0.");
121 }
122
123 if (length != null && length < 1) {
124 throw new RangeError("length must be greater than or equal to 0.");
125 }
126 149
127 if (match == null && position == null && length == null) match = lastMatch; 150 if (match == null && position == null && length == null) match = lastMatch;
128 if (position == null) { 151 if (position == null) {
129 position = match == null ? this.position : match.start; 152 position = match == null ? this.position : match.start;
130 } 153 }
131 if (length == null) length = match == null ? 1 : match.end - match.start; 154 if (length == null) length = match == null ? 1 : match.end - match.start;
132 155
133 var newlines = "\n".allMatches(string.substring(0, position)).toList(); 156 var url = sourceUrl == null ? null : sourceUrl.toString();
134 var line = newlines.length + 1; 157 var sourceFile = new SourceFile.text(url, string);
135 var column; 158 var span = sourceFile.span(position, position + length);
136 var lastLine; 159 throw new StringScannerException(message, string, sourceUrl, span);
137 if (newlines.isEmpty) {
138 column = position + 1;
139 lastLine = string.substring(0, position);
140 } else {
141 column = position - newlines.last.end + 1;
142 lastLine = string.substring(newlines.last.end, position);
143 }
144
145 var remaining = string.substring(position);
146 var nextNewline = remaining.indexOf("\n");
147 if (nextNewline == -1) {
148 lastLine += remaining;
149 } else {
150 length = math.min(length, nextNewline);
151 lastLine += remaining.substring(0, nextNewline);
152 }
153
154 var spaces = new List.filled(column - 1, ' ').join();
155 var underline = new List.filled(length, '^').join();
156
157 throw new FormatException(
158 "Error on line $line, column $column: $message\n"
159 "$lastLine\n"
160 "$spaces$underline");
161 } 160 }
162 161
163 // TODO(nweiz): Make this handle long lines more gracefully. 162 // TODO(nweiz): Make this handle long lines more gracefully.
164 /// Throws a [FormatException] describing that [name] is expected at the 163 /// Throws a [FormatException] describing that [name] is expected at the
165 /// current position in the string. 164 /// current position in the string.
166 void _fail(String name) { 165 void _fail(String name) {
167 error("expected $name.", position: this.position, length: 1); 166 error("expected $name.", position: this.position, length: 0);
168 } 167 }
169 } 168 }
OLDNEW
« no previous file with comments | « pkg/string_scanner/lib/src/span_scanner.dart ('k') | pkg/string_scanner/lib/src/utils.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698