Tag Archives: lisp

The less-familiar parts of Lisp for beginners — pprint-tab

Continuing through the commands related to pretty-printing, next is pprint-tab.  This function is used to move the effective cursor to a certain tab position, as if by the ~T argument to format.  As the use of format is not recommended inside a pprint-logical-block when *print-pretty* is non-nil, this function allows the effect of tabbing without the use of format.  Note that this function has no effect if *print-pretty* is nil.

The pprint-tab function takes a “kind” argument that determines how the tabs are calculated.

If kind is :line, the tabbing is set based on the start of the line, as if by a simple ~T argument to format.

If kind is :section, the tabbing is set based on the start of the enclosing section.

If kind is :line-relative, the tabbing is set based on the start of the line, but using relative tabulation, as defined by ~@T in format.

If kind is :section-relative, the tabbing is set based on the start of the enclosing section, again using relative tabulation as above.

The less-familiar parts of Lisp for beginners — pprint-pop

Next, we come to pprint-pop.  Like most of the pretty-printing functions we’ve described recently, this is intended to be used inside a block defined by pprint-logical-block.  In fact, this local macro cannot be used outside of one of those blocks, and may not even be bound.

The pprint-pop local macro is used to remove the next object from the list being printed, and return it to the caller.  It is important to use pprint-pop rather than a bare pop, because of the behaviour which we previously mentioned, that a pprint-logical-block may be executed in its entirety more than once, in order to do circularity detection.  Calling the pop macro would lead to unpredictable behaviour, as the list that the pprint-logical-block is processing would be modified between the two passes.

The less-familiar parts of Lisp for beginners — pprint-newline

Continuing our discussion of pretty-printing, we arrive at pprint-newline.  This is another function that is used inside the environment set up within the context of pprint-logical-block.  The pprint-newline function takes a ‘kind’ argument that can be one of four possible keyword values.

If kind is :linear, a newline will be printed only if the entire output of the section cannot fit on a single line.  In that case, all such linear-mode newlines will be output.  In the typical usage, this means you either print all objects on one line, or each object on its own line.  Compare this to the pprint-linear function.

If the kind is :miser, then this function inserts a newline exactly like :linear, but only when in “miser mode”.  Miser mode takes effect whenever the start of the printing block is closer to the right margin than the value of the variable *print-miser-width*.  If miser mode is not in effect, this function never inserts a newline.

If the kind is :fill, then this function inserts a newline only when the current line cannot hold the next object to be printed.  The effect is to put as many objects on each line as possible before starting a new line.  Compare this to the pprint-fill function.

If the kind is :mandatory, the function always inserts a newline, unconditionally.

You should use pprint-newline in pprint-logical-block blocks, and avoid the use of format or terpri functions, as the latter do not integrate with the formatting behaviour of pretty-printing.  For instance, the pprint-indent function, which only takes effect after the next newline, is not guaranteed to do anything if you use format or terpri instead of pprint-newline.

The less-familiar parts of Lisp for beginners — pprint-logical-block

Our exploration of pretty-printing has now reached the point where we’ll show real code instead of just describing some functions.  We’ve arrived at pprint-logical-block, which is the starting point for constructing pretty-printing functions.

The pprint-logical-block macro sets up a pretty-printing context inside of which the programmer writes his or her pretty-printing directives.  The required arguments are the stream and the object to be printed.  If the object to be printed is not a list, this macro simply sends the object to write, but if it is a list, then objects in the list can be retrieved with pprint-pop, and the logical block is exited by calling pprint-exit-if-list-exhausted.

One important restriction is that pprint-logical-block must not modify the values of variables not bound within the block itself.  The reason for this is that, during circularity detection, the entire block may be executed more than once, causing these side-effects to be invoked an unpredictable number of times.

So, now we will define a special pretty-printing function.  This function will act on lists whose first element is the keyword :SPECIAL.  In that case, this first entry in the list is skipped, and the following entries are printed out, one to a line, with an index number and the type, enclosed in brace brackets instead of parentheses, and with an SP: prefix to denote this behaviour:
pprint-logical-block.lisp

