commit 68ade6208de1e4eaffaab7727b8ce2a2a1b5b992 Author: Fulton Browne Date: Sun Aug 1 15:43:21 2021 -0700 init commit diff --git a/Changes b/Changes new file mode 100644 index 0000000..65d6d32 --- /dev/null +++ b/Changes @@ -0,0 +1,75 @@ +"LISP System Implementation", post-publication changes + +[!] indicates changes that break compatibility with the book version + +20200220 + Fixed a mistake in the description of IF*. Thanks, Brian! + +20191221 + Fixed various typos in the reference manual. Thanks to + Wojciech Gac for pointing them out! + +20190812 + string(Obmap) could be undefined in gc(). + +20190804 + Fixed GC leak in intern() function. + +20190728 + Corrected formal grammar (lisp9.txt), added RENAME test to + test suite. + +20190726 + Added new opcodes (FLUSH, RENAME) to the disassembler. + +20190724 + Made WITH-INFILE and WITH-OUTFILE restore the previous current + I/O port when exiting their dynamic extent via non-local exit. + + Made WITH-INPORT and WITH-OUTPORT close their respective file + when exiting their dynamic extent via non-local exit. + +20190719 + Added RENAME function for renaming files. + +20190711 + Added FLUSH function for writing pending output to ports. + +20190705 + Reset error handler (*Errtag*) in the REPL. + +20190627 + Even though the result if SYMNAME is constant, it still has + to copy the name, because vector atoms cannot share their + payloads. + +20190626 [!] + Comments are now the usual, non-persistent, reader-level + comments. It was a nice experiment, but in the end comments + as objects caused more trouble than benefit. + +20190626 + Reference trace printed in wrong order in error messages. + +20190617 [!] + SYMNAME returns an immutable string now. + +20190615 + vector(Obarray) could be undefined in gc(). + Thanks, Alexander Shendi! + +20190613 + Added README. + Thanks, Harsh Raju Chamarthi! + +20190610 + Sending SIGINT while running an image via START drops you to + the REPL. Did exit LISP9 before. + +20190603 + Fixed GC leak in cons3() with ptag==CONST_TAG. + +20190603 + veclen(Obarray) could be undefined in gc(). + Thanks, Alexander Shendi! + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9a9bf5a --- /dev/null +++ b/Makefile @@ -0,0 +1,55 @@ +A=lisp9.tgz +R=lisp9-20200220.tgz +CFLAGS= -g -O2 + +all: ls9 ls9.image # prolog # lisp9.ps + +ls9: ls9.c + $(CC) $(CFLAGS) -o ls9 ls9.c + +ls9.image: ls9 ls9.ls9 + rm -f ls9.image + echo "(dump-image \"ls9.image\")" | ./ls9 -q + echo "(save)" | ./ls9 -l src/help.ls9 -l src/disasm.ls9 -l src/grind.ls9 -l src/repl.ls9 + +lisp9.tr: lisp9.txt + ./ls9 src/print.ls9 -T -C -p 60 -l 6 -m -4 -t "LISP9 REFERENCE MANUAL" \ + lisp9.txt >lisp9.tr + +lisp9.ps: lisp9.tr + groff -Tps -P-p11i,8.5i lisp9.tr >lisp9.ps + +test: ls9 ls9.image + ./ls9 test.ls9 + +ptest: ls9 prolog + ./ls9 -i prolog -- -q src/test.out + diff -u src/test.OK src/test.out && rm src/test.out + +zebra: ls9 prolog src/zebra.pl9 + echo "prlist([])." \ + "prlist([H|T]) :- write(H), nl, prlist(T)." \ + ":- nl, zebra(H), !, prlist(H), fail." \ + | ./ls9 -i prolog -- -q -c src/zebra.pl9 + +prolog: ls9 src/prolog.ls9 src/prolog.pl9 + echo "(defun (start) (prolog) (quit)) (dump-image \"prolog\")" \ + | ./ls9 -ql src/prolog.ls9 + +arc: clean + tar cf - * | gzip -c9 >$A + +dist: clean + cd ..; tar -cvf - `cat lisp9/_nodist` lisp9 | gzip -9c >$R + mv ../$R . + +csums: + csum -u <_csums >_csums.new + mv _csums.new _csums + +mksums: clean + find . -type f | grep -v _csums |grep -v $A | csum >_csums + +clean: + rm -f ls9 ls9.image *.oimage prolog lisp9.ps lisp9.tr lisp9.ps \ + $A $R a.out *.core diff --git a/Notes b/Notes new file mode 100644 index 0000000..cc34f9a --- /dev/null +++ b/Notes @@ -0,0 +1,4 @@ + + ? strnum should parse #nR + + structure editor diff --git a/README b/README new file mode 100644 index 0000000..8113b84 --- /dev/null +++ b/README @@ -0,0 +1,63 @@ + + LISP9 -- an experimental LISP system + Nils M Holm, 2018, 2019 + In the public domain. + + If your country does not have a concept like the public + domain, the Creative Common Zero (CC0) licence applies. + See https://creativecommons.org/publicdomain/zero/1.0/ + + In order to build LISP9, an ANSI C89 / ISO C90 compiler is + needed. In addition, the rename() system call has to be + present, but it can be removed from the code without any + consequences other than the RENAME function not working. + + To build the LISP9 system, run "make". Without make, run + + cc -O2 -o ls9 ls9.c + echo '(dump-image "ls9.image")' | ./ls9 -q + + To make sure that the system works properly, run "make test" + or "./ls9 test.ls9". + + For a summary of command line options, run "./ls9 -h". + + To build an image containing the online help system, run + + echo "(save)" | ./ls9 -l src/help.ls9 + + When starting LISP9 for the next time, you can then use + + ,h,t to view the table of contents + ,h,t chapter to view the contents of a chapter + ,h topic to view the section about a given topic + + To build an image containing the online help system, the + LAM disassembler, and the pretty printer (grinder), run + + echo "(save)" | \ + ./ls9 -l src/help.ls9 -l src/disasm.ls9 -l src/grind.ls9 -l src/repl.ls9 + + To start the interpreter in interactive mode, run + + ./ls9 + + The interpreter prompt is a single "*". Expressions typed at + the prompt will evaluate and print their results. The most + recently printed result will be bound to the variable **, so + you can reuse the result without typing it, e.g.: + + * (mapcar list '(a b c) '(1 2 3)) + ((a 1) (b 2) (c 3)) + * (assq 'b **) + (b 2) + + There are a few shortcuts that work only on the REPL: + + ,c command will pass "command" to the shell + ,h topic will display sections of the manual (see above) + ,l file will load "file.ls9". + + To end a LISP9 session, send an EOF marker (control-D on Unix) + to the system or enter (quit). + diff --git a/_csums b/_csums new file mode 100644 index 0000000..ee3d9ce --- /dev/null +++ b/_csums @@ -0,0 +1,29 @@ +11981 2 ./Makefile +53964 1 ./Notes +39716 2 ./README +51736 109 ./ls9.c +50427 20 ./ls9.ls9 +62016 61 ./test.ls9 +29837 1 ./_nodist +22069 2 ./Changes +50958 110 ./lisp9.txt +42503 5 ./cover.ps +7095 1 ./src/array.ls9 +54773 19 ./src/boyer.ls9 +52587 1 ./src/ctak.ls9 +47196 6 ./src/disasm.ls9 +22523 11 ./src/grind.ls9 +13935 2 ./src/hash.ls9 +26170 4 ./src/help.ls9 +6616 1 ./src/ltak.ls9 +54934 4 ./src/meta.ls9 +49080 1 ./src/nreconc.ls9 +26599 6 ./src/print.ls9 +22774 53 ./src/prolog.ls9 +48668 1 ./src/start.ls9 +65089 2 ./src/zebra.pl9 +35990 1 ./src/iota.ls9 +3035 14 ./src/test.pl9 +34152 1 ./src/prolog.pl9 +13977 17 ./src/test.OK +34603 3 ./src/examples.pl9 diff --git a/cover.ps b/cover.ps new file mode 100644 index 0000000..8ab9199 --- /dev/null +++ b/cover.ps @@ -0,0 +1,113 @@ +%!PS-Adobe-3.0 +%%Creator: NMH +%%DocumentMedia: Plain 1242 810 0 white () +%%PageOrder: Ascend +%%LanguageLevel: 2 +%%BoundingBox: 0 0 1242 810 +%%EndComments + +0.0 0.25 0.0 setrgbcolor +0 0 moveto 1281 0 lineto 1281 809 lineto 0 809 lineto 0 0 lineto fill + +/scale 1.4 def +/xpos 670 def +/ypos 500 def + +/rl { exch scale mul exch scale mul rlineto } def +/rm { exch scale mul exch scale mul rmoveto } def + +/lisp { +70 0 rl 20 40 rl -30 0 rl 30 60 rl -40 0 rl -50 -100 rl + +70 0 rm 50 100 rl 40 0 rl -50 -100 rl -40 0 rl + +40 0 rm 20 40 rl 20 0 rl -10 20 rl 20 40 rl 70 0 rl +-20 -40 rl -20 0 rl 10 -20 rl -20 -40 rl -70 0 rl + +70 0 rm 20 40 rl 10 20 rm 20 40 rl 70 0 rl -30 -60 rl +-20 0 rl -20 -40 rl -50 0 rl + +70 0 rm 30 60 rm 20 40 rl 50 0 rl 20 -40 rl -30 -60 rl +-50 0 rl 20 40 rl -20 0 rl -10 20 rl +} def + +1 setlinecap +7 setlinewidth + +0 0.15 0 setrgbcolor +xpos ypos moveto +lisp fill + +0.0 1.0 0 setrgbcolor +xpos ypos moveto +lisp stroke + +/sp { 1.25 0 rm } def +/a { 0 5 rl 1 1 rl 1 0 rl 1 -1 rl 0 -2 rl -3 0 rl 3 0 rl 0 -3 rl sp } def +/b { 0 6 rl 2 0 rl 1 -1 rl 0 -2 rl -3 0 rl 2 0 rl 1 -1 rl 0 -2 rl -3 0 rl + 3 0 rm sp } def +/c { 3 1 rm -1 -1 rl -1 0 rl -1 1 rl 0 4 rl 1 1 rl 1 0 rl 1 -1 rl 0 -5 rm + sp } def +/d { 0 6 rl 2 0 rl 1 -1 rl 0 -4 rl -1 -1 rl -2 0 rl 3 0 rm sp } def +/e { 0 6 rl 3 0 rl -3 -3 rm 2 0 rl -2 0 rm 0 -3 rl 3 0 rl sp } def +/f { 0 6 rl 3 0 rl -3 -3 rm 2 0 rl -2 0 rm 0 -3 rl 3 0 rm sp } def +/g { 1 3 rm 2 0 rl 0 -2 rl -1 -1 rl -1 0 rl -1 1 rl 0 4 rl 1 1 rl 1 0 rl + 1 -1 rl 0 -5 rm sp } def +/h { 0 6 rl 0 0 rm 0 -3 rl 3 0 rl 0 3 rl 0 0 rm 0 -6 rl sp } def +/i { 3 0 rl 0 0 rm -1.5 0 rl 0 6 rl -1.5 0 rl 0 0 rm 3 0 rl 0 -6 rm sp } def +/j { 0 6 rm 3 0 rm 0 -5 rl -1 -1 rl -1 0 rl -1 1 rl 3 -1 rm sp } def +/k { 0 6 rl 0 0 rm 0 -2 rl 3 -3 rl 0 -1 rl -3 3 rm 3 3 rl 0 -6 rm sp } def +/l { 0 6 rl 0 0 rm 0 -6 rl 3 0 rl sp } def +/m { 0 6 rl 0 0 rm 1.5 -1.5 rl 1.5 1.5 rl 0 0 rm 0 -6 rl sp } def +/n { 0 6 rl 0 0 rm 0 -1 rl 3 -4 rl 0 5 rl 0 0 rm 0 -6 rl sp } def +/o { 0 1 rm 0 4 rl 1 1 rl 1 0 rl 1 -1 rl 0 -4 rl -1 -1 rl -1 0 rl -1 1 rl + 3 -1 rm sp } def +/p { 0 6 rl 2 0 rl 1 -1 rl 0 -1 rl -1 -1 rl -2 0 rl 3 -3 rm sp } def +/q { 0 1 rm 0 4 rl 1 1 rl 1 0 rl 1 -1 rl 0 -4 rl -1 -1 rl -1 0 rl -1 1 rl + 2 0 rm 1 -1 rl sp } def +/r { 0 6 rl 2 0 rl 1 -1 rl 0 -1 rl -1 -1 rl -2 0 rl 1 0 rm 2 -3 rl sp } def +/s { 3 5 rm -1 1 rl -1 0 rl -1 -1 rl 0 -1 rl 1 -1 rl 1 0 rl 1 -1 rl 0 -1 rl + -1 -1 rl -1 0 rl -1 1 rl 3 -1 rm sp } def +/t { 1.5 0 rm 0 6 rl -1.5 0 rl 0 0 rm 3 0 rl 0 -6 rm sp } def +/u { 1 0 rm -1 1 rl 0 5 rl 1 -6 rm 1 0 rl 1 1 rl 0 5 rl 0 -6 rm sp } def +/v { 1.5 0 rm -1.5 2 rl 0 4 rl 1.5 -6 rm 1.5 2 rl 0 4 rl 0 -6 rm sp } def +/w { 0 6 rl 0 -6 rm 1.5 1.5 rl 0 0 rm 1.5 -1.5 rl 0 0 rm 0 6 rl 0 -6 rm + sp } def +/x { 0 1 rl 3 4 rl 0 1 rl -3 0 rm 0 -1 rl 3 -4 rl 0 -1 rl sp } def +/y { 1.5 0 rm 0 3 rl -1.5 1.5 rl 0 1.5 rl 3 0 rm 0 -1.5 rl -1.5 -1.5 rl + 1.5 -3 rm sp } def +/z { 0 6 rm 3 0 rl 0 -1 rl -3 -4 rl 0 -1 rl 3 0 rl sp } def +/d0 { 0 1 rm 0 4 rl 1 1 rl 1 0 rl 1 -1 rl 0 -4 rl -1 -1 rl -1 0 rl -1 1 rl + 1 -1 rm 1 6 rl 1 -6 rm sp } def +/d1 { 3 0 rl -1.5 0 rm 0 6 rl -1.5 -1.5 rl 3 -4.5 rm sp } def +/d2 { 3 0 rl -3 0 rm 0 1 rl 3 3 rl 0 1 rl -1 1 rl -1 0 rl -1 -1 rl 3 -5 rm + sp } def +/d3 { 0 5 rm 1 1 rl 1 0 rl 1 -1 rl 0 -1 rl -1 -1 rl -1 0 rl 1 0 rm 1 -1 rl + 0 -1 rl -1 -1 rl -1 0 rl -1 1 rl 3 -1 rm sp } def +/d4 { 0 3 rm 0 3 rl 0 -3 rm 3 0 rl 0 3 rl 0 0 rm 0 -6 rl sp } def +/d5 { 3 6 rm -3 0 rl 0 -3 rl 2 0 rl 1 -1 rl 0 -1 rl -1 -1 rl -1 0 rl -1 1 rl + 3 -1 rm sp } def +/d6 { 3 5 rm -1 1 rl -1 0 rl -1 -1 rl 0 -2 rl 2 0 rl 1 -1 rl 0 -1 rl -1 -1 rl + -1 0 rl -1 1 rl 0 2 rl 3 -3 rm sp } def +/d7 { 0 6 rm 3 0 rl 0 -1 rl -3 -5 rl 3 0 rm sp } def +/d8 { 0 1 rm 0 1 rl 1 1 rl 1 0 rl 1 -1 rl 0 -1 rl -1 -1 rl -1 0 rl -1 1 rl + 1 2 rm -1 1 rl 0 1 rl 1 1 rl 1 0 rl 1 -1 rl 0 -1 rl -1 -1 rl 1 -3 rm + sp } def +/d9 { 3 3 rm -2 0 rl -1 1 rl 0 1 rl 1 1 rl 1 0 rl 1 -1 rl 0 -4 rl -1 -1 rl + -1 0 rl -1 1 rl 3 -1 rm sp } def + +670 420 moveto +5 setlinewidth +/scale 7.96 def + +r e f e r e n c e sp sp m a n u a l stroke + +670 100 moveto +/scale 5 def +3 setlinewidth +n i l s sp sp m sp sp h o l m stroke + +1010 100 moveto +d2 d0 d1 d9 sp d0 d7 sp d2 d6 stroke + +621 0 moveto 621 810 lineto stroke diff --git a/lisp9.txt b/lisp9.txt new file mode 100644 index 0000000..64591ee --- /dev/null +++ b/lisp9.txt @@ -0,0 +1,3621 @@ + + ____ _______________________ + / / / / / / \ + / / / / __/ - / - \ + / /__/ /_\ \ __/\__ / + / / / / / / / + /______/___/______/____/ /____/ + + 2019-07-28 + + By Nils M Holm, in the public domain + + LISP9 is a lexically scoped LISP-1 with tail call elimination. + It is a retro LISP in the sense that it does not add any new + ideas, but merely implements concepts that have been established + long ago. LISP9 is similar to R4RS Scheme to a degree that many + trivial Scheme programs can probably be converted to LISP9 by + performing a few simple substitutions, like SET! -> SETQ, etc. + + The LISP9 system consists of a read-eval-print loop (REPL), a + bytecode compiler, and an ad-hoc, SECD-like abstract machine. + LISP9 is also a retro LISP in the sense that its heap space + would roughly fit in the core memory of a moderately sized KL10, + but the default size of the heap space can be extended easily. + + Although the name of LISP9 may suggest that it runs on Plan 9, + it currently doesn't (unless you use APE). It may at some point, + though. + + + ** INTRODUCTION ************************************************ + + -- PROGRAMS ---------------------------------------------------- + + A program is a sequence of forms that will be evaluated from + top to bottom. When typed in at the REPL ("Read-Eval-Print Loop, + the interpreter prompt), the value of each form will print. + When loaded from a file, forms will evaluate silently. + + The following program computes the mean of a list of numbers: + + (defun (mean a) + (let loop ((p a) + (s 0)) + (if (null p) + (div s (length a)) + (loop (cdr p) (+ s (car p)))))) + + (mean '(1 2 3 4 5)) + + The first form (using DEFUN) defines the MEAN function and the + second form applies the function to a list of numbers from one + to five. + + If the program is to be run in batch mode, without user + interaction, the second form should print its result: + + (print (mean '(1 2 3 4 5))) + + While the above program is perfectly workable, there are many + ways to implement the MEAN function, for example using a DO loop: + + (defun (mean a) + (do ((p a (cdr p)) + (s 0 (+ s (car p)))) + ((null p) (div s (length a))))) + + or using the higher-order function FOLD: + + (defun (mean a) + (div (fold + 0 a) + (length a))) + + + -- FORMS ------------------------------------------------------- + + A LISP9 program is a set of "forms", where each form is either + + - a variable + - a data object + - a function applications + - or a special form + + A variable is represented by its name, which is a sequence of + symbolic characters, mostly letters, digits, and some special + characters. See the FORMAL SYNTAX for details. + + Examples: foo x123 * a+b + + Data objects include numbers, characters (chars), strings of + chars, vectors, lists, ordered pairs, symbols, and functions. + There are also some special objects. All types of data objects + will be explained in the next chapter. + + Examples: 123 '(1 2 3) 'foo + #\x #(4 5 6) cons + "hello!" '(a . b) nil + + Function applications apply a function to values (data objects + or variables). Functions themselves are typically bound to + variables. The application itself has the shape of a list, with + the function in the first position and the arguments in + subsequent positions. See the following sections for details. + + Examples: (+ 1 2 3) + (cons 'a 'b) + (cdr '(a b)) + (gensym) + + Special forms have the same shape as function applications, but + different semantics. They are mostly used to define variables + and macros, bind variables to values, and control the flow of + program evaluation. + + Examples: (def foo 17) + (let ((bar 18)) + (if (= 1 1) foo bar)) + + + -- EXPRESSIONS ------------------------------------------------- + + The terms "form" and "expression" are often used as synonyms, + but it is important to distinguish between special forms and + (non-special) forms. + + Every expression evaluates to a value, which is written, for + example, as + + 5 => 5 ; five evaluates to itself + + (+ 1 2) => 3 ; + applied to 1 and 3 evaluates to 3 + + (if 1 2 3) => 2 ; IF applied to 1, 2, 3 evaluates to 2 + + cons => # + ; the variable CONS evaluates to some + ; function + + The latter is an example for a variable evaluating to the value + that is bound to it. + + + -- SPECIAL FORMS ----------------------------------------------- + + Even special forms, like IF, evaluate to a value. Some special + forms have a syntax that deviates from that which was described + in the previous section. For instance, the first argument in + + (cond ((foo) bar) (else baz)) + + looks like the application of the value of (foo) to BAR. However, + this is part of COND's syntax rather than a function application. + What COND really does it to evaluate (foo) and, if it returns a + "true" value, evaluate BAR. Else it evaluates BAZ. + + In general, special forms do not evaluate their arguments, but + do something special with them. Hence their name. The arguments + of special forms are often written in angle bracket in their + descriptions to indicate that something special happens. E.g.: + + (COND ...) => OBJ + + + -- DEFINITIONS ------------------------------------------------- + + Top-level (global) variables are defined by the DEF special form: + + (DEF EXPR) + + Here denotes a symbol that will be used as a variable and + EXPR denotes an expression of any type. The DEF special form + evaluates EXPR, but not , which would be impossible anyway, + because it is not yet defined. It then defines and binds + it to the value of EXPR. For example: + + (def helpfile "lisp9.txt") + + (def square (lambda (x) (* x x))) + + DEF forms can only appear at the top level of a program, i.e. + they cannot appear as arguments in any other forms. They + evaluate to (the value of) EXPR, but since that value cannot be + used for anything, their value is specified as "unspecific". + + Note that everything (except for MACROS and local variables, + which will be explained later) is defined using DEF -- even + functions. The second example above creates a function that + computes the square of its argument and binds it to the newly + created variable SQUARE. Note that the DEFUN special form is + normally used to define functions, though: + + (defun (square x) (* x x)) + + DEFUN is a convenient abbreviation for the combination of DEF + and LAMBDA. + + A variable can be redefined by using DEF with the same name + again. Doing so will change the binding of the variable: + + (def foo 'zzz) + foo => zzz + + (def foo 'bar) + foo => bar + + Local functions are defined by LAMBDA special forms, which will + be explained below. + + + -- TYPE SIGNATURES --------------------------------------------- + + A type signature describes the data types expected by a function + and the type of object returned by it. A signature generally + looks like this: + + (FUNCTION TYPE1 TYPE2 ...) => TYPE + + meaning that FUNCTION expects TYPE1, TYPE2, etc, and returns + TYPE. When there are multiple arguments of the same type, they + use an index to distinguish them, e.g. + + (= N1 N2 ...) => T/NIL + + The ellipsis (...) indicates that the preceding type occurs + zero or more times. I.e. the above signature says that the "=" + function expects one or more fixnums. + + The following type names will be used in the remainder of this + document: + + EXPR Any type of data object can be used in the place of + OBJ EXPR, which is used to describe arguments. OBJ is used + for return types, meaning that any type of object may + be returned. + + LIST These are different types of lists and pairs, which + DLIST will be described later. They indicate the list, + ALIST dotted list, association list, and ordered pair, + PAIR respectively. These types are ordered by generality + as follows: PAIR > DLIST > LIST > ALIST. + + CHAR A char (character) is expected or returned. + Cn + + FIXNUM A fixnum (small integer) is expected or returned. + Nn + + FUN A function is expected or returned. The notation FUN^N + FUN^N indicates that a function of N arguments is expected. + + STRING A string is expected or returned. + Sn + + SYMBOL A string is expected or returned. + + T/NIL Used to indicate that a function returns either T + (truth) or NIL (falsity). Such functions are called + predicates. + + UNSPECIFIC Indicates that the value returned by a function is + uninteresting and should not be used. + + VECTOR A vector (one-dimensional array) is expected or + Vn returned. + + + -- FUNCTIONS --------------------------------------------------- + + Functions are created by the LAMBDA special form: + + (LAMBDA EXPR1 EXPR2 ...) + + The special form contains a formal argument list, which names + the variables of the function (see LAMBDA), and a body comprised + of any positive number of expressions. + + A function is applied by evaluating its arguments, binding each + of its variables for the corresponding argument and with these + bindings in effect evaluating the body of the function. Before + the value of the body is returned, the bindings will be + destroyed. + + For instance, the function application + + ((lambda (a b) (* a b b)) (+ 1 1) 5) + + will + + - evaluate (+ 1 1), giving 2 + - evaluate 5, giving 5 + - bind 2 to A + - bind 5 to B + - evaluate (* a b b) = (* 2 5 5), giving 50 + - unbind A and B, restoring their previous values + - return the result, 50 + + + -- LEXICAL SCOPE ----------------------------------------------- + + Variable bindings have lexical scope in LISP9, which means that + the bindings of free variables (variables that are not defined + by a function) will be closed over and "remembered" in the + resulting function object. For instance: + + (let ((a 1)) + (lambda (b) (+ a b))) ; remember a=1 + + will return a function that adds 1 to its argument, even if the + value bound to A should change at a later time. In the lexical + (textual) context of (lambda (b) (+ a b)), A has the value 5, + so the function will use exactly that value: + + (let ((a 1)) + (let ((f (lambda (b) (+ a b)))) ; remember a=1 + (let ((a 0)) + (f 5)))) => 6 + + Even though in the lexical context of the application of F, A + has the value 0, the value remembered in F will be used. + + Note that the binding of a variable can be changed (by using + SETQ) inside of the *same* lexical scope (indicated by LET), + even *after* defining a function: + + (let ((a 1)) + (let ((f (lambda (b) (+ a b)))) + (setq a 2) + (f 5))) => 7 + + This is particularly important at the top level, because it + allows to create mutually recursive functions: + + (defun (even n) (if (= 0 n) t (odd (- n 1)))) + (defun (odd n) (if (= 0 n) nil (even (- n 1)))) + + Here the binding of ODD in EVEN is changed when defining ODD. + + + -- RECURSION --------------------------------------------------- + + Recursion -- a function applying itself -- is a fundamental + principle in LISP9. All special forms that implement iteration + (loops) use recursion internally. Recursion is cheap in LISP9 + and tail-recursive functions are guaranteed to evaluate in + constant space. For instance, given: + + (defun (f x) (if (= 0 x) t (f (- x 1)))) + + the applications (f 1) and (f 1000) will both require the same + amount of space while evaluating. Given the functions + + (def (g x) x) + (def (f x) (if (= 0 x) t (g (f (- x 1))))) + + however, (f 1000) will use much more space than (f 1), because + F is not tail-recursive. A function is tail-recursive only if + no function is applied to the result of its recursive + application. In the second example, G is applied to the value + of (f (- x 1)), so F is not tail-recursive. + + Tail recursion also applies to mutually recursive functions. + When F tail-applies G and G tail-applies F, both functions are + tail-recursive. E.g.: + + (def (f x) (if (= 0 x) t (g x))) + (def (g x) (f (- x 1))) + + are both tail-recursive. + + Applications in the following positions in core special forms + are tail applications (T): + + (IF x T T) + (IF* x T) + (PROG x ... T) + (LAMBDA x ... T) + + The same is valid for some derived special forms. Such positions + will be called "tail positions" in the descriptions of derived + special forms. + + + -- SYNTAX ------------------------------------------------------ + + The core syntax of the LISP9 language consists of the data + external representations of the data object (see next chapter), + function application, and the following special forms: + + APPLY DEF IF IF* MACRO PROG QUOTE SETQ LAMBDA + + There are additional syntactic constructs, which are derived + from the above, which will be listed in the next section. + + + -- DERIVED SYNTAX (MACROS) ------------------------------------- + + A macro is used to define new special forms based on existing + ones. The special form + + (macro fun) + + defines a new special form introduced by the symbol in the place + of and binds it to the macro expander FUN. FUN is a + function that takes the arguments of the corresponding special + form as arguments and transforms them to a derived form that + implements the semantics of the special form. For instance, + + (macro when (lambda (p . xs) @(if ,p (prog . ,xs)))) + + creates a new derived special form WHEN that is like IF without + an alternative branch, but with any number of expressions in the + consequent branch. I.e. the derived syntax + + (when 1 2 3 4 5) + + is transformed to the core syntax + + (if 1 (prog 2 3 4 5)) + + by the WHEN macro. Predefined derived special forms of LISP9 + include: + + LET LET* LABELS AND OR COND CASE DO QQUOTE + + + -- EVALUATION -------------------------------------------------- + + Evaluation of expressions comprises the following steps, in + exactly this order + + (1) reading the expression (via READ) + + (2) expansion of abbreviations and derived syntax + + (3) compilation to bytecode + + (4) interpretation by an abstract machine + + Of course, the expansion of derived syntax in turn involves the + application of a macro expander, which is a function that is + already compiled to bytecode. I.e. step (4) takes places two + times, once while expanding macros in an expression, and another + time when interpreting the expanded form. + + Hence the entire LISP9 language is available when writing macros. + + + -- COMMENTS ---------------------------------------------------- + + A comment starts with a semicolon and extens up to the end of + the line in which it started: + + ; this is a comment + + Comments are ignored at the reader level, i.e. they may occur + anywhere in a program where non-relevant white space may occur. + (I.e., not in string literals, char literals, fixnum literals, + symbolic names, etc). + + In earlier versions of LISP9 comments were data objects. This + approach turned out to impose too many restrictions on the + placement of comments and has hence been abandoned. + + + ** DATA OBJECTS ************************************************ + + -- T ----------------------------------------------------------- + + T is a special object that indicates logical truth. Although all + objects exept for NIL indicate truth, T is the canonical "true" + value. It is returned by predicates in order to signal success + (a positive result). T is self-quoting and 'T equals T. + + Examples: + + (listp '(1 2 3)) => t + (if t 'con 'alt) => con + + + -- NIL --------------------------------------------------------- + + NIL is a special object that indicates both the end of a list + and logical falsity. It is returned by predicates to indicate + failure (a negative result). NIL is also the only value that + will select the alternative value when passed to IF and IF*. + NIL is self-quoting and 'NIL equals NIL. + + Examples: + + (null nil) => t + (not nil) => t + (listp 5) => nil + (if nil 'con 'alt) => alt + (if* nil 'alt) => alt + + + -- SYMBOLS ----------------------------------------------------- + + A symbol is a sequence of characters that represents itself. The + characters that may be used to construct symbols include + + - the letters a-z + - the letters A-Z (but the reader will fold them to lower case) + - the decimal digits 0-9 + - all special ASCII characters except for these: + ( ) [ ] { } ; # ' @ ` , + + A symbol that has the form of a FIXNUM will be interpreted as a + fixnum and not as a symbol. E.g. 0, -1, and +123 are not symbol + names, because they look like fixnums. + + A symbol must be quoted (see QUOTE) in expressions in order to + distinguish it from a VARIABLE. + + Examples: + + 'foo => foo + '* => * + x^2 => x^2 + + + -- LISTS ------------------------------------------------------- + + A list is a heterogenous, forward-linked data structure that + consists of a head (also called "car part") and tail (also + called "cdr part"). The external representation of a list is + + (x1 x2 ... xN) + + where the X's are the elements of the list. List elements can + only be extracted and removed from the head of a list: + + (car '(1 2 3)) => 1 + (cdr '(1 2 3)) => (2 3) + + There are also functions for accessing random elements of a + list, concatenating lists, etc, but note that all these + functions are based on the above CAR and CDR operations. + Hence all list operations have linear time complexity. + + The empty list () is a synonym for NIL. + + Non-empty list objects have to be quoted in expressions in order + to distinguish them from function applications and special forms. + E.g. + + (cons 1 2) ; is the application of CONS to 1 and 2 + '(cons 1 2) ; is a list containing CONS, 1, and 2 + + Examples: + + () => nil + '(foo bar baz) => (foo bar baz) + (cons (- 2 1) nil) => (1) + (list 1 2 (+ 1 2)) => (1 2 3) + + + -- PAIRS ------------------------------------------------------- + + A (dotted) "pair" is an ordered pair of the form (A . B) where A + is called the CAR and B the CDR part of the pair. A fresh pair + is created by the CONS function and its respective parts are + extracted by the CAR and CDR functions. The following equations + hold (where = indicates EQUAL): + + (car (cons x y)) = x + (cdr (cons x y)) = y + (cons (car z) (cdr z)) = z ; given that Z is a pair + + A "list" is either the empty list NIL or a pair whose CDR part + is another list. Note that this definition is recursive, so + + (A . NIL) + (B . (A . NIL)) + (C . (B . (A . NIL))) + etc + + are all lists. A list of the form (A . list) is normally written + as (A list). Hence + + (A . NIL) = (A) + (B . (A . NIL)) = (B . (A)) = (B A) + (C . (B . (A . NIL))) = (C . (B . (A))) = (C . (B A)) = (C B A) + + A "dotted list" is a list whose rightmost element is not NIL. It + is a generalization of the dotted pair. E.g.: + + (D C B . A) + (B C . A) + (B . A) + + Examples: + + '(a . b) => (a . b) + (cons 'a 'b) => (a . b) + (cons 'a (cons 'b nil)) => (a b) + (cons 'a '(b c)) => (a b c) + (cons 'a '(b . c)) => (a b . c) + (cons '(a) '(b c)) => ((a) b c) + + + -- ASSOCIATION LISTS ------------------------------------------- + -- A-LISTS ----------------------------------------------------- + + An association list (or A-list) is a list of pairs: + + (( . ) ( . ) ...) + + where the car part of each pair is a key associated with a + value, which is stored in the CDR part of the same key. Hence + A-lists implement a very simple (and O(n)) form of associative + storage. Values are retrieved from them using the ASSOC, ASSV, + and ASSQ functions + + Examples: + + '((a . 1) (b . 2) (c . 3)) + + '(("I" . 1) ("V" . 5) ("X" . 10) ("L" . 50) ("C" . 100) + ("D" . 500) ("M" . 1000)) + + + -- VECTORS ----------------------------------------------------- + + A vector is a one-dimensional heterogenous array of fixed size. + Elements of a vector can be accessed via their index (position) + within the vector. The external representation of a vector is + + #(x1 x2 ... xN) + + where the X's are the elements of the vector. Vector elements + can be retrieved using the VREF function and changed by VSET. + There are more functions for for manipulating vectors, which + will be explained in the section about vector functions. + + Vector literals are self-quoting and immutable. + + Examples: + + #(foo 123 "bar") => #(foo 123 "bar") + (vector 1 2 (+ 1 2)) => #(1 2 3) + + + -- STRINGS ----------------------------------------------------- + + A string is a one-dimensional homogeneous array of characters. + It has a fixed size. Individual chars of a string can be + accessed via their index (position) within the string. The + external representation of a string is + + "..." + + where the indicates any ASCII character. Chars can be + retrieved using the SREF function and changed by SSET. There + are more functions for for manipulating strings, which will be + explained in the section about string functions. + + To include a quote character (") in a string, it has to be + prefixed with a backslash (\). A backslash can be included by + prefixing it with another backslash. Any 8-bit value can be + included in a string using the notation \O, where O is an octal + number of three places at most. Furthermore, the following + abbreviations exist: + + Abbr. Octal ASCII + \t \11 horizontal tabulator + \n newline sequence, CR, LF, or CRLF + + The three-digit limit on \O codes exists in order to distinguish + character sequences from digit characters. For instance, "\123" + denotes the character #\S, but "\0123" denotes a newline char + (\012) followed by the digit "3". + + String literals are self-quoting and immutable. + + Examples: + + ; contains the characters + "hello" => "hello" ; h e l l o + "\"foo\"" => "\"foo\"" ; " f o o " + "a\\b" => "a\\b" ; a \ b + "0\0121" => "0\n1" ; 0 1 + "0\121" => "0Q" ; 0 Q + "\33[H" => "\33[H" ; [ H + + + -- FIXNUMS ----------------------------------------------------- + + A fixnum is a small integer number. Its range depends on the + implementation, but on a two's complement 32-bit machine it + extends from -2^31 to 2^31-1 (-2,147,483,648 to 2,147,483,647). + + The external representation of a fixnum object consists of the + decimal digits of the fixnum with an optional sign (+ or -) + attached. Optionally, it may be prefixed with #r, + specifying a base different than ten for reading the fixnum. + must be in the range from 2 to 36. For instance, + + #16rfff => 4095 ; hexa-decimal + #2r101010 => 42 ; binary + #8r177 => 127 ; octal + + Examples: + + 0 => 0 + -1 => -1 + +123 => 123 + 0099 => 99 + #8r+20 => 16 + #36r-z => -35 + + + -- CHARS ------------------------------------------------------- + + A char is a small integer representing a code point in the + extended ASCII alphabet (where no particular glyphs are + associated with the values 127 to 255). + + The external representation is + + #\ + + where the can be any of the following: + + - a printable ASCII character, representing itself + - \O, where O is an octal number representing the + corresponding code point + + Furthermore, the following abbreviated 's exist: + + Abbr. Octal ASCII + #\ht #\\11 HT, horizontal tab + #\nl #\\12 LF (newline) + #\sp #\\40 space + + Char literals are self-quoting. + + Examples: + + #\a => #\a + #\A => #\A + #\\40 => #\sp + #\sp => #\sp + + + ** BINDING FORMS *********************************************** + + -- (DEF EXPR) => UNSPECIFIC ------------------------------ + + Bind the value of EXPR to the variable . DEF forms must + only appear in the following contexts: + + - at the top level of a program + - in a PROG form that appears at the top level of a program + - at the beginning of the body of DEFUN + + Examples: + + (def foo 'bar) + (def 2^16 (expt 2 16)) + (def square (lambda (x) (* x x))) + + + -- (DEFUN ( . ) EXPR1 EXPR2 ...) => UNSPECIFIC --- + + The DEFUN form is an abbreviation for + + (def (lambda expr1 expr2 ...)) + + where is a formal argument list as specified in the + FORMAL SYNTAX section. Note that the dot in the above form only + serves to formalize the equivalence between DEF and DEFUN. You + would normally write + + (defun (foo . (x y)) (bar x y)) + + as + + (defun (foo x y) (bar x y)) + + although the former would also work. + + The body of a function defined by DEFUN may contain nested + applications of DEF and DEFUN at its beginning. Such nested + occurrences will be translated to LABELS (q.v.). + + Examples: + + (defun (square x) (* x x)) + (defun (compose f g) (lambda (x) (f (g x)))) + (defun (sqsum . x) (fold + 0 (mapcar square x))) + + (defun (length a) + (defun (length2 a n) + (if (null a) + n + (length2 (cdr a) (+ 1 n)))) + (length2 a 0)) + + + -- (LET (( ) ...) EXPR1 EXPR2 ...) => OBJ ---------- + + Evaluate all arguments and then bind each argument to its + corresponding variable . With these bindings in effect, + evaluate the given expressions EXPRi from the left to the right. + Return the value of the last EXPR. When LET has returned its + value, the bindings will no longer be in effect. + + The last EXPR in LET is a tail position. + + Formally, + + (let ((v1 a1) (v2 a2) ...) x) + + is equal to + + ((lambda (v1 v2 ...) x) a1 a2 ...) + + Because of this equivalence, all variables of LET must be + different. Because all s are evaluated before binding them + to their variables, arguments of LET cannot refer to variables + of LET. In cases where either of these restriction is not + acceptable, LET* should be used instead. + + Examples: + + (let ((a 1) (b 2)) (+ a b)) => 3 + (let ((x 'foo)) (let ((x 'bar)) x)) = > bar + + + -- (LET* (( ) ...) EXPR1 EXPR2 ...) => OBJ --------- + + Evaluate the first argument and bind it to the variable + . With this binding in effect, evaluate and bind it + to the variable . Proceed until all variables are bound to + arguments. With all of the above bindings in effect, evaluate + the given expressions and return the value of the last EXPR. + + LET* differs from LET in two points: + + - each argument can refer to any variable + given that js do not have to be different + + Therefore LET* can be used to write expressions that evaluate + like procedural programs, by evaluating the left to the right, + assigning a value to a variable in each step. + + The last EXPR in LET* is a tail position. + + Examples: + + (let* ((x nil) + (x (cons 'c x)) + (x (cons 'b x))) + (cons 'a x)) => (a b c) + + (let* ((s "*foobar*") + (k (ssize s)) + (k (- k 1)) + (s (substr s 1 k))) + s) => "foobar" + + + -- (LABELS (( ) ...) EXPR1 EXPR2 ...) => OBJ ------- + + Bind each given to an unspecific value and with these + bindings in effect, evaluate each argument and bind it + to its corresponding variable . Arguments will be + evaluated and bound from the left to the right, so each + argument can refer to any as long as j + defined by LABELS can refer to all defined by the same + LABELS form without any restrictions. + + The last EXPR in LABELS is a tail position. + + Examples: + + (labels + ((pow (lambda (x y) + (if (= 0 y) + 1 + (* x (pow x (- y 1))))))) + (pow 2 9)) => 512 + + (labels + ((even (lambda (n) + (if (= 0 n) t (odd (- n 1))))) + (odd (lambda (n) + (if (= 0 n) nil (even (- n 1)))))) + (list (even 5) (odd 5))) => (nil t) + + + -- (WITH (( ) ...) EXPR1 EXPR2 ...) => OBJ --------- + + Save the values of all 's in temporary locations. Then + evaluate arguments sequentially, like LET*, but rather + than binding their values to new variables, alter pre-existing + variables using SETQ. With the modified bindings in effect, + evaluate the 's. Before returning the value of the last + expression, restore the original values of the 's from the + temporary locations. + + The variables must exist prior to evaluating WITH. + + WITH implements what is widely known as "dynamic scoping", i.e. + the WITH form alters the value of existing variables inside of + its dynamic extent: + + (let ((a 'lexical)) + (let ((f (lambda () a))) + (with ((a 'dynamic)) + (f)))) => dynamic + + Replacing the WITH in the above program by LET would result in + "lexical scoping", which is the default. + + Examples: + + (def *barchar* #\#) + + (defun (plotbar n) + (cond ((> n 0) + (writec *barchar*) + (plotbar (- n 1))))) + + (plotbar 10) ; prints ########## + + (with ((*barchar* #\:)) + (plotbar 10)) ; prints :::::::::: + + + ** FUNCTIONS AND FUNCTION APPLICATION ************************** + + -- (LAMBDA EXPR1 ... EXPRn) => FUN ------------------- + + Create a function expecting the given formal arguments and + returning the value of EXPRn. can be + + - a list of variables + - a dotted list of variables + - a single variable + + In the first case, when applying the function to some arguments, + there must be exactly one argument per variable and variables + will be bound to arguments pairwise. The list may be empty, + creating a function of no variables. + + When is a single variable, all arguments passed to the + function will be collected in a list and bound to that variable. + + A dotted argument list is a cross between the above. First all + variables to the left of the dot will be bound to individual + arguments and then any remaining arguments will be collected in + a list and bound to the last variable. + + The expressions EXPR1 ... EXPRn-1 are evaluated for effect from + the left to the right (i.e., the body of a function is an + implied PROG form). Only the value of EXPRn will be returned. + + Examples: + + (lambda (x y) (- y x)) + + (lambda (x . y) + (apply princ x y) + (apply terpri y) + x) + + (lambda x x) + + + -- APPLICATION ------------------------------------------------- + -- (EXPR1 ...) => OBJ ------------------------------------------ + + Apply the function EXPR1 to some arguments and evaluate to the + value returned by that function. The number of expressions + passed to the function must match its number of formal arguments + (variables). See LAMBDA for details. + + Examples: + + ((lambda (x) x) 1) => 1 + + ((lambda (x y z) + (list x y z)) 'a 'b 'c) => (a b c) + + ((lambda () 'foo 'bar)) => bar + + ((lambda (x . y) y) 1 2 3)) => (2 3) + + + -- (APPLY EXPR1 EXPR2 ... EXPRn) => OBJ ------------------------ + + Apply the function EXPR1 to the arguments in EXPRn. EXPRn must + be a list. I.e. + + (apply f x) == (f . x) + + Note that EXPRn will be evaluated before applying APPLY, but the + arguments of EXPR1 will not be evaluated, so + + (apply cons '(a b)) => (a . b) + + although A and B are not individually quoted. + + When there are any expressions EXPR2 ... EXPRn-1 between the + function and its arguments, they will be consed to EXPRn before + applying EXPR1, i.e.: + + (apply princ x (list (errport))) + == (apply princ (cons x (list (errport)))) + + Examples: + + (apply cons '(a (b))) => (a b) + + (apply mapcar list '((1 2 3) (4 5 6))) => ((1 4) (2 5) (3 6)) + + (apply list 1 2 3 '(4 5)) => (1 2 3 4 5) + + + ** CONDITIONAL EVALUATION ************************************** + + -- (AND EXPR1 ...) => OBJ ------------------------------------- + -- (OR EXPR1 ...) => OBJ ------------------------------------- + -- (PROG EXPR1 ...) => OBJ ------------------------------------- + + All of these forms evaluate their expressions from the left to + the right. PROG evaluates all of them and returns the value of + the last expression. The value of (PROG) is NIL. + + AND evaluates expressions until one of them evaluates to NIL. + In this case it does not evaluate any subsequent expressions + and returns NIL immediately. When all expressions evaluate to + non-NIL values, it returns the value of the last expression. + AND implements the short-circuit logical "and". (AND) evaluates + to T, the neutral element of the and operation. + + OR evaluates expressions until one of them evaluates to a + non-NIL value. In this case, it returns that value immediately + and does not evaluate any subsequent expressions. When all + expressions evaluate to NIL, it returns NIL. OR implements the + short-circuit logical "or". (OR) evaluates to NIL, the neutral + element of the "or" operation. + + The last expression in each of these special forms is a tail + position. + + Examples: + + (and 1 2 3) => 3 + (or 1 2 3) => 1 + (prog 1 2 3) => 3 + + (and nil (div 1 0)) => nil + (or t (div 1 0)) => t + + (let ((x #\5)) + (or (c<= #\0 x #\9) + (c<= #\a x #\f))) => t + + (let ((x '(a b c))) + (and (pair x) + (pair (cdr x)) + (cadr x))) => b + + + -- (IF EXPR1 EXPR2 EXPR3) => OBJ ------------------------------- + -- (IF EXPR1 EXPR2) => OBJ ------------------------------- + + First evaluate EXPR1. When the value of EXPR1 is not NIL, + evaluate EXPR2 and return its value. When EXPR1 is NIL and a + third expression is given, evaluate that expression and + return its value. When EXPR1 is NIL and no EXPR3 is present, + return NIL. + + If EXPR1 is true (not NIL), EXPR3 (if present) will never be + evaluated. If EXPR1 is NIL, EXPR2 will never be evaluated. + + Examples: + + (if t 'yes 'no) => yes + (if nil 'yes 'no) => no + (if nil 'yes) => nil + + (if nil (div 1 0)) => nil + + + -- (IF* EXPR1 EXPR2) => OBJ ------------------------------------ + + First evaluate EXPR1. When EXPR1 is not NIL, return its value. + When EXPR1 is NIL, evaluate EXPR2 and return its value. When + EXPR1 is not NIL, EXPR2 will never be evaluates. EXPR1 will + only be evaluated once. Formally, + + (if* a b) + + is equivalent to + + (let ((g a)) (if g g b)) + + Rationale: IF* is trivial to implement and useful for + implementing derived forms, such as OR and COND. The + relationship between IF and IF* becomes obvious in the + following disassembled code: + + ; (if 1 2) ; (if* 1 2) + 0 (quote 1) 0 (quote 1) ; load A with 1 + 2 (brf 6) 2 (brt 6) ; branch on false / true + 4 (quote 2) 4 (quote 2) ; load A with 2 + 6 6 ; result in A + + Examples: + + (if* 1 2) => 1 + (if* nil 2) => 2 + + (if* (memq 'c '(a b c d e f)) 'no) => (c d e f) + + + -- (COND ...) => OBJ ---------------------------------- + + Each has one of the following forms: + + ( EXPR1 EXPR2 ...) + () + ( => FUN^1) + + The COND form first evaluates the predicate of the first clause. + When it evaluates to a non-NIL value, the rest of the clause + will be evaluated and otherwise the next clause will be tried. + When COND runs out of clauses, it will return an unspecific + value. + + The evaluation of the rest of a clause depend on the shape of + the clause. When there are some expressions following the + predicate, they will be evaluated from the left to the right + (like in PROG) and the value of the last of them will be + returned. + + When there is only a predicate in the clause, the value of the + predicate will be returned. + + When the second member of the clause is a double right arrow, + the third member must be a function of one argument. That + function will be applied to the predicate and the value of the + function will be the value of the entire COND form. + + In either of the three cases, no more clauses will be evaluated + after finding one with a true (non-NIL) predicate. + + The last clause of COND may have the form ELSE, introducing a + catch-all clause whose predicate is always true. + + The last expression in every clause of COND is a tail position. + This does not apply to predicate-only clauses, though. + + position. + + Examples: + + (cond (t 1) (t 2)) => 1 + (cond (nil 1) (t 2)) => 2 + + (let ((c #\*)) + (cond ((c<= #\0 c #\0) 'digit) + ((c<= #\a c #\z) 'letter) + (else 'unknown))) => unknown + + (cond ('true)) => true + + (cond ((assq 'b '((a 1) (b 2) (c 3))) => cdr)) => (2) + + + -- (CASE ...) => OBJ ---------------------------- + + Each has the following form: + + (( ...) EXPR1 ...) + + First the is evaluated, which must be an expression that + evaluates to T, nil, a symbol, a number, or a character. The + value of is then compared to each member of the first + (using MEMV). When is contained in the list of + members, the corresponding EXPRs will be evaluated and the value + of the last expression is returned. Otherwise CASE will continue + to test clauses from the left. When no clause has as a + member, CASE will return an unspecific result. + + is only evaluated once, so + + (case (print 'FOO) ((a)) ((b)) ((c))) + + will only print FOO once. + + The last may have the keyword ELSE in the place of its + member list. In this case, the expressions of that clause will + be evaluated when no other clause matches. + + The last expression in every clause of CASE is a tail position. + + Examples: + + (case 'b ((a) 1) ((b) 2) ((c) 3)) => 2 + + (case 5 + ((0 2 4 6 8) 'even) + ((1 3 5 7 9) 'odd) + (else 'not-a-digit)) => odd + + + ** LOOPS ****************************************************** + + -- (DO ( EXPR1 ...) ) => OBJ ------------ + + is similar to the bindings of LET, but each clause + has an optional third argument, so it may have the following + forms: + + (( ) ...) + (( ) ...) + + DO implements a loop, is the exit condition of the loop, + and is a set of expressions that will be evaluated in + each iteration of the loop. + + DO first evaluates all 's and binds the resulting values to + the corresponding variables, just like LET. With these bindings + in effect, it evaluates . When the value of is NIL, + it evaluates the expressions following from the left to + the right and returns the value of the last of them. + + When evaluates to NIL, is evaluated. is a + sequence of expressions that will evaluated from the left to + the right. After evaluating the expressions, all 's will + be evaluates and their values will be bound to the corresponding + 's. With these bindings in effect, DO will start over with + evaluating . + + is often used to increment or decrement a numeric value + or collect values in a list, but in fact can be any + expression, it does not even have to reference the corresponding + variable. + + The last expression in is a tail position. + + Examples: + + (do ((i 32 (+ 1 i))) + ((= 127 i) (terpri)) + (writec (char i))) ; writes the ASCII alphabet + + (do ((i 10 (- i 1)) + (a nil (cons i a))) + ((= 0 i) a)) => (1 2 3 4 5 6 7 8 9 10) + + (do () (nil)) ; never terminates + + + -- (LET (( ) ...) EXPR1 EXPR2 ...) => OBJ --- + + The "named LET" is a very general loop construct. It binds its + variables to arguments in the same was as LET does, but instead + of evaluating its body immediately, it creates a function + with all s as variables and (PROG EXPR1 EXPR2 ...) as its + body. It then applies this function to the given s. + + As long as no EXPR applies , named LET behaves in the same + way as LET, but when is applied to some values in the + body of named LET, then the body of named LET will be reentered + with the new values as arguments. The loop is exited by just + evaluating to a value (and not applying ). + + The last EXPR in LET is a tail position. + + Examples: + + (let loop ((i 0)) + (cond ((= 127 i) (terpri)) + (else + (writec (char i)) + (loop (+ 1 i))))) ; writes the ASCII alphabet + + (let loop ((a '(1 2 3 4 5)) + (e nil)) + (cond ((null a) e) + ((evenp (car a)) + (loop (cdr a) + (cons (car a) e))) + (else + (loop (cdr a) e)))) => (4 2) + + + ** QUOTATION *************************************************** + + -- (QUOTE EXPR) => OBJ ----------------------------------------- + + Return EXPR without evaluating it. The form (quote expr) may be + abbreviated as + + 'expr + + List and vector structures returned by QUOTE will be immutable, + so modifying them with SSET, SETCAR, VFILL, etc, will result + in an error. + + Applying QUOTE to a self-quoting object will not add another + level of quotation, e.g. (quote 123) is the same as 123. + However, (quote (quote 123)) will yield (quote 123). + + Example: + + 'foo => foo + ''foo => 'foo + '(1 2 3) => (1 2 3) + '(cons a b) => (cons a b) + + + -- (QQUOTE