Saturday, August 15th, 2015

Update: pollen-count

pollen, racket, programming

Table of Contents

1. Feedback

Matthew gave me some good feedback on the initial commit. He suggested that the use of a macro could simplify things by doing the counting and label gathering together before even hitting root. Macros are one of the coolest and most powerful features of lisps. There’s a bit more to Racket macros than the Common Lisp equivalent with which I’m familiar; this change has allowed me to investigate Racket macros a bit more but I’ll admit I’m not grokking them fully yet1.

Previously you had to manually set up the counters and define a map of tags to counters as shown in Listing 1.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#|
Define counters and map of tags to counters
|#
(define section-counter (make-counter 0 number->string))
(define subsection-counter (make-counter 0 number->string section-counter))
(define subsubsection-counter (make-counter 0 number->string subsection-counter))
(define figure-counter (make-counter 0 number->string))
(define footnote-counter (make-counter 0 number->string))
(define tag-counters (hash 'section section-counter
                           'subsection subsection-counter
                           'subsubsection subsubsection-counter
                           'figure figure-counter
                           'footnote footnote-counter))
Listing 1: Old setup

Furthermore, as can be seen in Listing 2, the code to replace section tags with HTML headings within the root function wasn’t pretty.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#|
Function to replace section tags after numbering
|#
(define (make-section tx heading)
  (make-txexpr heading
               (if (attrs-have-key? tx 'id)
                   (get-attrs tx)
                   (merge-attrs (get-attrs tx) 'id (gensym)))
               (if (attrs-have-key? tx 'data-number)
                   (append (list (attr-ref tx 'data-number) ". ") (get-elements tx))
                   (get-elements tx))))

#|
Function to replace figure tag after numbering
|#
(define (make-figure tx)
  (define els (get-elements tx))
  (make-txexpr 'figure
               (merge-attrs (get-attrs tx) 'style (string-append "width:" (car els) ";"))
               (list `(img ((src ,(cadr els))))
`(figcaption "Figure " ,(attr-ref tx 'data-number) ": " ,@(cddr els)))))

(define-values (xs-rep none)
    (splitf-txexpr refd
                   (λ (x) (and (txexpr? x) (member (car x) '(section
                                                             subsection
                                                             subsubsection
                                                             figure))))
                   (λ (x)
                     (match (car x)
                       ['section (make-section x 'h2)]
                       ['subsection (make-section x 'h3)]
                       ['subsubsection (make-section x 'h4)]
                       ['figure (make-figure x)]))))
Listing 2: More setup. My eyes!

2. Macros to the rescue!

With the new macro we define our countable tags, somewhere in directory-require.rkt, as per Listing 3. You can think about this as defining a function to replace a tag as you would otherwise in Pollen.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#|
Define section, subsection, subsubsection and figure tags.
We give the section tags gensym'd ids. If the section is
labelled the id will be overwritten with the label.
|#
(define-countable-tag (section . xs) (0 number->string #f ".") (count)
  `(h2 ((id ,(symbol->string (gensym)))) ,count ". " ,@xs))

(define-countable-tag (subsection . xs) (0 number->string section ".") (count)
  `(h3 ((id ,(symbol->string (gensym)))) ,count ". " ,@xs))

(define-countable-tag (subsubsection . xs) (0 number->string subsection ".") (count)
  `(h4 ((id ,(symbol->string (gensym)))) ,count ". " ,@xs))

(define-countable-tag (footnote . xs) (0 number->string #f ".") (count)
  `(p ((class "footnote")) ,count ". " ,@xs))

(define-countable-tag (figure src #:width [width "90%"] . xs) (0 number->string #f ".") (count)
  `(figure
    (img ((width ,width) (src ,src)))
    (figcaption ,count ": " ,@xs)))

(define-countable-tag (listing lang cap . xs) (0 number->string #f ".") (count)
  `(figure ((class "listing"))
    ,(apply highlight lang xs)
    (figcaption "Listing ",count ": " ,cap)))
Listing 3: Cool!

The only thing left to do in root is to call cross-reference on the document txexpr2.

Footnotes

1. Use­ful ref­er­ences for macros are Greg Hen­der­shott’s Fear of Macros, this talk by Matthew Flatt and, of course, the Rack­et doc­u­men­ta­tion.

2. Ac­tu­al­ly I lied! There’s an­oth­er bit of house keep­ing we need to do: re­set all the coun­ters with the reset-counter macro. See my directory-require.rktpollen.rkt.

Comments

comments powered by Disqus