This is an experimental Lisp Editor designed to make it easier to enter and edit Lisp programs on your T-Deck.
Unlike a screen editor like vi or pico, this editor understands the structure of a Lisp program, and works on Lisp subexpressions. In a screen editor you could inadvertently delete one of a pair of matching brackets – the program will then no longer make sense, and it may be very confusing getting it back to a valid program. In this Lisp Editor the edits will always leave the program correctly structured, making it easier to focus on the changes you want to make without having to worry about ending up with an invalid structure.
This version of the T-Deck Lisp Editor is written in Lisp. The advantage of this is that it’s easier to modify it to change its behaviour, or to add your own commands. In future it could be coded in C and provided as a uLisp Extension file.
28th October 2023: Following initial feedback on this article I’ve added a How to use it section to make it clearer how you use the editor.
To add the T-Deck Lisp Editor to your copy of uLisp
Download Release 5 of the T-Deck firmware from https://github.com/technoblogy/ulisp-tdeck.
This includes a special version of the prettyprinter, used by the Lisp Editor, that can highlight a specified subexpression.
Load this file: T-Deck Lisp Editor.
Save this file as LispLibrary.h.
Put this in the same Arduino project folder as the uLisp source file.
Comment out the definition of LispLibrary at the start of the uLisp source, since we want to supply the LispLibrary definitions in a separate file:
// Lisp Library // const char LispLibrary PROGMEM = "";
Add a reference to the LispLibrary.h file in the main uLisp source file by uncommenting the line:
Uncomment the #define lisplibrary:
This will cause the LispLibrary definitions to be loaded automatically each time you restart uLisp.
Compile and upload uLisp to your board.
The screen editor function definition edit will be added to your Lisp workspace, together with the auxiliary functions %edit and butlast, and the global variable cmds.
How to use it
All the editing commands are single lower-case letters. There are two types of commands: selection commands, to specify what you want to edit, and editing commands, to perform the edit.
After each command the Lisp Editor shows your program, restructured to reflect the changes you have made.
The main selection commands are d (cdr), a (car), and b (back).
d takes the cdr of the current expression, and displays a green block cursor to show your position.
a takes the car of the current expression, and highlights the resulting expression.
b backs up the tree, returning to the state before an a or d.
The main editing commands are r (replace) and i (insert).
r prompts for an expression at the bottom of the screen, and then replaces the current selection with what you type in.
i prompts for an expression at the bottom of the screen, and then inserts what you type in at the current position.
If you make a mistake at any time you can press z to undo the last operation(s) you performed.
Here’s an example session demonstrating how you use the Lisp Editor.
For this example we will try and write a simple routine to find the least prime factor of a number. Here’s our first attempt; it uses mod to find the remainder when the number n is divided by a series of divisors d from 1 to n:
(defun factor (n) (let ((d 1)) (loop (when (> d n) (return n)) (when (zerop (mod n d)) (return d)) (incf d))))
Let’s try the function with 2146654199, which is 46327 x 46337:
21326> (time (factor 2146654199)) 1 Time: 0 ms
Unfortunately it finds the factor 1, which is not what we intended. We need to change the initial value of d to 2.
Edit the function by typing:
The T-deck Lisp Editor shows the function definition:
Type d d to move the green block cursor to the start of the let expression:
Type a to select the let block:
Type d to move the cursor in front of the let assignment and a to select it:
Type a d a to select the 1, and type r to replace the currently selected expression.
You are prompted at the bottom of the screen to enter an expression to replace it:
Enter 2 and press Return to enter it.
The new value is shown in the definition:
We can make the factor function more efficient by only testing divisors up to the square root of n, because the smallest factor must be less than or equal to this. Let’s add this improvement:
Back up to select the let assignment again by typing b b b :
Position the cursor at the end of the let assignment by pressing d :
Now press i to insert an expression at the cursor position.
You are prompted at the bottom of the screen for the expression to insert:
Enter (top (sqrt n)) and press Return.
The expression is shown where you inserted it:
Next, change the comparison to compare d with top :
Type b b d a to select the loop block.
Type f n to find the next occurrence of n .
Press r top to replace it with top :
Finally, type s to save the changes to the definition of function and return to the uLisp prompt.
Check that it works:
21302> (time (factor 2146654199)) 46327 Time: 1.5 s
Here is an alphabetical summary of the commands:
|a||car||Selects the car of the current subexpression.|
|b||back||Backs up the tree.|
|c||cons||Prompts for an expression, and conses in onto the front of the current subexpression.|
|d||cdr||Moves the cursor to the cdr of the current subexpression.|
|f||find||Prompts for an expression, and selects the next occurrence of it.|
|i||insert||A synonym for c. Inserts what you type in at the cursor position.|
|q||quit||Quits from the editor, discarding your edits.|
|r||replace||Prompts for an expression, and replaces the selected subexpression with it.|
|s||save||Saves your edits to the function you were editing.|
|x||delete||Deletes the car of the current form.|
|z||undo||Undoes the last command.|
How it works
Here’s a brief summary of how the editor works:
The edit function reads the key presses you type, and rejects any illegal key presses. It builds up a list of the editing commands you have given in the global variable *cmds*. After each key press it calls %edit , which executes the list of editing commands on the function (or variable) you are editing, and prettyprints the result, using pprint.
This initial release of the Lisp Editor has the restriction that the function you’re editing must fit within the top 22 lines of the screen, above the two-line prompt area. I haven’t worked out the best way to extend the editor to cope with arbitrarily long programs, and would welcome any suggestions.
I can provide information about adding your own commands to the editor in a future article if it’s of interest.