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 |