Date: 2008-12-14 22:37:00
Tags: psil, html
discovering things that are already known

One of the goals of my Psil project is to make it easier to write server-side web apps that output HTML at some level. I've built up a little library of functions using Lisp-style macros that transforms code into a data structure, which another function then transforms into HTML. For example,

(p "This is a " (a (href "http://example.com") "test") " of this program.")

is transformed into

<p>This is a <a href="http://example.com">test</a> of this program.</p>

Each HTML tag (all 77 of them!) is defined as a macro that takes its arguments and builds up a data structure representing the HTML document. Then a function called render-html renders the data structure as HTML. The render-html function takes care of all HTML tag generation and character escaping (useful to help avoid XSS problems).

I have a sample CGI test program that I was using to test different aspects of this. After working with it for a bit, I realised that it wasn't too terribly different from what I could do in Python itself. So, here is the Psil program on the left, and an equivalent Python program on the right:

#!/usr/local/bin/psil

(include "html.psil")

(define (footer email)
  (address "email me at " (a (mailto email) "<" email ">")))
        
(print "Content-type: text/html")
(print)     
                
(print (render-html          
  (html ()                    
    (head ()                
      (title "test page"))
    (body ()
      (comment "the good stuff starts here")
      (p (style (concat "margin-left" ": " "1em"))
        (strong "this") " is a test "
        (.join "<" (map str (map (lambda (x) (* x x)) '(1 2 3))))
        " of this program")
      (hr)            
      (footer "test@example.com")))))
#!/usr/local/bin/python

from html import *

def footer(email):
  return address("email me at ", a({'mailto': email}, "<", email, ">"))

print "Content-type: text/html"
print   
        
print render_html(
  html(   
    head(   
      title("test page")),
    body(                   
      comment("the good stuff starts here"), 
      p({'style': "margin-left"+": "+"1em"},
        strong("this"), " is a test ",
        "<".join(map(str, map(lambda x: x*x, (1, 2, 3)))),
        " of this program"),
      hr(),   
      footer("test@example.com"))))

With suitable library functions, the transformation between the Psil code and the Python code is trivial. The transformation is essentially just moving the opening parenthesis to the other side of the function name, and adding commas between function arguments. (I also implemented attributes as Python dictionaries rather than lists as in the Psil version.)

Somebody once said that some majority of computer science papers were the author simply describing how they had discovered something that was already known. I feel a bit like that right now. Nevertheless, I'm going to continue working on Psil to see how it goes. Currently I'm thinking about how to implement Scheme-like continuations.

Greg Hewgill <greg@hewgill.com>