Literate WordPress May 6th, 2010
Patrick Stein

I use WordPress along with the CodeColor plugin. This works fairly well for me. I find myself, however, breaking code up into sometimes awkward chunks to describe the code here or leaving things in very long segments.

Further, I often leave them ordered in my post in an order where they would compile if you just copied them into a file in the order presented. That’s not always the most useful order to present the code.

Literate programming seeks to tackle exactly these problems. So, I got to thinking: Why don’t I write some filters for noweb to output into WordPress+CodeColorer.

My Solution

My solution is in two parts, one filter of general use to all noweb users which simply inserts @language tags into the noweb stream and a noweave backend that outputs something you can just paste into the WordPress (HTML) input box.

Here is an example of how I used this to process the post.nw file that I used to compose this post.

# example.sh
noweave -n -filter langify-nw -backend wordpress-nw post.nw

  • langify-nw script
  • wordpress-nw script
  • tee-nw script useful for debugging noweb streams
  • post.nw literate source for this post (technically, I tweaked it a bit after proofreading without going back to the source combining the first two paragraphs and changing <h2>’s to <h3>’s and adding this parenthetical)

The langify-nw script guesses the language for each chunk based on the filename extension on the top-level chunk that incorporates that chunk. That’s why I used filename extensions on the top-level chunks in this post.

An Example

Several months back, I posted some code for rendering anti-aliased text in OpenGL. At the bottom of that post, I had an unwieldy 33-line function that you have to scroll to see with the way my blog is styled.

This would have been an excellent candidate for literate programming. Here is that same function broken up with noweb chunks with some descriptive text sprinkled between chunks.

At the heart of the algorithm, I need to take some point <px, py, pz>, run it backwards through all of the projection matrices so that I have it in pixel coordinates, and calculate the distance between that and the screen coordinates of the origin. So, I have this little function here to do that calculation given the object-local coordinates <px, py, pz> and the origin’s screen coordinates <ox, oy, oz>.

;;; dist-to-point-from-origin labels decl
(dist-to-point-from-origin (px py pz ox oy oz)
   (multiple-value-bind (nx ny nz)
       (glu:un-project px py pz
                       :modelview modelview
                       :projection projection
                       :viewport viewport)
     (dist nx ny nz ox oy oz)))

That, of course, also needs to determine the distance between two three-dimensional points. For my purposes, I don’t need the straight-line distance. I just need the maximum delta along any coordinate axis.

;;; dist labels decl
(dist (x1 y1 z1 x2 y2 z2)
   (max (abs (- x1 x2))
        (abs (- y1 y2))
        (abs (- z1 z2))))

Also, to do those reverse-projections, I need to retrieve the appropriate transformation matrices.

;;; collect projection matrices
(modelview (gl:get-double :modelview-matrix))
(projection (gl:get-double :projection-matrix))
(viewport (gl:get-integer :viewport))

Once I have those pieces, I am just going to collect those matrices, unproject the origin, then find half of the minimum unprojected distance of the points <1, 0, 0> and <0, 1, 0> from the origin.

