2

Suppose I have a regular expression with many alternatives, such as

  "[a-z]+\\|[A-Z]+\\|[0-9]+"

and it has just matched some text. I'd then like to know which of the given alternatives caused the match.

Of course I can consult (match-string 0) to check the matched string, but I have a much more complicated situation in mind where this may not be as easy as it looks. Would there be an automatic way to do this?

I thought of grouping the alternatives and numbering them, as in

 "\\(?1:[a-z]+\\)+\\|\\(?2:[0-9]+\\)+\\|\\(?3:[A-Z]+\\)+"

and then searching for the apropriate n making (match-string n) non-nil, but I wonder if there is a better idea.

3 Answers 3

2

Capture groups are pretty much all we’ve got.

Perl 5 and its successor Raku allow you to name the capture groups and then produce a hash map from names to captures that makes this quite a lot easier. It is even possible to use named regexes to build new regexes, meaning that it is useful to encode an entire grammar as regexes with named parts. I wish we could evolve Emacs in that direction, but for now we only have numbered captures.

2
  • The first sentence is an answer. The remainder of the answer is not so useful. 1st) what you describe still uses capture groups, even if they are named and 2nd) the named regexps are more or less unrelated to the problem. I have the impression that the remainder of the answer is more some stuff for Reddit than for this question-answer-site.
    – Tobias
    Jan 23, 2023 at 11:34
  • 1
    Named capture groups are useful because the names don’t change as you edit the regexp. This allows the code to grow over time without a large maintenance burden. Emacs sadly does not support this useful feature.
    – db48x
    Jan 23, 2023 at 12:35
0

You don't say whether you're trying to determine this with code or visually/interactively.

  1. If you want to see interactively what's being matched by which regexp groups, regexp isearching shows you that if you use library Isearch+.

  2. If you want to know from Elisp which parts of the regexp matched text, you can use match-beginning or match-end, passing the different group numbers. These return nil for any group that wasn't matched.

1
  • Programatically
    – Ruy
    Jan 22, 2023 at 4:08
0

I think you cannot escape the capture groups if you do not want to match again with something like cl-assoc or alist-get with match-string as test function.

If you want to have the number of the capture group that matched you can also use cl-position-if together with match-data. That might be helpful if you have a lot of alternatives.

Example:

(let (ret
      case-fold-search)
  (with-temp-buffer
    (insert "foobar FFOOBAR 012345")
    (goto-char (point-min))
    (while (re-search-forward "\\(?1:[a-z]+\\)+\\|\\(?2:[0-9]+\\)+\\|\\(?3:[A-Z]+\\)+" nil t)
      (push (/ (cl-position-if #'identity (cddr (match-data))) 2) ret)
      ))
  (nreverse ret))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.