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 shelf.string_scanner; | 5 /// A library for parsing strings using a sequence of patterns. |
| 6 library string_scanner; |
6 | 7 |
| 8 // TODO(nweiz): Add some integration between this and source maps. |
7 /// A class that scans through a string using [Pattern]s. | 9 /// A class that scans through a string using [Pattern]s. |
8 class StringScanner { | 10 class StringScanner { |
9 /// The string being scanned through. | 11 /// The string being scanned through. |
10 final String string; | 12 final String string; |
11 | 13 |
12 /// The current position of the scanner in the string, in characters. | 14 /// The current position of the scanner in the string, in characters. |
13 int get position => _position; | 15 int get position => _position; |
14 set position(int position) { | 16 set position(int position) { |
15 if (position < 0 || position > string.length) { | 17 if (position < 0 || position > string.length) { |
16 throw new ArgumentError("Invalid position $position"); | 18 throw new ArgumentError("Invalid position $position"); |
(...skipping 28 matching lines...) Expand all Loading... |
45 /// Returns whether or not [pattern] matched. | 47 /// Returns whether or not [pattern] matched. |
46 bool scan(Pattern pattern) { | 48 bool scan(Pattern pattern) { |
47 var success = matches(pattern); | 49 var success = matches(pattern); |
48 if (success) _position = _lastMatch.end; | 50 if (success) _position = _lastMatch.end; |
49 return success; | 51 return success; |
50 } | 52 } |
51 | 53 |
52 /// If [pattern] matches at the current position of the string, scans forward | 54 /// If [pattern] matches at the current position of the string, scans forward |
53 /// until the end of the match. | 55 /// until the end of the match. |
54 /// | 56 /// |
55 /// If [pattern] did not match, throws a [FormatException] with [message]. | 57 /// If [pattern] did not match, throws a [FormatException] describing the |
56 void expect(Pattern pattern, String message) { | 58 /// position of the failure. [name] is used in this error as the expected name |
| 59 /// of the pattern being matched; if it's `null`, the pattern itself is used |
| 60 /// instead. |
| 61 void expect(Pattern pattern, {String name}) { |
57 if (scan(pattern)) return; | 62 if (scan(pattern)) return; |
58 throw new FormatException(message); | 63 |
| 64 if (name == null) { |
| 65 if (pattern is RegExp) { |
| 66 name = "/${pattern.pattern.replaceAll("/", "\\/")}/"; |
| 67 } else { |
| 68 name = pattern.toString() |
| 69 .replaceAll("\\", "\\\\").replaceAll('"', '\\"'); |
| 70 name = '"$name"'; |
| 71 } |
| 72 } |
| 73 _fail(name); |
| 74 } |
| 75 |
| 76 /// If the string has not been fully consumed, this throws a |
| 77 /// [FormatException]. |
| 78 void expectDone() { |
| 79 if (isDone) return; |
| 80 _fail("no more input"); |
59 } | 81 } |
60 | 82 |
61 /// Returns whether or not [pattern] matches at the current position of the | 83 /// Returns whether or not [pattern] matches at the current position of the |
62 /// string. | 84 /// string. |
63 /// | 85 /// |
64 /// This doesn't move the scan pointer forward. | 86 /// This doesn't move the scan pointer forward. |
65 bool matches(Pattern pattern) { | 87 bool matches(Pattern pattern) { |
66 _lastMatch = pattern.matchAsPrefix(string, position); | 88 _lastMatch = pattern.matchAsPrefix(string, position); |
67 return _lastMatch != null; | 89 return _lastMatch != null; |
68 } | 90 } |
| 91 |
| 92 // TODO(nweiz): Make this handle long lines more gracefully. |
| 93 /// Throws a [FormatException] describing that [name] is expected at the |
| 94 /// current position in the string. |
| 95 void _fail(String name) { |
| 96 var newlines = "\n".allMatches(string.substring(0, position)).toList(); |
| 97 var line = newlines.length + 1; |
| 98 var column; |
| 99 var lastLine; |
| 100 if (newlines.isEmpty) { |
| 101 column = position + 1; |
| 102 lastLine = string.substring(0, position); |
| 103 } else { |
| 104 column = position - newlines.last.end + 1; |
| 105 lastLine = string.substring(newlines.last.end, position); |
| 106 } |
| 107 lastLine += rest.replaceFirst(new RegExp(r"\n.*"), ''); |
| 108 throw new FormatException( |
| 109 "Expected $name on line $line, column $column.\n" |
| 110 "$lastLine\n" |
| 111 "${new List.filled(column - 1, ' ').join()}^"); |
| 112 } |
69 } | 113 } |
OLD | NEW |