OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 part of dart._js_helper; | 5 part of dart._js_helper; |
6 | 6 |
7 int stringIndexOfStringUnchecked(receiver, other, startIndex) { | 7 int stringIndexOfStringUnchecked(receiver, other, startIndex) { |
8 return JS('int', '#.indexOf(#, #)', receiver, other, startIndex); | 8 return JS('int', '#.indexOf(#, #)', receiver, other, startIndex); |
9 } | 9 } |
10 | 10 |
11 String substring1Unchecked(receiver, startIndex) { | 11 String substring1Unchecked(receiver, startIndex) { |
12 return JS('String', '#.substring(#)', receiver, startIndex); | 12 return JS('String', '#.substring(#)', receiver, startIndex); |
13 } | 13 } |
14 | 14 |
15 String substring2Unchecked(receiver, startIndex, endIndex) { | 15 String substring2Unchecked(receiver, startIndex, endIndex) { |
16 return JS('String', '#.substring(#, #)', receiver, startIndex, endIndex); | 16 return JS('String', '#.substring(#, #)', receiver, startIndex, endIndex); |
17 } | 17 } |
18 | 18 |
19 bool stringContainsStringUnchecked(receiver, other, startIndex) { | 19 bool stringContainsStringUnchecked(receiver, other, startIndex) { |
20 return stringIndexOfStringUnchecked(receiver, other, startIndex) >= 0; | 20 return stringIndexOfStringUnchecked(receiver, other, startIndex) >= 0; |
21 } | 21 } |
22 | 22 |
23 class StringMatch implements Match { | 23 class StringMatch implements Match { |
24 const StringMatch(int this.start, | 24 const StringMatch(int this.start, String this.input, String this.pattern); |
25 String this.input, | |
26 String this.pattern); | |
27 | 25 |
28 int get end => start + pattern.length; | 26 int get end => start + pattern.length; |
29 String operator[](int g) => group(g); | 27 String operator [](int g) => group(g); |
30 int get groupCount => 0; | 28 int get groupCount => 0; |
31 | 29 |
32 String group(int group_) { | 30 String group(int group_) { |
33 if (group_ != 0) { | 31 if (group_ != 0) { |
34 throw new RangeError.value(group_); | 32 throw new RangeError.value(group_); |
35 } | 33 } |
36 return pattern; | 34 return pattern; |
37 } | 35 } |
38 | 36 |
39 List<String> groups(List<int> groups_) { | 37 List<String> groups(List<int> groups_) { |
40 List<String> result = new List<String>(); | 38 List<String> result = new List<String>(); |
41 for (int g in groups_) { | 39 for (int g in groups_) { |
42 result.add(group(g)); | 40 result.add(group(g)); |
43 } | 41 } |
44 return result; | 42 return result; |
45 } | 43 } |
46 | 44 |
47 final int start; | 45 final int start; |
48 final String input; | 46 final String input; |
49 final String pattern; | 47 final String pattern; |
50 } | 48 } |
51 | 49 |
52 Iterable<Match> allMatchesInStringUnchecked(String pattern, String string, | 50 Iterable<Match> allMatchesInStringUnchecked( |
53 int startIndex) { | 51 String pattern, String string, int startIndex) { |
54 return new _StringAllMatchesIterable(string, pattern, startIndex); | 52 return new _StringAllMatchesIterable(string, pattern, startIndex); |
55 } | 53 } |
56 | 54 |
57 class _StringAllMatchesIterable extends Iterable<Match> { | 55 class _StringAllMatchesIterable extends Iterable<Match> { |
58 final String _input; | 56 final String _input; |
59 final String _pattern; | 57 final String _pattern; |
60 final int _index; | 58 final int _index; |
61 | 59 |
62 _StringAllMatchesIterable(this._input, this._pattern, this._index); | 60 _StringAllMatchesIterable(this._input, this._pattern, this._index); |
63 | 61 |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
115 } | 113 } |
116 | 114 |
117 String stringReplaceJS(receiver, replacer, replacement) { | 115 String stringReplaceJS(receiver, replacer, replacement) { |
118 // The JavaScript String.replace method recognizes replacement | 116 // The JavaScript String.replace method recognizes replacement |
119 // patterns in the replacement string. Dart does not have that | 117 // patterns in the replacement string. Dart does not have that |
120 // behavior. | 118 // behavior. |
121 replacement = JS('String', r'#.replace(/\$/g, "$$$$")', replacement); | 119 replacement = JS('String', r'#.replace(/\$/g, "$$$$")', replacement); |
122 return JS('String', r'#.replace(#, #)', receiver, replacer, replacement); | 120 return JS('String', r'#.replace(#, #)', receiver, replacer, replacement); |
123 } | 121 } |
124 | 122 |
125 String stringReplaceFirstRE(String receiver, | 123 String stringReplaceFirstRE(String receiver, JSSyntaxRegExp regexp, |
126 JSSyntaxRegExp regexp, String replacement, int startIndex) { | 124 String replacement, int startIndex) { |
127 var match = regexp._execGlobal(receiver, startIndex); | 125 var match = regexp._execGlobal(receiver, startIndex); |
128 if (match == null) return receiver; | 126 if (match == null) return receiver; |
129 var start = match.start; | 127 var start = match.start; |
130 var end = match.end; | 128 var end = match.end; |
131 return stringReplaceRangeUnchecked(receiver, start, end, replacement); | 129 return stringReplaceRangeUnchecked(receiver, start, end, replacement); |
132 } | 130 } |
133 | 131 |
134 | |
135 /// Returns a string for a RegExp pattern that matches [string]. This is done by | 132 /// Returns a string for a RegExp pattern that matches [string]. This is done by |
136 /// escaping all RegExp metacharacters. | 133 /// escaping all RegExp metacharacters. |
137 String quoteStringForRegExp(string) { | 134 String quoteStringForRegExp(string) { |
138 return JS('String', r'#.replace(/[[\]{}()*+?.\\^$|]/g, "\\$&")', string); | 135 return JS('String', r'#.replace(/[[\]{}()*+?.\\^$|]/g, "\\$&")', string); |
139 } | 136 } |
140 | 137 |
141 String stringReplaceAllUnchecked( | 138 String stringReplaceAllUnchecked( |
142 String receiver, Pattern pattern, String replacement) { | 139 String receiver, Pattern pattern, String replacement) { |
143 checkString(replacement); | 140 checkString(replacement); |
144 if (pattern is String) { | 141 if (pattern is String) { |
(...skipping 21 matching lines...) Expand all Loading... |
166 } else { | 163 } else { |
167 checkNull(pattern); | 164 checkNull(pattern); |
168 // TODO(floitsch): implement generic String.replace (with patterns). | 165 // TODO(floitsch): implement generic String.replace (with patterns). |
169 throw "String.replaceAll(Pattern) UNIMPLEMENTED"; | 166 throw "String.replaceAll(Pattern) UNIMPLEMENTED"; |
170 } | 167 } |
171 } | 168 } |
172 | 169 |
173 String _matchString(Match match) => match[0]; | 170 String _matchString(Match match) => match[0]; |
174 String _stringIdentity(String string) => string; | 171 String _stringIdentity(String string) => string; |
175 | 172 |
176 String stringReplaceAllFuncUnchecked( | 173 String stringReplaceAllFuncUnchecked(String receiver, Pattern pattern, |
177 String receiver, | 174 String onMatch(Match match), String onNonMatch(String nonMatch)) { |
178 Pattern pattern, | |
179 String onMatch(Match match), | |
180 String onNonMatch(String nonMatch)) { | |
181 if (onMatch == null) onMatch = _matchString; | 175 if (onMatch == null) onMatch = _matchString; |
182 if (onNonMatch == null) onNonMatch = _stringIdentity; | 176 if (onNonMatch == null) onNonMatch = _stringIdentity; |
183 if (pattern is String) { | 177 if (pattern is String) { |
184 return stringReplaceAllStringFuncUnchecked(receiver, pattern, | 178 return stringReplaceAllStringFuncUnchecked( |
185 onMatch, onNonMatch); | 179 receiver, pattern, onMatch, onNonMatch); |
186 } | 180 } |
187 // Placing the Pattern test here is indistingishable from placing it at the | 181 // Placing the Pattern test here is indistingishable from placing it at the |
188 // top of the method but it saves an extra check on the `pattern is String` | 182 // top of the method but it saves an extra check on the `pattern is String` |
189 // path. | 183 // path. |
190 if (pattern is! Pattern) { | 184 if (pattern is! Pattern) { |
191 throw new ArgumentError.value(pattern, 'pattern', 'is not a Pattern'); | 185 throw new ArgumentError.value(pattern, 'pattern', 'is not a Pattern'); |
192 } | 186 } |
193 StringBuffer buffer = new StringBuffer(); | 187 StringBuffer buffer = new StringBuffer(); |
194 int startIndex = 0; | 188 int startIndex = 0; |
195 for (Match match in pattern.allMatches(receiver)) { | 189 for (Match match in pattern.allMatches(receiver)) { |
196 buffer.write(onNonMatch(receiver.substring(startIndex, match.start))); | 190 buffer.write(onNonMatch(receiver.substring(startIndex, match.start))); |
197 buffer.write(onMatch(match)); | 191 buffer.write(onMatch(match)); |
198 startIndex = match.end; | 192 startIndex = match.end; |
199 } | 193 } |
200 buffer.write(onNonMatch(receiver.substring(startIndex))); | 194 buffer.write(onNonMatch(receiver.substring(startIndex))); |
201 return buffer.toString(); | 195 return buffer.toString(); |
202 } | 196 } |
203 | 197 |
204 String stringReplaceAllEmptyFuncUnchecked(String receiver, | 198 String stringReplaceAllEmptyFuncUnchecked(String receiver, |
205 String onMatch(Match match), | 199 String onMatch(Match match), String onNonMatch(String nonMatch)) { |
206 String onNonMatch(String nonMatch)) { | |
207 // Pattern is the empty string. | 200 // Pattern is the empty string. |
208 StringBuffer buffer = new StringBuffer(); | 201 StringBuffer buffer = new StringBuffer(); |
209 int length = receiver.length; | 202 int length = receiver.length; |
210 int i = 0; | 203 int i = 0; |
211 buffer.write(onNonMatch("")); | 204 buffer.write(onNonMatch("")); |
212 while (i < length) { | 205 while (i < length) { |
213 buffer.write(onMatch(new StringMatch(i, receiver, ""))); | 206 buffer.write(onMatch(new StringMatch(i, receiver, ""))); |
214 // Special case to avoid splitting a surrogate pair. | 207 // Special case to avoid splitting a surrogate pair. |
215 int code = receiver.codeUnitAt(i); | 208 int code = receiver.codeUnitAt(i); |
216 if ((code & ~0x3FF) == 0xD800 && length > i + 1) { | 209 if ((code & ~0x3FF) == 0xD800 && length > i + 1) { |
217 // Leading surrogate; | 210 // Leading surrogate; |
218 code = receiver.codeUnitAt(i + 1); | 211 code = receiver.codeUnitAt(i + 1); |
219 if ((code & ~0x3FF) == 0xDC00) { | 212 if ((code & ~0x3FF) == 0xDC00) { |
220 // Matching trailing surrogate. | 213 // Matching trailing surrogate. |
221 buffer.write(onNonMatch(receiver.substring(i, i + 2))); | 214 buffer.write(onNonMatch(receiver.substring(i, i + 2))); |
222 i += 2; | 215 i += 2; |
223 continue; | 216 continue; |
224 } | 217 } |
225 } | 218 } |
226 buffer.write(onNonMatch(receiver[i])); | 219 buffer.write(onNonMatch(receiver[i])); |
227 i++; | 220 i++; |
228 } | 221 } |
229 buffer.write(onMatch(new StringMatch(i, receiver, ""))); | 222 buffer.write(onMatch(new StringMatch(i, receiver, ""))); |
230 buffer.write(onNonMatch("")); | 223 buffer.write(onNonMatch("")); |
231 return buffer.toString(); | 224 return buffer.toString(); |
232 } | 225 } |
233 | 226 |
234 String stringReplaceAllStringFuncUnchecked( | 227 String stringReplaceAllStringFuncUnchecked(String receiver, String pattern, |
235 String receiver, | 228 String onMatch(Match match), String onNonMatch(String nonMatch)) { |
236 String pattern, | |
237 String onMatch(Match match), | |
238 String onNonMatch(String nonMatch)) { | |
239 int patternLength = pattern.length; | 229 int patternLength = pattern.length; |
240 if (patternLength == 0) { | 230 if (patternLength == 0) { |
241 return stringReplaceAllEmptyFuncUnchecked(receiver, onMatch, onNonMatch); | 231 return stringReplaceAllEmptyFuncUnchecked(receiver, onMatch, onNonMatch); |
242 } | 232 } |
243 int length = receiver.length; | 233 int length = receiver.length; |
244 StringBuffer buffer = new StringBuffer(); | 234 StringBuffer buffer = new StringBuffer(); |
245 int startIndex = 0; | 235 int startIndex = 0; |
246 while (startIndex < length) { | 236 while (startIndex < length) { |
247 int position = stringIndexOfStringUnchecked(receiver, pattern, startIndex); | 237 int position = stringIndexOfStringUnchecked(receiver, pattern, startIndex); |
248 if (position == -1) { | 238 if (position == -1) { |
249 break; | 239 break; |
250 } | 240 } |
251 buffer.write(onNonMatch(receiver.substring(startIndex, position))); | 241 buffer.write(onNonMatch(receiver.substring(startIndex, position))); |
252 buffer.write(onMatch(new StringMatch(position, receiver, pattern))); | 242 buffer.write(onMatch(new StringMatch(position, receiver, pattern))); |
253 startIndex = position + patternLength; | 243 startIndex = position + patternLength; |
254 } | 244 } |
255 buffer.write(onNonMatch(receiver.substring(startIndex))); | 245 buffer.write(onNonMatch(receiver.substring(startIndex))); |
256 return buffer.toString(); | 246 return buffer.toString(); |
257 } | 247 } |
258 | 248 |
259 | |
260 String stringReplaceFirstUnchecked( | 249 String stringReplaceFirstUnchecked( |
261 String receiver, Pattern pattern, String replacement, int startIndex) { | 250 String receiver, Pattern pattern, String replacement, int startIndex) { |
262 if (pattern is String) { | 251 if (pattern is String) { |
263 int index = stringIndexOfStringUnchecked(receiver, pattern, startIndex); | 252 int index = stringIndexOfStringUnchecked(receiver, pattern, startIndex); |
264 if (index < 0) return receiver; | 253 if (index < 0) return receiver; |
265 int end = index + pattern.length; | 254 int end = index + pattern.length; |
266 return stringReplaceRangeUnchecked(receiver, index, end, replacement); | 255 return stringReplaceRangeUnchecked(receiver, index, end, replacement); |
267 } | 256 } |
268 if (pattern is JSSyntaxRegExp) { | 257 if (pattern is JSSyntaxRegExp) { |
269 return startIndex == 0 | 258 return startIndex == 0 |
270 ? stringReplaceJS(receiver, regExpGetNative(pattern), replacement) | 259 ? stringReplaceJS(receiver, regExpGetNative(pattern), replacement) |
271 : stringReplaceFirstRE(receiver, pattern, replacement, startIndex); | 260 : stringReplaceFirstRE(receiver, pattern, replacement, startIndex); |
272 } | 261 } |
273 checkNull(pattern); | 262 checkNull(pattern); |
274 Iterator<Match> matches = pattern.allMatches(receiver, startIndex).iterator; | 263 Iterator<Match> matches = pattern.allMatches(receiver, startIndex).iterator; |
275 if (!matches.moveNext()) return receiver; | 264 if (!matches.moveNext()) return receiver; |
276 Match match = matches.current; | 265 Match match = matches.current; |
277 return receiver.replaceRange(match.start, match.end, replacement); | 266 return receiver.replaceRange(match.start, match.end, replacement); |
278 } | 267 } |
279 | 268 |
280 String stringReplaceFirstMappedUnchecked( | 269 String stringReplaceFirstMappedUnchecked(String receiver, Pattern pattern, |
281 String receiver, | 270 String replace(Match current), int startIndex) { |
282 Pattern pattern, | |
283 String replace(Match current), | |
284 int startIndex) { | |
285 Iterator<Match> matches = pattern.allMatches(receiver, startIndex).iterator; | 271 Iterator<Match> matches = pattern.allMatches(receiver, startIndex).iterator; |
286 if (!matches.moveNext()) return receiver; | 272 if (!matches.moveNext()) return receiver; |
287 Match match = matches.current; | 273 Match match = matches.current; |
288 String replacement = "${replace(match)}"; | 274 String replacement = "${replace(match)}"; |
289 return receiver.replaceRange(match.start, match.end, replacement); | 275 return receiver.replaceRange(match.start, match.end, replacement); |
290 } | 276 } |
291 | 277 |
292 String stringJoinUnchecked(array, separator) { | 278 String stringJoinUnchecked(array, separator) { |
293 return JS('String', r'#.join(#)', array, separator); | 279 return JS('String', r'#.join(#)', array, separator); |
294 } | 280 } |
295 | 281 |
296 String stringReplaceRangeUnchecked(String receiver, | 282 String stringReplaceRangeUnchecked( |
297 int start, int end, String replacement) { | 283 String receiver, int start, int end, String replacement) { |
298 var prefix = JS('String', '#.substring(0, #)', receiver, start); | 284 var prefix = JS('String', '#.substring(0, #)', receiver, start); |
299 var suffix = JS('String', '#.substring(#)', receiver, end); | 285 var suffix = JS('String', '#.substring(#)', receiver, end); |
300 return "$prefix$replacement$suffix"; | 286 return "$prefix$replacement$suffix"; |
301 } | 287 } |
OLD | NEW |