| 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 |