How can I use more than 9 regex capture groups in Emacs Lisp?

I have to do a regexp replacement with more than 9 capture groups. How can I do it?

Here there is my code (with \\10 and \\12 that they do not work as I expected to):

(perform-replace "\\\\href{https://doi.org/\$$.*\$$}{\\\\emph{\$$JHEP\\|JCAP\$$}\$$[ ]*\$$?{\\\\bfseries\$$[ ]*\$$?\$$[0-9]+\$$}\$$[ ]*\$$?(\$$[0-9]\$$\$$[0-9]\$$\$$[0-9]\$$\$$[0-9]\$$)\$$[ ]*\$$?\$$[0-9]+\$$}" "\\\\href{https://doi.org/\\1}{\\\\emph{\\2} {\\\\bfseries \\9\\10\\5} (\\7\\8\\9\\10) \\12}" t t nil 1 nil a z)
• The question is interesting per se. Emacs regexp only allow digits from 1 to 9 after the backslash for referencing. But you do not need all groups of the search string in the replacement. Therefore you can change some of the groups in your search string into shy groups (?:...). Afterwards you only really need 8 captured groups. – Tobias May 7 '18 at 10:34
• My assumption is that having more than 9 capture groups wouldn't be trivial, as syntax for matched groups probably was designed with just a single digit for the group in mind. Doing this sort of thing would probably require writing a parser by hand. You should be able to capture multiple digits wrapped into parens with a single group as well. – user12563 May 7 '18 at 10:37
• Here is an example with twenty-three (23) regex groups: stackoverflow.com/a/20960301/2112489 . I have increased it in my own setup to 35 regex groups, but I would imagine that 23 is sufficient for you to see how it might be done. To experiment with a regexp, you may enjoy typing the text to be matched in a scratch buffer and then type M-x re-builder -- the matches show up with color-codes in the scratch buffer and you can play around with changing the regex until you get the desired result. – lawlist May 7 '18 at 14:05

Going by the Emacs source code, it is absolutely possible to use more than 9 regex capture groups:

/* Since we have one byte reserved for the register number argument to
{start,stop}_memory, the maximum number of groups we can report
things about is what fits in that byte.  */
#define MAX_REGNUM 255

/* But patterns can have more than 'MAX_REGNUM' registers.  Just
ignore the excess.  */
typedef int regnum_t;

However you've run into a different limitation, a maximum of 9 backreferences:

case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9':
{
regnum_t reg = c - '0';

if (reg > bufp->re_nsub || reg < 1
/* Can't back reference to a subexp before its end.  */
|| group_in_compile_stack (compile_stack, reg))
FREE_STACK_RETURN (REG_ESUBREG);

laststart = b;
BUF_PUSH_2 (duplicate, reg);
}
break;

It is possible to overcome this limitation by using Lisp code instead of a replacement pattern:

(defvar big-rx
(rx (group (+ "x")) " " (group (+ "x")) " "
(group (+ "x")) " " (group (+ "x")) " "
(group (+ "x")) " " (group (+ "x")) " "
(group (+ "x")) " " (group (+ "x")) " "
(group (+ "x")) " " (group (+ "x")) " "
(group (+ "x"))))

(let ((text (mapconcat 'identity (mapcar (lambda (x) (make-string x ?x))
(number-sequence 1 11))
" ")))
(when (string-match big-rx text)
(setq text (replace-match (make-string 1 ?y) t t text 1))
(setq text (replace-match (make-string 11 ?y) t t text 11))))
;; => "y xx xxx xxxx xxxxx xxxxxx xxxxxxx xxxxxxxx xxxxxxxxx xxxxxxxxxx yyyyyyyyyyy"

The above example uses eleven capture groups and replaces the first and eleventh one.