From: Mattias EngdegÄrd Date: Wed, 15 May 2019 20:44:00 +0000 (+0200) Subject: Allow zero-argument rx `or' and `seq' forms X-Git-Tag: emacs-27.0.90~2805^2~22 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=afdc20d73c8588e5a744ecf7bffaf4401a557d20;p=emacs.git Allow zero-argument rx `or' and `seq' forms Make the rx `or' and `seq' forms accept zero arguments to produce a never-matching regexp and an empty string, respectively. * lisp/emacs-lisp/rx.el: Require cl-extra. (rx-constituents, rx-or): Permit zero args. (rx): Amend doc string for `or' and `seq'. * test/lisp/emacs-lisp/rx-tests.el (rx-or, rx-seq): Test the change. * etc/NEWS (Changes in Specialized Modes and Packages): Mention the change. --- diff --git a/etc/NEWS b/etc/NEWS index 9ca98c370e6..72702a9aaac 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1321,6 +1321,12 @@ when given in a string. Previously, '(any "\x80-\xff")' would match characters U+0080...U+00FF. Now the expression matches raw bytes in the 128...255 range, as expected. +*** The rx 'or' and 'seq' forms no longer require any arguments. +(or) produces a regexp that never matches anything, while (seq) +matches the empty string, each being an identity for the operation. +This also works for their aliases: '|' for 'or'; ':', 'and' and +'sequence' for 'seq'. + ** Frames +++ diff --git a/lisp/emacs-lisp/rx.el b/lisp/emacs-lisp/rx.el index 9d9028d87d5..ed32490ceee 100644 --- a/lisp/emacs-lisp/rx.el +++ b/lisp/emacs-lisp/rx.el @@ -106,15 +106,16 @@ ;;; Code: (require 'cl-lib) +(require 'cl-extra) ;; FIXME: support macros. (defvar rx-constituents ;Not `const' because some modes extend it. - '((and . (rx-and 1 nil)) + '((and . (rx-and 0 nil)) (seq . and) ; SRE (: . and) ; SRE (sequence . and) ; sregex - (or . (rx-or 1 nil)) + (or . (rx-or 0 nil)) (| . or) ; SRE (not-newline . ".") (nonl . not-newline) ; SRE @@ -390,9 +391,11 @@ FORM is of the form `(and FORM1 ...)'." "Parse and produce code from FORM, which is `(or FORM1 ...)'." (rx-check form) (rx-group-if - (if (memq nil (mapcar 'stringp (cdr form))) - (mapconcat (lambda (x) (rx-form x '|)) (cdr form) "\\|") + (cond + ((null (cdr form)) regexp-unmatchable) + ((cl-every #'stringp (cdr form)) (regexp-opt (cdr form) nil t)) + (t (mapconcat (lambda (x) (rx-form x '|)) (cdr form) "\\|"))) (and (memq rx-parent '(: * t)) rx-parent))) @@ -1121,6 +1124,7 @@ CHAR `(seq SEXP1 SEXP2 ...)' `(sequence SEXP1 SEXP2 ...)' matches what SEXP1 matches, followed by what SEXP2 matches, etc. + Without arguments, matches the empty string. `(submatch SEXP1 SEXP2 ...)' `(group SEXP1 SEXP2 ...)' @@ -1136,7 +1140,7 @@ CHAR `(| SEXP1 SEXP2 ...)' matches anything that matches SEXP1 or SEXP2, etc. If all args are strings, use `regexp-opt' to optimize the resulting - regular expression. + regular expression. Without arguments, never matches anything. `(minimal-match SEXP)' produce a non-greedy regexp for SEXP. Normally, regexps matching diff --git a/test/lisp/emacs-lisp/rx-tests.el b/test/lisp/emacs-lisp/rx-tests.el index 4a5919edf02..6f392d616d1 100644 --- a/test/lisp/emacs-lisp/rx-tests.el +++ b/test/lisp/emacs-lisp/rx-tests.el @@ -107,7 +107,13 @@ "ab")) (should (equal (and (string-match (rx (or "a" "ab" "abc")) s) (match-string 0 s)) - "a")))) + "a"))) + ;; Test zero-argument `or'. + (should (equal (rx (or)) regexp-unmatchable))) + +(ert-deftest rx-seq () + ;; Test zero-argument `seq'. + (should (equal (rx (seq)) ""))) (provide 'rx-tests) ;; rx-tests.el ends here.