2008/07/26

[Common Lisp] 文字列リテラルを作る

こんなふうに書きたくて(CL-INTERPOLのまねごと)

CL-USER> (let ((lang "Common Lisp"))
#"""母国語は "#,lang," です。""")
"母国語は \"Common Lisp\" です。"
CL-USER> (cl-ppcre:scan-to-strings #"""a\d+a""" "axaa123a")
"a123a"
#()
CL-USER> #"(+ 1 2) => #,(+ 1 2)"
"(+ 1 2) => 3"
CL-USER> (read-from-string "#\"(+ 1 2) => #,(+ 1 2)\"")
(FORMAT NIL "(+ 1 2) => ~a" (+ 1 2))
23

こんなふうに作ってみた。

(defun |#"-reader| (stream sub-char numarg)
(declare (ignore sub-char numarg))
(|#"-parser|
(if (equal #\" (peek-char nil stream t nil t))
(progn
(read-char stream)
(if (equal #\" (peek-char nil stream nil nil t))
(|#"""-reader"""| stream)
""))
(|#"-reader"| stream))))

(defun |#"-reader"| (stream)
(funcall (get-macro-character #\") stream #\"))

(defun |#"""-reader"""| (stream)
#1=(read-char stream t nil t)
(with-output-to-string (*standard-output*)
(loop for c1 = #1# then c2
for c2 = #1# then c3
for c3 = #1# then #1#
until (char= #\" c1 c2 c3)
do (write-char c1))))

(defun |#"-parser| (s)
(macrolet ((peek-equal (c)
`(equal ,c (peek-char nil in nil nil))))
(let* ((args nil)
(format
(with-output-to-string (out)
(with-input-from-string (in s)
(loop for c = #1=(read-char in nil nil)
while c
if (and (equal #\# c) (peek-equal #\,))
do (progn
#1#
(write-string "~a" out)
(push (read-preserving-whitespace in) args)
(when (peek-equal #\,)
#1#))
else
do (write-char c out))))))
`(format nil ,format ,@(reveres args)))))

(set-dispatch-macro-character
#\# #\" '|#"-reader|)

またあとで |#"-parser| をもっといじる。

(char= #\a nil) はエラーになるのね。(equal #\, #\c1 #\c2 #\c3) と書けるけど、(equal #\, #\c1 #\c2 #\c3) とは書けないのね。

""" は Python から。

0 件のコメント: