unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Howard Yeh! <hayeah@gmail.com>
To: bug-gnu-emacs@gnu.org
Subject: symbol-macrolet clobbers (match-data), bug or feature?
Date: Tue, 14 Aug 2007 20:43:11 -0000	[thread overview]
Message-ID: <1187124191.406368.291750@x40g2000prg.googlegroups.com> (raw)

Hi,

I am writing a macro to make match-data more convenient. I can use it
to implement the =~ operator.

(defmacro* with-match-data ((&key in-string) &rest body)
  "setup local symbol macros for $0 ... $n  for nth submatch.
   symbol macros <1 ... <n  for (match-beginning n in-string).
   etc.
   Should work properly for both string-match and buffer-match.
  "
  ;; code and bug below
  ... )

(defmacro* =~ (regex string &rest body)
  (let ((str (gensym)))
    `(let ((,str ,string))
       (when (string-match ,regex ,str)
	      (with-match-data (:in-string ,str)
		,@body)))))


(=~ "\\(abc\\)\\(efg\\)?" "abcefg"
       (list (match-data) <0 >0 $0 $1 $2))

((0 6 0 3 3 6) 0 6 "abcefg" "abc" "efg")


;;;;;;; bug || feature ;;;;;;;;;;

It seems that the expansion of `with-match-data' is correct (it works
if I evaluate the "macroexpand-all"ed expression). But `symbol-
macrolet' changes the match-data when the expression is interpreted.

(macroexpand-all
 '(=~ "\\(abc\\)\\(efg\\)?" "abcefg"
	(list (match-data) <0 >0 $0 $1 $2)))
=>
(let
    ((G34784 "abcefg"))
  (if
   (string-match "\\(abc\\)\\(efg\\)?" G34784)
   (progn
     (let
	 ((G34785 G34784))
       (progn
	 (list
	  (match-data)
	  (match-beginning 0)
	  (match-end 0)
	  (match-string 0 G34785)
	  (match-string 1 G34785)
	  (match-string 2 G34785)))))))

which is fine

(eval (macroexpand-all
  '(=~ "\\(abc\\)\\(efg\\)?" "abcefg"
    (list (match-data) <0 >0 $0 $1 $2))))

=> ((0 6 0 3 3 6) 0 6 "abcefg" "abc" "efg")

but evaluating the expression without first expanding it is not fine

(r=~ "\\(abc\\)\\(efg\\)?" "abcefg"
       (list (match-data) <0 >0 $0 $1 $2))

=> ((0 1) 0 1 "a" nil nil)

For a workaround, I save the match-data before entering symbol-
macrolet, and (set-match-data) before executing the body.


;;;;;; code ;;;;;;;;;

(defmacro* with-match-data ((&key in-string) &rest body)
  (let ((str (gensym))
	(md (gensym)))
    `(let ((,str ,in-string)
	   (,md (match-data)))  ;; workaround for the bug
       ;;(my-debug "before" (match-data))  ;; here match-data is from
the previous match.
       (symbol-macrolet ,(loop for i to 9 append
			      (let ((match (intern (concat "$" (number-to-string i))))
				    (beg (intern (concat "<" (number-to-string i))))
				    (end (intern (concat ">" (number-to-string i)))))
				(list `(,match (match-string ,i ,str))
				      `(,beg (match-beginning ,i))
				      `(,end (match-end ,i)))))
	 ;;(my-debug "after" (match-data))   ;; here match-data is something
else.
	 (macrolet (($ (i)
		      `(match-string ,i ,',str))
		    (sub (replacement i &key fixedcase literal-string)
		      `(replace-match ,replacement ,fixedcase ,literal-
string ,',str ,i)))
	   (symbol-macrolet (;; no convenient way to support before/after
match for buffer search
			     ;;before
			     ;;($b (substring ,str 0 (match-beginning 0)))
			     ;;after
			     ;;($a (substring ,str (match-end 0) (length ,str)))
			     ;;match
			     ($m (match-string 0 ,str))
			     (foo ,str)
			     )
	     (set-match-data ,md) ;; workaround to set match-data back to the
original data.
	     ,@body))))))

(defmacro* =~ (regex string &rest body)
  (let ((str (gensym)))
    `(let ((,str ,string))
       (when (string-match ,regex ,str)
	      (with-match-data (:in-string ,str)
		,@body)))))

             reply	other threads:[~2007-08-14 20:43 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-08-14 20:43 Howard Yeh! [this message]
2007-08-16  1:08 ` symbol-macrolet clobbers (match-data), bug or feature? Richard Stallman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1187124191.406368.291750@x40g2000prg.googlegroups.com \
    --to=hayeah@gmail.com \
    --cc=bug-gnu-emacs@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).