cl-markup

https://github.com/arielnetworks/cl-markup.git

git clone 'https://github.com/arielnetworks/cl-markup.git'

(ql:quickload :cl-markup)
30

CL-MARKUP - Modern markup generation library for Common Lisp

Features & advantages

Usage

(html
 (:body
  (:p :id "title" "aiueo")))
;=> "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"
;    \"http://www.w3.org/TR/html4/loose.dtd\">
;    <html><body><p id=\"title\">aiueo</p></body></html>"

Installation

Is this really fast?

Generally CL-MARKUP generates efficient codes which mainly consists of series of `write-string's as much as possible. See how following two examples are expanded by macro expansion.

As you can see, the codes are a bit more complicated than that of CL-WHO because CL-MARKUP alters the destination of output in run-time.

Example A:

;; Example A
(let ((*output-stream* t))
   (loop for (link . title) in '(("http://zappa.com/" . "Frank Zappa")
                                 ("http://marcusmiller.com/" . "Marcus Miller")
                                 ("http://www.milesdavis.com/" . "Miles Davis"))
         do (markup (:a :href link
                        (:b title))
                    (:br))))

;; Example A: generated by CL-MARKUP
(let ((*output-stream* t))
  (loop for (link . title) in '(("http://zappa.com/" . "Frank Zappa")
                                ("http://marcusmiller.com/" . "Marcus Miller")
                                ("http://www.milesdavis.com/" . "Miles Davis"))
        do (if *output-stream*
               (progn (write-string "<a href=\"" *output-stream*)
                      (write-string (escape-string (cl-markup::ensure-string link))
                                    *output-stream*)
                      (write-string "\"><b>" *output-stream*)
                      (write-string (escape-string (cl-markup::ensure-string title))
                                    *output-stream*)
                      (write-string "</b></a><br />" *output-stream*))
               (with-output-to-string (#:G0)
                 (write-string "<a href=\"" #:G0)
                 (write-string (escape-string (cl-markup::ensure-string link)) #:G0)
                 (write-string "\"><b>" #:G0)
                 (write-string (escape-string (cl-markup::ensure-string title)) #:G0)
                 (write-string "</b></a><br />" #:G0)))))

Example B:

;; Example B
(markup
 (:table :border 0 :cellpadding 4
         (loop for i below 25 by 5
               collect
               (markup
                 (:tr :align "right"
                      (loop for j from i below (+ i 5)
                            collect
                            (markup
                              (:td :bgcolor
                                   (if (oddp j)
                                       "pink"
                                       "green")
                                   (format nil "~@R" (1+ j))))))))))

;; Example B: generated by CL-MARKUP
(if *output-stream*
    (progn (write-string "<table border=\"0\" cellpadding=\"4\">"
                         *output-stream*)
           (write-string (let ((#:G0
                                (loop for i below 25 by 5
                                      collect (markup
                                               (:tr
                                                :align
                                                "right"
                                                (loop for j
                                                      from
                                                      i
                                                      below
                                                      (+ i 5)
                                                 collect (markup
                                                          (:td
                                                           :bgcolor
                                                           (if
                                                            (oddp j)
                                                            "pink"
                                                            "green")
                                                           (format
                                                            nil
                                                            "~@r"
                                                            (1+ j))))))))))
                                (if (consp #:G0)
                                    (with-output-to-string (#:G1)
                                      (dolist (#:G2 #:G0)
                                        (write-string #:G2 #:G1)))
                                    #:G0))
                               *output-stream*)
                           (write-string "</table>" *output-stream*))
                         (with-output-to-string (#:G0)
                           (write-string "<table border=\"0\" cellpadding=\"4\">"
                                         #:G0)
                           (write-string (let
                                          ((#:G0
                                            (loop for i below 25 by 5
                                             collect (markup
                                                      (:tr
                                                       :align
                                                       "right"
                                                       (loop for j
                                                             from
                                                             i
                                                             below
                                                             (+ i 5)
                                                        collect (markup
                                                                 (:td
                                                                  :bgcolor
                                                                  (if
                                                                   (oddp j)
                                                                   "pink"
                                                                   "green")
                                                                  (format
                                                                   nil
                                                                   "~@r"
                                                                   (1+
                                                                    j))))))))))
                                            (if
                                             (consp #:G0)
                                             (with-output-to-string
                                              (#:G1)
                                              (dolist
                                               (#:G2 #:G0)
                                               (write-string #:G2 #:G1)))
                                             #:G0))
                                           #:G0)
                                          (write-string "</table>" #:G0)))

Markup language

markup is the simplest way to generate HTML.

(markup (:p "あいうえお"))
;=> "<p>あいうえお</p>"

By default, CL-MARKUP follows XHTML valid styling.

(markup (:br))
;=> "<br />"

You can configure the style by setting *markup-language*.

(eval-when (:compile-toplevel :load-toplevel :execute)
  (setf *markup-language* :html))

Don't forget to wrap setf with eval-when since it is used in compile-time in order to expand markup. This also means you are NOT allowed to write codes like this:

;; THIS IS A WRONG EXAMPLE!!
(let ((*markup-language* :html))
  (markup (:br)))
;=> "<br />"

In case you really want to delay the decision until run-time, use markup*, a functional version of markup.

;; This is a correct one.
;; But I don't recommend this for performance.
(let ((*markup-language* :xhtml))
  (markup* '(:br)))
;=> "<br>"

Other macros such as html, xhtml, html5, and xml output DOCTYPE before markup.

(html (:p "あいうえお") (:br))
;=> "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\"><html><p>あいうえお</p><br></html>"

(html5 (:p "あいうえお") (:br))
;=> "<!DOCTYPE html><html><p>あいうえお</p><br></html>"

(xhtml (:p "あいうえお") (:br))
;=> "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html><p>あいうえお</p><br /></html>"

(xml (:p "あいうえお") (:br))
;=> "<?xml version=\"1.0\" encoding=\"UTF-8\"?><p>あいうえお</p><br />"

Escape

Embedded strings are escaped automatically.

(markup (:p "Tiffany & Co."))
;=> "<p>Tiffany &amp; Co.</p>"

If you don't want this behavior, set *auto-escape* nil or use raw for temporal suppression.

(let ((*auto-escape* nil))
  (markup (:p "Tiffany & Co.")))
;=> "<p>Tiffany & Co.</p>"

(markup (:p (raw "Tiffany & Co.")))
;=> "<p>Tiffany & Co.</p>"

Also, when you want to ensure a certain code to be escaped (maybe inside raw) use esc, which has the similar syntax as that of raw.

Direct output to stream

Markup macros returns html as a string. This behavior can be customized by modifying *output-stream* which is defaulted to *standard-output*.

;; Default behavior
(let (*output-stream*)
  (markup (:p "hoge"))
;=> "<p>hoge</p>"

;; Output to *standard-output* directly
(let ((*output-stream* t))
  (markup (:p "hoge")))
;;=> <p>hoge</p>
;=> "<p>hoge</p>"

Markup syntax

You can embed Lisp code in the body of each tag.

(markup (:ul (loop for item in '(1 2 3) collect (markup (:li item)))))

For more readability, CL-MARKUP provides a reader macro #M which can be enabled by (enable-markup-syntax).

(enable-markup-syntax)
#M(:ul (loop for item in '(1 2 3) collect #M(:li item))))

License

Copyright (c) 2011 Eitarow Fukamachi.

Contributors:

m7d - HTML5 support, April 21, 2012.

Licensed under the LLGPL License.