Introduction to newLISP/The debugger

The debugger edit

This section takes a brief look at the built-in debugger.

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

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

Used without arguments, it returns true if the debugger is currently active.

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 "\027[0;37m" "\027[0;0m")

But since I can never remember this, it's in my .init.lsp file, which loads when you start newLISP. If you can't use these sequences, you can use plain strings instead.

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:

(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
       (and
        (not (starts-with item "."))
        (set 'f-name (real-path item-name)) 
        (set 'mtime (file-info f-name 6))
        (if 
          (> (- (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 modified 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 is how to drop into the debugger halfway through a loop. Here's a small portion of code:

(set 'i 0)
(define (f1)
  (inc i))

(define (f2)
  (dotimes (x 100)
    (f1)
    (if (= i 50) (trace true))))

(f2)


Load it from file:

> (load {simpleloop.lsp})

-----
(define (f1)
  (inc i))

[-> 5 ] s|tep n|ext c|ont q|uit > i

50

[-> 5 ] s|tep n|ext c|ont q|uit > 

Notice how the function f1 appeared - you don't get a chance to see anything in f2. At the debugger prompt, you can type in any newLISP expression, and evaluate any functions. Here I've typed i to see the current value of that symbol. newLISP is happy to let you change the values of some symbols, too. You should be able to change the loop variable, for example. 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 will appear in the debugger.[/text]
  ; But this won't.
  (inc i))