# Number Named Regex Groups

Using named matching groups is often easier to understand than numbering groups but takes up more bytes. Given a regular expression as specified below, your program or function must convert named groups to numbered groups.

These specifications are based on python 3's re module.

A named group takes the form of (?P<name>...) where name is replaced with the name of the group and ... is a valid regex.

A reference to a named group takes the form of (?P=name) where name is replaced with the name of the group.

A numbered group takes the form of (...) where ... is a valid regex.

A reference to a numbered group takes the form of \number where number is replaced with the number of the group as follows:

groups are numbered from left to right, from 1 upward. Groups can be nested; to determine the number, just count the opening parenthesis characters, going from left to right. (Python howto)

The number of opening parentheses characters includes the group's opening parenthesis.

Thus your program or function must, given a valid regular expression, convert named groups to numbered groups and references to named groups to references to numbered groups with the right number.

## Example

The input is s(tar)+t(?P<name1>subre)cd(?P=name1). The (?P=name1) bit refers to the capturing group (?P<name1>subre). It is group 2 because the string s(tar)+t( contains two opening parentheses. Thus (?P=name1) is replaced with \2 to get s(tar)+t(?P<name1>subre)cd\2

The (?P<name1>subre) group can be converted to a non-named group by simply removing the name and identification to get (subre). Applying this into the string so far yields s(tar)+t(subre)cd\2. Since there are no named groups nor named references, the program is done and outputs s(tar)+t(subre)cd\2.

## Rules

• There are no non-capturing groups in the input regular expression (in other words, all groups are either numbered or named).
• You may assume that all input groups are named.
• Any further pedantics due to technical specifications of regular expressions should be ignored.
• The input will always contain no digits directly after named backreferences.
• Every reference to a named group in the input will have an actual named group to reference to.
• All parentheses in the input delimit a group
• The group names will contain no spaces.
• The output may optionally have a trailing newline.

• Brownie points for solving this challenge using a regular expression!

## Test Cases

Input
Output
-----
this has no* groups \bubbles
this has no* groups \bubbles
-----
this has (?P<name>named)+ groups (?P=name)
this has (named)+ groups \1
-----
-----
I need (?P<reference> very+ much) here is one: (?P=reference)
I need ( very+ much) here is one: \1
-----
t(?P<one>o)n(?P<another>s) of (?P<groups>named) groups (?P=another) (?P=one) (?P=groups)
t(o)n(s) of (named) groups \2 \1 \3
-----
Of course, we need (?P<moregroups>misdirection) (?P<n>n(?P<e>e(?P<s>s(?P<t>t)i)n)g) (?P=n)n(?P=e)e(?P=s)s(?P=t)ing
Of course, we need (misdirection) (n(e(s(t)i)n)g) \2n\3e\4s\5ing
-----
(?P<Lorem>Lorem ipsum dolor sit amet), (?P<consectetur>consectetur adipiscing elit), (?P<sed>sed do eiusmod tempor incididunt ut labore et dolore magna aliqua). (?P<Ut>Ut enim ad minim veniam, (?P<quis>quis) nostrud exercitation ullamco laboris nisi ut (?P<aliquip>aliquip ex ea commodo consequat.)) Duis aute irure dolor in (?P<finale>reprehenderit in voluptate velit esse cillum dolore eu fugiat (?P<null>null)a pariatur. (?P<Except>Excepteur (?P<sin>sin)t occae(?P<cat>cat) )cupidatat non proident, sunt) in culpa qui officia deserunt mollit anim id est laborum. (?P=cat)(?P=sin)(?P=Except)(?P=null)(?P=finale)(?P=aliquip)(?P=quis)(?P=Ut)(?P=sed)(?P=consectetur)(?P=Lorem)
(Lorem ipsum dolor sit amet), (consectetur adipiscing elit), (sed do eiusmod tempor incididunt ut labore et dolore magna aliqua). (Ut enim ad minim veniam, (quis) nostrud exercitation ullamco laboris nisi ut (aliquip ex ea commodo consequat.)) Duis aute irure dolor in (reprehenderit in voluptate velit esse cillum dolore eu fugiat (null)a pariatur. (Excepteur (sin)t occae(cat) )cupidatat non proident, sunt) in culpa qui officia deserunt mollit anim id est laborum. \11\10\9\8\7\6\5\4\3\2\1


(Above, I was referring to numbered groups as being disjoint from named groups, when named groups are also numbered. Pretend that I meant non-named groups when I said numbered groups.)

• "There are no escaped opening parentheses in the input string." What about parentheses inside character classes? "Any further pedantics due to technical specifications of regular expressions should be ignored." Does that include the assumption that there won't be digits after a group reference? (Which would mess up the backreference when converted to numbers.) Jul 10 '17 at 9:50
• Several of your test cases violate the "You may assume that all groups are named." rule (the second, fifth and sixth). Jul 10 '17 at 9:51
• regex101.com says that your fifth example is invalid in multiple ways (spaces inside group name, <>s around backreference name).
– Neil
Jul 10 '17 at 10:06

f=
s=>s.replace(/$$\?P=(\w+)$$|$$\?P<(\w+)>|\(/g,(_,b,n)=>b?\\+r[b]:(r[n]=++c,(),c=0,r={}) <textarea cols=30 rows=5 oninput=o.textContent=f(this.value)></textarea><div id=o> • If I'm not mistaken, it doesn't work for I need (?P<a reference> very+ much) here is one: (?P=<a reference>), for two reasons: \w+ doesn't allow spaces in the group name; and P=(\w+) doesn't allow <> around the group name in the reference. – Dada Jul 10 '17 at 8:58 • @Dada On the other hand, regex101.com says that those aren't valid... – Neil Jul 10 '17 at 10:06 • Spaces aren't valid in the group name of a reference. The question has already been edited to fix that Jul 12 '17 at 16:54 # PHP, 198 bytes preg_match_all("#\(\?P<.+?>|\(\?P=.+?$$|\(#",$a=$argn,$t);foreach($t[0]as$x)$x[1]?$x[3]=="<"?$a=strtr($a,[$x=>"("]).!$n[substr($x,4,-1)]=++$k:$a=strtr($a,[$x=>"\\".$n[substr($x,4,-1)]]):++$k;echo$a;