[Top][All Lists]

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

symbol-macrolet clobbers (match-data), bug or feature?

From: Howard Yeh!
Subject: symbol-macrolet clobbers (match-data), bug or feature?
Date: Tue, 14 Aug 2007 20:43:11 -0000
User-agent: G2/1.0


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).
   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)

(=~ "\\(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.

 '(=~ "\\(abc\\)\\(efg\\)?" "abcefg"
        (list (match-data) <0 >0 $0 $1 $2)))
    ((G34784 "abcefg"))
   (string-match "\\(abc\\)\\(efg\\)?" G34784)
         ((G34785 G34784))
          (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 
                                    (end (intern (concat ">" (number-to-string 
                                (list `(,match (match-string ,i ,str))
                                      `(,beg (match-beginning ,i))
                                      `(,end (match-end ,i)))))
         ;;(my-debug "after" (match-data))   ;; here match-data is something
         (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
                             ;;($b (substring ,str 0 (match-beginning 0)))
                             ;;($a (substring ,str (match-end 0) (length ,str)))
                             ($m (match-string 0 ,str))
                             (foo ,str)
             (set-match-data ,md) ;; workaround to set match-data back to the
original data.

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

reply via email to

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