OLD | NEW |
---|---|
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 library string_scanner.line_scanner; | 5 library string_scanner.line_scanner; |
6 | 6 |
7 import 'package:charcode/ascii.dart'; | |
8 | |
7 import 'string_scanner.dart'; | 9 import 'string_scanner.dart'; |
8 | 10 |
11 /// A regular expression matching newlines across platforms. | |
12 final _newlineRegExp = new RegExp(r"\r\n?|\n"); | |
13 | |
9 /// A subclass of [StringScanner] that tracks line and column information. | 14 /// A subclass of [StringScanner] that tracks line and column information. |
10 class LineScanner extends StringScanner { | 15 class LineScanner extends StringScanner { |
11 /// The scanner's current (zero-based) line number. | 16 /// The scanner's current (zero-based) line number. |
12 int get line => _line; | 17 int get line => _line; |
13 int _line = 0; | 18 int _line = 0; |
14 | 19 |
15 /// The scanner's current (zero-based) column number. | 20 /// The scanner's current (zero-based) column number. |
16 int get column => _column; | 21 int get column => _column; |
17 int _column = 0; | 22 int _column = 0; |
18 | 23 |
19 /// The scanner's state, including line and column information. | 24 /// The scanner's state, including line and column information. |
20 /// | 25 /// |
21 /// This can be used to efficiently save and restore the state of the scanner | 26 /// This can be used to efficiently save and restore the state of the scanner |
22 /// when backtracking. A given [LineScannerState] is only valid for the | 27 /// when backtracking. A given [LineScannerState] is only valid for the |
23 /// [LineScanner] that created it. | 28 /// [LineScanner] that created it. |
24 LineScannerState get state => | 29 LineScannerState get state => |
25 new LineScannerState._(this, position, line, column); | 30 new LineScannerState._(this, position, line, column); |
26 | 31 |
32 /// Whether the current position is between a CR character and an LF | |
33 /// charactet. | |
34 bool get _betweenCRLF => peekChar(-1) == $cr && peekChar() == $lf; | |
35 | |
27 set state(LineScannerState state) { | 36 set state(LineScannerState state) { |
28 if (!identical(state._scanner, this)) { | 37 if (!identical(state._scanner, this)) { |
29 throw new ArgumentError("The given LineScannerState was not returned by " | 38 throw new ArgumentError("The given LineScannerState was not returned by " |
30 "this LineScanner."); | 39 "this LineScanner."); |
31 } | 40 } |
32 | 41 |
33 super.position = state.position; | 42 super.position = state.position; |
34 _line = state.line; | 43 _line = state.line; |
35 _column = state.column; | 44 _column = state.column; |
36 } | 45 } |
37 | 46 |
38 set position(int newPosition) { | 47 set position(int newPosition) { |
39 var oldPosition = position; | 48 var oldPosition = position; |
40 super.position = newPosition; | 49 super.position = newPosition; |
41 | 50 |
42 if (newPosition > oldPosition) { | 51 if (newPosition > oldPosition) { |
43 var newlines = | 52 var newlines = _newlineRegExp |
44 "\n".allMatches(string.substring(oldPosition, newPosition)).toList(); | 53 .allMatches(string.substring(oldPosition, newPosition)).toList(); |
54 if (_betweenCRLF) newlines.removeLast(); | |
Bob Nystrom
2015/09/02 00:07:24
How about hoisting this to a function:
int _newli
nweiz
2015/09/02 00:10:57
Done.
| |
55 | |
45 _line += newlines.length; | 56 _line += newlines.length; |
46 if (newlines.isEmpty) { | 57 if (newlines.isEmpty) { |
47 _column += newPosition - oldPosition; | 58 _column += newPosition - oldPosition; |
48 } else { | 59 } else { |
49 _column = newPosition - newlines.last.end; | 60 _column = newPosition - newlines.last.end; |
50 } | 61 } |
51 } else { | 62 } else { |
52 var newlines = | 63 var newlines = _newlineRegExp |
53 "\n".allMatches(string.substring(newPosition, oldPosition)).toList(); | 64 .allMatches(string.substring(newPosition, oldPosition)).toList(); |
65 if (_betweenCRLF) newlines.removeLast(); | |
66 | |
54 _line -= newlines.length; | 67 _line -= newlines.length; |
55 if (newlines.isEmpty) { | 68 if (newlines.isEmpty) { |
56 _column -= oldPosition - newPosition; | 69 _column -= oldPosition - newPosition; |
57 } else { | 70 } else { |
58 _column = newPosition - string.lastIndexOf("\n", newPosition) - 1; | 71 _column = newPosition - |
72 string.lastIndexOf(_newlineRegExp, newPosition) - 1; | |
59 } | 73 } |
60 } | 74 } |
61 } | 75 } |
62 | 76 |
63 LineScanner(String string, {sourceUrl, int position}) | 77 LineScanner(String string, {sourceUrl, int position}) |
64 : super(string, sourceUrl: sourceUrl, position: position); | 78 : super(string, sourceUrl: sourceUrl, position: position); |
65 | 79 |
66 int readChar() { | 80 int readChar() { |
67 var char = super.readChar(); | 81 var char = super.readChar(); |
68 if (char == 0xA) { | 82 if (char == $lf || (char == $cr && peekChar() != $lf)) { |
69 _line += 1; | 83 _line += 1; |
70 _column = 0; | 84 _column = 0; |
71 } else { | 85 } else { |
72 _column += 1; | 86 _column += 1; |
73 } | 87 } |
74 return char; | 88 return char; |
75 } | 89 } |
76 | 90 |
77 bool scan(Pattern pattern) { | 91 bool scan(Pattern pattern) { |
78 if (!super.scan(pattern)) return false; | 92 if (!super.scan(pattern)) return false; |
79 | 93 |
80 var newlines = "\n".allMatches(lastMatch[0]).toList(); | 94 var newlines = _newlineRegExp.allMatches(lastMatch[0]).toList(); |
95 if (_betweenCRLF) newlines.removeLast(); | |
81 _line += newlines.length; | 96 _line += newlines.length; |
Bob Nystrom
2015/09/02 00:07:24
Even here.
nweiz
2015/09/02 00:10:57
Done.
| |
82 if (newlines.isEmpty) { | 97 if (newlines.isEmpty) { |
83 _column += lastMatch[0].length; | 98 _column += lastMatch[0].length; |
84 } else { | 99 } else { |
85 _column = lastMatch[0].length - newlines.last.end; | 100 _column = lastMatch[0].length - newlines.last.end; |
86 } | 101 } |
87 | 102 |
88 return true; | 103 return true; |
89 } | 104 } |
90 } | 105 } |
91 | 106 |
92 /// A class representing the state of a [LineScanner]. | 107 /// A class representing the state of a [LineScanner]. |
93 class LineScannerState { | 108 class LineScannerState { |
94 /// The [LineScanner] that created this. | 109 /// The [LineScanner] that created this. |
95 final LineScanner _scanner; | 110 final LineScanner _scanner; |
96 | 111 |
97 /// The position of the scanner in this state. | 112 /// The position of the scanner in this state. |
98 final int position; | 113 final int position; |
99 | 114 |
100 /// The zero-based line number of the scanner in this state. | 115 /// The zero-based line number of the scanner in this state. |
101 final int line; | 116 final int line; |
102 | 117 |
103 /// The zero-based column number of the scanner in this state. | 118 /// The zero-based column number of the scanner in this state. |
104 final int column; | 119 final int column; |
105 | 120 |
106 LineScannerState._(this._scanner, this.position, this.line, this.column); | 121 LineScannerState._(this._scanner, this.position, this.line, this.column); |
107 } | 122 } |
OLD | NEW |