10  Extending TeX with Scheme or Common Lisp

The command \eval allows you to use arbitrary Scheme or Common Lisp expressions, as opposed to just TeX macros, to guide the course of the typesetter. In the following we will describe just the Scheme version of TeX2page: read Common Lisp for Scheme if using the Common Lisp version of TeX2page.

The text written to standard output by the Scheme 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{ 

(display (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 Scheme, 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 Scheme 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, Scheme can be used to fill the breach. More generally, Scheme 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 Scheme, except that the pipe character (‘|’) functions as the TeX escape. Use || to represent a single pipe in the Scheme code. If you need to include an unmatched brace, simply put a bogus matching brace inside a Scheme 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 Scheme workarounds (integer‑>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 Scheme 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 Scheme:

\ifx\shipout\UnDeFiNeD 
\htmlonly 
 
\eval{ 

(define all-blanks?
  (lambda (s)
    (let loop ((L (string->list s)))
      (if (null? L) #t
          (let ((c (car L)))
            (if (char-whitespace? c) (loop (cdr L))
                #f))))))

} 
 
\def\spaceifnotempty{\eval{ 

(let ((x (ungroup (get-token))))
  (if (not (all-blanks? x))
      (begin (display (integer->char 92))
             (display "space"))
      0))

}} 
 
\endhtmlonly 
\fi 

Note that we had to use (integer‑>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 (display "\\space").)

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

If being processed by TeX2page only (as in our example), the code inside \eval is allowed to use not just general Scheme but also procedures like ungroup and get‑token, which are defined by TeX2page.

eval without regard to HTML

The key thing to remember is that an \eval-call is replaced by whatever text the Scheme 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.eval4tex in Scheme, 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 Scheme to create the aux files which it can use on its second run, whereas TeX2page, being written in Scheme, creates and loads the aux files immediately.

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