Chromium Code Reviews| 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 |