16.2 HTML 实用函数 (HTML Utilities)

  1. (defmacro as (tag content)
  2. `(format t "<~(~A~)>~A</~(~A~)>"
  3. ',tag ,content ',tag))
  4. (defmacro with (tag &rest body)
  5. `(progn
  6. (format t "~&<~(~A~)>~%" ',tag)
  7. ,@body
  8. (format t "~&</~(~A~)>~%" ',tag)))
  9. (defmacro brs (&optional (n 1))
  10. (fresh-line)
  11. (dotimes (i n)
  12. (princ "<br>"))
  13. (terpri))

图 16.3 标签生成例程

本节会定义一些生成 HTML 的例程。 图 16.3 包含了三个基本的、生成标签的例程。 所有例程都将它们的输出发送到 *standard-output* ;可以通过重新绑定这个变量,将输出重定向到一个文件。

aswith 都用于在标签之间生成表达式。其中 as 接受一个字符串,并将它打印在两个标签之间:

  1. > (as center "The Missing Lambda")
  2. <center>The Missing Lambda</center>
  3. NIL

with 则接受一个代码体(body of code),并将它放置在两个标签之间:

  1. > (with center
  2. (princ "The Unbalanced Parenthesis"))
  3. <center>
  4. The Unbalanced Parenthesis
  5. </center>
  6. NIL

两个宏都使用了 ~(...~) 来进行格式化,从而将标签转化为小写字母的标签。 HTML 并不介意标签是大写还是小写,但是在包含许许多多标签的 HTML 文件中,小写字母的标签可读性更好一些。

除此之外, as 倾向于将所有输出都放在同一行,而 with 则将标签和内容都放在不同的行里。 (使用 ~& 来进行格式化,以确保输出从一个新行中开始。) 以上这些工作都只是为了让 HTML 更具可读性,实际上,标签之外的空白并不影响页面的显示方式。

图 16.3 中的最后一个例程 brs 用于创建多个文本行。 在很多浏览器中,这个例程都可以用于控制垂直间距。

  1. (defun html-file (base)
  2. (format nil "~(~A~).html" base))
  3. (defmacro page (name title &rest body)
  4. (let ((ti (gensym)))
  5. `(with-open-file (*standard-output*
  6. (html-file ,name)
  7. :direction :output
  8. :if-exists :supersede)
  9. (let ((,ti ,title))
  10. (as title ,ti)
  11. (with center
  12. (as h2 (string-upcase ,ti)))
  13. (brs 3)
  14. ,@body))))

图 16.4 HTML 文件生成例程

图 16.4 包含用于生成 HTML 文件的例程。 第一个函数根据给定的符号(symbol)返回一个文件名。 在一个实际应用中,这个函数可能会返回指向某个特定文件夹的路径(path)。 目前来说,这个函数只是简单地将 .html 后缀追加到给定符号名的后边。

page 负责生成整个页面,它的实现和 with-open-file 很相似: body 中的表达式会被求值,求值的结果通过 *standard-output* 所绑定的流,最终被写入到相应的 HTML 文件中。

6.7 小节展示了如何临时性地绑定一个特殊变量。 在 113 页的例子中,我们在 let 的体内将 *print-base* 绑定为 16 。 这一次,通过将 *standard-output* 和一个指向 HTML 文件的流绑定,只要我们在 page 的函数体内调用 as 或者 princ ,输出就会被传送到 HTML 文件里。

page 宏的输出先在顶部打印 title ,接着求值 body 中的表达式,打印 body 部分的输出。

如果我们调用

  1. (page 'paren "The Unbalanced Parenthesis"
  2. (princ "Something in his expression told her..."))

这会产生一个名为 paren.html 的文件(文件名由 html-file 函数生成),文件中的内容为:

  1. <title>The Unbalanced Parenthesis</title>
  2. <center>
  3. <h2>THE UNBALANCED PARENTHESIS</h2>
  4. </center>
  5. <br><br><br>
  6. Something in his expression told her...

除了 title 标签以外,以上输出的所有 HTML 标签在前面已经见到过了。 被 <title> 标签包围的文本并不显示在网页之内,它们会显示在浏览器窗口,用作页面的标题。

  1. (defmacro with-link (dest &rest body)
  2. `(progn
  3. (format t "<a href=\"~A\">" (html-file ,dest))
  4. ,@body
  5. (princ "</a>")))
  6. (defun link-item (dest text)
  7. (princ "<li>")
  8. (with-link dest
  9. (princ text)))
  10. (defun button (dest text)
  11. (princ "[ ")
  12. (with-link dest
  13. (princ text))
  14. (format t " ]~%"))

图 16.5 生成链接的例程

图片 16.5 给出了用于生成链接的例程。 with-linkwith 很相似:它根据给定的地址 dest ,创建一个指向 HTML 文件的链接。 而链接内部的文本,则通过求值 body 参数中的代码段得出:

  1. > (with-link 'capture
  2. (princ "The Captured Variable"))
  3. <a href="capture.html">The Captured Variable</a>
  4. "</a>"

with-link 也被用在 link-item 当中,这个函数接受一个字符串,并创建一个带链接的列表项:

  1. > (link-item 'bq "Backquote!")
  2. <li><a href="bq.html">Backquote!</a>
  3. "</a>"

最后, button 也使用了 with-link ,从而创建一个被方括号包围的链接:

  1. > (button 'help "Help")
  2. [ <a href="help.html">Help</a> ]
  3. NIL