こんにちは、chibaです。
今回もネタがないのでEmacsネタです。
Dylanのマクロについての文献:
http://people.csail.mit.edu/jrb/Projects/dexprs.pdf
を読んでいて、XMLや、LISPは、Skeleton Syntax Tree Representationsに分類されるというのを読んで、そうか、HTMLも簡単にマクロ書けたりするのかもと思ったのでEmacs Lispでちょっと試してみました。
(defun split-id-or-class (string)
(let ((elts (split-string string "[\\.#]")))
(if (cdr elts)
elts
string)))
(defmacro defhtmltag (tag)
(let* ((tag-str (symbol-name tag))
(tags (split-string tag-str "[\\.#]"))
(tag-name (car tags))
(attr (cadr tags)))
`(defmacro ,tag (&rest body)
`(list ,(concat
"<"
,tag-name
(apply #'concat
(cond ((find ?. ,tag-str)
(list " class=\"" ,attr "\""))
((find ?# ,tag-str)
(list " id=\"" ,attr "\""))
('T (list ""))))
">")
,@body
,(concat "</" ,tag-name ">")))))
(defun flatten (lis)
(cond ((atom lis) lis)
((listp (car lis))
(append (flatten (car lis)) (flatten (cdr lis))))
(t (append (list (car lis)) (flatten (cdr lis))))))
(defmacro with-html-output-to-string (&rest body)
`(reduce (lambda (x y) (format "%s%s" x y))
(flatten (list ,@body))))
使い方としては、
(defhtmltag li.foo)
のようにすると、
(list "<li>" "こんにちは" "</li>")
のようなものにマクロが展開されるので、リストの入れ子を平坦にして一つの文字列に繋げれば完成です。
ちょっと込み入ったところだと
(defhtmltag table)
(defhtmltag tbody)
(defhtmltag td)
(defhtmltag tr)
(defun list-to-table (list)
(table
(tbody
(mapcar (lambda (x)
(tr
(mapcar (lambda (y) (td y))
x)))
list))))
(insert
(with-html-output-to-string
(list-to-table '((foo bar baz quux)
(1 2 3 4)
(z z z z)))))
;=> <li class="foo"><li class="foo"><li class="foo"><li class="foo">foo</li><li class="foo">bar</li><li class="foo">baz</li><li class="foo">quux</li></li><li class="foo"><li class="foo">1</li><li class="foo">2</li><li class="foo">3</li><li class="foo">4</li></li><li class="foo"><li class="foo">z</li><li class="foo">z</li><li class="foo">z</li><li class="foo">z</li></li></li></li>
ということもできます。
ちなみに、マクロ展開でHTML書くって新しいかも!と思いましたが、別にマクロにしなくても関数でも書けることに今気付きました…
とりあえず、もし、HTMLにマクロがあったとしたらLISPと似た感じになるのかなあという印象は持ちました。
■