(defun my-special-pprint (stream obj)
  (pprint-logical-block (stream obj 
                                :prefix "SP: { "
                                :suffix "}")
    (let ((ctr 0))
      (pprint-pop)
      (pprint-newline :mandatory stream)
      (do ()
          (nil)
        (pprint-exit-if-list-exhausted)
        (let ((entry (pprint-pop)))
          (write entry :stream stream)
          (pprint-tab :section-relative 6 6 stream)
          (format stream
                  "<--- Entry #~D.  ~A" 
                  ctr (type-of entry)))
        (incf ctr)
        (pprint-newline :mandatory stream)
        ))))

Next is the function that we use to demonstrate the new printing.  Notice that I set the *print-pretty* special variable in an enclosing let form to allow automatic restoration of its value after the various setf calls have finished modifying it.  We discussed this, and special variables in general, as an aside in the article on print.  Here is the function:
pprint-logical-block.lisp

(defun demonstrate ()
  (set-pprint-dispatch '(cons (member :SPECIAL))
                       'my-special-pprint 10.0)

  (let ((list-1 (list 1.0d0 "2" :THREE '(:SPECIAL 4.0d0) 5.0d0))
        (list-2 (list :SPECIAL 1.0d0 "2" :THREE '(:SPECIAL 4.0d0) 5.0d0))
        (*print-pretty* nil))

    (setf *print-pretty* nil)
    (format t "Non-pretty-printing a list:~%")
    (print list-1)
    (setf *print-pretty* t)
    (terpri)
    (terpri)
    (terpri)
    (format t "Pretty-printing the same list:~%")
    (print list-1)
    (terpri)
    (terpri)
    (terpri)
    (setf *print-pretty* nil)
    (format t "Non-pretty-printing another list:~%")
    (print list-2)
    (terpri)
    (terpri)
    (terpri)
    (setf *print-pretty* t)
    (format t "Pretty-printing the second list:~%")
    (print list-2)
    (values)))

Finally, here is the output. You’ll notice that this function is recursively dispatched when appropriate.
*slime-repl sbcl*

CL-USER> (demonstrate)
Non-pretty-printing a list:

(1.0d0 "2" :THREE (:SPECIAL 4.0d0) 5.0d0) 


Pretty-printing the same list:

(1.0d0 "2" :THREE
 SP: {
       4.0d0       <--- Entry #0.  DOUBLE-FLOAT
       }
 5.0d0) 


Non-pretty-printing another list:

(:SPECIAL 1.0d0 "2" :THREE (:SPECIAL 4.0d0) 5.0d0) 


Pretty-printing the second list:

SP: {
      1.0d0       <--- Entry #0.  DOUBLE-FLOAT
      "2"         <--- Entry #1.  (SIMPLE-ARRAY CHARACTER (1))
      :THREE      <--- Entry #2.  KEYWORD
      SP: {
            4.0d0       <--- Entry #0.  DOUBLE-FLOAT
            }           <--- Entry #3.  CONS
      5.0d0       <--- Entry #4.  DOUBLE-FLOAT
      } 
; No value

The less-familiar parts of Lisp for beginners — pprint-indent

We continue through our list of more obscure parts of Lisp, and our current focus on the pretty-printer, with pprint-indent.

This function must be called inside a pprint-logical-block block.  It has the effect of changing the indentation that will be used by the pretty-printer after the next newline.  There are two ways it can be used, with :BLOCK or with :CURRENT.

When called with the :BLOCK argument, this sets the indentation relative to the first character of the current block.  That is, if the current block can be thought of as a rectangular column between a left indent and a right margin, pprint-indent :BLOCK moves the left margin by an amount equal to its argument.  The argument can be negative, increasing the size of the printable column, but cannot push the left margin to the left of the end of the per-line prefix string, if any.

When called with the :CURRENT argument, this sets the indentation relative to the current position of the cursor on the line.