Read-time evaluation


#1

The latest release 3.0a of uLisp includes a feature from Common Lisp that I think will be especially useful on small memory implementations of uLisp: the #. read-time eval macro. This causes the atom or form after the #. to be evaluated when the code is read in.

To illustrate its use here’s a simple example.

A familiar routine to get the decimal value of a hexadecimal character is:

(defun hex (h)
  (let ((c (char-code h)))
    (cond
     ((<= (char-code #\0) c (char-code #\9))
      (- c (char-code #\0)))
     ((<= (char-code #\A) c (char-code #\F)) 
      (- c (- (char-code #\A) 10))))))

For example:

> (hex #\F)
15

It’s pretty clear to see what’s going on here, but we can write it more efficiently like this:

(defun hex (h)
  (let ((c (char-code h)))
    (cond
     ((<= 48 c 57)
      (- c 48))
     ((<= 65 c 70) 
      (- c 55)))))

However, now the meaning is much more obscure. For example, what’s “55” doing here?

With the #. read-time eval macro we can have the best of both worlds; a legible program, but efficient code:

(defun hex (h)
  (let ((c (char-code h)))
    (cond
     ((<= #.(char-code #\0) c #.(char-code #\9))
      (- c #.(char-code #\0)))
     ((<= #.(char-code #\A) c #.(char-code #\F)) 
      (- c #.(- (char-code #\A) 10))))))

When this is read in, uLisp evaluates the forms after each #. read-time eval macro, and stores the result in the code. We can see this if we prettyprint the definition:

> (pprintall)

(defun hex (h)
  (let ((c (char-code h)))
    (cond ((<= 48 c 57) (- c 48)) ((<= 65 c 70) (- c 55)))))

This not only reduces the code size, but makes the execution faster.

You can also include global variables in the code being evaluated at read-time. For example, you could now write the definition of diff more efficiently from my forum topic GPS interface using uLisp:

(defvar *degree* 600000)

(defun diff (deg1 deg2)
  (let ((result (- deg2 deg1)))
    (cond
     ((> result #.(* *degree* 180)) (- result #.(* 360 *degree*)))
     ((< result #.(* *degree* -180)) (+ result #.(* 360 *degree*)))
     (t result))))

Read-byte, case and char-code