From mboxrd@z Thu Jan 1 00:00:00 1970 From: Christopher Genovese Subject: Re: full parser implementation for tag queries (parentheses, fast heading match, and more) Date: Thu, 16 Aug 2012 13:47:54 -0400 Message-ID: References: Mime-Version: 1.0 Content-Type: multipart/mixed; boundary=047d7b33cab0d2913404c765a585 Return-path: Received: from eggs.gnu.org ([208.118.235.92]:34867) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1T24BA-0000KU-Bo for emacs-orgmode@gnu.org; Thu, 16 Aug 2012 13:48:22 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1T24B6-0002dI-Gs for emacs-orgmode@gnu.org; Thu, 16 Aug 2012 13:48:20 -0400 Received: from mail-pz0-f41.google.com ([209.85.210.41]:43640) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1T24B5-0002co-Sd for emacs-orgmode@gnu.org; Thu, 16 Aug 2012 13:48:16 -0400 Received: by dadi14 with SMTP id i14so572559dad.0 for ; Thu, 16 Aug 2012 10:48:14 -0700 (PDT) In-Reply-To: List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Sender: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org To: Samuel Wales Cc: emacs-orgmode@gnu.org --047d7b33cab0d2913404c765a585 Content-Type: multipart/alternative; boundary=047d7b33cab0d2912f04c765a583 --047d7b33cab0d2912f04c765a583 Content-Type: text/plain; charset=ISO-8859-1 Hi Samuel, Thanks for your note. Just FYI, the earlier parser code in this thread has been superseded by the code in the post "new tag query parser [3/5]". The sexp input is a nice idea, and it would be very easy I think. The function org-make-tags-matcher now takes a query string but could easily be modified to operate on a form as well. I've included some code below that basically does the job. It defines a function `mtrans' that transforms a sexp representation into a matcher. It can certainly be better optimized (and fully tested), but I think it would do just what you want if inserted in org-make-tags-matcher. (Note: The car of the matcher is the query string, for reasons that aren't entirely clear. Because this is dropped anyway in practice, I don't bother making it accurate in this code. As such, I'll just give the cdr in the examples below.) A few examples follow to give the idea and show the results. It seems to handle all the cases nicely. In the sexp representation, strings stand for exact string matches and both [] and (re ) stand for regex matches, with symbols for properties and standard boolean and comparison ops in the form. The keyword :todo-only acts like /! in the query strings, and = and <> also allow arbitrary lisp code for the property comparison with equal (as long as the form does not start with or, and, not, or re but then it can be shielded with (identity ...)). I then append the code, which is also attached. I see no problems with adding this to org-make-tags-matcher and would be interested in other opinions. Best, Christopher ;;; Examples (mtrans "foo") ; corresponds to query string "foo" => (member "foo" tags-list) (mtrans ["^f"]) ; or (mtrans '(re "^f")) corresponds to query string "{^f}" => (progn (setq org-cached-props nil) (org-match-any-p "^f" tags-list)) (mtrans '(and "foo" (not "bar") (re "^u.*v"))) ; query string "foo-bar+{^u.*v} => (progn (setq org-cached-props nil) (and (member "foo" tags-list) (not (member "bar" tags-list)) (org-match-any-p "^u.*v" tags-list))) (mtrans '(or (and "xyz" (= TODO ["^T"]) ["u\\{2,4\\}"] (<= LEVEL 3)) (> APROP "foo") (and (= BPROP 4) (<> HEADING "ignore")))) ; query string "xyz+TODO={^T}+{u\\{{2,4\\}}}+LEVEL<=3 | APROP > \"foo\" | BPROP=4&HEADING <> \"ignore\"" => (progn (setq org-cached-props nil) (or (and (member "xyz" tags-list) (org-string-match= (or todo "") "^T") (org-match-any-p "u\\{2,4\\}" tags-list) (<= level 3)) (org-string> (or (org-cached-entry-get nil "APROP") "") "foo") (and (= (org-cached-entry-get nil "BPROP") 4) (org-string<> (or heading "") "ignore")))) (mtrans '(or (and "foo" (not "bar") ["^u.*v"] (> LEVEL 2)) (= APROP "foo") (and (= BPROP ["/.*/"]) (<> BPROP "/ignore/")) (<> TODO "TODO") (> SCHEDULED "<2008-11-12>"))) ; query string "foo-bar+{^u.*v}+LEVEL>2 | APROP=\"foo\"| BPROP={/.*/} & BPROP <> "/ignore/" | TODO<>\"TODO\" | SCHEDULED > \"<2008-11-12>\"" => (progn (setq org-cached-props nil) (or (and (member "foo" tags-list) (not (member "bar" tags-list)) (org-match-any-p "^u.*v" tags-list) (> level 2)) (string= (or (org-cached-entry-get nil "APROP") "") "foo") (and (org-string-match= (or (org-cached-entry-get nil "BPROP") "") "/.*/") (org-string<> (or (org-cached-entry-get nil "BPROP") "") "/ignore/")) (org-string<> (or todo "") "TODO") (org-time> (or (org-cached-entry-get nil "SCHEDULED") "") 1226466000.0))) (mtrans '(and :todo-only (or (and (not ["^abc"]) ["ex"] (= A_PROP "foo")) (>= B_PROP 1.2e10) (and (< D_PROP "<2008-12-24 18:30>") (= FOO (call other lisp code here)))))) ; except for FOO part which has no analogue, query string "-{^abc}+{ex}&A_PROP=\"foo\" | B_PROP > 1.2e10 | DROP < \"<2008-12-24 18:30>\" & FOO = ..." => (progn (setq org-cached-props nil) (and (member todo org-not-done-keywords) (or (and (not (org-match-any-p "^abc" tags-list)) (org-match-any-p "ex" tags-list) (string= (or (org-cached-entry-get nil "A_PROP") "") "foo")) (>= (org-cached-entry-get nil "B_PROP") 12000000000.0) (and (org-time< (or (org-cached-entry-get nil "D_PROP") "") 1230094800.0) (equal (org-cached-entry-get nil "FOO") (call other lisp code here)))))) ;;; The Code (eval-when-compile (require 'cl)) (defun mtrans (matcher-sexp) "Create a tag/todo matcher from a sexp representation. In the sexp representation, components are transformed as follows: + A literal string becomes an exact tag match. + A [] or (re ) becomes a tag regex match + (or ...), (and ...), (not ) act as boolean operators, and processing continues on the 's + ( ) is a property comparison, where op must be one of <, <=, >=, >, =, ==, or <>. One of lhs or rhs must be a symbol naming a property and the other must be either a number, string, [] or (re ) for regex, or a generic form. (Only =, ==, and <> are allowed on the latter two.) Property symbols TODO, PRIORITY, HEADING, CATEGORY, are handled specially, otherwise, the symbol name is used as the property name. + A keyword :todo-only restricts attention to not done todo keywords, like /! does in standard queries. Returns a tags matcher in the standard form, although the string in the car of the matcher is (for now) fake, i.e., the query string would not generate the same (or any useful) matcher." (let ((query "!ignored!")) ; ignore making this now, as it is not really used anyway (cons query (cond ((atom matcher-sexp) (mtrans-1 matcher-sexp)) ((and (consp matcher-sexp) (listp (cdr matcher-sexp))) `(progn (setq org-cached-props nil) ,(mtrans-1 matcher-sexp))) (t (error "Badly formed matcher sexp")))))) (defun mtrans-1 (mitem) (if (atom mitem) (cond ((eq mitem :todo-only) '(member todo org-not-done-keywords)) ((stringp mitem) `(member ,mitem tags-list)) ((and (vectorp mitem) (stringp (aref mitem 0))) `(org-match-any-p ,(aref mitem 0) tags-list)) (t mitem)) (let ((head (car mitem))) (case head ((or and) `(,head ,@(mapcar 'mtrans-1 (cdr mitem)))) (not (when (cddr mitem) (error "not is a unary operator")) `(not ,(mtrans-1 (cadr mitem)))) ((< <= >= > = == <>) (let* ((arg1 (cadr mitem)) (arg2 (car (cddr mitem))) (rhs (or (mtrans-cmp-rhs-p arg1) (mtrans-cmp-rhs-p arg2)))) (cond ((and (symbolp arg1) rhs) (mtrans-cmp head arg1 rhs)) ((and (symbolp arg2) rhs) (mtrans-cmp head arg2 rhs)) (t (error "Badly formed property comparison")) (mtrans-cmp head (cadr mitem) (car (cddr mitem)))))) (re `(org-match-any-p ,(cadr mitem) tags-list)) (t mitem))))) (defun mtrans-cmp-rhs-p (item) (cond ((numberp item) `(number ,item)) ((and (stringp item) (string-match-p "[[<].*?[]>]" item)) `(time ,(org-matcher-time item))) ((stringp item) `(string ,item)) ((and (vectorp item) (stringp (aref item 0))) `(re ,(aref item 0))) ((consp item) `(form ,item)) (t nil))) (defun org-not-equal (a b) (not (equal a b))) (defvar mtrans-op-alist '((< (number . <) (string . string<) (time . org-time<) (re . nil) (form . nil)) (<= (number . <=) (string . org-string<=) (time . org-time<=) (re . nil) (form . nil)) (>= (number . >=) (string . org-string>=) (time . org-time>=) (re . nil) (form . nil)) (> (number . >) (string . org-string>) (time . org-time>) (re . nil) (form . nil)) (= (number . =) (string . string=) (time . org-time>) (re . org-string-match=) (form . equal)) (== (number . =) (string . string=) (time . org-time=) (re . org-string-match=) (form . equal)) (<> (number . org<>) (string . org-string<>) (time . org-time<>) (re . org-string-match<>) (form . org-not-equal))) "Maps comparison operators and types to suitable comparison function. A nil value means the comparison is erroneous.") (defvar mtrans-special-props-alist `((TODO todo string re form) (LEVEL level number) (HEADING heading string re form) (PRIORITY priority string re form) (CATEGORY (get-text-property (point) 'org-category) string re form)) "Maps special property names to their matcher symbol and constraints. Each value is of the form (MATCHER-SYMBOL TYPE...), where TYPE is a symbol for an allowed comparison value type.") (defun mtrans-cmp (op prop obj) (let* ((type (car obj)) (val (cadr obj)) (special (cdr (assoc prop mtrans-special-props-alist))) (prop-ref (or (car special) `(org-cached-entry-get nil ,(symbol-name prop)))) (func-alist (cdr (assq op mtrans-op-alist))) (func (cdr (assoc type func-alist)))) (when (and special (not (memq type (cdr special)))) (error "Type mismatch in %s comparison" prop)) (when (null func) (error "Improper operator for %s comparison" type)) `(,func ,(if (memq type '(number form)) prop-ref `(or ,prop-ref "")) ,val))) On Thu, Aug 16, 2012 at 1:02 AM, Samuel Wales wrote: > You have really dived into this. I think it's excellent to allow more > flexibility in searches. > > Just a brainstorm question, but having just modified the code, how > difficult do you think it would be to provide a sexp syntax? > > Despite all of your obvious hard work, I'd find sexp easier to look > up, make sense of, and remember. I favor identifiers-like-this over > single-character symbols, and (expressions (like this)) over > precedence rules. > > Maybe just me though. :) > > Samuel > > -- > The Kafka Pandemic: http://thekafkapandemic.blogspot.com > --047d7b33cab0d2912f04c765a583 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Hi Samuel,

=A0 Thanks for your note.=A0 Just FYI, the earlier parser= code in this thread has
been superseded by the code in the post "n= ew tag query parser [3/5]".

=A0=A0 The sexp input is a nice ide= a, and it would be very easy I think.
The function org-make-tags-matcher now takes a query string
but could e= asily be modified to operate on a form as well.

=A0=A0=A0 I've i= ncluded some code below that basically does the job. It defines a function =
`mtrans' that transforms a sexp representation into a matcher.=A0 It ca= n certainly be
better optimized (and fully tested), but I think it woul= d do just what you want
if inserted in org-make-tags-matcher. (Note: Th= e car of the matcher is the query string,
for reasons that aren't entirely clear. Because this is dropped anyway = in practice,
I don't bother making it accurate in this code. As such= , I'll just give the cdr in the examples below.)

=A0=A0=A0 A few= examples follow to give the idea and show the results. It seems to handle<= br> all the cases nicely. In the sexp representation, strings stand for exact s= tring matches
and=A0 both [<string>] and (re <string>) stan= d for regex matches, with symbols
for properties and standard boolean a= nd comparison ops in the form.
The keyword :todo-only acts like /! in the query strings, and =3D and <&= gt; also
allow arbitrary lisp code for the property comparison with equa= l (as long as the
form does not start with or, and, not, or re but then = it can be shielded with (identity ...)).

=A0=A0=A0 I then append the code, which is also attached. I see no prob= lems
with adding this to org-make-tags-matcher and would be interested i= n other opinions.

=A0=A0=A0 Best,

=A0=A0=A0=A0=A0 Christopher=

;;; Examples

(mtrans "foo")=A0=A0=A0=A0 ; correspond= s to query string=A0 "foo"
=A0 =3D> (member "foo" tags-list)


<= span style=3D"font-family:courier new,monospace">(mtrans ["^f"])= =A0=A0 ; or (mtrans '(re "^f"))=A0 corresponds to query strin= g "{^f}"

=A0 =3D>=A0 (progn
=A0=A0 =A0 =A0=A0 (setq org-cached-props nil)
=A0=A0 =A0=A0 =A0 (org-ma= tch-any-p "^f" tags-list))

(mtra= ns '(and "foo" (not "bar") (re "^u.*v")))= =A0 ; query string "foo-bar+{^u.*v}
=A0 =3D> (progn=
=A0=A0=A0=A0=A0=A0 (setq org-cached-props nil)
=A0=A0=A0=A0=A0=A0 (and
=A0=A0=A0=A0=A0=A0=A0 (member "foo" t= ags-list)
=A0=A0=A0=A0=A0=A0=A0 (no= t (member "bar" tags-list))
=A0=A0= =A0=A0=A0=A0=A0 (org-match-any-p "^u.*v" tags-list)))

(mtrans '(or (and= "xyz" (=3D TODO ["^T"]) ["u\\{2,4\\}"] (<= =3D LEVEL 3))
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 (> APROP "fo= o")
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 (and (=3D BPROP 4) (<&= gt; HEADING "ignore"))))

=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0 ; query string=A0 "xyz+TODO=3D{^T}+{u\\{{2,4\= \}}}+LEVEL<=3D3 | APROP > \"foo\" | BPROP=3D4&HEADING &= lt;> \"ignore\""
=A0 =3D> (progn=
=A0=A0=A0=A0=A0=A0 (setq org-cached-props nil)
=A0=A0=A0=A0=A0=A0 (or
=A0=A0=A0=A0=A0=A0=A0 (and
=A0=A0=A0=A0=A0=A0=A0=A0 = (member "xyz" tags-list)
=A0=A0=A0= =A0=A0=A0=A0=A0 (org-string-match=3D (or todo "") "^T")=
=A0=A0=A0=A0=A0=A0=A0=A0 = (org-match-any-p "u\\{2,4\\}" tags-list)
=A0=A0=A0=A0=A0=A0=A0=A0 (<=3D level 3))
=A0=A0=A0=A0=A0=A0=A0 (or= g-string> (or (org-cached-entry-get nil "APROP") "")= "foo")
=A0=A0=A0=A0=A0=A0=A0 (and
=A0=A0=A0=A0=A0=A0=A0=A0 = (=3D (org-cached-entry-get nil "BPROP") 4)
=A0=A0=A0=A0=A0=A0=A0=A0 (org-string<> (or heading ""= ;) "ignore"))))

(mtrans '(or (and "foo" (not "bar= ") ["^u.*v"] (> LEVEL 2))
=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0 (=3D APROP "foo")
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 (and (=3D BPROP ["/.*/"]) (&= lt;> BPROP "/ignore/"))
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0 (<> TODO "TODO")
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0 (> SCHEDULED "<2008-11-12>")))

=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0 ; query string "foo-bar+{^u.*v}+LEVEL>2 | APROP=3D\"foo\"= ;|=20 BPROP=3D{/.*/} & BPROP <> "/ignore/" | TODO<>\&qu= ot;TODO\" |=20 SCHEDULED > \"<2008-11-12>\""
=A0 =3D> (progn=
=A0=A0=A0=A0=A0=A0 (setq org-cached-props nil)
=A0=A0=A0=A0=A0=A0 (or
=A0=A0=A0=A0=A0=A0=A0 (and
=A0=A0=A0=A0=A0=A0=A0=A0 = (member "foo" tags-list)
=A0=A0=A0= =A0=A0=A0=A0=A0 (not (member "bar" tags-list))
=A0=A0=A0=A0=A0=A0=A0=A0 = (org-match-any-p "^u.*v" tags-list)
= =A0=A0=A0=A0=A0=A0=A0=A0 (> level 2))
=A0=A0=A0=A0=A0=A0=A0 (st= ring=3D (or (org-cached-entry-get nil "APROP") "") &quo= t;foo")
=A0=A0=A0=A0=A0=A0=A0 (and=
=A0=A0=A0=A0=A0=A0=A0=A0 = (org-string-match=3D (or (org-cached-entry-get nil "BPROP") "= ;") "/.*/")
=A0=A0=A0=A0=A0=A0= =A0=A0 (org-string<> (or (org-cached-entry-get nil "BPROP")= "") "/ignore/"))
=A0=A0=A0=A0=A0=A0=A0 (or= g-string<> (or todo "") "TODO")
=A0=A0=A0=A0=A0=A0=A0 (org-time> (or (org-cached-entry-get= nil "SCHEDULED") "") 1226466000.0)))

(mtrans '(and :todo-only
=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0 (or (and (not ["^abc"]) ["ex"] (= =3D A_PROP "foo"))
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 (>=3D B_PROP 1.2e10)=
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 (and (< D_PROP = "<2008-12-24 18:30>") (=3D FOO (call other lisp code here))= ))))

=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0 ; except for FOO part which has no analogue, query string=20 "-{^abc}+{ex}&A_PROP=3D\"foo\" | B_PROP > 1.2e10 | DR= OP <=20 \"<2008-12-24 18:30>\" & FOO =3D ..."
=A0 =3D> (progn=
=A0=A0=A0=A0=A0=A0 (setq org-cached-props nil)
=A0=A0=A0=A0=A0=A0 (and
=A0=A0=A0=A0=A0=A0=A0 (member todo org-not-done= -keywords)
=A0=A0=A0=A0=A0=A0=A0 (or=
=A0=A0=A0=A0=A0=A0=A0=A0 (and
=A0=A0=A0=A0=A0=A0=A0=A0= =A0 (not (org-match-any-p "^abc" tags-list))
=A0=A0=A0=A0=A0=A0=A0=A0=A0 (org-match-any-p "ex" tags-l= ist)
=A0=A0=A0=A0=A0=A0=A0=A0= =A0 (string=3D (or (org-cached-entry-get nil "A_PROP") "&quo= t;) "foo"))
=A0=A0=A0=A0=A0=A0=A0=A0= (>=3D (org-cached-entry-get nil "B_PROP") 12000000000.0)
=A0=A0=A0=A0=A0=A0=A0=A0 = (and
=A0=A0=A0=A0=A0=A0=A0=A0=A0 (org-time<= (or (org-cached-entry-get nil "D_PROP") "") 1230094800= .0)
=A0=A0=A0=A0=A0=A0=A0=A0= =A0 (equal (org-cached-entry-get nil "FOO") (call other lisp code= here))))))


;;; The Code

(eval-when-compile (require 'cl)= )

(defun mtrans (matcher-sexp)
=A0 "Create a tag/todo matcher from a sexp representation.
In the sexp representatio= n, components are transformed as follows:

=A0 + A literal string becomes = an exact tag match.
=A0 + A [<string>] = or (re <string>) becomes a tag regex match
=A0 + (or <item>...), (and <item>...), (not <item>)
=A0=A0=A0 act as boolean = operators, and processing continues on the <item>'s
=A0 + (<op> <lhs> <rhs>) is a property co= mparison, where op must be
=A0=A0=A0 one of=A0=A0 &l= t;, <=3D, >=3D, >, =3D, =3D=3D, or <>. One of lhs or rhs mus= t be a
=A0=A0=A0 symbol naming a property and = the other must be either a number,
=A0=A0=A0 string, [<st= ring>] or (re <string>) for regex, or a generic form.
=A0=A0=A0 (Only =3D, =3D=3D, and <> are allowed on t= he latter two.) Property
=A0=A0=A0 symbols TODO, P= RIORITY, HEADING, CATEGORY, are handled specially,
=A0=A0=A0 otherwise, the symbol name is used as the property name.

=A0 + A keyword :todo-only restricts attention to no= t done todo keywords,
=A0=A0=A0 like /! does i= n standard queries.

Returns a tags matcher in the standard form, althoug= h the string
in the car of the matcher is (for= now) fake, i.e., the query
string would not generate= the same (or any useful) matcher."
=A0 (= let ((query "!ignored!")) ; ignore making this now, as it is not = really used anyway
=A0=A0=A0 (cons query

=A0=A0=A0=A0=A0=A0=A0=A0=A0 (cond
=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0 ((atom matcher-sexp) (mtrans-1 matcher-sexp))
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 ((and (consp matcher-sexp) (listp (cd= r matcher-sexp)))
=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0 `(progn
=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0 (setq org-cached-props nil)

=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0 ,(mtrans-1 matcher-sexp)))
= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 (t (error "Badly formed matcher sexp&qu= ot;))))))

(defun mtrans-1 (mitem)
=A0 (if (atom mitem)
=A0=A0=A0=A0=A0 (cond
=A0=A0=A0=A0=A0=A0 ((eq mitem :todo-only)<= br style=3D"font-family:courier new,monospace"> =A0=A0=A0=A0=A0=A0=A0 = 9;(member todo org-not-done-keywords))
=A0=A0= =A0=A0=A0=A0 ((stringp mitem)
=A0=A0=A0=A0=A0=A0=A0 `(m= ember ,mitem tags-list))
=A0=A0=A0=A0=A0=A0 ((= and (vectorp mitem) (stringp (aref mitem 0)))
=A0=A0=A0=A0=A0=A0=A0 `(o= rg-match-any-p ,(aref mitem 0) tags-list))
=A0= =A0=A0=A0=A0=A0 (t mitem))
=A0=A0=A0 (let ((head (ca= r mitem)))
=A0=A0=A0=A0=A0 (case head =A0=A0=A0=A0=A0=A0 ((or a= nd)
=A0=A0=A0=A0=A0=A0=A0 `(,head ,@(mapcar &#= 39;mtrans-1 (cdr mitem))))
=A0=A0=A0=A0=A0=A0 (not
=A0=A0=A0=A0=A0=A0=A0 (when (cddr mitem) (error= "not is a unary operator"))
=A0=A0=A0=A0=A0=A0=A0 `(n= ot ,(mtrans-1 (cadr mitem))))
=A0=A0=A0=A0=A0= =A0 ((< <=3D >=3D > =3D =3D=3D <>)
=A0=A0=A0=A0=A0=A0=A0 (le= t* ((arg1 (cadr mitem))
=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0 (arg2 (car (cddr mitem)))
=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0 (rhs=A0 (or (mtrans-cmp-rhs-p arg1)
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0 (mtrans-cmp-rhs-p arg2))))
=A0=A0=A0=A0=A0=A0=A0=A0= =A0 (cond
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 ((and= (symbolp arg1) rhs)
=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0 (mtrans-cmp head arg1 rhs))
=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0 ((and (symbolp arg2) rhs)
=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0 (mtrans-cmp head arg2 rhs))
=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0 (t (error "Badly formed property comparison&q= uot;))
=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0 (mtrans-cmp head (cadr mitem) (car (cddr mitem))))))
=A0=A0=A0=A0=A0=A0 (re
=A0=A0=A0=A0=A0=A0=A0 `(o= rg-match-any-p ,(cadr mitem) tags-list))
=A0= =A0=A0=A0=A0=A0 (t mitem)))))

(defun mtrans-cmp-rhs-p (item)
=A0 (cond
=A0=A0 ((numberp item)
=A0=A0=A0 `(number ,item))
=A0=A0 ((and (stringp ite= m) (string-match-p "[[<].*?[]>]" item))
=A0=A0=A0 `(time ,(org-matcher-time item)))
=A0=A0 ((stringp item)
=A0=A0=A0 `(string ,item))
=A0=A0 ((and (vectorp ite= m) (stringp (aref item 0)))
=A0=A0=A0 `(re ,(a= ref item 0)))
=A0=A0 ((consp item)
=A0=A0=A0 `(form ,item))
=A0=A0 (t nil)))
(defun= org-not-equal (a b) (not (equal a b)))

(defvar mtrans-op-alist
=A0 '((<=A0 (number . <)=A0 (string . string<)=A0=A0=A0=A0=A0= =A0=A0=A0=A0 (time . org-time<)=A0 (re . nil)=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0 (form . nil))
=A0=A0=A0 (<=3D (numbe= r . <=3D) (string . org-string<=3D)=A0=A0=A0=A0 (time . org-time<= =3D) (re . nil)=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 (form . nil))<= /span>
=A0=A0=A0 (>=3D (numbe= r . >=3D) (string . org-string>=3D)=A0=A0=A0=A0 (time . org-time>= =3D) (re . nil)=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 (form . nil))<= /span>
=A0=A0=A0 (>=A0 (numbe= r . >)=A0 (string . org-string>)=A0=A0=A0=A0=A0 (time . org-time>)= =A0 (re . nil)=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 (form . nil))
=A0=A0=A0 (=3D=A0 (number= . =3D)=A0 (string . string=3D)=A0=A0=A0=A0=A0=A0=A0=A0=A0 (time . org-time= >)=A0 (re . org-string-match=3D)=A0 (form . equal))
=A0=A0=A0 (=3D=3D (number . =3D)=A0 (string . string=3D)=A0=A0=A0= =A0=A0=A0=A0=A0=A0 (time . org-time=3D)=A0 (re . org-string-match=3D)=A0 (f= orm . equal))
=A0=A0=A0 (<> (numb= er .=20 org<>)=A0 (string . org-string<>) (time . org-time<>)=20 (re . org-string-match<>) (form . org-not-equal)))
=A0 "Maps comparison= operators and types to suitable comparison function.
A nil value means the comparison is erroneous.")

(defvar mtrans-special-props-alist
=A0 `((TODO todo string re form)
=A0=A0=A0 (LEVEL level nu= mber)
=A0=A0=A0 (HEADING heading string re for= m)
=A0=A0=A0 (PRIORITY prior= ity string re form)
<= span style=3D"font-family:courier new,monospace">=A0=A0=A0 (CATEGORY (get-t= ext-property (point) 'org-category) string re form))

=A0 "Maps special pr= operty names to their matcher symbol and constraints.
Each value is of the form (MATCHER-SYMBOL TYPE...), where TYPE is
a symbol for an allowed c= omparison value type.")

(defun mtrans-cmp (op prop obj)
=A0 (let* ((type (car obj= ))
=A0=A0=A0=A0=A0=A0=A0=A0 (val=A0 (cadr obj)= )
=A0=A0=A0=A0=A0=A0=A0=A0 = (special (cdr (assoc prop mtrans-special-props-alist)))
=A0=A0=A0=A0=A0=A0=A0=A0 (prop-ref (or (car special)
=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 `(org-cached-entry-get nil ,(sym= bol-name prop))))
=A0=A0=A0=A0=A0=A0=A0=A0 (fu= nc-alist (cdr (assq op mtrans-op-alist)))

=A0=A0=A0=A0=A0=A0=A0=A0 = (func (cdr (assoc type func-alist))))
=A0=A0= =A0 (when (and special (not (memq type (cdr special))))
=A0=A0=A0=A0=A0 (error &q= uot;Type mismatch in %s comparison" prop))
=A0=A0=A0=A0=A0 (error &q= uot;Improper operator for %s comparison" type))
=A0=A0=A0 `(,func ,(if (memq type '(number form)) prop-ref `(or = ,prop-ref "")) ,val)))


On Thu, Aug 16, 2012 at 1:02 AM, Samuel = Wales <samologist@gmail.com> wrote:
You have really dived into this. =A0I think it's excellent to allow mor= e
flexibility in searches.

Just a brainstorm question, but having just modified the code, how
difficult do you think it would be to provide a sexp syntax?

Despite all of your obvious hard work, I'd find sexp easier to look
up, make sense of, and remember. =A0I favor identifiers-like-this over
single-character symbols, and (expressions (like this)) over
precedence rules.

Maybe just me though. =A0:)

Samuel

--
The Kafka Pandemic: http://thekafkapandemic.blogspot.com

--047d7b33cab0d2912f04c765a583-- --047d7b33cab0d2913404c765a585 Content-Type: application/octet-stream; name="tag-sexp-matchers.el" Content-Disposition: attachment; filename="tag-sexp-matchers.el" Content-Transfer-Encoding: base64 X-Attachment-Id: f_h5y5814u0 KGV2YWwtd2hlbi1jb21waWxlIChyZXF1aXJlICdjbCkpCgooZGVmdW4gbXRyYW5zIChtYXRjaGVy LXNleHApCiAgIkNyZWF0ZSBhIHRhZy90b2RvIG1hdGNoZXIgZnJvbSBhIHNleHAgcmVwcmVzZW50 YXRpb24uCkluIHRoZSBzZXhwIHJlcHJlc2VudGF0aW9uLCBjb21wb25lbnRzIGFyZSB0cmFuc2Zv cm1lZCBhcyBmb2xsb3dzOgoKICArIEEgbGl0ZXJhbCBzdHJpbmcgYmVjb21lcyBhbiBleGFjdCB0 YWcgbWF0Y2guCiAgKyBBIFs8c3RyaW5nPl0gb3IgKHJlIDxzdHJpbmc+KSBiZWNvbWVzIGEgdGFn IHJlZ2V4IG1hdGNoCiAgKyAob3IgPGl0ZW0+Li4uKSwgKGFuZCA8aXRlbT4uLi4pLCAobm90IDxp dGVtPikKICAgIGFjdCBhcyBib29sZWFuIG9wZXJhdG9ycywgYW5kIHByb2Nlc3NpbmcgY29udGlu dWVzIG9uIHRoZSA8aXRlbT4ncwogICsgKDxvcD4gPGxocz4gPHJocz4pIGlzIGEgcHJvcGVydHkg Y29tcGFyaXNvbiwgd2hlcmUgb3AgbXVzdCBiZQogICAgb25lIG9mICAgPCwgPD0sID49LCA+LCA9 LCA9PSwgb3IgPD4uIE9uZSBvZiBsaHMgb3IgcmhzIG11c3QgYmUgYQogICAgc3ltYm9sIG5hbWlu ZyBhIHByb3BlcnR5IGFuZCB0aGUgb3RoZXIgbXVzdCBiZSBlaXRoZXIgYSBudW1iZXIsCiAgICBz dHJpbmcsIFs8c3RyaW5nPl0gb3IgKHJlIDxzdHJpbmc+KSBmb3IgcmVnZXgsIG9yIGEgZ2VuZXJp YyBmb3JtLgogICAgKE9ubHkgPSwgPT0sIGFuZCA8PiBhcmUgYWxsb3dlZCBvbiB0aGUgbGF0dGVy IHR3by4pIFByb3BlcnR5CiAgICBzeW1ib2xzIFRPRE8sIFBSSU9SSVRZLCBIRUFESU5HLCBDQVRF R09SWSwgYXJlIGhhbmRsZWQgc3BlY2lhbGx5LCAKICAgIG90aGVyd2lzZSwgdGhlIHN5bWJvbCBu YW1lIGlzIHVzZWQgYXMgdGhlIHByb3BlcnR5IG5hbWUuCgogICsgQSBrZXl3b3JkIDp0b2RvLW9u bHkgcmVzdHJpY3RzIGF0dGVudGlvbiB0byBub3QgZG9uZSB0b2RvIGtleXdvcmRzLAogICAgbGlr ZSAvISBkb2VzIGluIHN0YW5kYXJkIHF1ZXJpZXMuCgpSZXR1cm5zIGEgdGFncyBtYXRjaGVyIGlu IHRoZSBzdGFuZGFyZCBmb3JtLCBhbHRob3VnaCB0aGUgc3RyaW5nCmluIHRoZSBjYXIgb2YgdGhl IG1hdGNoZXIgaXMgKGZvciBub3cpIGZha2UsIGkuZS4sIHRoZSBxdWVyeQpzdHJpbmcgd291bGQg bm90IGdlbmVyYXRlIHRoZSBzYW1lIChvciBhbnkgdXNlZnVsKSBtYXRjaGVyLiIKICAobGV0ICgo cXVlcnkgIiFpZ25vcmVkISIpKSA7IGlnbm9yZSBtYWtpbmcgdGhpcyBub3csIGFzIGl0IGlzIG5v dCByZWFsbHkgdXNlZCBhbnl3YXkKICAgIChjb25zIHF1ZXJ5CiAgICAgICAgICAoY29uZAogICAg ICAgICAgICgoYXRvbSBtYXRjaGVyLXNleHApIChtdHJhbnMtMSBtYXRjaGVyLXNleHApKQogICAg ICAgICAgICgoYW5kIChjb25zcCBtYXRjaGVyLXNleHApIChsaXN0cCAoY2RyIG1hdGNoZXItc2V4 cCkpKQogICAgICAgICAgICBgKHByb2duCiAgICAgICAgICAgICAgIChzZXRxIG9yZy1jYWNoZWQt cHJvcHMgbmlsKQogICAgICAgICAgICAgICAsKG10cmFucy0xIG1hdGNoZXItc2V4cCkpKQogICAg ICAgICAgICh0IChlcnJvciAiQmFkbHkgZm9ybWVkIG1hdGNoZXIgc2V4cCIpKSkpKSkKCihkZWZ1 biBtdHJhbnMtMSAobWl0ZW0pCiAgKGlmIChhdG9tIG1pdGVtKQogICAgICAoY29uZAogICAgICAg KChlcSBtaXRlbSA6dG9kby1vbmx5KQogICAgICAgICcobWVtYmVyIHRvZG8gb3JnLW5vdC1kb25l LWtleXdvcmRzKSkKICAgICAgICgoc3RyaW5ncCBtaXRlbSkKICAgICAgICBgKG1lbWJlciAsbWl0 ZW0gdGFncy1saXN0KSkKICAgICAgICgoYW5kICh2ZWN0b3JwIG1pdGVtKSAoc3RyaW5ncCAoYXJl ZiBtaXRlbSAwKSkpCiAgICAgICAgYChvcmctbWF0Y2gtYW55LXAgLChhcmVmIG1pdGVtIDApIHRh Z3MtbGlzdCkpCiAgICAgICAodCBtaXRlbSkpCiAgICAobGV0ICgoaGVhZCAoY2FyIG1pdGVtKSkp CiAgICAgIChjYXNlIGhlYWQKICAgICAgICgob3IgYW5kKQogICAgICAgIGAoLGhlYWQgLEAobWFw Y2FyICdtdHJhbnMtMSAoY2RyIG1pdGVtKSkpKQogICAgICAgKG5vdAogICAgICAgICh3aGVuIChj ZGRyIG1pdGVtKSAoZXJyb3IgIm5vdCBpcyBhIHVuYXJ5IG9wZXJhdG9yIikpCiAgICAgICAgYChu b3QgLChtdHJhbnMtMSAoY2FkciBtaXRlbSkpKSkKICAgICAgICgoPCA8PSA+PSA+ID0gPT0gPD4p CiAgICAgICAgKGxldCogKChhcmcxIChjYWRyIG1pdGVtKSkKICAgICAgICAgICAgICAgKGFyZzIg KGNhciAoY2RkciBtaXRlbSkpKQogICAgICAgICAgICAgICAocmhzICAob3IgKG10cmFucy1jbXAt cmhzLXAgYXJnMSkKICAgICAgICAgICAgICAgICAgICAgICAgIChtdHJhbnMtY21wLXJocy1wIGFy ZzIpKSkpCiAgICAgICAgICAoY29uZAogICAgICAgICAgICgoYW5kIChzeW1ib2xwIGFyZzEpIHJo cykKICAgICAgICAgICAgKG10cmFucy1jbXAgaGVhZCBhcmcxIHJocykpCiAgICAgICAgICAgKChh bmQgKHN5bWJvbHAgYXJnMikgcmhzKQogICAgICAgICAgICAobXRyYW5zLWNtcCBoZWFkIGFyZzIg cmhzKSkKICAgICAgICAgICAodCAoZXJyb3IgIkJhZGx5IGZvcm1lZCBwcm9wZXJ0eSBjb21wYXJp c29uIikpCiAgICAgICAgICAgKG10cmFucy1jbXAgaGVhZCAoY2FkciBtaXRlbSkgKGNhciAoY2Rk ciBtaXRlbSkpKSkpKQogICAgICAgKHJlCiAgICAgICAgYChvcmctbWF0Y2gtYW55LXAgLChjYWRy IG1pdGVtKSB0YWdzLWxpc3QpKQogICAgICAgKHQgbWl0ZW0pKSkpKQoKKGRlZnVuIG10cmFucy1j bXAtcmhzLXAgKGl0ZW0pCiAgKGNvbmQKICAgKChudW1iZXJwIGl0ZW0pCiAgICBgKG51bWJlciAs aXRlbSkpCiAgICgoYW5kIChzdHJpbmdwIGl0ZW0pIChzdHJpbmctbWF0Y2gtcCAiW1s8XS4qP1td Pl0iIGl0ZW0pKQogICAgYCh0aW1lICwob3JnLW1hdGNoZXItdGltZSBpdGVtKSkpCiAgICgoc3Ry aW5ncCBpdGVtKQogICAgYChzdHJpbmcgLGl0ZW0pKQogICAoKGFuZCAodmVjdG9ycCBpdGVtKSAo c3RyaW5ncCAoYXJlZiBpdGVtIDApKSkKICAgIGAocmUgLChhcmVmIGl0ZW0gMCkpKQogICAoKGNv bnNwIGl0ZW0pCiAgICBgKGZvcm0gLGl0ZW0pKQogICAodCBuaWwpKSkKCihkZWZ1biBvcmctbm90 LWVxdWFsIChhIGIpIChub3QgKGVxdWFsIGEgYikpKQoKKGRlZnZhciBtdHJhbnMtb3AtYWxpc3QK ICAnKCg8ICAobnVtYmVyIC4gPCkgIChzdHJpbmcgLiBzdHJpbmc8KSAgICAgICAgICAodGltZSAu IG9yZy10aW1lPCkgIChyZSAuIG5pbCkgICAgICAgICAgICAgICAgKGZvcm0gLiBuaWwpKQogICAg KDw9IChudW1iZXIgLiA8PSkgKHN0cmluZyAuIG9yZy1zdHJpbmc8PSkgICAgICh0aW1lIC4gb3Jn LXRpbWU8PSkgKHJlIC4gbmlsKSAgICAgICAgICAgICAgICAoZm9ybSAuIG5pbCkpCiAgICAoPj0g KG51bWJlciAuID49KSAoc3RyaW5nIC4gb3JnLXN0cmluZz49KSAgICAgKHRpbWUgLiBvcmctdGlt ZT49KSAocmUgLiBuaWwpICAgICAgICAgICAgICAgIChmb3JtIC4gbmlsKSkKICAgICg+ICAobnVt YmVyIC4gPikgIChzdHJpbmcgLiBvcmctc3RyaW5nPikgICAgICAodGltZSAuIG9yZy10aW1lPikg IChyZSAuIG5pbCkgICAgICAgICAgICAgICAgKGZvcm0gLiBuaWwpKQogICAgKD0gIChudW1iZXIg LiA9KSAgKHN0cmluZyAuIHN0cmluZz0pICAgICAgICAgICh0aW1lIC4gb3JnLXRpbWU+KSAgKHJl IC4gb3JnLXN0cmluZy1tYXRjaD0pICAoZm9ybSAuIGVxdWFsKSkKICAgICg9PSAobnVtYmVyIC4g PSkgIChzdHJpbmcgLiBzdHJpbmc9KSAgICAgICAgICAodGltZSAuIG9yZy10aW1lPSkgIChyZSAu IG9yZy1zdHJpbmctbWF0Y2g9KSAgKGZvcm0gLiBlcXVhbCkpCiAgICAoPD4gKG51bWJlciAuIG9y Zzw+KSAgKHN0cmluZyAuIG9yZy1zdHJpbmc8PikgKHRpbWUgLiBvcmctdGltZTw+KSAocmUgLiBv cmctc3RyaW5nLW1hdGNoPD4pIChmb3JtIC4gb3JnLW5vdC1lcXVhbCkpKQogICJNYXBzIGNvbXBh cmlzb24gb3BlcmF0b3JzIGFuZCB0eXBlcyB0byBzdWl0YWJsZSBjb21wYXJpc29uIGZ1bmN0aW9u LgpBIG5pbCB2YWx1ZSBtZWFucyB0aGUgY29tcGFyaXNvbiBpcyBlcnJvbmVvdXMuIikKCihkZWZ2 YXIgbXRyYW5zLXNwZWNpYWwtcHJvcHMtYWxpc3QKICBgKChUT0RPIHRvZG8gc3RyaW5nIHJlIGZv cm0pCiAgICAoTEVWRUwgbGV2ZWwgbnVtYmVyKQogICAgKEhFQURJTkcgaGVhZGluZyBzdHJpbmcg cmUgZm9ybSkKICAgIChQUklPUklUWSBwcmlvcml0eSBzdHJpbmcgcmUgZm9ybSkKICAgIChDQVRF R09SWSAoZ2V0LXRleHQtcHJvcGVydHkgKHBvaW50KSAnb3JnLWNhdGVnb3J5KSBzdHJpbmcgcmUg Zm9ybSkpCiAgIk1hcHMgc3BlY2lhbCBwcm9wZXJ0eSBuYW1lcyB0byB0aGVpciBtYXRjaGVyIHN5 bWJvbCBhbmQgY29uc3RyYWludHMuCkVhY2ggdmFsdWUgaXMgb2YgdGhlIGZvcm0gKE1BVENIRVIt U1lNQk9MIFRZUEUuLi4pLCB3aGVyZSBUWVBFIGlzCmEgc3ltYm9sIGZvciBhbiBhbGxvd2VkIGNv bXBhcmlzb24gdmFsdWUgdHlwZS4iKQoKKGRlZnVuIG10cmFucy1jbXAgKG9wIHByb3Agb2JqKQog IChsZXQqICgodHlwZSAoY2FyIG9iaikpCiAgICAgICAgICh2YWwgIChjYWRyIG9iaikpCiAgICAg ICAgIChzcGVjaWFsIChjZHIgKGFzc29jIHByb3AgbXRyYW5zLXNwZWNpYWwtcHJvcHMtYWxpc3Qp KSkKICAgICAgICAgKHByb3AtcmVmIChvciAoY2FyIHNwZWNpYWwpCiAgICAgICAgICAgICAgICAg ICAgICAgYChvcmctY2FjaGVkLWVudHJ5LWdldCBuaWwgLChzeW1ib2wtbmFtZSBwcm9wKSkpKQog ICAgICAgICAoZnVuYy1hbGlzdCAoY2RyIChhc3NxIG9wIG10cmFucy1vcC1hbGlzdCkpKQogICAg ICAgICAoZnVuYyAoY2RyIChhc3NvYyB0eXBlIGZ1bmMtYWxpc3QpKSkpCiAgICAod2hlbiAoYW5k IHNwZWNpYWwgKG5vdCAobWVtcSB0eXBlIChjZHIgc3BlY2lhbCkpKSkKICAgICAgKGVycm9yICJU eXBlIG1pc21hdGNoIGluICVzIGNvbXBhcmlzb24iIHByb3ApKQogICAgKHdoZW4gKG51bGwgZnVu YykKICAgICAgKGVycm9yICJJbXByb3BlciBvcGVyYXRvciBmb3IgJXMgY29tcGFyaXNvbiIgdHlw ZSkpCiAgICBgKCxmdW5jICwoaWYgKG1lbXEgdHlwZSAnKG51bWJlciBmb3JtKSkgcHJvcC1yZWYg YChvciAscHJvcC1yZWYgIiIpKSAsdmFsKSkpCgo= --047d7b33cab0d2913404c765a585--