1

I have a really useful regex for extracting DCOs:

(defvar my-dco-tag-re
  (rx (: bol (zero-or-more (in blank))                     ; fresh line
         (any "RSTA") (one-or-more (in alpha "-")) "-by: " ; tag
         (one-or-more (in alpha blank "-."))               ; name
         blank
         "<" (one-or-more (not (in ">"))) ">"))            ; email
  "Regexp to match DCO style tag.")

However I've just recently come up with another use case which doesn't want to start on the begining of the line. Is it possible to evaluate RX variables in RX? I thought I could do this:

(defvar my-bare-dco-tag-rx
  '((any "RSTA") (one-or-more (in alpha "-")) "-by: " ; tag
    (one-or-more (in alpha blank "-."))               ; name
    blank
    "<" (one-or-more (not (in ">"))) ">")             ; email
  "Regexp to match plain DCO tag")

(defvar my-dco-tag-re
  (rx (: bol (zero-or-more (in blank))  ; fresh line
         (eval my-bare-dco-tag-rx)))
  "Regexp to match DCO style tag.")

but it causes the evaluation to bug out. Any ideas?

Debugger entered--Lisp error: (error "Bad rx operator ‘(any \"RSTA\")’")
  error("Bad rx operator `%S'" (any "RSTA"))
  rx--translate-form(((any "RSTA") (one-or-more (in alpha "-")) "-by: " (one-or-more (in alpha blank "-.")) blank "<" (one-or-more (not (in ">"))) ">"))
  rx--translate(((any "RSTA") (one-or-more (in alpha "-")) "-by: " (one-or-more (in alpha blank "-.")) blank "<" (one-or-more (not (in ">"))) ">"))
  rx--translate-eval((my-bare-dco-tag-rx))
  rx--translate-form((eval my-bare-dco-tag-rx))
  rx--translate((eval my-bare-dco-tag-rx))
  mapcar(rx--translate (bol (zero-or-more (in blank)) (eval my-bare-dco-tag-rx)))
Basil
  • 12,019
  • 43
  • 69
stsquad
  • 4,626
  • 28
  • 45

1 Answers1

2

Any ideas?

The eval construct in rx expects a single rx form, but you are giving it a list of rx forms. Try sequencing the spliced forms instead (or adding : to the beginning of my-bare-dco-tag-rx):

(defvar my-dco-tag-re
  (rx (: bol (zero-or-more (in blank))
         (eval `(: ,@my-bare-dco-tag-rx))))
  "Regexp to match DCO style tag.")

;; Or, equivalently:

(defvar my-dco-tag-re
  (rx (: bol (zero-or-more (in blank))
         (eval (cons ': my-bare-dco-tag-rx))))
  "Regexp to match DCO style tag.")

BTW, Emacs 27 added new macros for defining named and optionally parameterised rx constructs, namely rx-define, rx-let, and rx-let-eval; see (info "(elisp) Extending Rx"). For example:

(rx-define my-bare-dco-tag
  (: (any "RSTA") (one-or-more (in alpha "-")) "-by: "
     (one-or-more (in alpha blank "-."))
     blank
     "<" (one-or-more (not (in ">"))) ">"))

(rx-define my-dco-tag
  (: bol (zero-or-more (in blank)) my-bare-dco-tag))

(rx my-bare-dco-tag)
;; => "[AR-T][[:alpha:]-]+-by: [.[:alpha:][:blank:]-]+[[:blank:]]<[^>]+>"

(rx my-dco-tag)
;; => "^[[:blank:]]*[AR-T][[:alpha:]-]+-by: [.[:alpha:][:blank:]-]+[[:blank:]]<[^>]+>"
Basil
  • 12,019
  • 43
  • 69