2024-05-26 21:12:28 +02:00

2.2 KiB

Plain regular expressions are difficult to write and sometimes difficult to read, therefore I prefer constructing regular expressions with the rx macro. It will take care of the special characters and the proper escaping, as I can't remember those details that well. With rx, you construct regular expressions in a Lisp format, e.g.:

  (rx string-start (or "foo" "bar") string-end)

It's not only more readable (in my opinion), but it's also easier to edit with all the Lisp writing tools that can be used within Emacs (e.g. paredit or puni).

However, using the rx macro is not always possible. Lisp data files may contain regular expressions, but they are treated as data and not as executable code.

One prime example is elfeed-score: an Emacs package that allows you to write scoring rules for elfeed entries. Scoring rules are defined with an Lisp data file, and a rule may allow regular expressions but they have to be written out verbatim.

Luckily there's a work-around with Org Mode:

  • Write the scoring rules in an lisp-data source block (see below).
  • Tangle the file to a location that is read by the elfeed-score package (see variable elfeed-score-score-file).
  • For the regular expression we use a small elisp block as follows:

    #+name: rx
    #+begin_src elisp :var expression='any :tangle=no
      (string-replace "\\" "\\\\" (format "\"%s\"" (rx-to-string expression)))

    It accepts an expression that is a Lisp form that rx accepts. It is converted to a string, surrounded by quotes and since we use it in a Lisp string, backslashes should be doubled.

  • By naming the source block rx we can call it from other source blocks using noweb expansion:

    #+begin_src lisp-data :tangle elfeed-scores.eld :noweb yes
      (("title" (:text <<rx('(or "foo" "bar"))>> :value 1 :type r)))

    Upon tangling, the <<rx('(...))>> is converted to a regex string and therefore readable by elfeed-score (mind the single quote!).