16

I want to use rx to create regular expressions with runtime values.

Currently, I'm doing this:

(setq strings '("foo" "bar" "baz"))
(eval `(rx symbol-start (or ,@strings) symbol-end))

However, I'd rather avoid using eval. I've found rx-to-string, but it's not clear what FORM I should write:

;; error: Unknown rx form `symbol-start
(rx-to-string '(symbol-start (or ,@strings) symbol-end))

How do I build an rx expression at runtime?

Wilfred Hughes
  • 6,890
  • 2
  • 29
  • 59

1 Answers1

16

rx-to-string takes a regexp form as an argument. The syntax is the same as the argument of rx.

(rx-to-string '(or "foo" "bar"))
"\\(?:\\(?:bar\\|foo\\)\\)"

What you tried passing is not a regexp form, but a list of regexp forms. Since what you mean is the sequence of regular expressions symbol-start followed by one of a bunch of strings followed by symbol-end, you need to lead with the sequence operator sequence (which can be abbreviated to seq or :, or weirdly even and).

(rx-to-string `(: symbol-start (or ,@strings) symbol-end))
"\\(?:\\_<\\(?:ba[rz]\\|foo\\)\\_>\\)"

rx is in fact a tiny wrapper around rx-to-string that operates at compile time because it's a macro. What makes this confusing is that if you pass multiple arguments to rx, there is an implicit sequence operator. The documentation of rx could stand to be clarified.

(rx (: symbol-start (or "foo" "bar" "baz") symbol-end))
"\\_<\\(?:ba[rz]\\|foo\\)\\_>"
(rx symbol-start (or "foo" "bar" "baz") symbol-end)
"\\_<\\(?:ba[rz]\\|foo\\)\\_>"
Wilfred Hughes
  • 6,890
  • 2
  • 29
  • 59