Line data Source code
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
3 : // BSD-style license that can be found in the LICENSE file.
4 :
5 : import 'package:charcode/ascii.dart';
6 :
7 : import 'string_scanner.dart';
8 :
9 : // Note that much of this code is duplicated in eager_span_scanner.dart.
10 :
11 : /// A regular expression matching newlines across platforms.
12 : final _newlineRegExp = new RegExp(r"\r\n?|\n");
13 :
14 : /// A subclass of [StringScanner] that tracks line and column information.
15 : class LineScanner extends StringScanner {
16 : /// The scanner's current (zero-based) line number.
17 0 : int get line => _line;
18 : int _line = 0;
19 :
20 : /// The scanner's current (zero-based) column number.
21 0 : int get column => _column;
22 : int _column = 0;
23 :
24 : /// The scanner's state, including line and column information.
25 : ///
26 : /// This can be used to efficiently save and restore the state of the scanner
27 : /// when backtracking. A given [LineScannerState] is only valid for the
28 : /// [LineScanner] that created it.
29 : ///
30 : /// This does not include the scanner's match information.
31 : LineScannerState get state =>
32 0 : new LineScannerState._(this, position, line, column);
33 :
34 : /// Whether the current position is between a CR character and an LF
35 : /// charactet.
36 0 : bool get _betweenCRLF => peekChar(-1) == $cr && peekChar() == $lf;
37 :
38 : set state(LineScannerState state) {
39 0 : if (!identical(state._scanner, this)) {
40 0 : throw new ArgumentError("The given LineScannerState was not returned by "
41 : "this LineScanner.");
42 : }
43 :
44 0 : super.position = state.position;
45 0 : _line = state.line;
46 0 : _column = state.column;
47 : }
48 :
49 : set position(int newPosition) {
50 0 : var oldPosition = position;
51 0 : super.position = newPosition;
52 :
53 0 : if (newPosition > oldPosition) {
54 0 : var newlines = _newlinesIn(string.substring(oldPosition, newPosition));
55 0 : _line += newlines.length;
56 0 : if (newlines.isEmpty) {
57 0 : _column += newPosition - oldPosition;
58 : } else {
59 0 : _column = newPosition - newlines.last.end;
60 : }
61 : } else {
62 0 : var newlines = _newlinesIn(string.substring(newPosition, oldPosition));
63 0 : if (_betweenCRLF) newlines.removeLast();
64 :
65 0 : _line -= newlines.length;
66 0 : if (newlines.isEmpty) {
67 0 : _column -= oldPosition - newPosition;
68 : } else {
69 0 : _column = newPosition -
70 0 : string.lastIndexOf(_newlineRegExp, newPosition) - 1;
71 : }
72 : }
73 : }
74 :
75 : LineScanner(String string, {sourceUrl, int position})
76 0 : : super(string, sourceUrl: sourceUrl, position: position);
77 :
78 : bool scanChar(int character) {
79 0 : if (!super.scanChar(character)) return false;
80 0 : _adjustLineAndColumn(character);
81 : return true;
82 : }
83 :
84 : int readChar() {
85 0 : var character = super.readChar();
86 0 : _adjustLineAndColumn(character);
87 : return character;
88 : }
89 :
90 : /// Adjusts [_line] and [_column] after having consumed [character].
91 : void _adjustLineAndColumn(int character) {
92 0 : if (character == $lf || (character == $cr && peekChar() != $lf)) {
93 0 : _line += 1;
94 0 : _column = 0;
95 : } else {
96 0 : _column += 1;
97 : }
98 : }
99 :
100 : bool scan(Pattern pattern) {
101 0 : if (!super.scan(pattern)) return false;
102 :
103 0 : var newlines = _newlinesIn(lastMatch[0]);
104 0 : _line += newlines.length;
105 0 : if (newlines.isEmpty) {
106 0 : _column += lastMatch[0].length;
107 : } else {
108 0 : _column = lastMatch[0].length - newlines.last.end;
109 : }
110 :
111 : return true;
112 : }
113 :
114 : /// Returns a list of [Match]es describing all the newlines in [text], which
115 : /// is assumed to end at [position].
116 : List<Match> _newlinesIn(String text) {
117 0 : var newlines = _newlineRegExp.allMatches(text).toList();
118 0 : if (_betweenCRLF) newlines.removeLast();
119 : return newlines;
120 : }
121 : }
122 :
123 : /// A class representing the state of a [LineScanner].
124 : class LineScannerState {
125 : /// The [LineScanner] that created this.
126 : final LineScanner _scanner;
127 :
128 : /// The position of the scanner in this state.
129 : final int position;
130 :
131 : /// The zero-based line number of the scanner in this state.
132 : final int line;
133 :
134 : /// The zero-based column number of the scanner in this state.
135 : final int column;
136 :
137 0 : LineScannerState._(this._scanner, this.position, this.line, this.column);
138 : }
|