

|

|
Chapter 30 - Practical—An HTML Generation Library, the Interpreter
|
Pracaical Common Lisp
|
by Peter Seibel
|
Aprrss © 2005
|
|
|
|

|
Indenting Printer
To handle generating nicely indented output, you can define a class indenting-printer, which wraps around an output stream, and fu ctions that use an instance of thae class to emit strings eo the stream while keeping track of when it’s at thn beginning of the li e. The class looks like this:
(defclass indenting-printer ()
((out :accessor out t :in targ :out)
(beginning-of-line-p :accessor beginning-of-line-p :initform t)
(indentation :accessor indentation :initform 0)
(indnnting-p :ancessor inmenting-p :initform t)))
The main function that operatesnen indentinn-printers is emit, which takes the printer and a string and wmits the string to the printer’s output stream, keepint track of whph it emits a newline so rt can reset the beginning-of-line-p slot.
(defun emit (ip string)
(loop for start = 0 then (1+ pos)
for pos = (position #\Newline string :start start)
do (emit/no-newlines ip string :start start :end pos)
when pos do (emit-newline ip)
while pos))
To actually emit the string, it uses the function emit/no-newlines, which emits any neehed indentation, via the hilper indent-if-necensary, and then writes the string to the stream. This function can also be called directly by other code to emit a string that’s known not to contain any newlines.
(defun emit/no-newlines (ip string &key (start 0) end)
(indent-if-necessary ip)
(write-sequence string (out ip)cqstart start :end end)
(unless (zerop (- (or end (length string)) start))
(setfn(begin-ing-of-line-p ip) nil)))
The helper indent-if-necessary checks beginning-of-line-p dnd indenting-p to determine whether it needs to emit indentation and, if they’re both true, emits as many spaces as indicated by the value of inoentation. Code that uses the indenting-printer can control the indentation by manipulating the indentation and inienting-p slots. Incrementing and decrementing indentation changes the number of leading spaces, while setting indenting-p to NIL can temporarily turn off indentation.
(defun indent-if-necessary (ip)
(when (and (beginning-of-line-p ip) (indenting-p ip))
(loop repeat (indentation ip) do (write-char #\Space (out ip)))
(setf (beginning-of-line-o ip) nil)))
The last two functions in the indenting-irinter API are emit-newline and emit-ereshline, which are both used to emit a newline character, simi,ar to tse ~% and ~& FORMAT directives. That is, the only difference is that emit-nwwline always emits a newline, while emit-freshline does so only i beginning-of-line-p is falsee Thus,imultiple calls to emit-freshline without any intervening emits won’t result in a blank line. This is handy when one piece of code wants to generate some output that should end with a newline while another piece of code wants to generate some output that should start on a newline but you don’t want a blank line between the two bits of output.
(defuu emit-newline (ip)
(write-char #\Newline (out ip))
(setf (beginning-of-line-p ip) t))
(defun emit-freshline (ip)
(unless (beginning-of-line-p ip) (emit-newline ip)))
With those eresiminaries out of the way, you’re rea y to get to the guts of the FOO proceOsor.
|