| 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 // 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 List<String> _stringList(List l) => l == null ? l : new JSArray<String>.of(l); | |
| 11 | |
| 12 /** | 10 /** |
| 13 * 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. |
| 14 * | 12 * |
| 15 * The RegExp's `lastIndex` property is zero when it is returned. | 13 * The RegExp's `lastIndex` property is zero when it is returned. |
| 16 * | 14 * |
| 17 * The returned regexp is shared, and its `lastIndex` property may be | 15 * The returned regexp is shared, and its `lastIndex` property may be |
| 18 * 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 |
| 19 * 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. |
| 20 */ | 18 */ |
| 21 regExpGetGlobalNative(JSSyntaxRegExp regexp) { | 19 regExpGetGlobalNative(JSSyntaxRegExp regexp) { |
| 22 var nativeRegexp = regexp._nativeGlobalVersion; | 20 var nativeRegexp = regexp._nativeGlobalVersion; |
| 23 JS("void", "#.lastIndex = 0", nativeRegexp); | 21 JS("void", "#.lastIndex = 0", nativeRegexp); |
| 24 return nativeRegexp; | 22 return nativeRegexp; |
| 25 } | 23 } |
| 26 | 24 |
| 27 /** | 25 /** |
| 28 * Computes the number of captures in a regexp. | 26 * Computes the number of captures in a regexp. |
| 29 * | 27 * |
| 30 * This currently involves creating a new RegExp object with a different | 28 * This currently involves creating a new RegExp object with a different |
| 31 * 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 |
| 32 * fast). | 30 * fast). |
| 33 * | 31 * |
| 34 * 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 |
| 35 * it finds a match. | 33 * it finds a match. |
| 36 */ | 34 */ |
| 37 int regExpCaptureCount(JSSyntaxRegExp regexp) { | 35 int regExpCaptureCount(JSSyntaxRegExp regexp) { |
| 38 var nativeAnchoredRegExp = regexp._nativeAnchoredVersion; | 36 var nativeAnchoredRegExp = regexp._nativeAnchoredVersion; |
| 39 var match = JS('JSExtendableArray', "#.exec('')", nativeAnchoredRegExp); | 37 JSExtendableArray match = |
| 38 JS('JSExtendableArray', "#.exec('')", nativeAnchoredRegExp); |
| 40 // The native-anchored regexp always have one capture more than the original, | 39 // The native-anchored regexp always have one capture more than the original, |
| 41 // and always matches the empty string. | 40 // and always matches the empty string. |
| 42 return match.length - 2; | 41 return match.length - 2; |
| 43 } | 42 } |
| 44 | 43 |
| 45 class JSSyntaxRegExp implements RegExp { | 44 class JSSyntaxRegExp implements RegExp { |
| 46 final String pattern; | 45 final String pattern; |
| 47 final _nativeRegExp; | 46 final _nativeRegExp; |
| 48 var _nativeGlobalRegExp; | 47 var _nativeGlobalRegExp; |
| 49 var _nativeAnchoredRegExp; | 48 var _nativeAnchoredRegExp; |
| (...skipping 19 matching lines...) Expand all Loading... |
| 69 // that it tries, and you can see if the original regexp matched, or it | 68 // that it tries, and you can see if the original regexp matched, or it |
| 70 // was the added zero-width match that matched, by looking at the last | 69 // was the added zero-width match that matched, by looking at the last |
| 71 // capture. If it is a String, the match participated, otherwise it didn't. | 70 // capture. If it is a String, the match participated, otherwise it didn't. |
| 72 return _nativeAnchoredRegExp = | 71 return _nativeAnchoredRegExp = |
| 73 makeNative("$pattern|()", _isMultiLine, _isCaseSensitive, true); | 72 makeNative("$pattern|()", _isMultiLine, _isCaseSensitive, true); |
| 74 } | 73 } |
| 75 | 74 |
| 76 bool get _isMultiLine => JS("bool", "#.multiline", _nativeRegExp); | 75 bool get _isMultiLine => JS("bool", "#.multiline", _nativeRegExp); |
| 77 bool get _isCaseSensitive => JS("bool", "!#.ignoreCase", _nativeRegExp); | 76 bool get _isCaseSensitive => JS("bool", "!#.ignoreCase", _nativeRegExp); |
| 78 | 77 |
| 79 static makeNative( | 78 static makeNative(@nullCheck String source, bool multiLine, |
| 80 String source, bool multiLine, bool caseSensitive, bool global) { | 79 bool caseSensitive, bool global) { |
| 81 checkString(source); | |
| 82 String m = multiLine ? 'm' : ''; | 80 String m = multiLine ? 'm' : ''; |
| 83 String i = caseSensitive ? '' : 'i'; | 81 String i = caseSensitive ? '' : 'i'; |
| 84 String g = global ? 'g' : ''; | 82 String g = global ? 'g' : ''; |
| 85 // We're using the JavaScript's try catch instead of the Dart one | 83 // We're using the JavaScript's try catch instead of the Dart one |
| 86 // to avoid dragging in Dart runtime support just because of using | 84 // to avoid dragging in Dart runtime support just because of using |
| 87 // RegExp. | 85 // RegExp. |
| 88 var regexp = JS( | 86 var regexp = JS( |
| 89 '', | 87 '', |
| 90 '(function() {' | 88 '(function() {' |
| 91 'try {' | 89 'try {' |
| 92 'return new RegExp(#, # + # + #);' | 90 'return new RegExp(#, # + # + #);' |
| 93 '} catch (e) {' | 91 '} catch (e) {' |
| 94 'return e;' | 92 'return e;' |
| 95 '}' | 93 '}' |
| 96 '})()', | 94 '})()', |
| 97 source, | 95 source, |
| 98 m, | 96 m, |
| 99 i, | 97 i, |
| 100 g); | 98 g); |
| 101 if (JS('bool', '# instanceof RegExp', regexp)) return regexp; | 99 if (JS('bool', '# instanceof RegExp', regexp)) return regexp; |
| 102 // The returned value is the JavaScript exception. Turn it into a | 100 // The returned value is the JavaScript exception. Turn it into a |
| 103 // Dart exception. | 101 // Dart exception. |
| 104 String errorMessage = JS('String', r'String(#)', regexp); | 102 String errorMessage = JS('String', r'String(#)', regexp); |
| 105 throw new FormatException("Illegal RegExp pattern: $source, $errorMessage"); | 103 throw new FormatException("Illegal RegExp pattern: $source, $errorMessage"); |
| 106 } | 104 } |
| 107 | 105 |
| 108 Match firstMatch(String string) { | 106 Match firstMatch(@nullCheck String string) { |
| 109 List m = JS('JSExtendableArray|Null', r'#.exec(#)', _nativeRegExp, | 107 List m = JS('JSExtendableArray|Null', r'#.exec(#)', _nativeRegExp, string); |
| 110 checkString(string)); | |
| 111 if (m == null) return null; | 108 if (m == null) return null; |
| 112 return new _MatchImplementation(this, _stringList(m)); | 109 return new _MatchImplementation(this, new JSArray<String>.of(m)); |
| 113 } | 110 } |
| 114 | 111 |
| 115 bool hasMatch(String string) { | 112 @notNull |
| 116 return JS('bool', r'#.test(#)', _nativeRegExp, checkString(string)); | 113 bool hasMatch(@nullCheck String string) { |
| 114 return JS('bool', r'#.test(#)', _nativeRegExp, string); |
| 117 } | 115 } |
| 118 | 116 |
| 119 String stringMatch(String string) { | 117 String stringMatch(String string) { |
| 120 var match = firstMatch(string); | 118 var match = firstMatch(string); |
| 121 if (match != null) return match.group(0); | 119 if (match != null) return match.group(0); |
| 122 return null; | 120 return null; |
| 123 } | 121 } |
| 124 | 122 |
| 125 Iterable<Match> allMatches(String string, [int start = 0]) { | 123 Iterable<Match> allMatches(@nullCheck String string, |
| 126 checkString(string); | 124 [@nullCheck int start = 0]) { |
| 127 checkInt(start); | |
| 128 if (start < 0 || start > string.length) { | 125 if (start < 0 || start > string.length) { |
| 129 throw new RangeError.range(start, 0, string.length); | 126 throw new RangeError.range(start, 0, string.length); |
| 130 } | 127 } |
| 131 return new _AllMatchesIterable(this, string, start); | 128 return new _AllMatchesIterable(this, string, start); |
| 132 } | 129 } |
| 133 | 130 |
| 134 Match _execGlobal(String string, int start) { | 131 Match _execGlobal(String string, int start) { |
| 135 Object regexp = _nativeGlobalVersion; | 132 Object regexp = _nativeGlobalVersion; |
| 136 JS("void", "#.lastIndex = #", regexp, start); | 133 JS("void", "#.lastIndex = #", regexp, start); |
| 137 List match = JS("JSExtendableArray|Null", "#.exec(#)", regexp, string); | 134 List match = JS("JSExtendableArray|Null", "#.exec(#)", regexp, string); |
| 138 if (match == null) return null; | 135 if (match == null) return null; |
| 139 return new _MatchImplementation(this, _stringList(match)); | 136 return new _MatchImplementation(this, new JSArray<String>.of(match)); |
| 140 } | 137 } |
| 141 | 138 |
| 142 Match _execAnchored(String string, int start) { | 139 Match _execAnchored(String string, int start) { |
| 143 Object regexp = _nativeAnchoredVersion; | 140 Object regexp = _nativeAnchoredVersion; |
| 144 JS("void", "#.lastIndex = #", regexp, start); | 141 JS("void", "#.lastIndex = #", regexp, start); |
| 145 List match = JS("JSExtendableArray|Null", "#.exec(#)", regexp, string); | 142 List match = JS("JSExtendableArray|Null", "#.exec(#)", regexp, string); |
| 146 if (match == null) return null; | 143 if (match == null) return null; |
| 147 // If the last capture group participated, the original regexp did not | 144 // If the last capture group participated, the original regexp did not |
| 148 // match at the start position. | 145 // match at the start position. |
| 149 if (match[match.length - 1] != null) return null; | 146 if (match[match.length - 1] != null) return null; |
| 150 match.length -= 1; | 147 match.length -= 1; |
| 151 return new _MatchImplementation(this, _stringList(match)); | 148 return new _MatchImplementation(this, new JSArray<String>.of(match)); |
| 152 } | 149 } |
| 153 | 150 |
| 154 Match matchAsPrefix(String string, [int start = 0]) { | 151 Match matchAsPrefix(String string, [int start = 0]) { |
| 155 if (start < 0 || start > string.length) { | 152 if (start < 0 || start > string.length) { |
| 156 throw new RangeError.range(start, 0, string.length); | 153 throw new RangeError.range(start, 0, string.length); |
| 157 } | 154 } |
| 158 return _execAnchored(string, start); | 155 return _execAnchored(string, start); |
| 159 } | 156 } |
| 160 | 157 |
| 161 bool get isMultiLine => _isMultiLine; | 158 bool get isMultiLine => _isMultiLine; |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 227 _current = null; | 224 _current = null; |
| 228 _string = null; // Marks iteration as ended. | 225 _string = null; // Marks iteration as ended. |
| 229 return false; | 226 return false; |
| 230 } | 227 } |
| 231 } | 228 } |
| 232 | 229 |
| 233 /** Find the first match of [regExp] in [string] at or after [start]. */ | 230 /** Find the first match of [regExp] in [string] at or after [start]. */ |
| 234 Match firstMatchAfter(JSSyntaxRegExp regExp, String string, int start) { | 231 Match firstMatchAfter(JSSyntaxRegExp regExp, String string, int start) { |
| 235 return regExp._execGlobal(string, start); | 232 return regExp._execGlobal(string, start); |
| 236 } | 233 } |
| OLD | NEW |