<HTML><HEAD></HEAD>
<BODY dir=ltr>
<DIV dir=ltr>
<DIV style="FONT-FAMILY: 'Calibri'; COLOR: #000000; FONT-SIZE: 12pt">
<DIV>Definite +1 on adding some way to determine capturing group match start 
positions.</DIV>
<DIV> </DIV>
<DIV>Mark Macdonald wrote:</DIV>
<DIV> </DIV>
<DIV>> This makes it hard to write something like a regex coach, which takes 
an arbitrary regular expression and input string, and outputs a highlighted 
version of the input string showing where the capturing groups matched.</DIV>
<DIV> </DIV>
<DIV>It makes it impossible to do accurately. There are a few crude approaches 
I’ve pursued in the past to make something like this work with handcrafted 
regexes in some cases only. E.g., you can use `str.replace()` to insert markers 
before and after backreferences, and then check for the position of your markers 
after the fact. But that certainly won’t work with any arbitrary regex fed in 
via a regex tester. Captured subpatterns might not even appear within the text 
of the match, due to lookahead.</DIV>
<DIV> </DIV>
<DIV>There are several JavaScript regex testers that try to report backreference 
positions, but they are incredibly easy to fool. See, e.g., <A 
title=http://leaverou.github.com/regexplained/ 
href="http://leaverou.github.com/regexplained/">http://leaverou.github.com/regexplained/</A> 
(plus <A title=https://github.com/LeaVerou/regexplained/issues/7 
href="https://github.com/LeaVerou/regexplained/issues/7">https://github.com/LeaVerou/regexplained/issues/7</A> 
) and <A title=http://www.gethifi.com/tools/regex 
href="http://www.gethifi.com/tools/regex">http://www.gethifi.com/tools/regex</A> 
.</DIV>
<DIV> </DIV>
<DIV>As for the proposed implementation, I have a few concerns:</DIV>
<DIV> </DIV>
<DIV>1. The main issue I see is that the proposal doesn’t provide a clean way to 
support named backreferences, should a future version of ES add named capturing 
groups. Future ES might want to share the proposed `captures` array or object 
for providing named backreferences, as well as their match positions. <A 
title=http://xregexp.com/syntax/named_capture_comparison/ 
href="http://xregexp.com/syntax/named_capture_comparison/">http://xregexp.com/syntax/named_capture_comparison/</A> 
shows where named backreferences are stored in various regex flavors (usually 
accessible via a method named `group()` or `groups()`, although XRegExp stores 
named backreference properties directly on the result array).</DIV>
<DIV> </DIV>
<DIV>2. Keep in mind that, since `str.match(nonglobalregex)` is an alias of 
`regex.exec(str)`, anything added to `regex.exec()` should also be added to the 
nonglobal `str.match()` overload.</DIV>
<DIV> </DIV>
<DIV>3. IMO, the name `captures` is misleading, given the specific proposal, 
since it seems to suggest that it stores the backreferences themselves, rather 
than their start positions.</DIV>
<DIV> </DIV>
<DIV>4. I dislike the idea of excluding backreference zero (i.e., the entire 
match) from any result array.</DIV>
<DIV> </DIV>
<DIV>5. The proposal does not mention what should happen when trying to access 
the start position of a nonparticipating capturing group. Presumably, the value 
should be `null` or `undefined`.</DIV>
<DIV> </DIV>
<DIV>Thanks for mentioning the prior art of `java.util.Matcher.start()` and 
Python's `re.MatchObject.start()`. They offer an alternative design that might 
be better, assuming that adding a `start()` method to the array returned by 
`exec()` is an acceptable solution. If future ES adopts named capture, it would 
be easy to change such a method to accept strings in addition to integers. (For 
whatever reason [probably just omission], Java 7’s `Matcher.start()` doesn’t 
accept strings, even though Java 7 supports named capture.)</DIV>
<DIV> </DIV>
<DIV>Another potential way to do this might be to change the string primitives 
in the `exec()` result array to `String` objects that can store properties. Then 
you could do something like `/.(.)/.exec('foo')[1].start === 1`.</DIV>
<DIV> </DIV>
<DIV>--Steven Levithan</DIV>
<DIV> </DIV>
<DIV 
style="FONT-STYLE: normal; DISPLAY: inline; FONT-FAMILY: 'Calibri'; COLOR: #000000; FONT-SIZE: small; FONT-WEIGHT: normal; TEXT-DECORATION: none">
<DIV style="FONT: 10pt tahoma">
<DIV> </DIV>
<DIV style="BACKGROUND: #f5f5f5">
<DIV style="font-color: black"><B>From:</B> <A title=mamacdon@gmail.com 
href="mailto:mamacdon@gmail.com">Mark Macdonald</A> </DIV>
<DIV><B>Sent:</B> Thursday, July 12, 2012 2:26 PM</DIV>
<DIV><B>To:</B> <A title=es-discuss@mozilla.org 
href="mailto:es-discuss@mozilla.org">es-discuss@mozilla.org</A> </DIV>
<DIV><B>Subject:</B> Regexp APIs and capturing group positions</DIV></DIV></DIV>
<DIV> </DIV></DIV>
<DIV 
style="FONT-STYLE: normal; DISPLAY: inline; FONT-FAMILY: 'Calibri'; COLOR: #000000; FONT-SIZE: small; FONT-WEIGHT: normal; TEXT-DECORATION: none">In 
ES 5.1, the regular expression APIs do not expose the index at which a capturing 
group matched. The RegExp.prototype.exec(string) function returns an Array 
giving (among other things) the <I>text</I> matched by capturing groups, but 
does not give the <I>positions </I>of the captured text within the input 
string.<BR><BR>For example, consider this code using the current regex 
APIs:<BR><SPAN style="FONT-FAMILY: courier new,monospace"></SPAN><BR 
style="FONT-FAMILY: courier new,monospace">
<DIV style="FONT-FAMILY: courier new,monospace; MARGIN-LEFT: 40px">var match = 
/(fox).*(dog)/.exec("The quick brown fox jumps over the lazy dog");<BR>match[1]; 
// "fox"<BR>match[2]; // "dog"<BR></DIV><BR>We want to get this:<BR>"fox" at 
index 16<BR>"dog" at index 40<BR><BR>But there is no way to obtain the indices 
16, 40 from the match object (or any other API I'm aware of). This makes it hard 
to write something like a regex coach, which takes an arbitrary regular 
expression and input string, and outputs a highlighted version of the input 
string showing where the capturing groups matched.<BR><BR>Proposal: When 
RegExp.prototype.exec(string) returns a nonnull value, the returned object shall 
have a property named "captures", which is an Array. The value of captures[n] is 
the index at which the n'th capturing group's match begins. As usual, groups are 
numbered from 1. The captures array does not have a "0" property (it would 
always be equal to the "index" property of the match object, and thus 
redundant).<BR><BR>Proposed code:<BR><BR>
<DIV style="MARGIN-LEFT: 40px"><SPAN 
style="FONT-FAMILY: courier new,monospace">var match = /(fox).*(dog)/.exec("The 
quick brown fox jumps over the lazy dog");</SPAN><BR 
style="FONT-FAMILY: courier new,monospace"><SPAN 
style="FONT-FAMILY: courier new,monospace">match.captures[1]; // 
16<BR>match.captures[2]; // 40</SPAN><BR></DIV><BR>This (combined with the group 
text from the match object) gives you enough information to enumerate the 
captured regions of the input string.<BR><BR>Prior art: Java's 
java.util.Matcher.start() [1], Python's re.MatchObject.start() 
[2].<BR><BR>Comments, suggestions?<BR><BR>Mark<BR><BR>[1] <A 
href="http://docs.oracle.com/javase/1.5.0/docs/api/java/util/regex/Matcher.html#start%28int%29" 
target=_blank>http://docs.oracle.com/javase/1.5.0/docs/api/java/util/regex/Matcher.html#start%28int%29</A><BR>[2] 
<A href="http://docs.python.org/library/re.html#match-objects" 
target=_blank>http://docs.python.org/library/re.html#match-objects</A><BR><BR></DIV></DIV></DIV></BODY></HTML>