Category Archives: Common Lisp

Quicklisp tip of the day: multiple local-projects folders

Quicklisp provides a folder where it easily finds your local projects:

~/quicklisp/local-projects

It is easy to tell quicklisp to find projects in other folders too. Just add path of additional folder to:

quicklisp-client:*local-project-directories*

Evaluate

(push #p"/path/to/your/lisp/projects/folder/" 
    quicklisp-client:*local-project-directories*)

.

Or put this in your init-file (.sbclrc, .lispworks, …).

CL-Markup

Generation of HTML markup with Common Lisp using cl-markup.

(defpackage :cl-markup-test (use :cl))
 
(in-package :cl-markup-test)
 
;; central feature is MARKUP:
 
(markup (:p 123))
=> "<p>123</p>"
 
;; MARKUP expects list arguments, atoms are beeing rejected
 
(markup "abc")
=> The value "abc" is not of type LIST.
   [Condition of type TYPE-ERROR]
 
;; arguments can be multiple lists
 
(markup (:p 123) (:p 234))
=> "<p>123</p><p>234</p>"
 
;; first element of list is used as tag, not bothering what type it is
;; and wether it smells like HTML
 
(markup ("p" 123))
=> "<p>123</p>"
 
(markup (p 123))
=> "<p>123</p>"
 
(markup ('p 123))
=> "<'p>123</'p>" ; oops
 
(markup (123 p))
=> error: The variable P is unbound.
   [Condition of type UNBOUND-VARIABLE]
 
(markup (123 "p"))
=> "<123>p</123>"  ; !!
 
;; whole HTML pages can be produced with cl-markup macros HTML, HTML5,
;; XHTML and XML.
 
(html5 (:p 42))
=> "<!DOCTYPE html><html><p>42</p></html>"
 
;; lisp expressions can be inserted everywhere except first position of list
 
(markup (:p (concatenate 'string "1" "2" "3")))
=> "<p>123</p>"
 
;; these expressions are considered good citizens when they produce
;; string results, else:
 
(markup (:p (+ 100 20 3)))
=> The value 123 is not of type STRING.
   [Condition of type TYPE-ERROR]
 
;; weird things seem to happen when combining snippets
 
(let ((snip (markup (:p "abc"))))
  (markup (:div snip)))
=> "<div>&lt;p&gt;abc&lt;/p&gt;</div>" ; !!
 
;; This is a feature called auto-escape and it provides correct
;; solutions for these kind of tasks:
 
(markup (:p "1<3"))
=> "<p>1&lt;3</p>"
 
(markup (:p "R&B"))
=> "<p>R&amp;B</p>"
 
;; auto-escaping can be turned off like this
 
(let* ((*auto-escape* nil)
       (snip (markup (:p "abc"))))
  (markup (:div snip)))
=> "<div><p>abc</p></div>"
 
;; another way to shelter strings against greedy auto-escape
;; is wrapping them in a list
 
(let ((snip (markup (:p "abc"))))
  (markup (:div (list snip))))
=> "<div><p>abc</p></div>"
 
;; same result using backquote syntax
 
(let ((snip (markup (:p "abc"))))
  (markup (:div `(,snip))))
=> "<div><p>abc</p></div>"
 
;; and this can also be done with CL-MARKUPs RAW macro.  RAW sounds
;; like a kind of strange name for a list-wrapping feature.  Maybe the
;; name tries to express that this procedure treats the string
;; like the raw markup from which it evolved.
 
(let ((snip (markup (:p "abc"))))
  (markup (:div (raw snip))))
=> "<div><p>abc</p></div>"
 
;; and now for the best of all: it is possible to write cl macros
;; producing HTML snippets.
 
(defmacro snip (name)
  `(markup (:p ,name)))
 
(snip "foo")
=> "<p>foo</p>"
 
;; It is save to handle string results.
 
;; Trying to pass markup asks for trouble
 
(defmacro snip-markup (name)
  `(:p ,name))
 
(markup (snip-markup "foo"))
=> "<snip-markup>foo</snip-markup>" ;; oops
 
;; MARKUP is a macro, it does not evaluate its arguments