Debugging in uLisp

If your uLisp program is refusing to run, or is not doing what you wanted or expected it to do, what should you do next? Here are some suggestions for debugging uLisp programs.

Trace your functions

The trace facility allows you to debug your program by following the execution of specified functions. When you turn on tracing for a function, the function’s name, arguments, and returned value are printed out while your program is executing.

uLisp allows you to trace up to three functions at a time. The commands (trace ...) and (untrace ...) allow you to turn on and off tracing for particular functions.

Example

As an example, consider this recursive function bi, which bisects a list into two halves:

(defun bi (n lis)
  (if (zerop n)
      (list nil lis)
    (let ((l (bi (1- n) (cdr lis))))
      (list (cons (car lis) (first l))
            (second l)))))

It takes an integer, n, and a list, lis, and divides the list into two parts so the first half contains n elements:

> (bi 3 '(a b c d e f g))
((a b c) (d e f g))
We can get an insight into how it works by turning on trace:
> (trace bi)
(bi)
The trace command returns a list of all the functions currently being traced. Now if we call bi the calls are traced:
> (bi 3 '(a b c d e f g))
0: (bi 3 (a b c d e f g))
  1: (bi 2 (b c d e f g))
    2: (bi 1 (c d e f g))
      3: (bi 0 (d e f g))
      3: bi returned (nil (d e f g))
    2: bi returned ((c) (d e f g))
  1: bi returned ((b c) (d e f g))
0: bi returned ((a b c) (d e f g))
((a b c) (d e f g))

Pretty print your functions

It's much easier to check the structure of your functions if you list them in a neatly formatted way. For example, after defining the function bi in the previous example you could print it in a structured way using the command:

> (pprint bi)

(lambda (n lis)
  (if (zerop n)
    (list nil lis)
    (let ((l (bi (1- n) (cdr lis))))
      (list (cons (car lis) (first l)) (second l)))))

Insert a break

The break function interrupts your program and jumps to a prompt where you can examine the values of the local variables, or even change them. You can then type nil to continue, executing the rest of your program from where was interrupted.

For example, here's a program intended to set the brightness of an LED for values of x from 1 to 16 by calculating y = x2 - 1 and setting the analogue output to y:

(defun set (x) (let ((x (* x x)) (y (- x 1))) (analogwrite 9 y)))

Unfortunately the LED is always dim, even when we execute (set 16). What's going on? The solution is to insert a call to break:

(defun set (x) (let ((x (* x x)) (y (- x 1))) (break) (analogwrite 9 y)))

Now when we run it we can examine the variables when the program is interrupted with the break:

> (set 16)

Break!
: 1> x
256

: 1> y
15

Type nil to exit from break and continue execution.

The problem is revealed: y is taking the value of x from the parameter, not the previous let assignment. What we need is let*:

(defun set (x) (let* ((x (* x x)) (y (- x 1))) (analogwrite 9 y))) 
Now the program works as intended!

Insert print statements

The print function takes an argument, prints its value, and returns the same value, so you can wrap a print statement around most parts of a uLisp program to see what's happening without affecting its operation.

For example, here's a program which is intended to sum the numbers 1 to 4:

(let ((sum 0)) (dotimes (x 4) (incf sum x)) sum)

When we run it we get the answer 6 rather than 10 as expected. What's going on? One approach is to put print around the x as follows:

(let ((sum 0)) (dotimes (x 4) (incf sum (print x))) sum)

Now when we run it the value of x gets printed each time around the loop:

> (let ((sum 0)) (dotimes (x 4) (incf sum (print x))) sum)

0 
1 
2 
3 
6
The problem is revealed: the dotimes loop counts from 0 to 3 rather than 1 to 4.

Contact us

If all else fails, post your problem on the uLisp Forum so that the community can help you.