Seriesでリーダーマクロ — #:g1

Posted 2010-11-07 14:06:00 GMT

リーダーマクロを書くのにSeriesを使うのも抵抗があるなあということで、実験。
Gauche風に、fooという正規表現にマッチする関数が、#/foo/と書けるようにしてみます。
CL-PPCRE:SCANに展開されて、

(#/f\\\/oo/ "f\\/oo")
;=> 0
;   5
;   #()
;   #()
上記のように動作すれば良しとします。
ということで、
(defun |#/-READER| (stream char arg)
  (declare (ignore char arg))
  (let ((g (gensym))
        (re (ppcre:regex-replace-all
             "\\\\/"
             (collect 'string
                      (choose
                       (let ((prev nil))
                         (until-if (lambda (c)
                                     (cond ((and (eql #\/ c)
                                                 (not (eql #\\ prev)))
                                            'T)
                                           (:else (setq prev c)
                                                  nil)))
                                   (scan-stream stream #'read-char)))))
             "/")))
    `(lambda (,g)
       (ppcre:scan ,re ,g))))

(set-dispatch-macro-character #\# #\/ #'|#/-READER|)

と書いてみましたが、微妙な感じに。
特に、"\\\\/"を"/"に置換しているところが悲しいですね。
それはさておき、リーダーマクロの展開ですが、
(#/f\\\/oo/ "f\\/oo")
;⇒
((LAMBDA (#:G3528) (CL-PPCRE:SCAN "f\\\\/oo" #:G3528)) "f\\/oo")
という風に展開されます。
書いていて、バックスラッシュの解釈をどうすれば良いんだったか分からなくなってきたので、テストを書いてみましたが、余計分からなくなってきました。
(defpackage :g000001-test
  (:use :cl :lisp-unit))

(in-package :g000001-test)

(do-symbols (s :g000001) (shadowing-import s))

(remove-all-tests :g000001-test)

(define-test |#/-READER| (assert-equal "(LAMBDA (#:G0) (CL-PPCRE:SCAN \"Foo\" #:G0))" (let ((*readtable* (copy-readtable nil)) (*gensym-counter* 0)) (set-dispatch-macro-character #\# #\/ #'|#/-READER|) (write-to-string (read-from-string "#/Foo/")))) (assert-equal "(LAMBDA (#:G0) (CL-PPCRE:SCAN \"F/oo\" #:G0))" (let ((*readtable* (copy-readtable nil)) (*gensym-counter* 0)) (set-dispatch-macro-character #\# #\/ #'|#/-READER|) (write-to-string (read-from-string "#/F\\/oo/"))) ) (assert-equal "(LAMBDA (#:G0) (CL-PPCRE:SCAN \"F\\\\\\\\/oo\" #:G0))" (let ((*readtable* (copy-readtable nil)) (*gensym-counter* 0)) (set-dispatch-macro-character #\# #\/ #'|#/-READER|) (write-to-string (read-from-string "#/F\\\\\\/oo/")))))

(run-all-tests :g000001-test) ;-> #/-READER: 3 assertions passed, 0 failed.


comments powered by Disqus