Search code examples
formatcommon-lisp

Translating pretty-print code to format directives


I am writing pretty-printing code in the style of Common Lisp, the language. While I have the intuition that this code could be written more compactly using format directives, I still did not succeed to do so, so I am sharing my example and I am asking if someone is able to write a format directive with the same semantic.

CL-USER> (let ((properties
          '(:a "the letter a" :b "the letter b"
            :a-property-with-a-long-key "and a very long value")))
(pprint-logical-block (nil properties :prefix "(" :suffix ")")
  (loop (write-char #\()
    (pprint-indent :block 1)
    (write (pprint-pop))
    (write-char #\Space)
    (pprint-newline :linear)
    (write (pprint-pop))
    (write-char #\))
    (pprint-exit-if-list-exhausted)
    (pprint-indent :block 0)
    (write-char #\Space)
    (pprint-newline :linear))))
;; Ouput
((:A
  "the letter a")
 (:B
  "the letter b")
 (:A-PROPERTY-WITH-A-LONG-KEY
  "and a very long value"))
;; Return value
NIL

(The code above is certainly much more understandable than the yet-to-find format directive replacing it. However a format directive would work great when generating code on a ad hoc basis.)


Solution

  • Try something like this:

    (let ((properties '(:a "the letter a" 
                        :b "the letter b"
                        :a-property-with-a-long-key "and a very long value")))
      (format t "(~{(~s~& ~s)~^~&~@{ (~s~&  ~s)~^~&~}~})" properties))
    

    Output:

    ((:A
     "the letter a")
     (:B
      "the letter b")
     (:A-PROPERTY-WITH-A-LONG-KEY
      "and a very long value"))
    

    Used directives:

    • ~{...~} iterates over elements of list
    • ~@{...~} iterates over remaining elements from list
    • ~s prints the element (= (write (pprint-pop)))
    • ~& prints conditional new line (= pprint-newline) (replace with ~% for unconditional newline)
    • ~^ stops if there are no other elements in list (= pprint-exit-if-list-exhausted)