`(newline *html-pretty-printer*)

`(write-char #Newline *html-output*)))

(defmethod op->code ((op (eql :freshline)) &rest operands)

(if *pretty*

`(freshline *html-pretty-printer*)

(error 'Bad op when not pretty-printing: ~a' op)))

(defmethod op->code ((op (eql :indent)) &rest operands)

(if *pretty*

`(indent *html-pretty-printer*)

(error 'Bad op when not pretty-printing: ~a' op)))

(defmethod op->code ((op (eql :unindent)) &rest operands)

(if *pretty*

`(unindent *html-pretty-printer*)

(error 'Bad op when not pretty-printing: ~a' op)))

(defmethod op->code ((op (eql :toggle-indenting)) &rest operands)

(if *pretty*

`(toggle-indenting *html-pretty-printer*)

(error 'Bad op when not pretty-printing: ~a' op)))

The two most interesting op->code methods are the ones that generate code for the :embed-value and :embed-code ops. In the :embed-value method, you can generate slightly different code depending on the value of the escapes operand since if escapes is NIL, you don't need to generate a call to escape. And when both *pretty* and escapes are NIL, you can generate code that uses PRINC to emit the value directly to the stream.

(defmethod op->code ((op (eql :embed-value)) &rest operands)

(destructuring-bind (value escapes) operands

(if *pretty*

(if escapes

`(raw-string *html-pretty-printer* (escape (princ-to-string ,value) ,escapes) t)

`(raw-string *html-pretty-printer* (princ-to-string ,value) t))

(if escapes

`(write-sequence (escape (princ-to-string ,value) ,escapes) *html-output*)

`(princ ,value *html-output*)))))

Thus, something like this:

HTML> (let ((x 10)) (html (:p x)))

<p>10</p>

NIL

works because html translates (:p x) into something like this:

(progn

(write-sequence '<p>' *html-output*)

(write-sequence (escape (princ-to-string x) '<>&') *html-output*)

(write-sequence '</p>' *html-output*))

When that code replaces the call to html in the context of the LET, you get the following:

(let ((x 10))

(progn

(write-sequence '<p>' *html-output*)

(write-sequence (escape (princ-to-string x) '<>&') *html-output*)

(write-sequence '</p>' *html-output*)))

and the reference to x in the generated code turns into a reference to the lexical variable from the LET surrounding the html form.

The :embed-code method, on the other hand, is interesting because it's so trivial. Because process passed the form to embed-code, which stashed it in the :embed-code op, all you have to do is pull it out and return it.

(defmethod op->code ((op (eql :embed-code)) &rest operands)

(first operands))

This allows code like this to work:

HTML> (html (:ul (dolist (x '(foo bar baz)) (html (:li x)))))

<ul>

<li>FOO</li>

<li>BAR</li>

<li>BAZ</li>

</ul>

NIL

The outer call to html expands into code that does something like this:

(progn

(write-sequence '<ul>' *html-output*)

(dolist (x '(foo bar baz)) (html (:li x)))

(write-sequence '</ul>' *html-output*))))

Then if you expand the call to html in the body of the DOLIST, you'll get something like this:

(progn

(write-sequence '<ul>' *html-output*)

(dolist (x '(foo bar baz))

(progn

(write-sequence '<li>' *html-output*)

(write-sequence (escape (princ-to-string x) '<>&') *html-output*)

(write-sequence '</li>' *html-output*)))

(write-sequence '</ul>' *html-output*))

This code will, in fact, generate the output you saw.

FOO Special Operators

You could stop there; certainly the FOO language is expressive enough to generate nearly any HTML you'd care to. However, you can add two features to the language, with just a bit more code, that will make it quite a bit more powerful: special operators and macros.

Special operators in FOO are analogous to special operators in Common Lisp. Special operators provide ways to express things in the language that can't be expressed in the language supported by the basic evaluation rule. Or, another way to look at it is that special operators provide access to the primitive mechanisms used by the language evaluator.[318]

To take a simple example, in the FOO compiler, the language evaluator uses the embed-value function to generate code that will embed the value of a variable in the output HTML. However, because only

Вы читаете Practical Common Lisp
Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату