Any true LISP environment provides a command line interface called REPL (read-eval-print-loop). Programmer can at any time write an expression, press enter and see the result of the evaluation. What happens is that first the string input is converted to lisp symbolic expression (list data structure) by reader (parser, interpreter), which also ignores the comments and performs the auto-upper-casing. Then the expression is evaluated or evaled, and the result is printed back to the output window. So, after writing (+ 3 5)
, following text appears in output:
USER>(+ 3 5) 8
In principle, the REPL is itself a LISP program (LOOP (PRINT (EVAL (READ))))
. The PRINT
function outputs strings in the same format that the READ
function can read. LabLisp does not have yet function READ
that would read from user input, and also not yet the EVAL
as a function.
LabLisp environment is integrated to other programs and the LabLisp window is invoked with the LISP alien button :
The resizable, non-modal, LabLisp window is quite simple. It contains three panels in separator - each panel can be resized with respect to other. Upper panel is the output view, in the middle panel, the list of running processes is shown, and lowest panel is the user input window. For convenience, there are two modes for entering commands. In the default single-line mode, the command is sent after pressing enter. Arrows up and down access the command history. In the multi-line editing mode (selected by checkbox in the lower right corner), the user can write several lines of code using enter key for line breaks and the up and down arrows for moving in the source text. Command is then executed after pressing the Run button.
In LabLisp (since version 1.2) it is possible to run several processes at the same time. LabLisp provides simple multitasking, with the intended use of running concurrent scripts in the data acquisition software – for example, user can execute some data analysis script, while another script is controlling an ongoing experiment. The multiple processes are living in the same LISP environment and have access to the same global variable and function namespace. The multitasking is emulated by sequential calls to evaluation steps of the individual processes. This means the processes cannot access the same variable at the same time, but of course it is possible to create chaos (race condition) if the concurrent processes access and uncontrollably change same global variable sequentially.
Running processes are indexed and identified by an internal counter, and when multiple processes are running, their outputs are marked with prompts p#00>
, p#01>
etc. Additionally, the processes are named. The name does not serve as an identifier, only as convenience. It can be freely changed by the user or by the process itself (name-process “bla”)
. The name can be used to report the status of running process in the panel showing the list of running processes.
Typically, a process will be running a LabLisp program performing some experiment, which means that most of the time the process is waiting for some hardware or measurement operation to finish. LabLisp provides a special form (wait c)
, where c
is a condition, that is repeatedly evaluated, until it is nil
.
Process can be killed by user, using the process number as identifier (kill 0)
, or abort itself by (self-kill)
.
Usual LISP print behavior is that printed output can be read back by the reader. There are some objects that cannot be printed: function and package objects are printed as, e.g. #<function LIST>
, #<The USER package>
. Reader cannot reconstruct the object from such printed form and instead reads NIL
.
Although we did not yet mention how lists are constructed, let's just say that dotted list is printed as (A B C D . E)
and can be read exactly back by reader. The dot .
operator needs to have white space before. Token a.b
will not be read as cons (A . B)
, but as single symbol A.B
. For details, see section List construction with conscells. Unlike the quoting/unquoting operators (‘
, ,
etc.), the dot .
does not represent any named special operator (like QUOTE
, UNQUOTE
). It is purely reader operation, and is resolved by the reader.
It is possible to create cyclic lists, which can be printed with anchor #1=
and link #1#
system.
USER>(setq li '(a b c)) ; LI is proper list (A B C) USER>(setf (cdddr li) li) ; SETFing LI to the tail of itself #1=(A B C . #1#) ; now LI is cyclic A B C A B C A B ...
Token #1=
(anchor) represents the object that will be linked. Token #1#
(link) corresponds to the place where the linked object is located. Multiple links and anchors can appear in single expression. Their numbers are positive integers (#2#
, #3#
…).
Reader understands the system of anchors and links, which can be used also for non cyclic, shared structure. Experimenting might be dangerous, since the EVAL
algorithm is not protected against cyclic forms. This is bit unfortunate omission in the recent updates to LabLisp. All other functions are safe against cyclic data lists.
Printer (PRINT
function) will always print cyclic list using the anchor-link. However, there can be shared, but non-cyclic structures, that can be optionally printed with anchor-link. Let's have an example:
USER>(setq li '(a b c)) ; LI is proper list (A B C) USER>(list li li) ; LI is existing list in memory ((A B C) (A B C)) ; both first and second element are the same list LI
Output of this example is shared structure, both first and second element are the same list (A B C)
- identical entity, but printed twice. There is global variable *PRINT-SHARED*
, by default is NIL
, leading to the behavior above.
USER>(setq *print-shared* T) ; toggle to T T USER>(list li li) (#1=(A B C) #1#) ; under link #1# is the same (A B C)
After switching the *PRINT-SHARED*
variable to T
, the shared structure is printed using anchor and link.
For details, see sections List construction with conscells and Cyclic lists.
With the help of some special characters, the reader can be switched to different modes. Obviously, text between normal double quotes “text”
is read as string literal, but reader still visits the text and checks for escaped characters. In case we need to read string that includes some of the escaping characters, but we do not want to escape anything, pair of sharp-double quotes #“ ”#
can be used:
USER>#"text \\ with "" || stuff"# "text \\ with "" || stuff"
There is another, particularly special reader mode, designed for reading Windows filenames with paths, i.e. delimited by back-slashes. Text between the pair #* *#
will be read to strings, back-slashes (escape characters) will be read as they are, but space will be used as separator. File paths that include spaces must be additionally enclosed by double-quotes. It is good idea to have folder structure with folder names without spaces (thank you, “Program Files”).
USER>(list #* c:\data\ c:\user\file.txt "c:\Program Files\" *#) ("c:\data\" "c:\user\file.txt" "c:\Program Files\")
In this example we have created list of 3 strings.
Apart of simple command line history, mentioned above, lisp environment holds symbols that are bound to recent results and recently evaled forms.
Asterisk symbol *
evals to latest result. Remember that LabLISP, like Common LISP, has a separate value and function namespace, so *
represents multiplication if used as first symbol in a form, but as standalone, it evals to previously returned value.
>(* 3 5) 15 >* 15 >(* * *) ; is (* 15 15) 225
Symbols **
and ***
return last-but-one result and third last result.
In case the recent results were multiple values, we can recall them as list using symbols /
(as division), //
and ///
.
>(round 3.14) 3 0.14 >/ (3 0.14)
Finally, plus symbol +
and ++
and +++
recall recently evaluated forms.
>(+ 5 7) 12 >+ (+ 5 7)
The recently evaled forms are also accessible by pressing arrow-up, but the +
symbol can be used in situations like this:
>(defmacro mac (a b) `(+ ,a ,b)) ; let's define simple macro MAC USER>(mac 5 6) ; we try it on some values 11 USER>(macroexpand +) ; now we save ourselves some typing (+ 5 6) T
Note that whenever we use one of these symbols, the returns are not “saved”.
USER>(+ 1 2) 3 USER>(+ 3 4) 7 USER>(+ 5 6) 11 USER>* ; last return was 11 11 ; but this value is not stored to the recent USER>** 7 ; last-but-one is still 7 USER>* 11 ; and 11 is still stored as last return USER>*** 3 ; 3 is still stored as third last return
next → Forms and evaluation rules