]> git.eshelyaron.com Git - emacs.git/commitdiff
Allow zero-argument rx `or' and `seq' forms
authorMattias Engdegård <mattiase@acm.org>
Wed, 15 May 2019 20:44:00 +0000 (22:44 +0200)
committerMattias Engdegård <mattiase@acm.org>
Mon, 20 May 2019 09:39:46 +0000 (11:39 +0200)
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 (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.

etc/NEWS
lisp/emacs-lisp/rx.el
test/lisp/emacs-lisp/rx-tests.el

index 9ca98c370e6b847785671b21f20f642c5d459b73..72702a9aaac76ed69a15456c114f3b2b54f91cc6 100644 (file)
--- 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
 
 +++
index 9d9028d87d5b769114ec35c06536c91df5eb051c..9478bd3bbdb5004c4af2978901595f6b6880ac11 100644 (file)
 ;; 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 +390,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 +1123,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 +1139,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
index 4a5919edf029e473973a830b94c2bd57b51226aa..6f392d616d1629c543b8a804e44f87c934a928f3 100644 (file)
                    "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.