(archive 'newLISPer)

October 25, 2007

Duck season: part 1

Filed under: newLISP — newlisper @ 17:50

In these days of 80 GB iPods and 500 GB hard drives it’s always refreshing to see some elegant minimalism in action. newLISP is surprisingly modest in size. At just 243 KB, the newLISP binary is easily an order of magnitude smaller than just one of the JPEG images output by my digital camera. Of course, with the addition of modules, utilities, documentation, and Java graphics libraries, the size of a practical newLISP installation is a download of over 1 MB. Ouch.

Tucked away in a corner, somehow, somewhere, there’s a little debugger. It’s simple, but useful. If, like me, you haven’t used this very much, join me in this quick tour.

The key function is trace. To start and stop the debugger, use trace with either true or false:

(trace true)  ; start debugging
(trace nil)   ; stop debugging

Used on its own, trace returns true if the debugger is active.

One more thing and we’re ready to go bug hunting.The trace-highlight command lets you control the display of the expression that’s currently being evaluated, and some of the prompts. I’m using a VT100-compatible terminal, so I can use weird escape sequences to set the colours. Type this in at the newLISP prompt:

(trace-highlight "27[0;37m" "27[0;0m")

But since I can never remember this, it’s in my .init.lsp file, which runs if /usr/share/newlisp/init.lsp does when you start newLISP. If you can’t use these sequences, you can use plain strings instead. (Unicode symbols also work in this context, but I find the colour change more effective than funny little pictures.)

The other debugging function is debug. This is really just a short-cut for switching tracing on, running a function in the debugger, and then switching tracing off again.

So, suppose we want to run a file called old-file-scanner.lsp, which contains the following code:

#!/usr/bin/env newlisp

(define (walk-tree folder)
  (dolist (item (directory folder))
   (set 'item-name (string folder "/" item))
   (if (and (not (starts-with item ".")) (directory? item-name))
       ; folder 
       (walk-tree item-name)
       ; file
        (not (starts-with item "."))
        (set 'f-name (real-path item-name)) 
        (set 'mtime (file-info f-name 6))
          (> (- (date-value) mtime) (* 5 365 24 60 60)) ; non-leap years :)
            (push (list mtime item-name) results)))   

(set 'results '())
(walk-tree {/usr/share})
(map (fn (i) (println (date (first i)) { } (last i))) (sort results))

This scans a directory and subdirectories for files previously modfied 5 or more years ago. (Notice that the file ends with expressions that will be evaluated immediately the file is loaded.) First, switch tracing on:

(trace true)

Then load and start debugging:

(load {old-file-scanner.lsp})

Or, instead of those two lines, type this one:

(debug (load {old-file-scanner.lsp}))

Either way, you’ll see the first expression in the walk-tree function highlighted, awaiting evaluation:

Now you can press the s, n, or c keys (‘step’, ‘next’, and ‘continue’) to proceed through your functions: ‘step’ evaluates every expression, and steps into other functions as they are called; ‘next’ evaluates everything until it reaches the next expression at the same level; and ‘continue’ runs through without stopping again.

If you’re clever, you can put a (trace true) expression just before the place where you want to start debugging. If possible, newLISP will stop just before that expression and show you the function it’s just about to evaluate. In this case, you can start executing the script with a simple (load…) expression – don’t use debug if you want to skip over the preliminary parts of the script. I think newLISP usually prefers to enter the debugger at the start of a function or function call – you probably won’t be able to drop in to a function part way through. But you might be able to organize things so that you can do something similar. Here, for example, is a script that drops into the debugger halfway through a loop:

(set 'i 0)

(define (f1)
  (inc 'i))

(define (f2)
  (dotimes (x 100)
    (if (= i 50) (trace true))))
> (load {simplelooop.lsp})
(define (f1 )
  (inc 'i))
[-> 5 ] s|tep n|ext c|ont q|uit > i
[-> 5 ] s|tep n|ext c|ont q|uit > 

Notice how the function f1 appeared – you don’t see anything in f2 this way.

At the debugger prompt, you can type in any newLISP expression, and evaluate any functions. newLISP seems to be happy to let you change the values of some symbols, too. But don’t redefine any functions… if you try to pull the rug from underneath newLISP’s feet you’ll probably succeed in making it fall over.

The source code displayed by the debugger doesn’t include comments, so if you want to leave yourself helpful remarks – or inspiration – when looking at your code, use text strings rather than comments:

  (define (f1)
    [text]This sentence will appear in the debugger.[/text]
    ; But this sentence won't.
     (inc 'i))

Sometimes, though, it’s not the detailed workings of individual functions that you’ll want to examine. It can be more useful to stand further away and observe the data as it flows from from function to function.

Next time – after Duck Season, Rabbit Season!

Comment from cormullion

still testing cookies…


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: