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 _js_helper; | 5 part of _js_helper; |
6 | 6 |
7 // Helper method used by internal libraries. | 7 // Helper method used by internal libraries. |
8 regExpGetNative(JSSyntaxRegExp regexp) => regexp._nativeRegExp; | 8 regExpGetNative(JSSyntaxRegExp regexp) => regexp._nativeRegExp; |
9 | 9 |
10 /** | 10 /** |
11 * Returns a native version of the RegExp with the global flag set. | 11 * Returns a native version of the RegExp with the global flag set. |
12 * | 12 * |
13 * The RegExp's `lastIndex` property is zero when it is returned. | 13 * The RegExp's `lastIndex` property is zero when it is returned. |
14 * | 14 * |
15 * The returned regexp is shared, and its `lastIndex` property may be | 15 * The returned regexp is shared, and its `lastIndex` property may be |
16 * modified by other uses, so the returned regexp must be used immediately | 16 * modified by other uses, so the returned regexp must be used immediately |
17 * when it's returned, with no user-provided code run in between. | 17 * when it's returned, with no user-provided code run in between. |
18 */ | 18 */ |
19 regExpGetGlobalNative(JSSyntaxRegExp regexp) { | 19 regExpGetGlobalNative(JSSyntaxRegExp regexp) { |
20 var nativeRegexp = regexp._nativeGlobalVersion; | 20 var nativeRegexp = regexp._nativeGlobalVersion; |
21 JS("void", "#.lastIndex = 0", nativeRegexp); | 21 JS('void', '#.lastIndex = 0', nativeRegexp); |
22 return nativeRegexp; | 22 return nativeRegexp; |
23 } | 23 } |
24 | 24 |
25 /** | 25 /** |
26 * Computes the number of captures in a regexp. | 26 * Computes the number of captures in a regexp. |
27 * | 27 * |
28 * This currently involves creating a new RegExp object with a different | 28 * This currently involves creating a new RegExp object with a different |
29 * source and running it against the empty string (the last part is usually | 29 * source and running it against the empty string (the last part is usually |
30 * fast). | 30 * fast). |
31 * | 31 * |
32 * The JSSyntaxRegExp could cache the result, and set the cache any time | 32 * The JSSyntaxRegExp could cache the result, and set the cache any time |
33 * it finds a match. | 33 * it finds a match. |
34 */ | 34 */ |
35 int regExpCaptureCount(JSSyntaxRegExp regexp) { | 35 int regExpCaptureCount(JSSyntaxRegExp regexp) { |
36 var nativeAnchoredRegExp = regexp._nativeAnchoredVersion; | 36 var nativeAnchoredRegExp = regexp._nativeAnchoredVersion; |
37 var match = JS('JSExtendableArray', "#.exec('')", nativeAnchoredRegExp); | 37 var match = JS('JSExtendableArray', '#.exec("")', nativeAnchoredRegExp); |
38 // The native-anchored regexp always have one capture more than the original, | 38 // The native-anchored regexp always have one capture more than the original, |
39 // and always matches the empty string. | 39 // and always matches the empty string. |
40 return match.length - 2; | 40 return match.length - 2; |
41 } | 41 } |
42 | 42 |
43 class JSSyntaxRegExp implements RegExp { | 43 class JSSyntaxRegExp implements RegExp { |
44 final String pattern; | 44 final String pattern; |
45 final _nativeRegExp; | 45 final _nativeRegExp; |
46 var _nativeGlobalRegExp; | 46 var _nativeGlobalRegExp; |
47 var _nativeAnchoredRegExp; | 47 var _nativeAnchoredRegExp; |
48 | 48 |
49 String toString() => "RegExp/$pattern/"; | 49 String toString() => 'RegExp/$pattern/'; |
50 | 50 |
51 JSSyntaxRegExp(String source, | 51 JSSyntaxRegExp(String source, |
52 { bool multiLine: false, | 52 { bool multiLine: false, |
53 bool caseSensitive: true }) | 53 bool caseSensitive: true }) |
54 : this.pattern = source, | 54 : this.pattern = source, |
55 this._nativeRegExp = | 55 this._nativeRegExp = |
56 makeNative(source, multiLine, caseSensitive, false); | 56 makeNative(source, multiLine, caseSensitive, false); |
57 | 57 |
58 get _nativeGlobalVersion { | 58 get _nativeGlobalVersion { |
59 if (_nativeGlobalRegExp != null) return _nativeGlobalRegExp; | 59 if (_nativeGlobalRegExp != null) return _nativeGlobalRegExp; |
60 return _nativeGlobalRegExp = makeNative(pattern, | 60 return _nativeGlobalRegExp = makeNative(pattern, |
61 _isMultiLine, | 61 _isMultiLine, |
62 _isCaseSensitive, | 62 _isCaseSensitive, |
63 true); | 63 true); |
64 } | 64 } |
65 | 65 |
66 get _nativeAnchoredVersion { | 66 get _nativeAnchoredVersion { |
67 if (_nativeAnchoredRegExp != null) return _nativeAnchoredRegExp; | 67 if (_nativeAnchoredRegExp != null) return _nativeAnchoredRegExp; |
68 // An "anchored version" of a regexp is created by adding "|()" to the | 68 // An "anchored version" of a regexp is created by adding "|()" to the |
69 // source. This means that the regexp always matches at the first position | 69 // source. This means that the regexp always matches at the first position |
70 // that it tries, and you can see if the original regexp matched, or it | 70 // that it tries, and you can see if the original regexp matched, or it |
71 // was the added zero-width match that matched, by looking at the last | 71 // was the added zero-width match that matched, by looking at the last |
72 // capture. If it is a String, the match participated, otherwise it didn't. | 72 // capture. If it is a String, the match participated, otherwise it didn't. |
73 return _nativeAnchoredRegExp = makeNative("$pattern|()", | 73 return _nativeAnchoredRegExp = makeNative('$pattern|()', |
74 _isMultiLine, | 74 _isMultiLine, |
75 _isCaseSensitive, | 75 _isCaseSensitive, |
76 true); | 76 true); |
77 } | 77 } |
78 | 78 |
79 bool get _isMultiLine => JS("bool", "#.multiline", _nativeRegExp); | 79 bool get _isMultiLine => JS('bool', '#.multiline', _nativeRegExp); |
80 bool get _isCaseSensitive => JS("bool", "!#.ignoreCase", _nativeRegExp); | 80 bool get _isCaseSensitive => JS('bool', '!#.ignoreCase', _nativeRegExp); |
81 | 81 |
82 static makeNative( | 82 static makeNative( |
83 String source, bool multiLine, bool caseSensitive, bool global) { | 83 String source, bool multiLine, bool caseSensitive, bool global) { |
84 checkString(source); | 84 checkString(source); |
85 String m = multiLine == true ? 'm' : ''; | 85 String m = multiLine == true ? 'm' : ''; |
86 String i = caseSensitive == true ? '' : 'i'; | 86 String i = caseSensitive == true ? '' : 'i'; |
87 String g = global ? 'g' : ''; | 87 String g = global ? 'g' : ''; |
88 // We're using the JavaScript's try catch instead of the Dart one to avoid | 88 // We're using the JavaScript's try catch instead of the Dart one to avoid |
89 // dragging in Dart runtime support just because of using RegExp. | 89 // dragging in Dart runtime support just because of using RegExp. |
90 var regexp = JS('', | 90 var regexp = JS('', |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
128 checkString(string); | 128 checkString(string); |
129 checkInt(start); | 129 checkInt(start); |
130 if (start < 0 || start > string.length) { | 130 if (start < 0 || start > string.length) { |
131 throw new RangeError.range(start, 0, string.length); | 131 throw new RangeError.range(start, 0, string.length); |
132 } | 132 } |
133 return new _AllMatchesIterable(this, string, start); | 133 return new _AllMatchesIterable(this, string, start); |
134 } | 134 } |
135 | 135 |
136 Match _execGlobal(String string, int start) { | 136 Match _execGlobal(String string, int start) { |
137 Object regexp = _nativeGlobalVersion; | 137 Object regexp = _nativeGlobalVersion; |
138 JS("void", "#.lastIndex = #", regexp, start); | 138 JS('void', '#.lastIndex = #', regexp, start); |
139 List match = JS("JSExtendableArray|Null", "#.exec(#)", regexp, string); | 139 List match = JS('JSExtendableArray|Null', '#.exec(#)', regexp, string); |
140 if (match == null) return null; | 140 if (match == null) return null; |
141 return new _MatchImplementation(this, match); | 141 return new _MatchImplementation(this, match); |
142 } | 142 } |
143 | 143 |
144 Match _execAnchored(String string, int start) { | 144 Match _execAnchored(String string, int start) { |
145 Object regexp = _nativeAnchoredVersion; | 145 Object regexp = _nativeAnchoredVersion; |
146 JS("void", "#.lastIndex = #", regexp, start); | 146 JS('void', '#.lastIndex = #', regexp, start); |
147 List match = JS("JSExtendableArray|Null", "#.exec(#)", regexp, string); | 147 List match = JS('JSExtendableArray|Null', '#.exec(#)', regexp, string); |
148 if (match == null) return null; | 148 if (match == null) return null; |
149 // If the last capture group participated, the original regexp did not | 149 // If the last capture group participated, the original regexp did not |
150 // match at the start position. | 150 // match at the start position. |
151 if (match[match.length - 1] != null) return null; | 151 if (match.removeLast() != null) return null; |
152 match.length -= 1; | |
153 return new _MatchImplementation(this, match); | 152 return new _MatchImplementation(this, match); |
154 } | 153 } |
155 | 154 |
156 Match matchAsPrefix(String string, [int start = 0]) { | 155 Match matchAsPrefix(String string, [int start = 0]) { |
157 if (start < 0 || start > string.length) { | 156 if (start < 0 || start > string.length) { |
158 throw new RangeError.range(start, 0, string.length); | 157 throw new RangeError.range(start, 0, string.length); |
159 } | 158 } |
160 return _execAnchored(string, start); | 159 return _execAnchored(string, start); |
161 } | 160 } |
162 | 161 |
163 bool get isMultiLine => _isMultiLine; | 162 bool get isMultiLine => _isMultiLine; |
164 bool get isCaseSensitive => _isCaseSensitive; | 163 bool get isCaseSensitive => _isCaseSensitive; |
165 } | 164 } |
166 | 165 |
167 class _MatchImplementation implements Match { | 166 class _MatchImplementation implements Match { |
168 final Pattern pattern; | 167 final Pattern pattern; |
169 // Contains a JS RegExp match object. | 168 // Contains a JS RegExp match object. |
170 // It is an Array of String values with extra "index" and "input" properties. | 169 // It is an Array of String values with extra 'index' and 'input' properties. |
171 final List<String> _match; | 170 final List<String> _match; |
172 | 171 |
173 _MatchImplementation(this.pattern, this._match) { | 172 _MatchImplementation(this.pattern, this._match) { |
174 assert(JS("var", "#.input", _match) is String); | 173 assert(JS('var', '#.input', _match) is String); |
175 assert(JS("var", "#.index", _match) is int); | 174 assert(JS('var', '#.index', _match) is int); |
176 } | 175 } |
177 | 176 |
178 String get input => JS("String", "#.input", _match); | 177 String get input => JS('String', '#.input', _match); |
179 int get start => JS("int", "#.index", _match); | 178 |
180 int get end => start + _match[0].length; | 179 int get start => |
| 180 JS('returns:int;depends:none;effects:none;gvn:true', '#.index', _match); |
| 181 |
| 182 int get end => |
| 183 start + |
| 184 JS('returns:int;depends:none;effects:none;gvn:true', '#[0].length', |
| 185 _match); |
181 | 186 |
182 String group(int index) => _match[index]; | 187 String group(int index) => _match[index]; |
183 String operator [](int index) => group(index); | 188 String operator [](int index) => group(index); |
184 int get groupCount => _match.length - 1; | 189 int get groupCount => _match.length - 1; |
185 | 190 |
186 List<String> groups(List<int> groups) { | 191 List<String> groups(List<int> groups) { |
187 List<String> out = []; | 192 List<String> out = []; |
188 for (int i in groups) { | 193 for (int i in groups) { |
189 out.add(group(i)); | 194 out.add(group(i)); |
190 } | 195 } |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
229 _current = null; | 234 _current = null; |
230 _string = null; // Marks iteration as ended. | 235 _string = null; // Marks iteration as ended. |
231 return false; | 236 return false; |
232 } | 237 } |
233 } | 238 } |
234 | 239 |
235 /** Find the first match of [regExp] in [string] at or after [start]. */ | 240 /** Find the first match of [regExp] in [string] at or after [start]. */ |
236 Match firstMatchAfter(JSSyntaxRegExp regExp, String string, int start) { | 241 Match firstMatchAfter(JSSyntaxRegExp regExp, String string, int start) { |
237 return regExp._execGlobal(string, start); | 242 return regExp._execGlobal(string, start); |
238 } | 243 } |
OLD | NEW |