[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 |
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)))))
- symbol-macrolet clobbers (match-data), bug or feature?,
Howard Yeh! <=