リスト修行(3) SchemeでHTMLを書こう

HTMLって閉じタグ書くのが面倒ダヨネ。Schemeで書けば閉じタグ要らないじゃん!!と思ったので、今回の修行はリストをHTMLに変換してみたいと思います。

仕様

  1. リストの先頭はタグで始まり、子要素が続く。タグはシンボルで書く。
  2. ひとつめの子要素が2重のリストの場合はオプションとなる。オプション内の仕様については考え中。

というシンプルな仕様です。

ちなみに、PLT Web Server Manualのまねっこです。

ではではコーディング。

ゴニョゴニョ

(require (lib "1.ss" "srfi"))
(require (lib "13.ss" "srfi"))
; Gauche用
; (use srfi-1)
; (use srfi-13)

(define s-tag `(html
                (head
                 (title "ボクノス"))
                (body
                 (h1 "ボクノス")
                 (div (("class" "entry") ("id" "foo"))
                  (h2 "今日のひとこと")
                  (p "ちょっと大変だった。")))))

(define (make-tag t)
  (cond ((null? t) "")
        ((not (pair? t)) t)
        ((null? (cdr t)) (string-append "<" (symbol->string (car t)) " />"))
        ((pair? (car t)) "(オプションが入る予定。未実装)")
        (else
          (let ((head (symbol->string (car t)))
                (body (cdr t)))
            (string-append "<" head ">"
                           (string-join (map make-tag body) "\n")
                           "</" head ">")))))

(make-tag s-tag)

タグの閉じ方を思いつくまでに時間がかかった。

実行結果は・・・。

<html><head><title>ボクノス</title></head>
<body><h1>ボクノス</h1>
<div>(オプションが入る予定。未実装)
<h2>今日のひとこと</h2>
<p>ちょっと大変だった。</p></div></body></html>

おぉ。改行がイマイチだけど、ちゃんとできた!!

閉じタグが無い分、断然スッキリ書けそうですねぇ。

オプション部分を攻める

オプションも作りました。

こんなリストがあって、

(div (("class" "entry") ("id" "foo")) (p "ほげ"))

divをタグとして取り出して、オプションの取り出しやすいリストを作ります。

((("class" "entry") ("id" "foo")) (p "ほげ"))

次にオプションを文字列に切り出したリストを作ります。

(" class=\"entry\" id=\"foo\"" (p "ほげ"))

car,cdrすれば、オプションと中身が取り出せると。


方針が決まったらコーディング。

(define (make-tag t)
  (cond ((null? t) "")
        ((not (pair? t)) t)
        ((null? (cdr t)) (string-append "<" (symbol->string (car t)) " />"))
        (else
          (let* ((head (symbol->string (car t)))
                 (opt  (option (cdr t)))
                 (body (cdr opt)))
            (string-append "<" head
                           (car opt)
                           ">"
                           (string-join (map make-tag body) "\n")
                           "</" head ">")))))

; body -> (cons "options" body)
(define (option t)
  (if (and (pair? (car t)) (pair? (caar t)))
    (cons (fold-right
            (lambda (a b)
              (string-append " " (car a) "=\"" (cadr a) "\"" b))
            "" (car t))
          (cdr t))
    (cons "" t)))

テストぉ〜。

<html><head><title>ボクノス</title></head>
<body><h1>ボクノス</h1>
<div class="entry" id="foo"><h2>今日のひとこと</h2>
<p>ちょっと大変だった。</p></div>
<div class="entry" id="bar"><h2>今日のふたことめ</h2>
<p>転んでも泣かない。</p></div></body></html>

おぉぉぉぉ。


かなり実用的になってきました。


さて次はテンプレートを。と思う所ですが、もう既に完成しているという噂が。

,を書けばテンプレートシステム完成です!!


Scheme凄いね・・・自由過ぎる・・・。

追記

バグを修正した。もうちょっとスッキリ書けると思う。