emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Pattern matching on match-string groups #elisp #question


From: Stefan Monnier
Subject: Re: Pattern matching on match-string groups #elisp #question
Date: Thu, 25 Feb 2021 10:32:44 -0500
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux)

> The closest equivalent pcase magic I can think of is:
>
>   (pcase "lorem-foo-1-ipsum-bar-21_baz-42"
>     ((rx (let group-1 "foo-" (+ num)) (* nonl)
>          (let group-2 "bar-" (+ num)) (* nonl)
>          (let group-3 "baz-" (+ num)))
>      (format "%s-%s-%s" group-1 group-2 group-3)))
>   ;; => "foo-1-bar-21-baz-42"
>
> The same won't work with pcase-let, so it's either unsupported or a bug.

I'd say it's a bug.  The patch below would fix it.  Mattias, WDYT?

FWIW, I have a similar pcase pattern using "old style regexps" instead of
rx, but I haven't bothered to install it:

    (pcase-defmacro re-match (re)
      "Matches a string if that string matches RE.
    RE should be a regular expression (a string).
    It can use the special syntax \\(?VAR: to bind a sub-match
    to variable VAR.  All other subgroups are treated as shy.
    
    Multiple uses of this macro in a single `pcase' are not optimized
    together, so don't expect lex-like performance.  But in order for
    such optimization to be possible in some distant future, back-references
    are not supported."
      (let ((start 0)
            (last 0)
            (new-re '())
            (vars '())
            (gn 0))
        (while (string-match "\\\\(\\(?:\\?\\([-[:alnum:]]*\\):\\)?" re start)
          (setq start (match-end 0))
          (let ((beg (match-beginning 0))
                (name (match-string 1 re)))
            ;; Skip false positives, either backslash-escaped or within [...].
            (when (subregexp-context-p re start last)
              (cond
               ((null name)
                (push (concat (substring re last beg) "\\(?:") new-re))
               ((string-match "\\`[0-9]" name)
                (error "Variable can't start with a digit: %S" name))
               (t
                (let* ((var (intern name))
                       (id (cdr (assq var vars))))
                  (unless id
                    (setq gn (1+ gn))
                    (setq id gn)
                    (push (cons var gn) vars))
                  (push (concat (substring re last beg) (format "\\(?%d:" id))
                        new-re))))
              (setq last start))))
        (push (substring re last) new-re)
        (setq new-re (mapconcat #'identity (nreverse new-re) ""))
        `(and (pred stringp)
              (app (lambda (s)
                     (when (string-match ,new-re s)
                       (vector ,@(mapcar (lambda (x) `(match-string ,(cdr x) s))
                                         vars))))
                   (,'\` [,@(mapcar (lambda (x) (list '\, (car x))) vars)])))))

[ Not sure why I decided to use a vector internally.  ]


        Stefan


diff --git a/lisp/emacs-lisp/rx.el b/lisp/emacs-lisp/rx.el
index 58584f300c..619bc32752 100644
--- a/lisp/emacs-lisp/rx.el
+++ b/lisp/emacs-lisp/rx.el
@@ -1437,7 +1437,8 @@ rx
                    construct."
   (let* ((rx--pcase-vars nil)
          (regexp (rx--to-expr (rx--pcase-transform (cons 'seq regexps)))))
-    `(and (pred (string-match ,regexp))
+    `(and (pred stringp)
+          (app (lambda (s) (string-match ,regexp s)) (pred identity))
           ,@(let ((i 0))
               (mapcar (lambda (name)
                         (setq i (1+ i))




reply via email to

[Prev in Thread] Current Thread [Next in Thread]