Skip to content

Commit dd9a561

Browse files
committed
Improved comments; throw when addToken is given a nonnative regex.
1 parent 3f7b39c commit dd9a561

File tree

3 files changed

+41
-8
lines changed

3 files changed

+41
-8
lines changed

src/xregexp.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ var XRegExp = (function(undefined) {
446446
do {
447447
// Check for custom tokens at the current position
448448
tokenResult = runTokens(pattern, flags, pos, scope, context);
449-
// If the matched token used the `reparse` option, splice its result into the
449+
// If the matched token used the `reparse` option, splice its output into the
450450
// pattern before running tokens again at the same position
451451
if (tokenResult && tokenResult.reparse) {
452452
pattern = pattern.slice(0, pos) +
@@ -459,7 +459,7 @@ var XRegExp = (function(undefined) {
459459
output += tokenResult.output;
460460
pos += (tokenResult.matchLength || 1);
461461
} else {
462-
// Check for native tokens at the current position. This could use the native
462+
// Get the native token at the current position. This could use the native
463463
// `exec`, except that sticky processing avoids string slicing in browsers that
464464
// support flag y
465465
match = self.exec(pattern, nativeTokens[scope], pos, 'sticky')[0];
@@ -536,12 +536,26 @@ var XRegExp = (function(undefined) {
536536
* {scope: 'all'}
537537
* );
538538
* XRegExp('\\a[\\a-\\n]+').test('\x07\n\x07'); // -> true
539+
*
540+
* // Add the U (ungreedy) flag from PCRE and RE2, which reverses greedy and lazy quantifiers
541+
* XRegExp.addToken(
542+
* /([?*+]|{\d+(?:,\d*)?})(\??)/,
543+
* function(match) {return match[1] + (match[2] ? '' : '?');},
544+
* {flag: 'U'}
545+
* );
546+
* XRegExp('a+', 'U').exec('aaa')[0]; // -> 'a'
547+
* XRegExp('a+?', 'U').exec('aaa')[0]; // -> 'aaa'
539548
*/
540549
self.addToken = function(regex, handler, options) {
541550
options = options || {};
551+
var optionalFlags = options.optionalFlags, i;
542552

543-
var optionalFlags = options.optionalFlags,
544-
i;
553+
// Cannot use XRegExp regexes because the XRegExp construction process (in `runTokens`)
554+
// uses `XRegExp.exec`, which copies regexes the first time they're used. The copying thus
555+
// triggers an infinite loop, since it is building a new XRegExp
556+
if (regex[REGEX_DATA] && !regex[REGEX_DATA].isNative) {
557+
throw new Error('Cannot use XRegExp regexes with addToken');
558+
}
545559

546560
if (options.flag) {
547561
registerFlag(options.flag);

tests/spec/s-xregexp-methods.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ describe('XRegExp.addToken()', function() {
179179
expect(XRegExp('\x0A').test('1x2')).toBe(true);
180180
});
181181

182+
// This test is merely tracking current behavior. The current behavior is not ideal, though.
183+
// Change this if the error is no longer necessary in future versions
184+
it('should throw an exception when given an XRegExp as the search pattern', function() {
185+
expect(function() {XRegExp.addToken(XRegExp('\\x0B'), function() {return 'B';});}).toThrow();
186+
});
182187
});
183188

184189
describe('XRegExp.cache()', function() {

xregexp-all.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ var XRegExp = (function(undefined) {
473473
do {
474474
// Check for custom tokens at the current position
475475
tokenResult = runTokens(pattern, flags, pos, scope, context);
476-
// If the matched token used the `reparse` option, splice its result into the
476+
// If the matched token used the `reparse` option, splice its output into the
477477
// pattern before running tokens again at the same position
478478
if (tokenResult && tokenResult.reparse) {
479479
pattern = pattern.slice(0, pos) +
@@ -486,7 +486,7 @@ var XRegExp = (function(undefined) {
486486
output += tokenResult.output;
487487
pos += (tokenResult.matchLength || 1);
488488
} else {
489-
// Check for native tokens at the current position. This could use the native
489+
// Get the native token at the current position. This could use the native
490490
// `exec`, except that sticky processing avoids string slicing in browsers that
491491
// support flag y
492492
match = self.exec(pattern, nativeTokens[scope], pos, 'sticky')[0];
@@ -563,12 +563,26 @@ var XRegExp = (function(undefined) {
563563
* {scope: 'all'}
564564
* );
565565
* XRegExp('\\a[\\a-\\n]+').test('\x07\n\x07'); // -> true
566+
*
567+
* // Add the U (ungreedy) flag from PCRE and RE2, which reverses greedy and lazy quantifiers
568+
* XRegExp.addToken(
569+
* /([?*+]|{\d+(?:,\d*)?})(\??)/,
570+
* function(match) {return match[1] + (match[2] ? '' : '?');},
571+
* {flag: 'U'}
572+
* );
573+
* XRegExp('a+', 'U').exec('aaa')[0]; // -> 'a'
574+
* XRegExp('a+?', 'U').exec('aaa')[0]; // -> 'aaa'
566575
*/
567576
self.addToken = function(regex, handler, options) {
568577
options = options || {};
578+
var optionalFlags = options.optionalFlags, i;
569579

570-
var optionalFlags = options.optionalFlags,
571-
i;
580+
// Cannot use XRegExp regexes because the XRegExp construction process (in `runTokens`)
581+
// uses `XRegExp.exec`, which copies regexes the first time they're used. The copying thus
582+
// triggers an infinite loop, since it is building a new XRegExp
583+
if (regex[REGEX_DATA] && !regex[REGEX_DATA].isNative) {
584+
throw new Error('Cannot use XRegExp regexes with addToken');
585+
}
572586

573587
if (options.flag) {
574588
registerFlag(options.flag);

0 commit comments

Comments
 (0)