Add cl-who demo draft
authorLucian Mogosanu <lucian@mogosanu.ro>
Mon, 1 Jul 2019 07:29:42 +0000 (10:29 +0300)
committerLucian Mogosanu <lucian@mogosanu.ro>
Mon, 1 Jul 2019 07:29:42 +0000 (10:29 +0300)
drafts/000-cl-who-ii.markdown [new file with mode: 0644]

diff --git a/drafts/000-cl-who-ii.markdown b/drafts/000-cl-who-ii.markdown
new file mode 100644 (file)
index 0000000..27e6c52
--- /dev/null
@@ -0,0 +1,229 @@
+---
+postid: 000
+title: CL-WHO demo: The Coad Pit
+date: June 29, 2019
+author: Lucian MogoČ™anu
+tags: tech, tmsr
+---
+
+In the midst of an [ongoing battle][btcbase-1919627] with the monster
+that is [Hunchentoot][hunchentoot-i], I have received the suggestion
+that I can easily communicate intermediate results by simply posting
+said code on the web in an [annotatable form][btcbase-1919634]. I
+judge this to be a great idea, even though the items in question
+represent merely coad[^1]. Moreover, I've previously promised to
+provide examples for [other WWWisms][cl-who], and it occurs to me that
+this is just the right occasion to do so.
+
+Thus this post answers the following couple of questions: a. how can
+one use CL-WHO to generate a simple web site? and b. given a code base
+consisting of source code, how can we turn that into a HTMListic
+representation with per-line references, *using our tool*[^2]?
+
+[As previously described][cl-who-again] CL-WHO gives us a template
+language, wrapped around the `with-html-output` macro. For example, to
+write a simple web page to `*standard-output*`, one would write:
+
+~~~~ {.commonlisp}
+(cl-who:with-html-output (*standard-output*)
+  (:html (:body
+          (:b (cl-who:str "Hello, world!")))))
+~~~~
+
+which yields the output:
+
+~~~~
+<html><body><b>Hello, world!</b></body></html>
+~~~~
+
+First, let's make some sense of what we just wrote. The full
+description of the template language can be found in [the
+docs][cl-who-syntax], but in short, we: 1. define a context for HTML
+output; in which we 2. generate a HTML whose body contains; 3. a
+"bold" context, containing 4. the string (`str`) "Hello, world!".
+
+Now let's say we want to do something fancier, such as looping over a
+list of items:
+
+~~~~ {.commonlisp}
+(cl-who:with-html-output (*standard-output* nil)
+   (:html (:body
+           (:ul
+            (loop for x in '(a b c d e f)
+                for i = 1 then (1+ i) do
+                (cl-who:htm
+                 (:li (cl-who:esc
+                       (format nil "[~d] ~s" i x))))))))))
+~~~~
+
+gives us:
+
+~~~~
+<html><body><ul><li>[1] A</li><li>[2] B</li><li>[3] C</li><li>[4] D</li><li>[5] E</li><li>[6] F</li></ul></body></html>
+~~~~
+
+In addition to the loop itself, this example has two unknowns, namely
+the `htm` and the `esc` keywords. Firstly, `esc` is very similar to
+the previously-presented `str`, only that it does a HTML escape (of
+e.g. ">" into "&gt;").
+
+Secondly, `htm` is a sort of a shorthand for `with-html-output` that
+is necessary for the following reason: the HTML generation mechanism
+of WHO looks at cons cells whose first element is a keyword
+(interpreted as a HTML tag), and it collects them, until it reaches
+something else (e.g. our loop construct above). When it gets there, it
+treats the input as a standard S-expression, to be evaluated by the
+Lisp evaluator as-is. However, this S-expression now also contains the
+definition of `htm` in its macro-scope, which causes further
+occurences of `htm` to be expanded, which allows nested
+HTML-in-Lisp-in-HTML-... expressions.
+
+To put this convoluted[^3] explanation in simpler words:
+`with-html-output` throws us into a HTML template context; `loop` gets
+us out of there, but `htm` brings us back in, which is precisely the
+"mix HTML and CL" machinery at work. Use it wisely, and it shall serve
+you well.
+
+Now that we have these basics fixed, we can get to what really
+interests us, that is, a HTML representation of source code with
+line-by-line references. For the sake of simplicity and modularity, we
+are going to break this into a set of macros, each doing one
+thing. For example, the CL-WHO template for a line number:
+
+~~~~ {.commonlisp}
+(defmacro tcp-line-number (n &optional (padding 0))
+  "Template for the line number."
+  `(cl-who:str (format nil "~vd " ,padding ,n)))
+~~~~
+
+Note: the `~vd` format string allows us to do variable padding with
+spaces, which is quite useful here to keep our lines aligned. Next,
+the line content and the actual line:
+
+~~~~ {.commonlisp}
+(defmacro tcp-line-content (line)
+  "Template for the line content."
+  `(cl-who:esc ,line))
+
+(defmacro tcp-line (number padding content)
+  "Template for the line."
+  `(cl-who:htm
+    (:span :class "line"
+          :id (format nil "L~d" ,number)
+          (:a :href  (format nil "#L~d" ,number)
+              :class "lineno"
+              (tcp-line-number ,number ,padding))
+          (:span :class "linecont"
+                 (tcp-line-content ,content)))
+    (:br)))
+~~~~
+
+Next we need to put all the lines together in a code block, which for
+our convenience will be a `pre` tag:
+
+~~~~ {.commonlisp}
+(defmacro tcp-block (lines)
+  "Template for generating a code block out of a list of lines."
+  (with-gensyms (i padding line)
+   `(cl-who:htm
+     (:pre :class "coadblock"
+      (loop for ,padding = (length (format nil "~d" (length ,lines)))
+        for ,i = 1 then (1+ ,i)
+        for ,line in ,lines do
+          (tcp-line ,i ,padding ,line))))))
+~~~~
+
+... and, for even more convenience, we'll output a HTML header with
+some CSS to bellify the coad loox:
+
+~~~~ {.commonlisp}
+(defmacro tcp-html-header (title)
+  `(cl-who:htm
+    (:title (cl-who:str ,title))
+    (:style :type "text/css"
+           (cl-who:str "
+.coadblock {background-color:#e8e8e8;
+            border-style:solid;border-width:1px;padding-left:5px;}
+.line {float:left; width:100%;}
+.lineno {font-weight:bold;text-decoration:none;color:black;}
+.lineno:visited {color:black;}
+:target {background-color:lightyellow;}"))))
+~~~~
+
+Finally, we put everything together in a function that receives a page
+title, a list of strings, each string a line in the source file, and
+outputs them into a file:
+
+~~~~ {.commonlisp}
+(defun write-code-page (title lines out-path)
+  "Write LINES to OUT-PATH as a HTML file."
+  (with-open-file (out out-path
+                      :direction :output
+                      :if-exists :supersede)
+    (cl-who:with-html-output (out nil
+                                 :prologue t
+                                 :indent nil)
+      (cl-who:htm
+       (:html (:head (tcp-html-header title))
+             (:body (:b (cl-who:str title))
+                    (tcp-block lines)))))))
+~~~~
+
+That's it, really, our coad-pit-generating coad. Go ahead and test
+it. This isn't quite the whole thing, but I'm deliberately omitting it
+since there's a lot of it that's out of the scope of a CL-WHO demo: we
+need to a. take a code base given as input, and; b. based on its input
+path and output path and; c. based on its path inside the website
+we're deploying to; d. we must process the code tree; and e. for each
+source file, output the generated page, and f. for each directory,
+create the output directory and output an index page. I initially
+thought this would fit in about two hundred lines of Lisp, but it runs
+a bit over three hundred, so I'm illustrating the demo by having it
+generate itself.
+
+The full coad can be examined on this [demo page][cl-who-demo]. For
+the future, I am preparing a new site, The Coad Pit, that will be soon
+available over at [coad.thetarpit.org][coad], containing e.g. CL-WHO,
+Hunchentoot and other coads and codes that I've gathered over time.
+
+[^1]: "Coad" is a TMSR term of art, representing, well... [let's
+      see][btcbase-1760554]:
+
+      > **mircea_popescu**: is "coad" code ?  
+      > **asciilifeform**: yea, but think 'with lower case c'  
+      > **asciilifeform**: i.e snippets
+
+      In other words, coad is code that comes with no guarantees that
+      it does what the author says it does, nor, in some cases, that
+      it does anything useful at all, nor that it's usable, readable,
+      fittable in head and so on -- making its value as a published
+      item somewhat questionable. However, [some] coad may have the
+      potential to be turned into actual code, i.e. into something
+      that I for example could sign without thinking twice, which is
+      why putting coad on the 'net in some form or the other is not at
+      all a useless endeavour.
+
+[^2]: There's nothing fundamentally new in "code sites". Shithub does
+      it, owing to ye olde [LXR][lxr]; meanwhile, Phf's btcbase has a
+      very neat [V patch explorer][btcbase-patches] that's been in
+      Republican use for years now.
+
+      So the only thing I'm adding to it is the didactic aspect, I
+      guess.
+
+[^3]: The reader might get a reasonably good insight on what happens
+      behind the scenes by running `macroexpand-1` on the example
+      presented. Other than that, it's just CL macros all the way
+      down, and this unfortunately can't be properly explained in this
+      humble article.
+
+[btcbase-1919627]: http://btcbase.org/log/2019-06-23#1919627
+[hunchentoot-i]: /posts/y05/093-hunchentoot-i.html
+[btcbase-1919634]: http://btcbase.org/log/2019-06-23#1919634
+[btcbase-1760554]: http://btcbase.org/log/2017-12-29#1760554
+[cl-who]: /posts/y05/092-cl-who.html#selection-71.157-75.46
+[lxr]: http://archive.is/O6l48
+[btcbase-patches]: http://btcbase.org/patches[cl-who-again]: http://thetarpit.org/posts/y05/092-cl-who.html#selection-67.223-67.610
+[cl-who-syntax]: http://archive.is/3kH5V#selection-835.0-835.20
+[cl-who-demo]: /uploads/2019/06/cl-who-demo/
+[coad]: http://coad.thetarpit.org/