;;; calculate-cutoff.lisp
(defun calculate-cutoff (font-loader size)
  (gl:with-pushed-matrix
    #<:use "scale based on font-size">
    (let (#<:use "collect projection matrices">)
      (labels (#<:use "dist labels decl">
               #<:use "dist-to-point-from-origin labels decl">)
        (multiple-value-bind (ox oy oz)
            (glu:un-project 0.0 0.0 0.0
                            :modelview modelview
                            :projection projection
                            :viewport viewport)
          (/ (min (dist-to-point-from-origin 1 0 0 ox oy oz)
                  (dist-to-point-from-origin 0 1 0 ox oy oz))
             2)))))))

Oh, and being the tricksy devil that I am, I scaled things based on my font size before doing any of that.

;;; scale based on font-size
(let ((ss (/ size (zpb-ttf:units/em font-loader))))
  (gl:scale ss ss ss)

Getting started with Clojure/Emacs/Slime May 4th, 2010
Patrick Stein

I spent some considerable time yesterday poring over the shelves in the programmer’s section of a local bookstore yesterday. Based on the available jobs at the moment, I was trying to decide whether it would be less painful to learn C#/.NET/AFW/blurpz or Hibernate/Springs/Struts/glorpka. My lambda, those things are fugly. When I open a book to find that my simple database example takes eight XML configuration files and twenty-five lines of calls to the same function (with a 25-character identifier (which, technically, should be namespace qualified, too)), I just don’t want to go there.

So, I walked away with Programming Clojure and a determination to think really hard about how to get paid to do something that’s not intensely painful.

Well, yesterday afternoon and late-night were intensely painful trying to get Clojure/Emacs/Slime all working together. Today, magickly, I messed something up in my .emacs file that convinced swank-clojure to download its own copies of the three JAR files it needs and zoom… I’m out of the gate.

Someday, I’d still like to be able to use my own JAR files for all of this, but in the meantime, I’m up and running.

Here’s what works

This is the relevant configuration from my .emacs file. It draws partly from these instructions by I’m not sure who, partly from this message by Constantine Vetoshev, partly from how my .emacs file was previously arranged, partly from sources now lost in the browser history sea, and partly from sheer luck.

First, some generic stuff up at the beginning:

(defun add-subdirs-to-load-path (dir)
  (let ((default-directory (concat dir "/")))
    (normal-top-level-add-subdirs-to-load-path)))

(add-to-list 'load-path "~/.emacs.d/site-lisp")
(add-subdirs-to-load-path "~/.emacs.d/site-lisp")

Then, prepping slime a bit:

(require 'slime-autoloads)
(add-to-list 'load-path "~/.emacs.d/site-lisp/slime/contrib")

(add-hook 'lisp-mode-hook (lambda () (slime-mode t)))
(add-hook 'inferior-lisp-mode-hook (lambda () (inferior-slime-mode t)))
(setq common-lisp-hyperspec-root
      "file:///Developer/Documentation/Lisp/clhs/HyperSpec/")

(slime-setup '(slime-repl))

(setq slime-net-coding-system 'utf-8-unix)

Then, setting up some general stuff for easy lisp implementations. (The –sbcl-nolineedit is something I personally use in my .sbclrc to decide whether to load linedit.)

(setq slime-lisp-implementations
      '((sbcl ("sbcl" "--sbcl-nolineedit"))
        (ccl ("ccl"))
        (ccl64 ("ccl64"))))

Some commands to simplify things so I don’t have to remember to M–– M-x slime:

(defmacro defslime-start (name mapping)
  `(defun ,name ()
     (interactive)
     (let ((slime-default-lisp ,mapping))
       (slime))))

(defslime-start ccl 'ccl)
(defslime-start ccl64 'ccl64)
(defslime-start clojure 'clojure)
(defslime-start sbcl 'sbcl)

Then, Clojure-specific SLIME stuff

(autoload 'clojure-mode "clojure-mode" "A major mode for Clojure" t)
(add-to-list 'auto-mode-alist '("\\.clj$" . clojure-mode))
(require 'swank-clojure)

(setq slime-lisp-implementations
      (append slime-lisp-implementations
              `((clojure ,(swank-clojure-cmd) :init swank-clojure-init))))

And, a touch more slime stuff to make things a little happier.

(add-hook 'slime-mode-hook
          (lambda ()
            (setq slime-truncate-lines nil)
            (slime-redirect-inferior-output)))

In my .emacs.d/site-lisp, I did the following:

% rm -rf slime swank-clojure clojure-mode
% git clone git://git.boinkor.net/slime.git
% git clone http://github.com/technomancy/swank-clojure.git
% git clone http://github.com/jochu/clojure-mode.git

What didn’t work

Before accidentally triggering swank-clojure to download its own JARs, I tried installing what I could with ELPA. I tried installing clojure, clojure-contrib, and swank-clojure with Lein. I tried installing them with Maven. I tried various combinations of versions of clojure and swank-clojure.

I have no idea how the JARs that swank-clojure built itself got built. I cannot reproduce it.

Edit: Ah, it appears that the Subversion repository for Clojure that I found is deprecated. But, I don’t have the energy to try the git repository myself at this point. Maybe next week.

TC Lispers April Presentations online April 29th, 2010
Patrick Stein

The Twin Cities Lisp Users Group meeting for April was last Monday. The main topic was Web Frameworks, but there were also two shorter talks.

Weblocks Presentation

Patrick Stein gave this presentation at the TC Lispers meeting in April 2010.

Weblocks on Vimeo.

Allegro Serve and Web Actions Presentation

Robert Goldman gave this presentation at the TC Lispers meeting in April 2010.

Apology: Unfortunately, ScreenFlow bombed out on me when I went to stop recording. It subsequently saw that it had a partial project there but was unable to recover it. As such, there is no video available for this presentation. Feh. — Patrick

Hunchentoot Presentation

Paul Krueger gave this presentation at the TC Lispers meeting in April 2010.

Hunchentoot on Vimeo.

Cocoa Lisp Controller Presentation

Paul Krueger gave this presentation at the TC Lispers meeting in April 2010.

Cocoa Lisp Controller on Vimeo.

CL-Growl Presentation

Patrick Stein gave this presentation at the TC Lispers meeting in April 2010.

CL-Growl on Vimeo.

CL-Growl patched for CCL April 29th, 2010
Patrick Stein

Alexandre Paes submitted a patch for my CL-Growl library so it now works with CCL (aka. Clozure, formerly OpenMCL), too.

Here is the source tarball: cl-growl_1.1.2010.04.29.tar.gz and the corresponding GPG signature: cl-growl_1.1.2010.04.29.tar.gz.asc. For other ways to access this code, see the CL-Growl page.

Thank you, Alex!

Weblocks Slides April 26th, 2010
Patrick Stein

At the TC Lispers meeting tonight, I will be giving a little presentation about weblocks. Here are slides for that presentation.

ps. if you’re in range, come to the meeting… Common Roots Cafe in Minneapolis.

Updates In Email

Email:

l