`(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.
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