OLD | NEW |
---|---|
(Empty) | |
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 library string_scanner; | |
kevmoo
2014/04/02 16:30:09
A short doc-comment on the library is nice, if you
nweiz
2014/04/02 20:26:40
Done.
| |
6 | |
7 // TODO(nweiz): Add some integration between this and source maps. | |
8 /// A class that scans through a string using [Pattern]s. | |
9 class StringScanner { | |
10 /// The string being scanned through. | |
11 final String string; | |
12 | |
13 /// The current position of the scanner in the string, in characters. | |
14 int get position => _position; | |
15 set position(int position) { | |
kevmoo
2014/04/02 16:30:09
void? silly, but I like it
nweiz
2014/04/02 20:26:40
It's redundant with "set".
| |
16 if (position < 0 || position > string.length) { | |
17 throw new ArgumentError("Invalid position $position"); | |
18 } | |
19 | |
20 _position = position; | |
21 } | |
22 int _position = 0; | |
23 | |
24 /// The data about the previous match made by the scanner. | |
25 /// | |
26 /// If the last match failed, this will be `null`. | |
27 Match get lastMatch => _lastMatch; | |
28 Match _lastMatch; | |
29 | |
30 /// The portion of the string that hasn't yet been scanned. | |
31 String get rest => string.substring(position); | |
32 | |
33 /// Whether the scanner has completely consumed [string]. | |
34 bool get isDone => position == string.length; | |
35 | |
36 /// Creates a new [StringScanner] that starts scanning from [position]. | |
37 /// | |
38 /// [position] defaults to 0, the beginning of the string. | |
39 StringScanner(this.string, {int position}) { | |
40 if (position != null) this.position = position; | |
41 } | |
42 | |
43 /// If [pattern] matches at the current position of the string, scans forward | |
44 /// until the end of the match. | |
45 /// | |
46 /// Returns whether or not [pattern] matched. | |
47 bool scan(Pattern pattern) { | |
48 var success = matches(pattern); | |
49 if (success) _position = _lastMatch.end; | |
50 return success; | |
51 } | |
52 | |
53 /// If [pattern] matches at the current position of the string, scans forward | |
54 /// until the end of the match. | |
55 /// | |
56 /// If [pattern] did not match, throws a [FormatException] describing the | |
57 /// position of the failure. [name] is used in this error as the expected name | |
58 /// of the pattern being matched; if it's `null`, the pattern itself is used | |
59 /// instead. | |
60 void expect(Pattern pattern, {String name}) { | |
61 if (scan(pattern)) return; | |
62 | |
63 if (name == null) { | |
64 if (pattern is RegExp) { | |
65 name = "/${pattern.pattern.replaceAll("/", "\\/")}/"; | |
66 } else { | |
67 name = pattern.toString() | |
68 .replaceAll("\\", "\\\\").replaceAll('"', '\\"'); | |
69 name = '"$name"'; | |
70 } | |
71 } | |
72 _fail(name); | |
73 } | |
74 | |
75 /// If the string has not been fully consumed, this throws a | |
76 /// [FormatException]. | |
77 void expectDone() { | |
78 if (isDone) return; | |
79 _fail("no more input"); | |
80 } | |
81 | |
82 /// Returns whether or not [pattern] matches at the current position of the | |
83 /// string. | |
84 /// | |
85 /// This doesn't move the scan pointer forward. | |
86 bool matches(Pattern pattern) { | |
87 _lastMatch = pattern.matchAsPrefix(string, position); | |
88 return _lastMatch != null; | |
89 } | |
90 | |
91 // TODO(nweiz): Make this handle long lines more gracefully. | |
92 /// Throws a [FormatException] describing that [name] is expected at the | |
93 /// current position in the string. | |
94 void _fail(String name) { | |
95 var newlines = "\n".allMatches(string.substring(0, position)).toList(); | |
96 var line = newlines.length + 1; | |
97 var column; | |
98 var lastLine; | |
99 if (newlines.isEmpty) { | |
100 column = position + 1; | |
101 lastLine = string.substring(0, position); | |
102 } else { | |
103 column = position - newlines.last.end + 1; | |
104 lastLine = string.substring(newlines.last.end, position); | |
105 } | |
106 lastLine += rest.replaceFirst(new RegExp(r"\n.*"), ''); | |
107 throw new FormatException( | |
108 "Expected $name on line $line, column $column.\n" | |
109 "$lastLine\n" | |
110 "${new List.filled(column - 1, ' ').join()}^"); | |
111 } | |
112 } | |
OLD | NEW |