From 0dfe23c528409f4eeb503677cb639860dc527ba1 Mon Sep 17 00:00:00 2001 From: Bram Schoenmakers Date: Sun, 26 May 2024 20:54:36 +0200 Subject: [PATCH] Add gist on using rx inside lisp-data --- gists.org | 44 +++++++++++++++++++++++++++++++++++++++ gists/rx-in-lisp-data.org | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 gists/rx-in-lisp-data.org diff --git a/gists.org b/gists.org index b10e96a..6daf0ce 100644 --- a/gists.org +++ b/gists.org @@ -1140,6 +1140,50 @@ Which covers my (potential) GPG/SSH usage within Emacs. Now, anytime a I perform (run-with-timer nil (* 2 60 60) (lambda () (run-with-idle-timer 120 nil 'elfeed-update)))) #+end_src +* Use rx style regular expressions in Lisp data files + +#+begin_src org :noweb no :tangle gists/rx-in-lisp-data.org + 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.: + + ,#+begin_src elisp + (rx string-start (or "foo" "bar") string-end) + ,#+end_src + + ,#+RESULTS: + : \`\(?:bar\|foo\)\' + + It's not only more readable (in my opinion), but it's also easier to construct with all the Lisp writing tools that can be used within Emacs (e.g. paredit or puni). + + However, using =rx= 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. + - 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: + + ,#+begin_src org + ,,#+name: rx + ,,#+begin_src elisp :var expression='any :tangle=no + (string-replace "\\" "\\\\" (format "\"%s\"" (rx-to-string expression))) + ,,#+end_src + ,#+end_src + + 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: + + ,#+begin_src org + ,,#+begin_src lisp-data :tangle elfeed-scores.eld :noweb yes + (("title" (:text <> :value 1 :type r))) + ,,#+end_src + ,#+end_src + + Upon tangling, the <> is converted to a regex string and therefore readable by elfeed-score. +#+end_src + * Meta ** License diff --git a/gists/rx-in-lisp-data.org b/gists/rx-in-lisp-data.org new file mode 100644 index 0000000..797cbb9 --- /dev/null +++ b/gists/rx-in-lisp-data.org @@ -0,0 +1,39 @@ +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.: + +#+begin_src elisp + (rx string-start (or "foo" "bar") string-end) +#+end_src + +#+RESULTS: +: \`\(?:bar\|foo\)\' + +It's not only more readable (in my opinion), but it's also easier to construct with all the Lisp writing tools that can be used within Emacs (e.g. paredit or puni). + +However, using =rx= 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. +- 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: + + #+begin_src org + ,#+name: rx + ,#+begin_src elisp :var expression='any :tangle=no + (string-replace "\\" "\\\\" (format "\"%s\"" (rx-to-string expression))) + ,#+end_src + #+end_src + + 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: + + #+begin_src org + ,#+begin_src lisp-data :tangle elfeed-scores.eld :noweb yes + (("title" (:text <> :value 1 :type r))) + ,#+end_src + #+end_src + + Upon tangling, the <> is converted to a regex string and therefore readable by elfeed-score.