Suppose you want to have a selection of your own favourite Lisp functions available whenever you start up uLisp. What’s the best way to do this, and what are the pros and cons of each?
There are now three different ways to do this:
- Saved image: Save an image of the workspace containing the functions, and make it load automatically at startup.
- Lisp Library: Add the definitions of your functions at the start of the uLisp source in the LispLibrary string, and make them load automatically at startup.
- Extensions File: Define the functions in C in a separate Extensions File.
The pros and cons of each of these methods of adding functions are summarised in the following table, and each method is described in greater detail below:
Method | Written in | Uses up Lisp workspace | Need to edit uLisp source | Can still use save-image |
---|---|---|---|---|
Saved image | Lisp | Yes | Only first time | No |
Lisp Library | Lisp | Yes | Yes | Yes |
Extensions File | C | No | Yes | Yes |
I’ll demonstrate each of these methods by providing the function remove:
(defun remove (x lst)
(cond
((null lst) nil)
((eq x (car lst)) (remove x (cdr lst)))
(t (cons (car lst) (remove x (cdr lst))))))
This returns a copy of lst with items eq to x removed:
> (remove 'the '(the cat sat on the mat))
(cat sat on mat)
> (remove 'dog '(the cat sat on the mat))
(the cat sat on the mat)
Saved image
To make uLisp automatically reload a saved image on startup you first need to uncomment the directive:
#define resetautorun
and recompile and upload uLisp.
You also need to provide a function with no parameters which will be run on startup, such as this function that flashes the built-in LED to confirm that the Lisp image has been loaded:
(defun flash ()
(pinmode :led-builtin t)
(digitalwrite :led-builtin t)
(delay 500)
(digitalwrite :led-builtin nil))
Also, define remove as shown above.
Now save the current Lisp workspace by entering:
(save-image 'flash)
After restarting uLisp you should see the LED flash, and you can confirm that your functions have been loaded by typing:
(pprintall)
Lisp Library
The Lisp Library feature allows you to extend uLisp with your own library of function or variable definitions written in Lisp. The definitions are stored in the uLisp source file, as text strings in a C string called LispLibrary[] .
You have to do a small amount of formatting to make the uLisp function definitions compatible with C strings. For example, you have to enclose each line of the function in double quotes, and escape any double quotes that occur in the Lisp function:
// Lisp Library
const char LispLibrary[] PROGMEM =
"(defun remove (x lst)"
" (cond"
" ((null lst) nil)"
" ((eq x (car lst)) (remove x (cdr lst)))"
" (t (cons (car lst) (remove x (cdr lst))))))";
For a full explanation, with examples, see Lisp Library.
To make the functions you have defined load at startup you need to uncomment the #define :
#define lisplibrary
and recompile and upload uLisp.
Again, you can confirm that your functions have been loaded by typing:
(pprintall)
Extensions file
As of release 4.4 of uLisp you can extend uLisp without needing to edit the original uLisp source file, by including extra user functions in a separate Extensions file.
The functions need to be written in C, and so this requires a greater degree of programming knowledge, and also requires you to understand how functions are implemented in uLisp. However, for some tasks, such as adding functions to uLisp to interface with an external library, this is the only way to do it. For more information see Adding your own functions.
Here’s a possible implementation of the Lisp remove function in C:
object *remove (object *x, object *lst) {
if (lst == NULL) return nil;
else if (x == car(lst)) return remove(x, cdr(lst));
else return cons(car(lst), remove(x, cdr(lst)));
}
object *fn_remove (object *args, object *env) {
(void) env;
return remove(first(args), second(args));
}
// Symbol names
const char stringremove[] PROGMEM = "remove";
// Documentation strings
const char docremove[] PROGMEM = "(remove x lst)\n"
"Returns a copy of lst with items eq to x removed.";
// Symbol lookup table
const tbl_entry_t lookup_table2[] PROGMEM = {
{ stringremove, fn_remove, 0222, docremove },
};
This defines it recursively, just like the Lisp version, to show how you can do an almost direct conversion from Lisp to C.
Here’s the whole Extensions file that you can compile with uLisp to incorporate this function: Remove Extension file.