During my regular reviews, I use the following org-mode matching query:


(it's actually much, much longer, but it contains that).

However, I need to change and adapt the query every month. I'd like to write something like:


Or even just:


This could be done easily if I could use pre-defined constants or functions when prompted for text to match, which could be defined in my file prelude or .emacs configuration file. However, I tried to use functions (substring) when prompted for text using sparse tree matching, and that did not work.

Any ideas?

  • How about customizing the variable org-agenda-custom-commands as described in the manual section relating to sparse-trees? orgmode.org/manual/Sparse-trees.html
    – lawlist
    May 13, 2018 at 14:42
  • @lawlist I think org-agenda-custom-commands only assigns key sequences to specific tag-searches. I am afraid that it does not solve the problem of evaluating elisp expressions within the tag search. Please elaborate on the topic if I am mistaken.
    – Tobias
    May 14, 2018 at 8:00

1 Answer 1


We can introduce a new syntax element to evaluate lisp forms in a tag/property-search by an filter-args advice of org-tags-expand. Let us borrow the syntax from interactive regular expression search query-replace-regexp: \, is followed by a lisp expression that is evaluated. For the main part of org-make-tags-matcher the prefix-operator \, together with the following lisp expression is replaced by princ of the evaluation result of the lisp expression.

While there is no full parser yet for tag searches we use the ppss parser of elisp.

Note, that we give the minus sign - punctuation syntax. So you cannot evaluate a variable foo-bar by \,foo-bar. You have to use something like \,(identity foo-bar) for that special case.

(defvar org-parse-tag-syntax-table nil
  "Syntax table for parsing MATCHes for tag searches.")

(setq org-parse-tag-syntax-table
      (with-syntax-table (make-syntax-table)
    (modify-syntax-entry ?- ".")

(defun org-tags-expand-filter-args-ad (args)
  "Allow lisp expressions by the \\,-operator in MATCH argument of `org-tags-expand'.
Use it as a filter argument advice for `org-tags-expand'."
  (let ((match (car args)))
    (if (stringp match)
        (setq words-include-escapes nil)
        (insert match)
        (set-syntax-table org-parse-tag-syntax-table)
        (goto-char (point-min))
          (skip-chars-forward "\\s \t\n")
          (if (or (looking-at "\\\\,")
                (looking-back "\\\\," (line-beginning-position)))
            (let* ((b (match-beginning 0))
               (bExpr (goto-char (match-end 0)))
               (e (progn
                (point))) ;; The doc string of `forward-sexp' does nothing say about the return value:-(.
               (formStr (buffer-substring bExpr e)))
              (delete-region b e)
              (insert (princ (eval (read formStr)))))
          (null (eobp))))
        (cons (buffer-string) (cdr args))

(advice-add 'org-tags-expand :filter-args #'org-tags-expand-filter-args-ad)

(defun beginning_of_next_month ()
  "Return beginning of next month as org-date string in double quotes."
  (let ((decoded-time (decode-time (current-time))))
    (cl-loop for i from 0 below 3 do ;; indexes of SEC MINUTE HOUR
         (setf (nth i decoded-time) 0))
    (setf (nth 3 decoded-time) 1)
    (setf (nth 4 decoded-time) (1+ (mod (nth 4 decoded-time) 12))) ;; increment month
    (format "\"%s\"" (format-time-string (car org-time-stamp-formats) (apply #'encode-time decoded-time)))))

The above lisp code is tested with the org-file below and with the following versions of the software:

`emacs-version` `org-version` 9.1.6
* Test Cases
**  WAITING some waiting work item                                      :work:
SCHEDULED: <2018-05-14 Mon>

**  WAITING second waiting work item                                    :work:
SCHEDULED: <2018-06-14 Mon>

* Query run via elisp
#+BEGIN_SRC emacs-lisp :results silent
(org-match-sparse-tree nil "TODO=\"WAITING\"+SCHEDULED<\\,(beginning_of_next_month)+work")

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.