Oct 30, 2021 — A Simple Presentation DSL in Tcl

Background

As I’ve written previously, one of Tcl’s greatest strengths is that it allows for the programmer to define new, first-class constructs in the language. In this post, I’d like to touch upon another one of its strengths: the ability to quickly create Domain Specific Languages.

Earlier today, I was watching a presentation given by Richard Hipp for the Tcl / Sqlite conference in 2020. In his presentation, he gave the link to his slides. They were just simple HTML pages that linked to one another.

Recently, I’ve been thinking more and more about Domain Specific Languages, so I figured I’d take a stab at making a DSL that generated HTML pages from "presentation files".

First, I started by simply writing a presentation in my theoretical DSL, not thinking too hard about the format. My goal was to write using an intuitive language and worry about the implementation later on.

Example presentation file

presentation-start

  slide
    title "A Simple Presentation DSL in Tcl"
    - "Presented by Christopher Chase."
    - "October 2021"

  slide
    title "Tcl"
    - "Tcl is a dynamic lanugage...."

  slide
    title "Third page"
    - "Bullet 1"
    - "Bullet 2"

presentation-end

About 30 minutes later (it’d take shorter if I was using Tcl every day, but it’s been a while!), I had a working implementation.

Implementation

array set presentation {}

proc presentation-start {} {
        global presentation

        unset presentation
        array set presentation {}
        set presentation(started) 1
        set presentation(slideCount) 0
}

proc presentation-end {} {
        global presentation

        # Loop through each of the slides and generate output (e.g. HTML)

        set currSlide 1

        while {$currSlide <= $presentation(slideCount) } {

                set isFirst [expr $currSlide == 1]
                set hasMore [expr $currSlide <  $presentation(slideCount)]
                set isLast  [expr $currSlide == $presentation(slideCount)]


                # If we're on the first slide, use the title as the directory name
                if {$isFirst} {
                        set dirName "$presentation(slide,$currSlide,title)"
                        file mkdir $dirName
                        cd $dirName
                }

                set fh [open $currSlide.html w+]

                puts $fh "<h1>$presentation(slide,$currSlide,title)</h1>"

                # Loop through each bullet on this slide
                set currBullet 1
                while {$currBullet <= $presentation(slide,$currSlide,bulletCount)} {
                        puts $fh "<p>$presentation(slide,$currSlide,$currBullet,text)</p>"
                        incr currBullet
                }

                if {$isFirst} {
                        # If currSlide is 1, we only link to next slide.
                        puts $fh "<a href='[expr $currSlide + 1].html'>Next</a>"
                } elseif {$hasMore} {
                        # If 1 < currSlide < slideCount, we link to prev and next
                        puts $fh "<a href='[expr $currSlide - 1].html'>Prev</a>"
                        puts $fh "<a href='[expr $currSlide + 1].html'>Next</a>"
                } elseif {$isLast} {
                        # If currSlide = slideCount, we link only to prev
                        puts $fh "<a href='[expr $currSlide - 1].html'>Prev</a>"
                }

                flush $fh
                close $fh
                incr currSlide
        }

}

proc slide {} {
        global presentation
        set sNum [incr presentation(slideCount)]
        set  presentation(slide,$sNum,bulletCount) 0
}

proc title {t} {
        global presentation

        set sNum $presentation(slideCount)
        set presentation(slide,$sNum,title) $t
}

proc - {t} {
        global presentation
        set sNum $presentation(slideCount)
        set bNum [incr presentation(slide,$sNum,bulletCount)]
        set presentation(slide,$sNum,$bNum,text) $t
}

The above code, less than 100 lines, reads a presentation file, and for each presentation, creates a new directory and then for each slide of the presentation, creates a new HTML file that links to the previous or next slide (depending on what page is being rendered.)