(archive 'newLISPer)

February 12, 2008

A newLISP CGI web page in 10 easy steps

Filed under: newLISP — newlisper @ 19:36

I recently needed an odd sort of calculator. It had to work in multiples of 7.5 and 1.5, and add numbers differently according to the day of the week. I was starting to wonder how I could avoid using a Microsoft Excel spreadsheet to do the job; I’m not a big fan of MicroVBasicSoft or whatever it’s called these days. Then I realised that I might be able to do it by writing a newLISP CGI web page instead.

I’m still struggling to understand the basics of CGI and newLISP. Learning CGI isn’t that easy when most of the examples are written in Perl. So I thought this would be good way to get some practice. For the purposes of this post, though, I’ve changed the example to a simple table that adds rows and columns and puts the totals in the last column and bottom row. Let’s see if I can describe it in 10 easy steps.

Step 1: prepare ingredients

This one is easy:

#!/usr/bin/env newlisp
(load {/usr/share/newlisp/modules/cgi.lsp})

I want the cgi.lsp module – part of the standard newLISP install – mainly for the CGI:get function, which retrieves values from the incoming form. I’ve not looked at cgi.lsp much, so I don’t know what’s in it.

Step 2: set up a data store

This step sets up a small data storage area. The data submitted by the user will be loaded into the store for subsequent processing. I use a list of lists rather than an array because:

  1. Lists are more flexible than arrays, and just as quick if they’re not too big.

  2. Er, that’s it, really.

(set 'rows 10 'columns 10)
(set 'data (dup (dup 0 rows) columns))

To access the value of the cell at row 3 and column 4, I can use the implicit addressing technique: (data 3 4).

Step 3: process the data

In step 3, I can write the functions that actually do something with the data. When these functions are called, there will be data in the data store.

; get column n of data
(define (column n a-list)
  (map (fn (f) (nth (f n))) a-list))

(define (sum-rows)
    ; in each row, add cells and put value in final cell
    (for (r 0 (- rows 1))
       (nth-set (data r (- columns 1)) (apply add (chop (data r))))))

(define (sum-columns)
    (for (c 0 (- columns 1))
       (nth-set (data (- rows 1) c) (apply add (chop (column c data))))))

(define (class r c)
   (cond 
      ((= c (- columns 1)) "total")
      ((= r (- rows 1)) "total")
      (true "cell")))

The class function returns a suitable CSS class for a cell given its location. I can use this when building the HTML form to assign different colours to the various input boxes.

Step 4: extract data from the incoming form

This step feels like it’s in the wrong place. I want to extract data from the incoming form, because it contains the numbers that the user has typed into the input boxes. But what form? What boxes? I haven’t created them yet – they won’t get created until step 8. Well, I think it’s confusing!

; extract data from the form
(for (r 0 (- rows 1))
   (for (c 0 (- columns 1))
      (set 'raw (float (CGI:get (string r c))))
      (if (number? raw)
          (nth-set (data r c) raw))))

The data store is filled with numbers.

Step 5: do the calculation

If the user clicks the ‘Calculate’ button to submit the form, a call to CGI:get {recalc} will return true, so the main action part of the script can take place here.

(when (CGI:get {recalc}) (sum-rows) (sum-columns))

I haven’t yet defined that form, though…

Step 6: tidy up

Before the data store’s contents are displayed, I want to change the zeroes to spaces, purely for aesthetic reasons. I’ve used the set-ref-all function (new for version 9.3) to convert them.

(set-ref-all (data 0) "")

Step 7: generate HTML, part 1

The output consists of a header line followed by the HTML page. Most of the HTML is a simple form that uses the POST method to send the completed form to this script.

(print "Content-type: text/html\r\n\r\n")

(print [text]<html>
<head><title>A newLISP calculator</title>
<style>#calc .cell {background-color: #ffeeff; }
#calc .total { background-color: #ddffff; }
</style>
</head>
<body>
<form id="calc" action="index.cgi" method="POST">
[/text])

Step 8: generate HTML, part 2

I’m in the middle of the form. Instead of generating the form manually, I’ve got newLISP to do it for me. Each input box is named after its row and column, and is assigned a class name for styling. There’s a simple data-fetch function in it too. newLISP’s format function works fine here – originally I used a more complicated template function but this works well.

(for (r 0 (- rows 1))
  (for (c 0 (- columns 1))
    (println (format [text]<input type="text" class="%s" name="%s" size="6" value="%s">[/text] 
        (class r c) (string r c) (string (data r c))) ))

   ; next row
   (println "
\n"))

Step 9: generate HTML, part 3

The form has a ‘Calculate’ button which sends the ‘recalc’ message to this script – I dealt with the message in step 5 but this is where the message is defined.

(println
[text]    
    <p><input type="submit" name="recalc" value="Calculate"></p>
    </form>
</body>
</html>
[/text])

(exit)

Step 10: Serve hot

It looks like I made it in 10 steps. To test the script, start up a newLISP web server and point it at the right place. This is what I do on my Mac:

$ sudo newlisp -http -d 80 -w ~/lisp/cgi-test/

And then visit http://localhost/index.cgi in a browser and try it out. This last step is probably different on other platforms

Any tips or advice from the CGImen or CGIwomen out there will be welcome!

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: