10  Extending TeX​ ​with Lisp

The command \eval allows you to use arbitrary Lisp expressions, as opposed to just TeX​ ​macros, to guide the course of the typesetter.

The text written to standard output by the Lisp code is substituted for the \eval statement. E.g., consider the following complete document, root2.tex:

\input tex2page

The square root of 2 is
\eval{
(princ (sqrt 2))

}.

\bye
Running TeX2page on root2.tex produces the following HTML output:

The square root of 2 is 1.4142135623730951.

In effect, TeX2page processes the \eval call using Lisp, producing some output in an auxiliary TeX​ ​file, which is then re-inserted into the document at the location of the \eval.

A definition for \eval that TeX​ ​can use is provided in the macro file eval4tex.tex. tex2page.tex will automatically load eval4tex.tex if it finds it in TEXINPUTS. Thus, running TeX​ ​on root2.tex produces a DVI file whose content matches the HTML version.

It is clear that Lisp code via \eval can serve as a very powerful second extension language for TeX, and that its benefits are available to both the DVI and the HTML outputs. As we have seen, TeX2page implements a subset of the TeX​ ​macro language, and for those cases where this macro language isn’t enough, Lisp can be used to fill the breach. More generally, Lisp may be preferable to the TeX​ ​macro language even for just DVI, where no HTML version of the document is contemplated. We’ll explore both of these aspects of \eval.

\eval’s argument is a balanced-brace expression. At the top-level, i.e., not within the body of a macro, \eval’s argument is sent verbatim to Lisp, except that the pipe character (‘|’) functions as the TeX​ ​escape. Use || to represent a single pipe in the Lisp code. If you need to include an unmatched brace, simply put a bogus matching brace inside a Lisp comment.

Inside a macro body, it is too late for \eval to set the catcodes to make verbatim any character within its argument. Either define or use control sequences to represent special characters, or use Lisp workarounds (e.g., code‑char) to construct those characters.

Let us first look at a simple example where \eval lets you define an HTML version of an already existing TeX​ ​macro that is either impossible or at least prohibitively difficult to process using TeX2page’s mimicry of TeX. Consider a hypothetical \proto macro, used to introduce the description of a Lisp operator by presenting a prototypical use of it. Typical calls to \proto are:

\proto{cons}{a d}{procedure}
\proto{car}{c}{procedure}
\proto{cdr}{c}{procedure}
which typeset as follows:

(cons​ ​a d) ;procedure

(car​ ​c) ;procedure

(cdr​ ​c) ;procedure

The macro \proto takes three arguments: the operator name; the metavariables for its operands; and the operator kind. In particular, it typesets the operator and the operands in different fonts, surrounding the call in parens. Note the intervening space between operator and operands.

In the case where there are no operands, the intervening space should not. Thus,

\proto{gentemp}{}{procedure}
should not produce

(gentemp​ ​​ ​) ;procedure

but rather

(gentemp) ;procedure

(I.e., no space between gentemp and the closing paren.)

The \proto macro can be written in TeX​ ​as follows:

\def\proto#1#2#3{\noindent
  \hbox{{\tt(#1}\spaceifnotempty{#2}{\it#2}{\tt)}%
    \qquad ;#3}\par}
where \spaceifnotempty is a helper macro that expands to a space only if its argument is not empty. TeX2page can expand this definition for \proto, provided it knows how to deal with the \spaceifnotempty.

One way to write \spaceifnotempty in TeX is:

\newdimen\templen
\newbox\tempbox

\def\spaceifnotempty#1{%
  \setbox\tempbox\hbox{#1}%
  \templen\wd\tempbox
  \ifdim\templen>0pt{\ }\fi}
This piece of box-measuring contortion is too much for TeX2page’s mimicry of the TeX​ ​macro system. However, it’s easy enough to achieve the same effect using the string-processing capabilities of Lisp:

\ifx\shipout\UnDeFiNeD
\eval{
(defun all-blanks-p (s)
  (every (lambda (c) (or (char= c #\space) (char= c #\tab)
                          (not (graphic-char-p c))))
        s))

}

\def\spaceifnotempty{\eval{
(let ((x (ungroup (get-token))))
  (unless (all-blanks-p x)
    (princ (code-char 92))
    (princ "space")))

}}
\fi
Note that we had to use (code‑char 92) to refer to the backslash character, as the \eval is inside a macro body and ‘\’ is not and cannot be made a letter. (Otherwise we could have simply written (princ "\\space").)

Later \evals can use definitions introduced in previous \evals, as with all‑blanks‑p in our example.

If being processed by TeX2page only (as in our example), the code inside \eval is allowed to use not just general Lisp but also procedures like ungroup and get‑token, which are defined by TeX2page. (There is no need to package-qualify their names, as \eval code is evaluated inside the tex2page package.)

eval without regard to HTML

The key thing to remember is that an \eval-call is replaced by whatever text the Lisp code in that \eval-call writes to its standard output. This approach will work whether the document is being processed by TeX2page to produce HTML or by TeX​ ​to produce DVI.

For those TeX​ ​documents that are not intended for HTML conversion, but nevertheless use \eval, this macro is available in the macro file eval4tex.tex. Run TeX​ ​(or LaTeX) on such a document, say jobname.tex, and then evaluate the resultant jobname‑Z‑E.lisp in Lisp, to create the necessary aux TeX​ ​files. Running TeX​ ​on the master document a second time will insert these aux TeX​ ​files at the location of the corresponding \eval calls. This is quite analogous to how TeX2page would have processed the \evals, except that TeX​ ​requires you to explicitly call Lisp to create the aux files which it can use on its second run, whereas TeX2page, being written in Lisp, creates and loads the aux files immediately.

For complete details on using \eval with TeX, please consult the companion manual, An \eval for TeX [44].