Skip to content
This repository was archived by the owner on Feb 10, 2025. It is now read-only.

Commit 42cb2dc

Browse files
committed
Add breaking changes and release 1.0.0.
This makes `.error()` default to empty spans rather than single-character ones. This better represents the semantics of failing at a particular position in the text. It also makes `lastMatch` reset whenever the scanner's position changes. This makes `.error()` behave more consistently when primarily doing character-based scanning, since it won't unexpectedly emit an error for stale match data. [email protected] Review URL: https://codereview.chromium.org//2056933002 .
1 parent f83ceb2 commit 42cb2dc

File tree

7 files changed

+71
-9
lines changed

7 files changed

+71
-9
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
## 1.0.0
2+
3+
* **Breaking change**: `StringScanner.error()`'s `length` argument now defaults
4+
to `0` rather than `1` when no match data is available.
5+
6+
* **Breaking change**: `StringScanner.lastMatch` and related methods are now
7+
reset when the scanner's position changes without producing a new match.
8+
9+
**Note**: While the changes in `1.0.0` are user-visible, they're unlikely to
10+
actually break any code in practice. Unless you know that your package is
11+
incompatible with 0.1.x, consider using 0.1.5 as your lower bound rather
12+
than 1.0.0. For example, `string_scanner: ">=0.1.5 <2.0.0"`.
13+
114
## 0.1.5
215

316
* Add `new SpanScanner.within()`, which scans within a existing `FileSpan`.

lib/src/line_scanner.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ class LineScanner extends StringScanner {
2626
/// This can be used to efficiently save and restore the state of the scanner
2727
/// when backtracking. A given [LineScannerState] is only valid for the
2828
/// [LineScanner] that created it.
29+
///
30+
/// This does not include the scanner's match information.
2931
LineScannerState get state =>
3032
new LineScannerState._(this, position, line, column);
3133

lib/src/span_scanner.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ class SpanScanner extends StringScanner implements LineScanner {
3838
///
3939
/// This is the span for the entire match. There's no way to get spans for
4040
/// subgroups since [Match] exposes no information about their positions.
41-
FileSpan get lastSpan => _lastSpan;
41+
FileSpan get lastSpan {
42+
if (lastMatch == null) _lastSpan = null;
43+
return _lastSpan;
44+
}
4245
FileSpan _lastSpan;
4346

4447
/// The current location of the scanner.
@@ -102,7 +105,7 @@ class SpanScanner extends StringScanner implements LineScanner {
102105
if (position == null) {
103106
position = match == null ? this.position : match.start;
104107
}
105-
if (length == null) length = match == null ? 1 : match.end - match.start;
108+
if (length == null) length = match == null ? 0 : match.end - match.start;
106109

107110
var span = _sourceFile.span(position, position + length);
108111
throw new StringScannerException(message, span, string);

lib/src/string_scanner.dart

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,21 @@ class StringScanner {
3232
}
3333

3434
_position = position;
35+
_lastMatch = null;
3536
}
3637
int _position = 0;
3738

3839
/// The data about the previous match made by the scanner.
3940
///
4041
/// If the last match failed, this will be `null`.
41-
Match get lastMatch => _lastMatch;
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+
if (_position != _lastMatchPosition) _lastMatch = null;
46+
return _lastMatch;
47+
}
4248
Match _lastMatch;
49+
int _lastMatchPosition;
4350

4451
/// The portion of the string that hasn't yet been scanned.
4552
String get rest => string.substring(position);
@@ -118,7 +125,10 @@ class StringScanner {
118125
/// Returns whether or not [pattern] matched.
119126
bool scan(Pattern pattern) {
120127
var success = matches(pattern);
121-
if (success) _position = _lastMatch.end;
128+
if (success) {
129+
_position = _lastMatch.end;
130+
_lastMatchPosition = _position;
131+
}
122132
return success;
123133
}
124134

@@ -159,6 +169,7 @@ class StringScanner {
159169
/// This doesn't move the scan pointer forward.
160170
bool matches(Pattern pattern) {
161171
_lastMatch = pattern.matchAsPrefix(string, position);
172+
_lastMatchPosition = _position;
162173
return _lastMatch != null;
163174
}
164175

@@ -181,7 +192,7 @@ class StringScanner {
181192
///
182193
/// If [position] and/or [length] are passed, they are used as the error span
183194
/// instead. If only [length] is passed, [position] defaults to the current
184-
/// position; if only [position] is passed, [length] defaults to 1.
195+
/// position; if only [position] is passed, [length] defaults to 0.
185196
///
186197
/// It's an error to pass [match] at the same time as [position] or [length].
187198
void error(String message, {Match match, int position, int length}) {
@@ -191,7 +202,7 @@ class StringScanner {
191202
if (position == null) {
192203
position = match == null ? this.position : match.start;
193204
}
194-
if (length == null) length = match == null ? 1 : match.end - match.start;
205+
if (length == null) length = match == null ? 0 : match.end - match.start;
195206

196207
var sourceFile = new SourceFile(string, url: sourceUrl);
197208
var span = sourceFile.span(position, position + length);

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: string_scanner
2-
version: 0.1.5
2+
version: 1.0.0
33
author: "Dart Team <[email protected]>"
44
homepage: https://github.com/dart-lang/string_scanner
55
description: >

test/error_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ void main() {
5959
});
6060

6161
group("with position and/or length", () {
62-
test('defaults to length 1', () {
62+
test('defaults to length 0', () {
6363
var scanner = new StringScanner('foo bar baz');
6464
scanner.expect('foo ');
6565
expect(() => scanner.error('oh no!', position: 1),
66-
throwsStringScannerException('o'));
66+
throwsStringScannerException(''));
6767
});
6868

6969
test('defaults to the current position', () {

test/string_scanner_test.dart

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,32 @@ void main() {
261261
});
262262
});
263263

264+
group('after a scan', () {
265+
var scanner;
266+
setUp(() {
267+
scanner = new StringScanner('foo bar');
268+
expect(scanner.scan('foo'), isTrue);
269+
});
270+
271+
test('readChar returns the first character and unsets the last match', () {
272+
expect(scanner.readChar(), equals($space));
273+
expect(scanner.lastMatch, isNull);
274+
expect(scanner.position, equals(4));
275+
});
276+
277+
test('a matching scanChar returns true and unsets the last match', () {
278+
expect(scanner.scanChar($space), isTrue);
279+
expect(scanner.lastMatch, isNull);
280+
expect(scanner.position, equals(4));
281+
});
282+
283+
test('a matching expectChar returns true and unsets the last match', () {
284+
scanner.expectChar($space);
285+
expect(scanner.lastMatch, isNull);
286+
expect(scanner.position, equals(4));
287+
});
288+
});
289+
264290
group('at the end of a string', () {
265291
var scanner;
266292
setUp(() {
@@ -346,6 +372,13 @@ void main() {
346372
expect(scanner.rest, equals('bar'));
347373
});
348374

375+
test('setting and resetting position clears lastMatch', () {
376+
var oldPosition = scanner.position;
377+
scanner.position = 1;
378+
scanner.position = oldPosition;
379+
expect(scanner.lastMatch, isNull);
380+
});
381+
349382
test('setting position beyond the string throws an ArgumentError', () {
350383
expect(() {
351384
scanner.position = 8;

0 commit comments

Comments
 (0)