3714 lines
145 KiB
Plaintext
3714 lines
145 KiB
Plaintext
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
A GUIDE TO THE PC-LISP INTERPRETER (V3.00)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
By Peter Ashwood-Smith
|
||
~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Ottawa, Canada.
|
||
~~~~~~~~~~~~~~~
|
||
|
||
|
||
|
||
|
||
Copyright (C) 1985,1986,1987,1989,1990 - Peter Ashwood-Smith
|
||
|
||
|
||
|
||
for my wife, Guylaine
|
||
|
||
|
||
|
||
mail: Peter Ashwood-Smith
|
||
#8, du Muguet,
|
||
Hull, Quebec,
|
||
Canada,
|
||
J9A-2L8.
|
||
|
||
phone: (819) 595-9032.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
1
|
||
|
||
|
||
|
||
INTRODUCTION
|
||
~~~~~~~~~~~~
|
||
PC-LISP is a small implementation of LISP for just about any
|
||
machine with a good C compiler. This manual is biased towards the
|
||
UNIX and MS-DOS versions.
|
||
|
||
While small, it is capable of running a pretty good subset
|
||
of Franz LISP. The functions are supposed to perform in the same
|
||
way as Franz with a few exceptions made for effeciencies sake.
|
||
Version 3.00 has the following features.
|
||
|
||
- Types fixnum,flonum,list,port,symbol,string, hunk,
|
||
array. Forms lambda, nlambda, macro and lexpr.
|
||
|
||
- Read Macros including splicing read macros.
|
||
|
||
- Full garbage collection of ALL types.
|
||
|
||
- Compacting relocating heap management.
|
||
|
||
- Access to some MSDOS BIOS graphics routines.
|
||
|
||
- Over 160 built in functions, sufficient to allow you
|
||
to implement many other Franz functions in PC-LISP.
|
||
|
||
- Stack overflow detection & full error checking
|
||
on all calls, tracing of user defined functions,
|
||
and dumping of stack via (showstack).
|
||
|
||
- One level of break from which bindings at point
|
||
of error can be seen.
|
||
|
||
- Reasonable size, requires minumum of 300K (machine
|
||
RAM required may differ depending on OS size).
|
||
|
||
- Access to as much (non extended) memory as you've
|
||
got and control over how this memory is spread
|
||
among the various data types.
|
||
|
||
This program is Shareware. This means that it you are free
|
||
to distribute it or post it to any BBS that you want. The more
|
||
the better. The idea is that if you feel you like the program and
|
||
are pleased with it then send us $15 to help cover development
|
||
costs. Source code for this program is available upon request.
|
||
You must however send me 3 blank diskettes and about $1.50 to
|
||
cover first class postage. The program can be compiled with any
|
||
good C compiler that has a pretty complete libc. In particular
|
||
the program will compile with almost no changes on most UNIX
|
||
systems. A source code guide will probably be included with the
|
||
source if it is finished at the time I receive your source
|
||
request. If you send diskettes, SEND NEW, GOOD QUALITY DISKS as I
|
||
have had problems writing IBM-PC readable data to old or poor
|
||
quality diskettes with my Tandy 2000's 720K disk drives.
|
||
|
||
|
||
|
||
|
||
2
|
||
|
||
|
||
|
||
A WARNING
|
||
~~~~~~~~~
|
||
PC-LISP is distributed as ShareWare. The executable and
|
||
source code may be freely distributed. It is contrary to the
|
||
purpose of ShareWare to charge more than media and or mailing
|
||
costs for this program in any form source,disk,tape etc. If you
|
||
use PC-LISP you do so at your own risk. I will not be held
|
||
responsible for loss or dammage of any kind as a result of the
|
||
correct or incorrect use of this program. If you modify the
|
||
source and redistribute this source or its resulting executable I
|
||
ask that you add a "modified by x" or a "ported to z by y" line
|
||
to the initial banner and comment the code accordingly. Please do
|
||
not remove my name from the banner.
|
||
|
||
A NOTE
|
||
~~~~~~
|
||
The rest of this manual assumes some knowledge of LISP,
|
||
MSDOS/UNIX and a little programming experience. If you are new to
|
||
LISP or programming in general you should work your way through a
|
||
book on LISP such as LISPcraft by Robert Wilensky. You can use
|
||
the interpreter to run almost all of the examples in the earlier
|
||
chapters. I obviously cannot attempt to teach you LISP here
|
||
because it would require many hundreds of pages and there are
|
||
much better books on the subject than I could write. Also, there
|
||
are other good books on Franz LISP besides LISPcraft.
|
||
|
||
IF YOU WANT TO TRY PC-LISP RIGHT NOW
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
Make sure that PC-LISP.EXE and PC-LISP.L are in the same
|
||
directory. Then type PC-LISP from the DOS prompt. Wait until you
|
||
get the "-->" prompt. Here is what you should see starting by
|
||
typing pc-lisp at the prompt:
|
||
|
||
PC-LISP V3.00 Copyright (C) 1990 by Peter Ashwood-Smith
|
||
NNN cell bytes, NNN alpha bytes, NNN heap bytes
|
||
--- [pc-lisp.l] loaded ---
|
||
-->
|
||
|
||
Be patient, it takes a few seconds to load the program
|
||
especially off a floppy. When you see the first line with the
|
||
version number it will take another second or two to produce the
|
||
status line. (The N's depend on how much memory you have). At
|
||
this point PC-LISP is up and running and is reading LISP from the
|
||
file PC-LISP.L. Again this takes a second or two.
|
||
|
||
If your machine has some sort of graphics capability you can
|
||
try the graphics demo as follows. Type "(load 'turtle)" without
|
||
the "'s. Wait until you see the "t" and the prompt "-->" again,
|
||
then type "(GraphicsDemo)". You should see some Logo like
|
||
squirals etc. If you do not have any graphics capability try
|
||
"(load 'queens)" or "(load 'hanoi)" and then (queens 5) or (hanoi
|
||
5) respectively. For a more extensive example turn to the last
|
||
couple of chapters in LISPcraft and look at the deductive data
|
||
base retriever. Type (load 'match) and look at the match.l
|
||
documentation.
|
||
|
||
|
||
3
|
||
|
||
|
||
|
||
EXAMPLE LOAD FILES AND THE PC-LISP.L FILE
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
Included with PC-LISP (V3.00) are a number of .L files.
|
||
These include: PC-LISP.L, MATCH.L, TURTLE.L, DRAGON.L, DIFF.L and
|
||
perhaps a few others. These are as follows.
|
||
|
||
PC-LISP.L
|
||
~~~~~~~~~
|
||
A file of extra functions to help fill the gap between PC
|
||
and Franz LISP. This file defines the pretty print function and a
|
||
number of macros etc. It will be automatically loaded from the
|
||
current directory or from the directory whose path is set in
|
||
LISP_LIB when PC-LISP is executed. The functions in this file are
|
||
NOT documented in this manual, look instead at a Franz manual.
|
||
|
||
MATCH.L
|
||
~~~~~~~
|
||
A small programming example taken from the last 2 chapters
|
||
of LISPcraft. It is a deductive data base retriever. This is
|
||
along the lines of PROLOG. Very few changes were necessary to get
|
||
this to run under PC-LISP.
|
||
|
||
TURTLE.L
|
||
~~~~~~~~
|
||
Turtle Graphics primitives and a small demonstration
|
||
program. To run the demo you call the function "GraphicsDemo"
|
||
without any parameters. This should run albeit slowly on just
|
||
about every MS-DOS machine. The graphics primitives look at the
|
||
global variable !Mode to decide what resolution to use. If you
|
||
have mode 8 (640X400) you should use it as the lines are much
|
||
sharper. Turtle graphic modes can be set by typing (setq !Mode -
|
||
number-). Have a look at TURTLE.L to see how they work.
|
||
|
||
DRAGON.L
|
||
~~~~~~~~
|
||
A very slow example of a dragon curve. This one was
|
||
translated from a FORTH example in the April/86 BYTE. It takes a
|
||
long time on my 8Mhz 80186 machine so it will probably run for a
|
||
few hours on a PC or AT. I usually let it run for about 1/2 hour
|
||
before getting tired of waiting. To run it you just type (load
|
||
'dragon) then type (DragonCurve 16). If you have a higher
|
||
resolution machine like a Tandy 2000 then type (setq !Mode 8)
|
||
before you run it and it will look sharper at this (640x400)
|
||
resolution.
|
||
|
||
DIFF.L
|
||
~~~~~~
|
||
Is an example of symbolic computation. It takes a simple
|
||
expression and computes it's first, second, third, fourth and
|
||
fifth symbolic derivative. Again this is just a small example
|
||
that should not be taken too seriously in itself.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
4
|
||
|
||
|
||
|
||
USERS GUIDE
|
||
~~~~~~~~~~~
|
||
The PC-LISP program is self contained. To run it just type
|
||
the command PC-LISP or whatever you called it. When it starts it
|
||
will start grabbing memory in chunks of 16K each. By default PC-
|
||
LISP will grab 50 blocks but by setting the LISP_MEM environment
|
||
variable this can be controlled. Note, there is a hard limit of
|
||
75 blocks. The LISP_MEM environment variable is set in MS-DOS or
|
||
UNIX as follows:
|
||
|
||
set LISP_MEM=(28B,4A,4H)
|
||
|
||
Which means allocate up to 28 blocks total, of which 4 are
|
||
for alpha objects and 4 are for heap objects. The remainder go
|
||
for cons cell, file, array base, flonum and fixnum objects. By
|
||
default PC-LISP will allocate up to 50 blocks. 1 of which is
|
||
dedicated for alpha and 1 for heap. Note the environment variable
|
||
MUST be formatted as above. No spaces are permitted, the brackets
|
||
must be present as must the B,A and H (all capitals) after the
|
||
block counts.
|
||
|
||
After allocating memory PC-LISP will then print the banner
|
||
message followed by the actual amount of memory allocated for
|
||
each of the three basic object types. Next, before processing the
|
||
command line, PC-LISP will look for a file called "pc-lisp.l"
|
||
first in the current directory, next in the library directories
|
||
specified in the LISP_LIB environment variable as per the (load)
|
||
function. If it finds pc-lisp.l it will read and evaluate
|
||
commands from this file until the end of file is reached. Finally
|
||
PC-LISP will read the parameters on the command line. The command
|
||
line may contain any number of files eg:
|
||
|
||
PC-LISP file file .... file
|
||
|
||
The files on the command line are processed one by one. This
|
||
consists of loading each file as per the (load) function. This
|
||
means that PC-LISP will look in the current directory for 'file',
|
||
then in 'file'.l, then in the directories given in the LISP_LIB
|
||
environment variable, when found the file is read and every list
|
||
is evaluated. The results are NOT echoed to the console. Finally
|
||
when all the files have been processed you will find yourself
|
||
with the PC-LISP top level prompt '-->'. Typing control-Z and
|
||
ENTER (MS-DOS end of file) or CONTROL-D (UNIX end of file) when
|
||
you see the '-->' prompt will cause PC-LISP to exit to whatever
|
||
program called it. If an error occurs you will see the prompt
|
||
'er>'. For more info see the 'TERMINATION OF EVALUATION' section
|
||
of this manual and the commands (showstack), (trace), and
|
||
(untrace).
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
5
|
||
|
||
|
||
|
||
SYNTAX OR WHAT IS A LIST ANYWAY?
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
You will now be in the PC-LISP interpreter and can start to
|
||
play with it. Basically it is expecting you to type an S-
|
||
expression whose value it will evaluate and return. Formally an
|
||
S-expression can be defined with a B.N.F Grammar where + means at
|
||
least one occurence of and, * means any number of occurences of.
|
||
|
||
|
||
<S-expression> ::= <fixnum> | <flonum> | <string> | <symbol>
|
||
| '(' <elements> ')'
|
||
|
||
+
|
||
<elements> ::= (<S-expression>) '.' <S-expression>
|
||
*
|
||
| (<S-expression>)
|
||
|
||
|
||
Where characters whose ascii values are in 0..31 are ignored
|
||
and have no effect other than delimiting other input items. Also
|
||
characters between ; and the end of a line are ignored in the
|
||
same way as the white space characters just described, these are
|
||
used to introduce comments into your LISP programs.
|
||
|
||
The the basic list elements <fixnum>, <flonum>, <string> and
|
||
<symbol> are defined as follows.
|
||
|
||
A <fixnum> is a sign + , - or none followed by a sequence of
|
||
digits 0..9. If the sequence of digits represents a fixnum larger
|
||
than can be stored in a 32 bit integer it is taken to be the
|
||
nearest <flonum>. A <fixnum> can always be spotted when it is
|
||
printed by the lack of a radix point. Examples are: 2, +2, -2,
|
||
and -333333 .
|
||
|
||
A <flonum> is a sign + , - or none followed by digits 0..9
|
||
which may be followed by a radix point and more digits 0..9 this
|
||
may optionally be followed by an exponent specifier 'e' or 'E'
|
||
which may optionally be followed by a sign + , - or none,
|
||
optionally followed by the exponent digits 0..9. A <flonum> can
|
||
always be spotted when it is printed by the presence of either a
|
||
radix point, or the exponent specifier 'e'. Examples : 2.0,
|
||
-2.0, +2.0, -2e10, -2e+20, -4.0E-13, 2E, -2E
|
||
|
||
A <string> is a " followed by up to 254 characters followed
|
||
by a terminating " or |. If the character \ is present in the
|
||
string and the following character is one of t,b,n,r or f the two
|
||
characters are replaced by a tab, backspace, newline, carriage
|
||
return or form feed respectively. If the \ is not followed by one
|
||
of the previously mentioned special characters, the following
|
||
character is used to subtitute the \ and itself in the string.
|
||
The \ is called the escape character and allows you to put non
|
||
printing formatting characters into a string. It also allows you
|
||
to put a " or | into a string which you could not otherwise do.
|
||
Examples: "abcd", "a\tb", "a\"b", "a\|b".
|
||
|
||
|
||
6
|
||
|
||
|
||
|
||
SYNTAX OR WHAT IS A LIST ANYWAY? CONT'D
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
A <symbol> is either a string delimited with |'s instead of
|
||
the "'s, or a sequence of characters none of which are spaces or
|
||
non printing characters with ascii values < 32 or > 126. A \ may
|
||
be used to escape the following character just as in a string but
|
||
is also legal without the delimiters. If not delimited the
|
||
character after the escape is taken literally rather than
|
||
translated to a newline etc. If delimited any character may be
|
||
placed between the | delimiters with the exception of " or |
|
||
which must be preceeded by the escape character if they are to be
|
||
literally included in the symbol. If the symbol is not delimited
|
||
by |'s and does not contain an escaped character then the
|
||
characters must be in a sequence that follows the following
|
||
rules. The characters ( ) [ ] " | and ; are reserved and will
|
||
cause termination of the symbol. The set of characters that are
|
||
skipped as white space (those with ascii values in the range
|
||
0..31) are termed white space characters. The set of characters
|
||
that have been defined as read macros are termed macro trigger
|
||
characters. Only the ' char is initially a read macro trigger
|
||
character. The special characters are all of these above
|
||
character classes. Using these definitions, a symbol can either
|
||
start with a character in 0..9 or a character not in 0..9. If the
|
||
character is not in 0..9 then the the following characters can be
|
||
chosen from among all but the special characters. If the first
|
||
character in the symbol is in 0..9 then the last character must
|
||
be chosen from among the set of all characters that are neither
|
||
special nor in 0..9. A symbol may be composed of up to 254
|
||
characters all of which are significant. Here are a few
|
||
examples: \( a1 1a 1- 1234abc #hi# !hi% An_ATOM |ab\nc| junk.l
|
||
ThisIsOneRatherLargeAtomThatDemonstratesLength \1 2e1\0
|
||
|
||
An atomic S-expression is just one of a fixnum, flonum,
|
||
string and symbol. The only other type of S-expression that can
|
||
be input is a list S-expression.
|
||
|
||
In order to describe what a list S-expression is you need to
|
||
know some lisp terminology for the parts of a list. First a list
|
||
consists of two parts, the first element of the list is called
|
||
the car of the list and the rest of the elements in the list is
|
||
called the cdr of the list. For example the list (a b c) has car
|
||
a and cdr (b c). Now that we know the two parts of a list, we
|
||
need to know how to build a list. A list is built with a cons or
|
||
constructor cell. The constructor cell has two parts to it, the
|
||
first is the car of the list and the second is the cdr of the
|
||
list. Hence one cons cell describes one list. Its car part
|
||
describes the first element in the list, and its cdr part
|
||
describes the list of the rest of the elements in the list. For
|
||
the example list (a b c), the internal structure may look
|
||
something like this: (where a [ | ] represents a cons cell *-->
|
||
is a pointer, / is a nil pointer)
|
||
|
||
[*|*] ---> [*|*] ---> [*|/]
|
||
| | |
|
||
a b c
|
||
|
||
|
||
7
|
||
|
||
|
||
|
||
SYNTAX OR WHAT IS A LIST ANYWAY? CONT'D
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Here is an example of a simple nested list which can be
|
||
input as : (a (b c) nil d) and which results in a structure like
|
||
this:
|
||
|
||
[*|*] ---> [*|*] ---> [/|*] ---> [*|/]
|
||
| | |
|
||
v v v
|
||
a [*|*] ---> [*|/] d
|
||
| |
|
||
v v
|
||
b c
|
||
|
||
The dot '.' can be used to separate the last element in a
|
||
list from the others in the list. When this occurs the
|
||
constructed list will have a slightly different last cons cell
|
||
second field. Rather than pointing to another cons cell whose car
|
||
points to the last element, this field will point directly to the
|
||
last element. For example inputting (a . b) creates the following
|
||
list structure, which will also print as (a . b).
|
||
|
||
[*|*]
|
||
| |
|
||
v v
|
||
a b
|
||
|
||
However if the last element in the list is another list and
|
||
we preceed it by a dot, the list is spliced into the upper list
|
||
as if the last element were not really a list. For example if I
|
||
were to input (a . (b . (c))) the following structure which is
|
||
identical to that constructed by (a b c) would be built. It will
|
||
also print as (a b c).
|
||
|
||
[*|*] ---> [*|*] ---> [*|/]
|
||
| | |
|
||
v v v
|
||
a b c
|
||
|
||
|
||
The dotted pair is not normally used except when you wish to
|
||
save storage. An example might be when you create a list of
|
||
symbols and their associated values. In this case making the
|
||
symbol and its associated value a dotted pair will save 1 cons
|
||
cell or about 10 bytes per symbol value pair.
|
||
|
||
Finally, I have shown these structures with symbol elements.
|
||
You can have absolutly any type as an element of a list,
|
||
including of course a list as shown in the second example above.
|
||
This is a very quick look at list structure and you should look
|
||
at LISPcraft for more details.
|
||
|
||
|
||
|
||
|
||
|
||
8
|
||
|
||
|
||
|
||
META SYNTAX
|
||
~~~~~~~~~~~
|
||
Following are some syntactic properties that are really
|
||
above the level of the syntax of a simple S-expression. Thus they
|
||
are called meta syntax conventions. I consider Meta syntax as
|
||
anything that does not conform to the B.N.F grammar previously
|
||
given. These extensions to the syntax of S-expressions consist of
|
||
any extra syntax intdoduced by built in or user defined read
|
||
macros and the replacement of multiple parenthesis which occurs
|
||
when a single super parenthesis is used.
|
||
|
||
PC-LISP supplies one built in read macro called 'quote' and
|
||
written using the little ' symbol. This read macro is just a
|
||
short hand way of writing the list (quote S). Where S is the S-
|
||
expression that follows the ' in the input stream. Here are some
|
||
examples of the simple conversion that the read macro performs on
|
||
your input.
|
||
|
||
'apples -- goes to --> (quote apples)
|
||
'|too late| (quote |too late|)
|
||
'(1 2 3) (quote (1 2 3))
|
||
''a (quote (quote a))
|
||
'"hi" (quote "hi")
|
||
|
||
If you are new to LISP you will soon see just how useful
|
||
this little read macro is when you start typing expressions. It
|
||
reduces the amount of typing you must do, reduces the amount of
|
||
list nesting you have to look at and draws attention to data in
|
||
your expressions.
|
||
|
||
User defined read macros are also provided. See the
|
||
(setsyntax) function in the next section of the manual. The
|
||
backquote macro together with comma (,) and at (@) are
|
||
implemented in the PC-LISP.L load file, but are not documented
|
||
here. Again, see LISPcraft for a discussion of these read macros.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
9
|
||
|
||
|
||
|
||
META SYNTAX CONT'D
|
||
~~~~~~~~~~~~~~~~~~
|
||
|
||
PC-LISP also provides the meta or super parenthesis [ ].
|
||
One of the problems with LISP is the often overwhelming number of
|
||
parenthesis. It is very common to not supply enough closing )'s
|
||
and therefore have syntactic/semantic errors in your program. The
|
||
[ and ] characters when properly used allow you to force certain
|
||
structures even if enough )'s have not been provided. They
|
||
operate as follows. When the [ is encountered in the input, it
|
||
acts like a ( except that a note is made of the number of
|
||
unclosed ('s so far. Now when a ] is encountered in the input,
|
||
all lists up to and including the matching [ are closed. If there
|
||
is no matching [, ie none has been entered or all have been
|
||
closed with a ] then all open lists are closed. These parenthesis
|
||
may be nested up to 16 levels deep. But, deep nesting reduces
|
||
their usefullness. NOTE: If you open a list with a [ you must
|
||
close it with a ]. If you close it with a ) you will cause the
|
||
next [ ] pair to function incorrectly. The super nesting
|
||
information is reset whenever a new file is processed, or
|
||
whenever the break level is entered. That is, meta parenthesis
|
||
cannot be used accross a load or read of another file. Finally,
|
||
here are a few example legal inputs which use the meta
|
||
parenthesis and the list that results from their input.
|
||
|
||
((("hello world\n"] -- goes to --> ((("hello world\n")))
|
||
(([(((8 9] 10 ] ((((((8 9)))) 10))
|
||
[[[[[a]]]]] (((((a)))))
|
||
|
||
|
||
I should just mention again the fact that meta parenthesis
|
||
will not operate accross multiple reads. For example suppose you
|
||
were using (read) to get sublists from lists in one file, and
|
||
then switched to reading lists from another file, then returned
|
||
to the original file. If the original input file made use of the
|
||
super parenthesis and the particular sublist being read was
|
||
between a pair of superparenthesis, this information would be
|
||
lost when you resume reading the file. Hence the next ] you hit
|
||
will terminate all open lists rather than those opened after the
|
||
lost [. The moral of this example is not to use the super
|
||
parenthesis in a data file whose reading may be interrupted by
|
||
other I/O. This is not a particularly imposing limitation.
|
||
|
||
A FRANZ DIFFERENCE
|
||
~~~~~~~~~~~~~~~~~~
|
||
PC-LISP V3.00 is different from Franz in how the \ character
|
||
is interpreted when followed by n,t,r etc. in a string or |
|
||
delimited symbol. Franz does not convert them to newline, tab,
|
||
carriage return etc. Instead, Franz simply takes the next
|
||
character literally. You can override the 'smart-backslash' by
|
||
using (sstatus) to set the option to nil. The smart backslash is
|
||
much more convenient though because you can say (patom "stuff\n")
|
||
instead of (patom "stuff") (terpri). It is however non portable
|
||
so don't use the smart-backslash unless you are only writing for
|
||
PC-LISP.
|
||
|
||
|
||
10
|
||
|
||
|
||
|
||
SYNTAX ERRORS
|
||
~~~~~~~~~~~~~
|
||
When you enter a list which is not correct syntactically
|
||
the interpreter will return the wonderfully informative 'syntax
|
||
error' message. This message may be followed by a message as to
|
||
the cause such as 'atom too big' or it may be followed by a
|
||
pretty print of an expressopm which was close to where the error
|
||
was detected. You will have to figure out where it is in the
|
||
input list. Note that if you do not finish entering a list, ie
|
||
you put one too few closing )'s on the end, the interpreter will
|
||
wait until you enter it before continuing. If you are not sure
|
||
what has happened just type "]]" and all lists will be closed and
|
||
the interpreter will try to do something with the list. If you
|
||
are running input from a file the interpreter will detect the end
|
||
of file and give you a 'syntax error' because the list was
|
||
unclosed. Try also (showstack), it can help pinpoint the error in
|
||
a large load file. V3.00's syntax error handling could be
|
||
improved.
|
||
|
||
EVALUATING S-EXPRESSIONS
|
||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||
The interpreter expects an S-expression to be typed at the
|
||
prompt '-->'. The interpreter will evaluate the expression and
|
||
print the resulting S-expression. If the expression is either a
|
||
fixnum or a flonum, the interpreter just returns it because a
|
||
number evaluates to itself. If the expression is a string, the
|
||
interpreter also returns it because a string evaluates to itself.
|
||
If however the expression is a symbol, the interpreter returns
|
||
the binding of the symbol. It is an error to try to evaluate a
|
||
symbol that has no binding. Certain predefined atoms are
|
||
prebound, while all other symbols are unbound until bound by a
|
||
function call or a set / setq. If the expression is a list, then
|
||
the first element in the list is taken to be a function name or
|
||
description, the rest of the elements are taken to be parameters
|
||
to the function. The interpreter will normally evaluate each of
|
||
the arguments and then pass them to the appropriate function
|
||
whose result is returned. For example: The list S-expression with
|
||
a '+' as the first element and fixnums as elements will evaluate
|
||
as the sum of the fixnums. Eg.
|
||
|
||
-->(+ 2 4 6 8)
|
||
20
|
||
|
||
We can also compose these function calls by using list
|
||
nesting. Sublists are evaluated prior to upper levels. Eg:
|
||
|
||
-->(- (+ 6 8) (+ 2 4))
|
||
8
|
||
|
||
We can also perform operations on other objects besides
|
||
numbers. Suppose that we wanted to reverse the list (time flies
|
||
like arrows). Trying the built in function reverse we get:
|
||
|
||
-->(reverse (time flies like arrows))
|
||
--- error in built in function [apply] ---
|
||
|
||
|
||
11
|
||
|
||
|
||
|
||
EVALUATING S-EXPRESSIONS CONT'D
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
But the interpreter will be confused! It does not know that
|
||
'time' is data and not a function taking arguments 'flies',
|
||
'like' and 'arrows'. To indicate it is upset PC-LISP prints the
|
||
error message above and alters the prompt. More on this later.
|
||
What can we do to fix this? We must use the function 'quote'
|
||
which returns its arguments unevaluated, hence the name
|
||
"quote".
|
||
|
||
-->(reverse (quote (time flies like arrows)))
|
||
(arrows like flies time)
|
||
|
||
Will give us the desired result (arrows like flies time). We
|
||
can do the same thing without using the (quote) function
|
||
directly. Remember the read macro ' above? Well it will replace
|
||
the entry '(time flies like arrows) with (quote(time flies like
|
||
arrows)). So more concisely we can ask PC-LISP to evaluate:
|
||
|
||
-->(reverse '(time flies like arrows))
|
||
(arrows like flies time)
|
||
|
||
This gives us the correct result without as much typing. You
|
||
will now note that the subtraction of 2+4 from 6+8 could also
|
||
have been entered as:
|
||
|
||
-->(- (+ '6 '8) (+ '2 '4))
|
||
8
|
||
|
||
However, the extra 's are redundant because a fixnum
|
||
evaluates to itself. In general a LISP expression is evaluated by
|
||
first evaluating each of its arguments, and then applying the
|
||
function to the arguments, where the function is the first thing
|
||
in the list. Remember that evaluation of the function (quote s1)
|
||
returns s1 unevaluated. LISP will also allow the function name
|
||
to be replaced by a function body called a lambda expression.
|
||
Which is just a function body without a name. Example:
|
||
|
||
-->((lambda(x)(+ x 10)) 14)
|
||
24
|
||
|
||
Which would be processed as follows. First the parameters to
|
||
the lambda expression are evaluated. That's just 14. Next the
|
||
body of the lambda expression is evaluated but with the value 14
|
||
bound to the formal parameter given in the lambda expression. So
|
||
the body evaluated is (+ x 10) where x is bound to 14. The result
|
||
is just 24. Note that lambda expressions can be passed as
|
||
parameters as can built in functions or user defined functions.
|
||
Hence I can evaluate the following input. Note I use the ]
|
||
character to close the three open lists rather than typing ))) at
|
||
the end of the line.
|
||
|
||
-->((lambda(f x)(f (car x))) '(lambda(l)(car l)) '((hi]
|
||
hi
|
||
|
||
|
||
|
||
12
|
||
|
||
|
||
|
||
EVALUATING S-EXPRESSIONS CONT'D
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
Which evaluates as follows. The parameters to the call which
|
||
are the expressions '(lambda(l)(cdr l)) and '((hi)) are
|
||
evaluated. This results in the expressions being returned because
|
||
they are quoted. These are then bound to 'f and 'x respectively
|
||
and the body of the first lambda expression is evaluated. This
|
||
means that the expression ((lambda(l)(car l))(car ((hi)))) is
|
||
evaluated. So again the parameters to the function are evaluated.
|
||
Since the only parameter is (car ((hi))) it is evaluated
|
||
resulting in (hi). This is then bound to l and (car l) is
|
||
evaluated giving hi.
|
||
|
||
PC-LISP is also capable of handling all other function body
|
||
kinds. These are lambda, nlambda, lexpr and macro kinds. These
|
||
expression kinds may all have multiple bodies which are evaluated
|
||
in order, the last one producing the value that is returned. See
|
||
the section on BUILT IN FUNCTIONS and MACROS for more details on
|
||
these kinds and how they operate. Better yet read LISPcraft.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
13
|
||
|
||
|
||
|
||
TERMINATION OF EXPRESSION EVALUATION
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
There are three distinct ways that evaluation can terminate.
|
||
First, evaluation can end naturally when there is no more work to
|
||
do. In this case the resulting S-expression is printed on the
|
||
console and you are presented with the prompt "-->". Second, you
|
||
can request premature termination by hitting the CONTROL-BREAK or
|
||
CONTROL-C keys simultaneously (MS-DOS) or the INTR key (UNIX)
|
||
(hereafter referred to as CONTROL-BREAK for both UNIX and MS-
|
||
DOS). Note that this will only interrupt list evaluation, it will
|
||
NOT interrupt garbage collection which continues to completion.
|
||
So, if you hit CONTROL-BREAK (ie INTR,CONTROL-C or CONTROL-BREAK)
|
||
and you don't get any response, wait a second or two because it
|
||
will respond after garbage collection ends. Finally, execution
|
||
can terminate when PC-LISP detects a bad parameter to a built in
|
||
function, a stack overflows, a division by zero is attempted, or
|
||
an atom is unbound etc. In all cases but a normal termination you
|
||
will be returned to a break error level. This is when the prompt
|
||
looks like 'er>'. This means that variable bindings are being
|
||
held for you to examine. So if the evaluation aborts with the
|
||
message "error in built in function [car]", you can examine the
|
||
atom bindings that were in effect when this error occurred by
|
||
typing the name of the atom desired. This causes its binding to
|
||
be displayed. When you are finished with the break level just hit
|
||
CONTROL-Z plus ENTER (MS-DOS) or CONTROL-D (UNIX) and you will be
|
||
placed back in the normal top level and all bindings that were
|
||
non global will be gone. Note you can do anything at the break
|
||
level that you can do at the top level. If further errors occur
|
||
you will stay in the break level and any bindings at the time of
|
||
the second error will be in effect as well as any bindings that
|
||
were in effect at the previous break level. If bindings effecting
|
||
atoms whose values are being held in the first break level are
|
||
rebound at the second break level these first bindings will be
|
||
hidden by the secondary bindings.
|
||
|
||
An error in built in functions 'eval' or 'apply' can mean
|
||
two things. First, your expression could contain a bad direct
|
||
call to eval or apply. Or, your code may be trying to apply a
|
||
function that does not exist to a list of parameters, or trying
|
||
to apply a bad lambda form. The interpreter does not distinguish
|
||
an error made in a direct call by you to eval/apply or an
|
||
indirect call to eval/apply, made by the interpreter on your
|
||
behalf to get the expression evaluated.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
14
|
||
|
||
|
||
|
||
TERMINATION OF EXPRESSION EVALUATION CONT'D
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
There are a variety of math errors that are detected under
|
||
certain implementations of PC-LISP. The MS-DOS and AT&T UNIX
|
||
versions will both trap domain, argument singularity etc. errors
|
||
as per the MATH(3M) library. These errors generate similar
|
||
messages as the "error evaluating built in function" errors. The
|
||
Berkeley UNIX math library will not trap these in the same way.
|
||
Instead, you will get a system error message as descrbed by
|
||
perror() in the UNIX programmers guide. You will have to look at
|
||
the (showstack) to figure out which expression generated the
|
||
error. The same is true for floating point exceptions and any
|
||
other detectable system error such as (but not limited to) I/O
|
||
errors. This is because PC-LISP checks for system errors after
|
||
every evaluation so system errors such as "diskfull" will not
|
||
pass unnoticed.
|
||
|
||
It is also useful to know what the circumstances of the
|
||
failure were. You can display the last 20 evaluations with the
|
||
command (showstack). This will print the stack from the top to
|
||
the 20th element of the stack. This gives you the path of
|
||
evaluation that lead to the error. For more information on the
|
||
(showstack) command look in the section FUNCTIONS WITH SIDE
|
||
EFFECTS OR THAT ARE EFFECTED BY SYSTEM.
|
||
|
||
It is possible but hopefully pretty unlikely that the
|
||
interpreter will stop on an internal error. If this happens try
|
||
to duplicate it and let me know so I can fix it.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
15
|
||
|
||
|
||
|
||
DATA TYPES IN PC-LISP
|
||
~~~~~~~~~~~~~~~~~~~~~
|
||
PC-LISP has the following data types, 32 bit integers,
|
||
double precision floating point numbers, lists, ports for file
|
||
I/O, alpha atoms, strings, hunks, and MacLisp style arrays. The
|
||
(type) function returns these atoms:
|
||
|
||
fixnum - a 32 bit integer (possibly 64 on some UNIXes)
|
||
|
||
flonum - a double precision floating point number.
|
||
|
||
list - a list of cons cells.
|
||
|
||
symbol - an alpha atom, with print name up to 254 chars
|
||
which may include spaces tabs etc, but which
|
||
should not include an (ascii 0) character.
|
||
Symbols may have property, bindings and functions
|
||
associated with them. Symbols with same print
|
||
name are the same object.
|
||
|
||
string - A string of characters up to 254 in length. It
|
||
has nothing else associated with it. Strings
|
||
with same print name are not necessarily the
|
||
same object.
|
||
|
||
port - A stream that is open for read or write. This
|
||
type can only be created by (fileopen).
|
||
|
||
hunk - An array of 1 to 126 elements. The elements may
|
||
be of any other type including hunks. Franz
|
||
allows 127, the missing element is due to a space
|
||
saving decision. This type can only be created
|
||
by a call to (hunk) or (makhunk).
|
||
|
||
array - An array of any number of dimensions that can
|
||
have any type of element. Size is restricted
|
||
only by available memory. (no 64K limit)
|
||
|
||
Fixnums and flonums are together known as numbers. The read
|
||
function will always read a number as a flonum and then see if it
|
||
can represent it as a fixnum without loss of precision. Hence if
|
||
the number 50000000000 is entered it will be represented as a
|
||
flonum because it exceeds the precision of a fixnum. If a number
|
||
has a decimal point or exponent specifier 'e' or 'E' in it, it is
|
||
assumed to be a flonum even if there are no non zero digits
|
||
following the radix point.
|
||
|
||
Fixnums and flonums will not appear the same when printed.
|
||
The print function will output a flonum with a radix point and
|
||
perhaps an exponent specifier if it will make the output smaller.
|
||
Naturally, a fixnum never has a radix point.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
16
|
||
|
||
|
||
|
||
DATA TYPES IN PC-LISP (CONT'D)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Hunks when printed appear as { e0 e1 e2 .... eN }. They are
|
||
indexed from zero. They cannot be entered, ie there is no read
|
||
mechanism for creating them you must create them with a function
|
||
call. Hunks are subject to compaction and relocation like any
|
||
other PC-LISP object. The storage for the hunk itself comes from
|
||
the heap, storage for the cell that handles the hunk comes from
|
||
the cons, etc. space.
|
||
|
||
Arrays are implemented as 126-ary trees of hunks. They are
|
||
also indexed from 0. Because they are implemented in terms of
|
||
hunks, they are subject to compaction and reclaimation. The
|
||
storage for the array is thus not really contiguous. However it
|
||
appears so to the caller. Although you do not need to know how an
|
||
array is implemented to use them, here is how it works in PC-LISP
|
||
for your interest. Formally, an array tree is defined recursively
|
||
as follows:
|
||
|
||
BASE : If the size of the array is < 126 the array tree is just
|
||
a hunk the exact size as the array.
|
||
|
||
INDUCTION: If the size of the array is >= 126, the array
|
||
tree is a hunk of size exactly 126 or 125. The entries 0 .. 124
|
||
contain array trees each of which has size equal to the parent's
|
||
size divided by 125 (truncated division). If the remainder of the
|
||
size of the array divided by 125 is zero, the hunk is of size 125
|
||
and has no 125th entry. If the remainder of the size of the array
|
||
divided by 125 is non zero, then the size of the hunk is 126 and
|
||
the 125th entry is used to either store the remainder array, or
|
||
the remainder element as follows. If the remainder array is of
|
||
size exactly 1, it is not stored, the 125th entry of the parent
|
||
is used to hold the entry instead. If however the remainder is
|
||
greater than 1, the 125 entry of the parent holds a hunk of size
|
||
equal to the remainder.
|
||
|
||
Arrays when printed will print as array[nnn] where nnn is
|
||
the number of elements in the array. Multidimensional arrays are
|
||
stored in exactly the same way as linear arrays. The only
|
||
difference is in how the element number is computed when doing
|
||
array accesses. They will also print as array[nnn] where nnn is
|
||
the total number of elements in all dimensions of the array. It
|
||
is possible to allocate some pretty big arrays in PC-LISP,
|
||
however you will need to adjust the LISP_MEM environment variable
|
||
H option to make sure there is enough heap space for them.
|
||
|
||
Also note that the array hunk tree is allocated all at once
|
||
so for large arrays it takes some time to initialize. Also, the
|
||
array access functions (store) and (arraycall) are provided as
|
||
macros in pc-lisp.l. Finally note that unlike Franz, you cannot
|
||
specify a user written access function for the array or alter any
|
||
of the other array specific data besides the raw array tree.
|
||
|
||
|
||
|
||
|
||
17
|
||
|
||
|
||
|
||
THE BUILT IN FUNCTIONS AND VARIABLES
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Following is a list of each built in function. I will denote
|
||
the allowed arguments as follows:
|
||
|
||
- a1...aN are alpha atom parameters, type symbol.
|
||
|
||
- h1...hN are string or alpha atoms, type string or symbol.
|
||
|
||
- x1...xN are integer atom parameters, type fixnum (32bits).
|
||
|
||
- f1...fN are double precision reals, type flonum.
|
||
|
||
- n1...nN are number atom parameters, type flonum or fixnum.
|
||
|
||
- z1...zN are numbers but all are of the same type.
|
||
|
||
- l1...lN are lists, must be nil or of type list.
|
||
|
||
- p1...pN are port atom parameters, type port.
|
||
|
||
- s1...sN are S-expressions (any atom type or list)
|
||
|
||
- H is a hunk.
|
||
|
||
- A is a symbol which is bound to an array.
|
||
|
||
Additional Definitions:
|
||
~~~~~~~~~~~~~~~~~~~~~~~
|
||
"{a|d}+" means any sequence of characters of length greater
|
||
than 0 consisting of a's and d's in any combination. This
|
||
defines the car,cdr,cadr,caar,cadar... function class as
|
||
follows: "c{a|d}+r".
|
||
|
||
"[ -stuff- ]" indicates that -stuff- is/are optional and if
|
||
not provided a default will be provided for you.
|
||
|
||
"*-stuff-*" indicates that -stuff- is not evaluated. An
|
||
example of this is the function (quote *s1*) whose single S-
|
||
expression parameter s1 is enclosed in *'s to indicate that quote
|
||
is passed the argument s1 unevaluated.
|
||
|
||
For the simpler functions I will describe the functions
|
||
using a sort of "if (condition) result1 else result2" notation
|
||
which should be pretty obvious to most people. For functions that
|
||
are a little more complex I will give a short English description
|
||
and perhaps an example. If the example code shows the '-->'
|
||
prompt you should be able to type exactly what follows each
|
||
prompt and get the same responses from PC-LISP. If the example
|
||
does not show a '-->' prompt the example is a code fragment and
|
||
will not necessarily produce the results shown.
|
||
|
||
|
||
|
||
|
||
|
||
18
|
||
|
||
|
||
|
||
PREDEFINED GLOBAL VARIABLES (ATOMS)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
A number of atoms are globally prebound by PC-LISP. These
|
||
variables are testable and setable by you but in some cases
|
||
altering the bindings is highly inadvisable. Note that a binding
|
||
can be inadvertantly altered by defining one of these atoms as a
|
||
local or parameter atom to a function or a prog, or directly by
|
||
using 'set' or 'setq'.
|
||
|
||
"displace-macros" - This atom when non nil will cause macro
|
||
expansion to be follwed by code substitution if such substitution
|
||
is possible. The default value is nil meaning no substitution.
|
||
|
||
"t" - This atom means 'true', it is bound to itself.
|
||
Various predicates return this to indicate a true condition. You
|
||
should NOT change the binding of this atom, to do so will cause
|
||
PC-LISP to produce incorrect answers.
|
||
|
||
"nil" - This is not really an atom, it represents the empty
|
||
list (). It is not bound to () but is rather equivalent to () in
|
||
all contexts. Any attempt to create a symbol with print name
|
||
"nil" will result in ().
|
||
|
||
"$ldprint" - Is initially bound to "t". When not bound to
|
||
"nil" this atom causes the printing of the -- [file loaded] --
|
||
message when the function (load file) is executed. When "nil"
|
||
this atom prevents the printing of the above message. This is
|
||
useful when you want to load files silently under program
|
||
control. It will also inhibit the pc-lisp.l loaded message.
|
||
|
||
"$gcprint" - Is initially bound to "nil". When bound to
|
||
"nil" garbage collection proceeds silently. If bound non "nil"
|
||
then at the end of a garbage collection cycle 4 numbers are
|
||
printed. The first is the number of collection cycles that have
|
||
occured since PC-LISP was started, the second is the percentage
|
||
of cons cells that are in use, the third the percentage of alpha
|
||
cells, and the third the percentage of heap space that is in use.
|
||
These last three numbers are exactly what you get back with a
|
||
call to (memstat).
|
||
|
||
"$gccount$ - Is initially bound to 0. It increases by one
|
||
every time garbage collection occurs. This number is the same as
|
||
the first number printed when $gcprint is bound non "nil" and
|
||
garbage collection occurs. While you can set $gccount$ to any
|
||
value you want, its global binding will be reset to the correct
|
||
garbage collection cycle count whenever collection finishes.
|
||
|
||
"piport", "poport", "errport" - Are bound to the standard
|
||
input, standard output and standard error ports respectively. You
|
||
can use these to force patom, princ, print and pp-form to send
|
||
their output to the standard output or error. Or, to force read
|
||
and readc to get their input from the standard input. They are
|
||
initially bound to the keyboard and screen. You can alter their
|
||
bindings if you wish but this is not recommended.
|
||
|
||
|
||
19
|
||
|
||
|
||
|
||
THE MATH FUNCTIONS
|
||
~~~~~~~~~~~~~~~~~~
|
||
Functions that operate on numbers, fixnums or flonums. Note
|
||
that the arrow --X--> may indicate what type is returned. If X is
|
||
's' then the same type as the parameter(s) selected is returned.
|
||
If X is 'f' then a flonum type is returned. If X is 'x' then a
|
||
fixnum is returned. If X is 'b' then the best type is returned,
|
||
this means that a fixnum is returned if possible. Note that you
|
||
should use fixnums together with "1+, 1- zerop" when ever
|
||
possible because doing so gives nearly a 50% decrease in run time
|
||
for many expressions, especially counted loops or recursion.
|
||
|
||
TRIG AND OTHER MATH FUNCTIONS
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
(abs n1) --s-> absolute value of n1 is returned.
|
||
(acos n1) --f-> arc cosine of n1 is returned.
|
||
(asin n1) --f-> arc sine of n1 is returned.
|
||
(atan n1 n2) --f-> arc tangent of (quotient n1 n2).
|
||
(cos n1) --f-> cosine of n1, n1 is radians
|
||
(exp n1) --f-> returns e to the power n1.
|
||
(expt n1 n2) - b-> n1^n2 via exp&log if n1 or n2 flonum.
|
||
(fact x1) --x-> returns x1! ie x1*(x1-1)*(x1-2)*....1
|
||
(fix n1) --x-> returns nearest fixnum to number n1.
|
||
(float n1) --f-> returns nearest flonum to number n1.
|
||
(log n1) --f-> natural logarithm of n1 (ie base e).
|
||
(log10 n1) --f-> log base 10 of n1 {not present in Franz}
|
||
(lsh x1 x2) --x-> x1 left shifted x2 bits (x2 may be < 0).
|
||
(max n1..nN) --s-> largest of n1...nN or (0 if N = 0)
|
||
(min n1..nN) --s-> smallest of n1..nN or (0 if N = 0)
|
||
(mod x1 x2) --x-> remainder of x1 divided by x2.
|
||
(random [x1])--x-> random fixnum, or random in 0...x1-1.
|
||
(sin n1) --f-> sine of n1, n1 is radians.
|
||
(sqrt n1) --f-> square root of n1.
|
||
(1+ x1) --x-> x1+1.
|
||
(add1 n1) --b-> n1+1 (done with fixnums if n1 is fixnum).
|
||
(1- x1) --x-> x1-1.
|
||
(sub1 n1) --b-> n1-1 (done with fixnums if n1 is fixnum).
|
||
|
||
BASIC MATH FUNCTIONS
|
||
~~~~~~~~~~~~~~~~~~~~
|
||
(* x1 ...... ..xN) --x-> x1*x2*x3*.....nN (or 1 if N = 0)
|
||
(times n1 .. ..nN) --b-> n1*n2*n3......nN (or 1 if N = 0)
|
||
(product n1....nN) --b-> Ditto
|
||
(+ x1....... ..xN) --x-> x1+x2+x3+.....xN (or 0 if N = 0)
|
||
(add n1 .......nN) --b-> n1+n2+n3+.....nN (or 0 if N = 0)
|
||
(sum n1 .......nN) --b-> Ditto
|
||
(plus n1.......nN) --b-> Ditto
|
||
(- x1....... ..xN) --x-> x1-x2-x3-.....xN (or 0 if N = 0)
|
||
(diff n1.......nN) --b-> n1-n2-n3-.....nN (or 0 if N = 0)
|
||
(difference....nN) --b-> Ditto
|
||
(/ x1....... ..xN) --x-> x1/x2/x3/.....xN (or 1 if N = 0)
|
||
(quotient n1...nN) --b-> n1/n2/n3/.....xN (or 1 if N = 0)
|
||
|
||
Note that the Basic functions that operate on numbers will
|
||
return a fixnum if the result can be stored in one.
|
||
|
||
|
||
20
|
||
|
||
|
||
|
||
THE BOOLEAN FUNCTIONS
|
||
~~~~~~~~~~~~~~~~~~~~~
|
||
These functions all return boolean values. The objects t and
|
||
nil represent true and false respectively. Note however that most
|
||
functions treat a non nil value as being t. t is a predefined
|
||
atom whose binding is t while nil is not a real atom but rather
|
||
a lexical item that is EQUIVALENT to () in all contexts. Hence
|
||
nil and () are legal as both an atom and a list in all functions.
|
||
|
||
Note when comparing flonums you cannot use (eq) because they
|
||
are not identical objects. (eq) however will work on fixnums as
|
||
in Franz.
|
||
|
||
(alphalessp h1 h2) ---> if (h1 ASCII before h2) t else nil;
|
||
(arrayp s1) ---> if (s1 is type Array) t else nil;
|
||
(atom s1) ---> if (s1 not type list) t else nil;
|
||
(and s1 s2 .. sN) ---> if (a1...aN all != nil) t else nil;
|
||
(boundp a1) ---> if (a1 bound) (a1.eval(a1)) else nil;
|
||
(eq s1 s2) ---> if (s1,s2 same obj/fix) t else nil;
|
||
(equal s1 s2) ---> if (s1 has s2's structure) t else nil;
|
||
(evenp n1) ---> if (n1 mod 2 is zero) t else nil;
|
||
(fixp s1) ---> if (s1 of type fixnum) t else nil;
|
||
(floatp s1) ---> if (s1 of type flonum) t else nil;
|
||
(greaterp n1...nN) ---> if (n1>n2>n3...>nN) t else nil;
|
||
(hunkp s1) ---> if (s1 of type hunk) t else nil;
|
||
(lessp n1...nN) ---> if (n1<n2<n3...<nN) t else nil;
|
||
(listp s1) ---> if (s1 of type list) t else nil;
|
||
(minusp n1) ---> if (n1 < 0 or 0.0) t else nil;
|
||
(not s1) ---> if (s1 != nil) nil else t;
|
||
(null s1) ---> Ditto
|
||
(numberp s1) ---> if (s1 is fix of float) t else nil;
|
||
(numbp s1) ---> Ditto.
|
||
(or s1 s2 .. sN) ---> if (any si != nil) t else nil;
|
||
(oddp n1) ---> if (n1 mod2 is non zero) t else nil;
|
||
(plusp n1) ---> if (n1 > 0 or 0.0) t else nil;
|
||
(portp s1) ---> if (s1 of type port) t else nil;
|
||
(zerop n1) ---> if (n1 = 0 or 0.0) t else nil;
|
||
(< z1 z2) ---> if (z1 < z2) t else nil;
|
||
(= z1 z2) ---> if (z1 = z2) t else nil;
|
||
(> z1 z2) ---> if (z1 > z2) t else nil;
|
||
|
||
Note carefully the difference between (eq) and (equal). One
|
||
checks for identical objects or fixnums, ie the same object,
|
||
while the other checks for two objects that have the same
|
||
structure and identical leaves.
|
||
|
||
Note that the (and) and (or) functions evaluate their
|
||
arguments one by one until the result is known. Ie, short circuit
|
||
evaluation is performed.
|
||
|
||
Note that proper choice of fixnums over flonums and proper
|
||
choice of fixnum functions can yield large performance
|
||
improvements.
|
||
|
||
|
||
|
||
|
||
21
|
||
|
||
|
||
|
||
LIST & ATOM CREATORS AND SELECTORS
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
These functions will take lists and atoms as parameters and
|
||
return larger or smaller lists or atoms. They have no side
|
||
effects on the LISP system nor are their results affected by
|
||
anything other than the values of the parameters given to them.
|
||
These functions are all nondestructive as they do not alter their
|
||
parameters in any way.
|
||
|
||
|
||
(append l1..ln) ---> list made by joining all of l1..ln.
|
||
If any of l1..ln is nil they are
|
||
ignored.
|
||
|
||
(ascii n1) ---> atom with name 'char' where 'char'
|
||
has ordinal value n1:(0 < n1 < 256).
|
||
|
||
(assoc s1 s2) ---> if s2 is a list of (key.value) pairs
|
||
then assoc --> (key.value) from s2,
|
||
where (equal key s1) is t else nil.
|
||
|
||
(car l1) ---> first element in l1. If l1 is nil
|
||
car returns nil.
|
||
|
||
(cdr l1) ---> Everything but the car of l1. If
|
||
l1 is nil cdr returns nil.
|
||
|
||
(c{a|d}+r l1) ---> performs repeated car or cdr's on
|
||
l1 as given by reverse of {a|d}+.
|
||
Returns nil if it cars or cdrs off
|
||
the end of a list.
|
||
|
||
(character-index h1 h2) -x-> Returns the index (from 1) of first
|
||
char in h2 in h1. h2 can be a fixnum
|
||
ascii value. Returns nil if none.
|
||
|
||
(concat s1 .. sN) ---> Forms a new atom by concatenating
|
||
all the strings,atoms,fixnums and
|
||
flonums print names.
|
||
|
||
(cons s1 s2) ---> list with s1 as 1st elem s2 is rest.
|
||
If s2 is nil the list has one
|
||
element. If s2 is an atom the pair
|
||
print with a dot. (cons 'a 'b) will
|
||
print as (a . b).
|
||
|
||
(explode h1) ---> list of chars in print name of h1.
|
||
If h1 is nil returns (n i l)
|
||
|
||
(exploden h1) ---> list of ascii values of chars in h1.
|
||
If h1 is nil returns (110 105 108).
|
||
|
||
(get_pname h1) ---> String equal to print name of atom
|
||
h1 or same as string h1.
|
||
|
||
|
||
22
|
||
|
||
|
||
|
||
LIST & ATOM CREATORS AND SELECTORS (CONT'D)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
(hunk-to-list H) ---> Returns a list whose elements are
|
||
(eq) to those of hunk H and in the
|
||
same order.
|
||
|
||
(implode l1) ---> atom with name formed by compressing
|
||
first char of each atoms print name
|
||
in l1. Imploding (n i l) returns
|
||
the empty list nil. Small fixnums in
|
||
0..255 are treated as ascii chars.
|
||
|
||
(last l1) ---> returns the last element in l1. If
|
||
l1 is nil it returns nil.
|
||
|
||
(length l1) -x-> fixnum = to length of list l1.
|
||
The length of nil is 0.
|
||
|
||
(listarray A [n1]) ---> Returns all of A or just first n1
|
||
elements as a list.
|
||
|
||
(list s1 s2...sN) ---> a list with elements (s1 s2 ...sN)
|
||
If N = 0 list returns nil.
|
||
|
||
(member s1 l1) ---> If (s1 (equal) to element of l1)
|
||
returns l1 (from match) else nil.
|
||
|
||
(memq s1 l1) ---> If (s1 (eq) to element of l1)
|
||
returns l1 (from match) else nil.
|
||
|
||
(nth n1 l1) ---> n1'th element of l1 (indexed from 0)
|
||
like (cad...dr l1) with n1 d's.
|
||
|
||
(nthcdr n1 l1) ---> returns result of cdr'ing down the
|
||
list n1 times. If n1 < 0 it returns
|
||
(nil l1).
|
||
|
||
(nthchar h1 n1) ---> n1'th char in the print name of h1
|
||
indexed from 1.
|
||
|
||
(pairlis l1 l2 l3) ---> l1 is list of atoms. l2 is a list
|
||
of S-expressions. l3 is a list of
|
||
((a1.s1)....) The result is the
|
||
pairing of atoms in l1 with values
|
||
in l2 with l3 appended (see assoc).
|
||
|
||
(quote *s1*) ---> s1, unevaluated!
|
||
|
||
(reverse l1) ---> copy of l1 reversed at top level.
|
||
|
||
(type s1) ---> list,flonum,port,symbol, fixnum,
|
||
hunk or array as determined by the
|
||
type of the parameter s1.
|
||
|
||
|
||
|
||
23
|
||
|
||
|
||
|
||
LIST & ATOM CREATORS AND SELECTORS (CONT'D)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
(sizeof h1)
|
||
~~~~~~~~~~~
|
||
Will return the number of bytes necessary to store an object
|
||
of type h1. Legal values for h1 are 'list,'symbol,'flonum,
|
||
'fixnum, 'string , 'hunk, 'array and 'port. The size returned is
|
||
the amount of memory used to store the cell, incidental heap
|
||
space, property list space, binding stack space and function body
|
||
space is not counted for types 'symbol, 'string, 'hunk or 'array.
|
||
|
||
(stringp s1)
|
||
~~~~~~~~~~~~
|
||
Will return t if the S-expression s1 is of type string,
|
||
otherwise it returns nil.
|
||
|
||
(substring h1 n1 [n2])
|
||
~~~~~~~~~~~~~~~~~~~~~~
|
||
If n1 is positive substring will return the substring in
|
||
string h1 starting at position n1 (indexed from 1) for n2
|
||
characters or until the end of the string if n2 is not present.
|
||
If n1 is negative the substring starts at |n1| chars from the end
|
||
of the string and continues for n2 characters or to the end of
|
||
the string if n2 is not present. If the range specified is not
|
||
contained within the bounds of the string, nil is returned.
|
||
|
||
(memusage s1) { not in Franz }
|
||
~~~~~~~~~~~~~
|
||
Will return the approximate amount of storage that the S-
|
||
expression s1 is occupying in bytes. The printname heap space is
|
||
included in this computation as are file true name atoms. This
|
||
function is not smart, it will count an atom twice if it is
|
||
found more than once in the list. The space count does not
|
||
include storage needed for binding stacks, property lists, or
|
||
function bodies that are associated with a particular atom. Hunk
|
||
and string space include the heap space owned by the cell. If an
|
||
S-expression is a list all the elements (memusage) will be added
|
||
to get the total (memusage) for the list.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
24
|
||
|
||
|
||
|
||
NONINTERNING/INTERNING FUNCTIONS
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
Unless otherwise stated in this manual, any function that
|
||
returns an atom will intern it (put it on the oblist). However
|
||
the following functions are not included in the above statement.
|
||
Note also that the list returned by (oblist) is a copy of the
|
||
real oblist. Note carefully that the atoms created by read are
|
||
interned. See a really good LISP manual on this stuff because it
|
||
can be really confusing.
|
||
|
||
(copysymbol a1 s1)
|
||
~~~~~~~~~~~~~~~~~~
|
||
Returns an UNINTERNED copy of atom a1. If the flag parameter
|
||
s1 is non nil then the returned atom has property, value, and
|
||
function definitions eq to a1 otherwise its property, value and
|
||
function definitions are nil, undefined, and undefined
|
||
respectively.
|
||
|
||
(gensym [a1])
|
||
~~~~~~~~~~~~~
|
||
Returns an UNINTERNED atom whose print name is of the form
|
||
Xnnnnn where X is either 'g' or the print name of a1 (if a1 is
|
||
provided) and nnnnnn is some number such that no interned or
|
||
uninterned atom in the system has the same print name. Note that
|
||
the the existence of a clashing interned or uninterned atom is
|
||
checked before selecting the value of nnnnn.
|
||
|
||
(intern a1)
|
||
~~~~~~~~~~~
|
||
Will INTERN a1 on the oblist. If an atom with the same print
|
||
name as a1 is already on the oblist the EXISTING interned atom is
|
||
returned. Otherwise, a1 is physically added to the oblist and is
|
||
returned.
|
||
|
||
(remob a1)
|
||
~~~~~~~~~~
|
||
Will return a1 after having physically removed a1 from the
|
||
oblist. Future calls to read will create a new atom with the same
|
||
print name as a1. This can be confusing if a1 had a function
|
||
definition, property, or value assocaited with it.
|
||
|
||
(maknam l1)
|
||
~~~~~~~~~~~
|
||
Takes a list of symbols/strings/fixnums as parameter and
|
||
returns an UNINTERNED atom whose print name is the concatenation
|
||
of the first characters in the print names of every symbol and/or
|
||
the ascii characters whose values are given as fixnums in l1.
|
||
|
||
(uconcat a1 a2 ... aN)
|
||
~~~~~~~~~~~~~~~~~~~~~~
|
||
Returns an UNINTERNED atom whose print name is the
|
||
concatenation of each of the print names of a1...aN. If N=0, or
|
||
if N=1 and a1 is nil, then the empty list nil is returned. Note
|
||
that the empty list nil is neither interned or uninterned because
|
||
it is not really an atom. Like concat, it handles flo/fixnums.
|
||
|
||
|
||
25
|
||
|
||
|
||
|
||
FILE I/O FUNCTIONS
|
||
~~~~~~~~~~~~~~~~~~
|
||
These functions manipulate port atoms and allow character or
|
||
S-expression I/O. A port atom is returned by (fileopen) and will
|
||
print as %file@nn% where 'file' is the name of the port and nn is
|
||
the file number or -1 if the file is closed. All I/O is checked
|
||
and an error closing, reading or writing a port will be trapped.
|
||
|
||
(close p1)
|
||
~~~~~~~~~~
|
||
Closes the port p1 and returns t. It will then invalidate
|
||
the port p1. Any further I/O to p1 is illegal. I/O errors may be
|
||
trapped when the close is issued.
|
||
|
||
(fileopen h1 h2)
|
||
~~~~~~~~~~~~~~~~
|
||
Opens a file whose name is h1 for mode h2 access. h1 should
|
||
be a path or device name. h2 should be one of 'r,'w,'a,'r+,'w+,
|
||
or 'a+ meaning respectively: (r) Read only. (w) Truncate or
|
||
create for writing. (a) Open for writing at end of file or create
|
||
for writing. (r+) Open for update (read+write). (w+) Truncate or
|
||
create for update. And (a+) open or create for update at end of
|
||
file. MS-DOS device names like 'con', 'lpt1' etc are accepted.
|
||
Fileopen will not search for a file on the PATH or in the
|
||
LISP_LIBs. The MICROSOFT C MS-DOS version allows the addition of
|
||
a mode 'b meaning binary (no newline translation). It is appended
|
||
to the above modes eg 'rb or 'wb meaning read binary, write
|
||
binary etc. Depending on the compiler/operating system there may
|
||
be other modes allowed. (See the LibC manual for fopen(3S) mode
|
||
strings). Fileopen returns an open port atom, or nil if the
|
||
file/device could not be opened in the requested I/O mode.
|
||
|
||
(filepos p1 [x1])
|
||
~~~~~~~~~~~~~~~~~
|
||
If fixnum parameter x1 is not provided filepos will return
|
||
the current file position where the next read/write operation
|
||
will take place for port p1. If x1 is provided it is interpreted
|
||
as a new position where the next read/write should take place.
|
||
The read/write pointer is seeked accordingly and the value x1 is
|
||
returned if the seek completes successfully. Otherwise nil.
|
||
|
||
(load h1)
|
||
~~~~~~~~~
|
||
Will try to find the file whose name is h1 and load it into
|
||
PC-LISP. Loading means reading every list, and evaluating it. The
|
||
results of the evaluation are NOT printed on the console. In
|
||
trying to find the file h1, load uses the following strategy.
|
||
First it looks for file h1 in the current directory, then it
|
||
looks for h1.l in the current directory. Then it gets the value
|
||
of the environment variable LISP_LIB which should be a comma
|
||
separated sequence of MS-DOS paths (exactly the same syntax as
|
||
for PATH). It then repeats the above searching strategy for every
|
||
directory in the path list. For example if I entered this from
|
||
the COMMAND shell:
|
||
|
||
|
||
|
||
26
|
||
|
||
|
||
|
||
FILE I/O FUNCTIONS (CONT'D)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
"set LISP_LIB=c:\usr\libs\lisp\bootup;c:\lisp\work\;"
|
||
|
||
then ran PC-LISP, it would try to load the file PC-LISP.L first
|
||
from the current directory, then from the two directories on the
|
||
C drive that are specified in the above assignment. Future calls
|
||
to (load h1) will also look for files in the same way. When a
|
||
file has been successfully loaded PC-LISP examines the value of
|
||
atom $ldprint. If this value is non-nil (default is t) PC-LISP
|
||
will print a message saying that the file was loaded
|
||
successfully. If this value is nil then no message is printed. In
|
||
either case if the load is successful a value of t is returned
|
||
and if the load fails a value of nil is returned.
|
||
|
||
(patom s1 [p1]) & (princ s1 [p1])
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
Both cause the S-expression s1 to be printed without
|
||
delimiters or escapes on the output port p1, or on the standard
|
||
output if no p1 parameter is given. Without delimiters means that
|
||
if an atom has a print name that is not legal without the | |
|
||
delimiters or without an escape \, neither will be added when
|
||
printing the atom with patom. Patom returns s1 while princ
|
||
returns t. Strings will print without quotes or escapes.
|
||
|
||
(print s1 [p1])
|
||
~~~~~~~~~~~~~~~
|
||
Will cause the S-expression s1 to be printed with delimiters
|
||
and escapes if necessary on the output port p1, or on the
|
||
standard output if no p1 parameter is given. All atoms that would
|
||
require | | delimiting, strings that require " " delimiting and
|
||
characters that would have to be preceeded by the escape to be
|
||
input, will be printed with the delimiters and any necessary
|
||
escapes. If a character is one of the format characters tab, back
|
||
space, carriage return, line feed or form feed, it will print
|
||
preceeded by the escape as \t \b \r \n or \f respectively. If the
|
||
characters ascii value is < 32 or > 126 and it is not a format
|
||
character, it will print as \?. Print returns the expression s1.
|
||
|
||
(read [p1 [s1]])
|
||
~~~~~~~~~~~~~~~~
|
||
Reads the next S-expression from p1 or from the standard
|
||
input if p1 is not given and returns it. If s1 is given and end
|
||
of file is read the read function will return s1. If s1 is not
|
||
given and end of file is read the read function will return nil.
|
||
|
||
(readc [p1 [s1]])
|
||
~~~~~~~~~~~~~~~~~
|
||
Reads the next character from p1 or from the standard input
|
||
if p1 is not given and returns it as an atom with a single
|
||
character name. If s1 is given and end of file is read the readc
|
||
function will return s1. If s1 is not given and end of file is
|
||
read the readc function will return nil.
|
||
|
||
|
||
|
||
27
|
||
|
||
|
||
|
||
FILE I/O FUNCTIONS (CONT'D)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
(resetio)
|
||
~~~~~~~~~
|
||
Will close all open files except the standard input,
|
||
standard output and standard error ports. It is useful when too
|
||
many (load)s are aborted due to errors in the load file. It
|
||
always returns true.
|
||
|
||
(sys:unlink h1)
|
||
~~~~~~~~~~~~~~~
|
||
Will erase the file whose name is the print name of atom h1.
|
||
If the erase is successful a value of 0 is returned. If the erase
|
||
is unsuccessful a value of -1 is returned.
|
||
|
||
(truename p1)
|
||
~~~~~~~~~~~~~
|
||
Will return an atom whose print name is the same as the name
|
||
of the file associated with port p1. This is just the same as the
|
||
value printed between the % and @ signs when a port is printed.
|
||
|
||
(flatsize s1 [x1])
|
||
~~~~~~~~~~~~~~~~~~
|
||
Returns the number of character positions necessary to print
|
||
s1 using the call (print s1). If x1 is present then flatsize will
|
||
stop computing the output size of s1 as soon as it determines
|
||
that the size is larger than x1. This feature is useful if you
|
||
want to see if something will fit in some small given amount of
|
||
space but not knowing if the list is very big or not.
|
||
|
||
(flatc s1 [x1])
|
||
~~~~~~~~~~~~~~~
|
||
Returns the number of character positions necessary to print
|
||
s1 using the call (patom s1). x1 is the same as in flatsize.
|
||
|
||
(pp-form s1 [ p1 [x1] ] )
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
Causes the expression s1 to be pretty-printed on port p1
|
||
indented by x1 spaces. If p1 is absent the standard output is
|
||
assumed. If x1 is absent an indent of 0 is assumed. If s1
|
||
contains a list such as (prog .... label1 ... label2...) the
|
||
normal indenting will be ignored for label1 & label2 etc. This
|
||
causes the labels to stand out. For example IF the following
|
||
function were present in PC-LISP then I could run pp-form:
|
||
|
||
-->(pp-form (getd 'character-index-written-in-lisp))
|
||
(lambda (a c)
|
||
(prog (n)
|
||
(setq n 1 a (explode a))
|
||
(cond ((fixp c) (setq c (ascii c))))
|
||
nxt:
|
||
(cond ((null a) (return nil)))
|
||
(cond ((eq (car a) c) (return n)))
|
||
(setq n (1+ n) a (cdr a))
|
||
(go nxt:)))
|
||
|
||
|
||
28
|
||
|
||
|
||
|
||
FILE I/O FUNCTIONS (CONT'D)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
(drain [p1])
|
||
~~~~~~~~~~~~
|
||
Will cause p1 or the poport to be drained. If the port is an
|
||
input port then all unread characters will be discarded. If the
|
||
port is an output port then all unwritten characters will be
|
||
flushed.
|
||
|
||
(zapline)
|
||
~~~~~~~~~
|
||
Will cause all characters up to and including the next new
|
||
line (ascii 10) to be read and discarded. The port that is read
|
||
is the last one that was used for input. This function is useful
|
||
for defining comment skipping macros. For example.
|
||
|
||
-->(setsyntax '# 'vsplicing-macro
|
||
'(lambda()(zapline)))
|
||
|
||
Will define # as a comment starting character. When it is
|
||
encountered by (read) it will call (zapline) which skips all
|
||
characters up to an including the end of the line. Since
|
||
(zapline) returns nil and the macro is 'vsplicing-macro, nil is
|
||
spliced into the input list. In other words the nil has no effect
|
||
on the input list. This is the reason for reading from the last
|
||
file used for input.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
29
|
||
|
||
|
||
|
||
FUNCTIONS WITH SIDE EFFECTS OR THAT ARE EFFECTED BY SYSTEM
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
These functions will either have an effect on the way the
|
||
system behaves in the future or will give you a result about the
|
||
way the system has behaved in the past. Future calls will not
|
||
necessarily give the same results.
|
||
|
||
(def *a1* *l1*)
|
||
~~~~~~~~~~~~~~~
|
||
a1 is a function name and l1 is a lambda, nlambda, lexpr or
|
||
macro body. The body is associated with the atom a1 from now on
|
||
and can be used as a user defined function. Def returns a1.
|
||
Note that a lambda expressions parameter list may contain the
|
||
&aux,&optional and &rest flags. See Defun for details.
|
||
|
||
-->(def first (lambda(x)(car x)))
|
||
-->(def llast (lexpr(n)(last (arg n))))
|
||
-->(def myadd (nlambda(l)(eval(cons '+ l))))
|
||
-->(def firstm (macro(l)(cons 'car (cdr l))))
|
||
-->(def X*2orY (lambda(x &optional(y 2))(* x y)))
|
||
|
||
(defun *a1* [*a2*] *s0* *s1* *s2* ....*sN*)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
Defun will do the same job as "def" except that it will
|
||
build the expression body for you. a1 is the name of the
|
||
expression that you are defining, a2 is an optional expression
|
||
kind indicator which may be either expr, fexpr or macro. The
|
||
default is expr. These kinds correspond directly to lambda,
|
||
nlambda and macro forms. s0 specifies the formal parameters to
|
||
the expression. Usually this is just a list of symbols. If it is
|
||
a single symbol it is assumed that the symbol is the single
|
||
parameter to an lexpr form and an lexpr form will be constructed
|
||
from the ensuing bodies s1....sn. If it is a list of symbols then
|
||
a lambda, nlambda or macro body will be constructed from the
|
||
bodies s1...sn according to the kind specified by parameter a2.
|
||
For example, these calls to defun do the same job as the above
|
||
calls to def.
|
||
|
||
-->(defun first(x)(car x))
|
||
-->(defun llast n (last (arg n)))
|
||
-->(defun myadd fexpr(l)(eval(cons '+ l)))
|
||
-->(defun firstm macro(l)(cons 'car (cdr l)))
|
||
-->(defun X*2orY (x &optional(y 2)) (* x y))
|
||
|
||
A simple function definition (ie not an fexpr or macro) may
|
||
contain the flags &optional,&rest and &aux in its parameter list.
|
||
These flags must occur in the above order if all are present.
|
||
They have the following effects: The function will be allowed to
|
||
take a variable number of args (via an lexpr) and the parameters
|
||
up to the &optional flag must be present when the function is
|
||
called. The parameters after the &optional do not have to be
|
||
present, and if not present they will default to nil unless the
|
||
formal parameter form (var default) ie (y 2) is used. If this is
|
||
the case the parameter will be bound instead to 'default'.
|
||
|
||
|
||
|
||
30
|
||
|
||
|
||
|
||
FUNCTIONS WITH SIDE EFFECTS OR THAT ARE EFFECTED BY SYSTEM
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
CONT'D (defun|def &optiona,&aux,&rest flags)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
If &rest is present it may be followed by exactly one parameter
|
||
name. When the function is called any unaccounted for 'extra'
|
||
parameters will be turned into a list and bound to this
|
||
parameter. If &aux is present then the symbols present in the
|
||
following forms will all be bound either to nil, or if the form
|
||
is (var defualt) the symbol 'var' will be bound initially to
|
||
'default'. This has the effect of introducting local variables
|
||
with either nil or predefined default values. After this nasty
|
||
English description, I think that an example is in order.
|
||
|
||
-->(defun foo(a &optiona(b 2) c &rest d &aux (e 2) f)
|
||
(list a b c d e f))
|
||
foo
|
||
-->(foo 1)
|
||
(1 2 nil nil 2 nil) ; a=1 b=2 c=nil d=nil e=2 f=nil
|
||
-->(foo "hi" "there")
|
||
("hi" "there" nil nil 2 nil) ; b="there" not the default.
|
||
-->(foo 1 2 3 4)
|
||
(1 2 3 (4) 2 nil) ; e = unaccounted for parms.
|
||
-->(foo)
|
||
--- error evaluating built in function[arg] ---
|
||
er>(pp foo)
|
||
(def foo (lexpr(_N_)
|
||
((lambda(a b c d e f)(list a b c d e f))
|
||
(arg 1)
|
||
(arg? 2 2)
|
||
(arg? 3 nil)
|
||
(listify 4)
|
||
2
|
||
nil)))
|
||
|
||
This function has 1 required argument 'a', 2 optional
|
||
arguments 'b' and 'c' whose default values are 2 and nil
|
||
respectively. Any additional arguments are to be bound as a list
|
||
to 'e'. The function has two local variables 'e' and 'f' whose
|
||
default values are respectively 2 and nil. The function when
|
||
called simply makes a list of all it's arguments, any left over
|
||
arguments, and its local variables. Invoking the function with a
|
||
varying number of arguments shows the effect in the returned
|
||
list. The (foo) invokation shows that at least one argument is
|
||
required otherwise the (arg 1) function fails. Finally I dumped
|
||
the actual function definition. It is an lexpr expression with an
|
||
immediate invokation of a lambda expression with arguments ((arg
|
||
1).....nil). The special function (arg? nn exp) is like (arg nn)
|
||
except that if nn is not in the range of the actual parameters it
|
||
returns exp. Franz does not do this but PC-LISP does because it
|
||
is much more effecient than using a (cond) to get the arg or
|
||
default value. Because the (arg?) function is not present in
|
||
Franz, I do not recommend you use it directly, instead use defun
|
||
to create the correct forms for you, this will be portable.
|
||
|
||
|
||
|
||
31
|
||
|
||
|
||
|
||
FUNCTIONS WITH SIDE EFFECTS OR THAT ARE EFFECTED BY SYSTEM
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
CONT'D
|
||
~~~~~~
|
||
|
||
(exec *s1* *s2* .... *sN*)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
Will execute the program s1+' '+s2+' '+....+' '+sN. This is
|
||
done by using the system() call. For example if you are in PC-
|
||
LISP and you want to edit a file you could type.
|
||
|
||
-->(exec zed "lisp.h")
|
||
|
||
And the command "zed lisp.h" would be executed. Note that I
|
||
put quotes around the lisp.h file name because the '.' could
|
||
cause syntax problems. (exec) will return the return status of
|
||
the executed command as a fixnum. Note that if you get -1 back
|
||
from (exec) either the command cannot be found, or there is not
|
||
enough memory to run it. If there is not enough memory to run the
|
||
command you must set your LISP_MEM B setting a little smaller to
|
||
leave some memory for the commands you wish to execute. Note that
|
||
you can start up an MS-DOS shell as follows:
|
||
|
||
-->(exec command)
|
||
|
||
Which will execute command.com (assuming it is on your PATH)
|
||
and put you in the shell. You can then execute normal DOS
|
||
commands and return to PC-LISP by typing exit at the DOS prompt.
|
||
The pc-lisp.l file contains a definition of (shell) which does
|
||
exactly this. Note for UNIX you would have to do something like:
|
||
|
||
-->(exec "/bin/sh") or (exec "/bin/csh") etc...
|
||
|
||
(exit)
|
||
~~~~~~
|
||
PC-LISP will exit to whatever program envoked it this is
|
||
usally the COMMAND.COM program. Depending on how big you set
|
||
LISP_MEM MSDOS may ask for a system disk to reload COMMAND.COM.
|
||
Note that the video mode will be left alone if you call exit. But
|
||
if you leave via CONTROL-Z the video mode will be reset to
|
||
80x25B&W if you have changed it via (#scrmde#)).
|
||
|
||
(gc)
|
||
~~~~
|
||
Starts garbage collection of alpha and cell space. Returns t
|
||
when the cycle has ended. Reducing the settings of the LISP_MEM
|
||
blocks(B) or alpha(A) and or increasing the value of heap(H) will
|
||
decrease the time needed to complete garbage collection but will
|
||
reduce the available cell memory thus increasing the number of
|
||
garbage collections that are required in a given period. (gc) is
|
||
a useful way to spend idle time. If for example you have
|
||
displayed some computation and are waiting for a response from
|
||
the user, you can invoke (gc) after prompting but before reading
|
||
the response. Invoking (gc) yourself reduces the number of times
|
||
PC-LISP must do it for you.
|
||
|
||
|
||
32
|
||
|
||
|
||
|
||
FUNCTIONS WITH SIDE EFFECTS OR THAT ARE EFFECTED BY SYSTEM
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
CONT'D
|
||
~~~~~~
|
||
(get a1 a2)
|
||
~~~~~~~~~~~
|
||
Will return the value associated with property key a2 in
|
||
a1's property list. This value will have been set by a previous
|
||
call to (putprop a1 s1 a2). Example:
|
||
|
||
-->(get 'frank 'lastname)
|
||
|
||
(getd a1)
|
||
~~~~~~~~~
|
||
Will return the array, lambda, nlambda, fexpr or macro
|
||
expression that is associated with a1 or nil if no such
|
||
expression is associated with a1.
|
||
|
||
(getenv h1)
|
||
~~~~~~~~~~~
|
||
Will return an atom whose print name is the string set by
|
||
environment variable h1. For example we can get the PATH variable
|
||
setting by evaluating (getenv 'PATH). Note that these must be in
|
||
upper case because MS-DOS converts the variable names to upper.
|
||
|
||
(hashtabstat)
|
||
~~~~~~~~~~~~~
|
||
Will return a list containing 503 fixnums. Each of these
|
||
represents the number of elements in the bucket for that hash
|
||
location in the heap hash table. 503 is the size of the hash
|
||
table. This is not especially useful for you but it gives me a
|
||
way of checking how the hashing function is distributing the
|
||
heap using cells. Heap using cells are symbol, string and hunk.
|
||
The cell itself is allocated from the alpha or other memory
|
||
blocks while its variable length space is allocated from the
|
||
heap. Hence this table contains the oblist plus strings and
|
||
hunks and uninterned symbols. This table should not be confused
|
||
with the oblist which runs through this table.
|
||
|
||
(memstat) { not present in Franz }
|
||
~~~~~~~~~
|
||
Returns three fixnums. The first is the percentage of cell
|
||
space that is in use. The second is the percentage of alpha cell
|
||
space and the third is the percentage of heap space in use. When
|
||
any of these reach 100%, garbage collection will occur. Alpha and
|
||
cell space is collected together. Heap space is only collected
|
||
when you run out. After garbage collection you will see these
|
||
three percentages drop. The alpha and cell percentages should
|
||
drop to tell you how much memory is actually in use at that
|
||
moment. The heap space when compacted and gathered will not
|
||
necessarily drop to indicate how much you really have left. This
|
||
is because heap space is gathered in blocks of 16K, not all at
|
||
once as with atoms and cells. So, there will almost certainly be
|
||
more than 20% free heap space in other non compacted blocks even
|
||
if memstat reports 80% of the heap space is in use.
|
||
|
||
|
||
33
|
||
|
||
|
||
|
||
FUNCTIONS WITH SIDE EFFECTS OR THAT ARE EFFECTED BY SYSTEM
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
CONT'D
|
||
~~~~~~
|
||
(oblist)
|
||
~~~~~~~~
|
||
Returns a list of most known symbols in the system at the
|
||
current moment. Note that if you call oblist and assign the
|
||
result somewhere you will cause every one of those objects to be
|
||
kept by the system. If there are lots of large alpha atoms the
|
||
heap and alpha space will be tied up until you set the assigned
|
||
variable to some other value. Several special internal atoms are
|
||
not placed in the returned list to keep them out of user code.
|
||
|
||
(plist a1)
|
||
~~~~~~~~~~
|
||
Will return the property list for atom a1. The property list
|
||
is of the form ((ke1 . value1)(key2 . value2)...(keyn . valuen)).
|
||
Note that plist returns a top level copy of the property list
|
||
because remprop destroys this list's top level structure.
|
||
|
||
(putd a1 l1)
|
||
~~~~~~~~~~~~
|
||
Identical to "def" except that the parameters a1 and l1 are
|
||
evaluated. This allows you to write functions that create
|
||
other functions and then add them to the LISP interpreter.
|
||
|
||
(putprop a1 s1 a2)
|
||
~~~~~~~~~~~~~~~~~~
|
||
Adds to the property list of a1 the value s1 associated with
|
||
the property indicator a2. It returns the value of a1. For
|
||
example: (putprop 'Peter 'AshwoodSmith 'LastName)
|
||
|
||
(remprop a1 a2)
|
||
~~~~~~~~~~~~~~~
|
||
Removes the property associated with key a2 from the
|
||
property list of atom a1. The top level structure of the property
|
||
list is actually destroyed. It returns the old property list
|
||
starting at the point where the deletion was made.
|
||
|
||
(set a1 s1)
|
||
~~~~~~~~~~~
|
||
Will bind a1 to s1 at current scope level or globally if no
|
||
scope yet exists for a1. (set) returns s1.
|
||
|
||
(setplist a1 l1)
|
||
~~~~~~~~~~~~~~~~
|
||
Will set the property list of atom a1 to the list l1 where
|
||
the list must be ((keyn.valn)..). It returns this new list l1.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
34
|
||
|
||
|
||
|
||
FUNCTIONS WITH SIDE EFFECTS OR THAT ARE EFFECTED BY SYSTEM
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
CONT'D
|
||
~~~~~~
|
||
(setq *a1* s1 *a2* s2 ..... *an* sn)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
Like set but takes any number of atoms "an" and values "sn".
|
||
(setq) evaluates only the values s1...sn,(not the atoms) and then
|
||
binds the values to the atom as per (set). If the atom is unbound
|
||
before the call to setq it will be bound globally otherwise only
|
||
the current binding is altered. The expressions are evaluated
|
||
left to right and the bind is made after each expression is
|
||
evaluted. Setq will return the value of the last evaluated
|
||
expression. If no parameters are given (setq) returns nil.
|
||
|
||
-->(setq x '(a b c)
|
||
(a b c)
|
||
-->(setq x (cdr x) y (car x))
|
||
b
|
||
-->x
|
||
(b c)
|
||
-->y
|
||
b
|
||
|
||
(PAR-setq *a1* s1 *a2* s2 ..... *an* sn) {not in Franz}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
PAR-setq does not exist in Franz Lisp. It was added to PC-
|
||
LISP to help with the implementation of a (do) macro. PAR-setq
|
||
does the same thing as (setq) except that the assignments are
|
||
done in parallel. Ie the bindings are only done after all of the
|
||
s1 to sn expressions have been evaluated. PAR-setq should not be
|
||
used unless portability is not important.
|
||
|
||
-->(PAR-setq x '(a b c))
|
||
(a b c)
|
||
-->(PAR-setq x (cdr x) y (car x))
|
||
a
|
||
-->x
|
||
(b c)
|
||
-->y
|
||
a
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
35
|
||
|
||
|
||
|
||
FUNCTIONS WITH SIDE EFFECTS OR THAT ARE EFFECTED BY SYSTEM
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
CONT'D
|
||
~~~~~~
|
||
(setsyntax a1 a2 l1)
|
||
~~~~~~~~~~~~~~~~~~~~
|
||
Is a way of defining a read expression macro l1 to be
|
||
associated with chracter a1 and invoked in 'vmacro or 'vsplicing-
|
||
macro mode depending on a2. This function allows you to alter the
|
||
way that (read) works. Basically after calling setsyntax the
|
||
expression l1 will be invoked whenever the character a1 is found
|
||
in the input stream and this character is not escaped or hidden
|
||
in a comment or delimiters of some kind. For example a macro :
|
||
that pretty prints the following function name could be defined
|
||
as follows:
|
||
|
||
-->(setsyntax '|:| 'vmacro '(lambda()(list 'pp (read))))
|
||
|
||
Then if I typed :pp at the input prompt the character :
|
||
would be read causing the expression (list 'pp (read)) to be
|
||
invoked. This would then read the pp atom and construct the list
|
||
(pp pp) which would then be passed back to the read function
|
||
which would pass it back to the eval loop which will evaluate it
|
||
and pretty print the function pp. Read macro expressions are
|
||
lambda expressions that take no parameters. Any calls to (read)
|
||
must not have any arguments, (read) will know where to read the
|
||
next expression from because of a global binding performed by the
|
||
read macro driver on behalf of the read function.
|
||
|
||
Splicing macros are also available. Just replace the 'vmacro
|
||
parameter with 'vsplicing-macro. What will happen is that the
|
||
returned list will be spliced into the input expression, rather
|
||
than forming a sublist expression in the current input. This is
|
||
useful if you want to define your own comment delimiters and
|
||
return nil. For example let's define a new comment delimiter say
|
||
the < and > characters.
|
||
|
||
-->(defun SkipToEnd()
|
||
(cond ((eq (readc) '|>|) nil)
|
||
(t (SkipToEnd))))
|
||
SkipToEnd
|
||
-->(setsyntax '|<| 'vsplicing-macro '(lambda()(SkipToEnd)))
|
||
t
|
||
-->(and t <junk junk junk> t)
|
||
t
|
||
|
||
What I have done is first write a comment skipping function
|
||
that just reads input character by character until the > is
|
||
found. I then associated the character '<' with a lambda
|
||
expression that calls this skipper. The macro is a splicing macro
|
||
as (and t <junk...junk> t) demonstrates. Think about what would
|
||
have happened if the macro were non splicing and I put a comment
|
||
in the (and ....) list. Try it and see, then you will know why
|
||
splicing macros are needed.
|
||
|
||
|
||
|
||
36
|
||
|
||
|
||
|
||
FUNCTIONS WITH SIDE EFFECTS OR THAT ARE EFFECTED BY SYSTEM
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
CONT'D
|
||
~~~~~~
|
||
(sys:time) & (time-string [n1])
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
sys:time returns a fixnum representing the time in seconds
|
||
since UNIX/MS-DOS creation. Time-string takes a fixnum n1 and
|
||
returns to a human readable string representation of the time n1.
|
||
If n1 is not provided time-string uses the current sys:time.
|
||
|
||
(trace [*a1* *a2* *a3* ..... *an*])
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
Will turn on tracing of the user defined functions a1...an.
|
||
Note that you cannot trace built in functions. If you call trace
|
||
with no parameters it will return a list of all user defined
|
||
functions that have been set for tracing by a previous call to
|
||
trace, otherwise trace returns exactly the list (a1 a2...an)
|
||
after enabling tracing of each of these user defined functions.
|
||
If any of the atoms is not a user defined function trace stops
|
||
and returns an error. All atoms up to the point of error will be
|
||
traced.
|
||
|
||
(untrace [*a1* *a2* *a3* ..... *an*])
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
Will disable tracing of the listed functions which must all
|
||
be user defined. If no parameters are given it disables tracing
|
||
of all functions. Untrace returns a list of all functions whose
|
||
tracing has been disabled. Here is a demonstration of how you can
|
||
use them. This is the sort of sequence that you should see on the
|
||
console. The comments ;... were added to tell you what is going
|
||
on.
|
||
|
||
-->(defun factorial(n) ; define n! = n * (n-1)!
|
||
(cond ((zerop n) 1)
|
||
(t (* n (factorial (1- n]
|
||
factorial
|
||
-->(trace factorial) ; ask LISP to trace n!
|
||
(factorial)
|
||
-->(factorial 5) ; ask LISP for 5!
|
||
<enter> factorial( 5 ) ; entered with parm=5
|
||
<enter> factorial( 4 ) ; " " " 4
|
||
<enter> factorial( 3 ) ; " " " 3
|
||
<enter> factorial( 2 ) ; " " " 2
|
||
<enter> factorial( 1 ) ; " " " 1
|
||
<enter> factorial( 0 ) ; " " " 0
|
||
<EXIT> factorial 1 ; exit 0! = 1
|
||
<EXIT> factorial 1 ; exit 1! = 1
|
||
<EXIT> factorial 2 ; exit 2! = 1
|
||
<EXIT> factorial 6 ; exit 3! = 6
|
||
<EXIT> factorial 24 ; exit 4! = 24
|
||
<EXIT> factorial 120 ; exit 5! = 120
|
||
120
|
||
-->(untrace factorial) ; ask LISP to shut up
|
||
|
||
|
||
|
||
37
|
||
|
||
|
||
|
||
FUNCTIONS WITH SIDE EFFECTS OR THAT ARE EFFECTED BY SYSTEM
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
CONT'D
|
||
~~~~~~
|
||
|
||
(showstack)
|
||
~~~~~~~~~~~
|
||
When called after an error causing entry to the break level
|
||
will display the last 20 evaluations including the one which
|
||
caused the error. The top of the internal stack is copied
|
||
whenever LISP is about to enter the break level (prompt 'er>').
|
||
This means that if you execute some function and it aborts
|
||
prematurely you can call showstack from the break level and see
|
||
exactly what lead to the error. Whenever a new error occurs the
|
||
old copy of the top 20 elements on the internal stack is lost and
|
||
a new trace is copied for you to display via (showstack). This is
|
||
unlike Franz which allows lots of break levels. For example
|
||
consider this example session with PC-LISP which is similar to an
|
||
example in LISPcraft.
|
||
|
||
-->(defun foobar(y)(prog(x)(setq x (cons (car 8) y]
|
||
foobar
|
||
-->(foobar '(a b c))
|
||
--- error evaluating built in function [car] ---
|
||
er>x
|
||
()
|
||
er>y
|
||
(a b c)
|
||
er>(showstack)
|
||
|
||
[] (car 8)
|
||
[] (cons <**> y)
|
||
[] (setq x <**>)
|
||
[] (prog(x) <**>)
|
||
[] (foobar '(a b c))
|
||
|
||
t
|
||
|
||
In this example I declared a function called 'foobar' which
|
||
runs a prog and does a single assignment to x. When I execute it
|
||
with parameter '(a b c). PC-LISP correctly tells me that there
|
||
was an error evaluating the built in function 'car'. I can
|
||
examine the values of x and y and see that x is still set to the
|
||
empty list () that the prog call set it to. y is bound to the
|
||
parameter passed to foobar as expected. Next I called (showstack)
|
||
to see the trace of execution. I see that the top evaluation (car
|
||
8) is the culprit. The <**> symbols in the show stack are just a
|
||
short hand way of saying look at the entry above to see what the
|
||
<**> should be replaced with. This greatly reduces the amount of
|
||
information that you have to look at when you read a stack dump.
|
||
It also allows you to follow the stream of partial evaluations by
|
||
looking at each <**> in turn. Note that infinite recursion leaves
|
||
a telltale stream of <**>'s.
|
||
|
||
|
||
|
||
|
||
38
|
||
|
||
|
||
|
||
FUNCTIONS WITH SIDE EFFECTS OR THAT ARE EFFECTED BY SYSTEM
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
CONT'D
|
||
~~~~~~
|
||
(sstatus *a1* *s1*)
|
||
~~~~~~~~~~~~~~~~~~~
|
||
Returns t but has the side effect of setting system option
|
||
a1 to setting s1. The legal values of a1 are symbols or strings
|
||
with print names in the following list: smart-backslash,
|
||
ignoreeof, chainatom, and automatic-reset. Any S-expression is
|
||
legal for s1 but it is only tested for nil or non nil. The
|
||
effects are as follows.
|
||
|
||
(sstatus smart-backslash nil) will cause \n \t \r etc. in a
|
||
string or delimited symbol to be interpreted as n,t,r etc. On the
|
||
other hand (sstatus smart-backslash t) causes the \n \t \r etc.
|
||
sequences to be interpreted as newline, tab, carriage return as
|
||
described in the section on SYNTAX (this is the default).
|
||
|
||
(sstatus ignoreeof nil) will cause an exit to occur when the
|
||
EOF sequence is typed on the console from the top level. This is
|
||
the default. On the other hand (sstatus ignoreeof t) will cause
|
||
an EOF sequence typed on the console from the top level to be
|
||
ignored. This is used to protect against accidental exit from the
|
||
top level (exit must be done by evaluating (exit) explicitly).
|
||
|
||
(sstatus chainatom nil) will cause an error to occur when
|
||
either (car) or (cdr) of an object that is not a list is
|
||
evaluated. This is the default. (sstatus chainatom t) will cause
|
||
nil to be returned by (car) and (cdr) if they are evaulated with
|
||
a non list parameter.
|
||
|
||
(sstatus automatic-reset nil) will cause entry to the break
|
||
level to occur after an error is detected (this is the default).
|
||
But, (sstatus automatic-reset t) will cause the break level to be
|
||
skipped and no bindings will be held. It is as if you typed the
|
||
EOF sequence from the break level after every error. This is
|
||
useful in Franz where break levels can go N deep, it has limited
|
||
use in PC-LISP.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
39
|
||
|
||
|
||
|
||
LIST EVALUATION CONTROL FUNCTIONS
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
These functions are the control flow functions for LISP they
|
||
affect which lists are evaluated and how. They operate on the
|
||
basic LISP function types, descriptions of which follow.
|
||
|
||
(lambda l1 s1....sn)
|
||
~~~~~~~~~~~~~~~~~~~~
|
||
This is not a function but it is a list construct which
|
||
can act as a function in any context where a function is legal. A
|
||
lambda expression is a function body. The S-expressions s1..sn
|
||
are expressions that are evaluated in the order s1...sn. The
|
||
result is the evaluation of sn. The atoms in the list l1 are
|
||
called bound variables. They will be bound to values that occur
|
||
on the right of the lambda expression before the S-expressions
|
||
s1..sn are evaluated and unbound after the value of sn is
|
||
returned.
|
||
|
||
(nlambda l1 s1....sn)
|
||
~~~~~~~~~~~~~~~~~~~~~
|
||
This is a function body construct similar to lambda but with
|
||
a few major differences. The first is that the list l1 must only
|
||
specify one formal parameter. This will be set to a list of the
|
||
UNEVALUATED parameters that fall on the right of the nlambda
|
||
expression when it is being evaluated. This function allows you
|
||
to write functions with a variable number of parameters and to
|
||
control the evaluation of these parameters. For example we can
|
||
write a function called 'ADDEM that behaves the same way as '+ in
|
||
nearly all contexts as follows:
|
||
|
||
-->(def ADDEM (nlambda(l)(eval(cons '+ l))))
|
||
or
|
||
-->(defun ADDEM fexpr(l)(eval(cons '+ l)))
|
||
|
||
Both of which create the same nlambda expression. This
|
||
function will behave as follows when spotted on the left of a
|
||
sequence of parameters 1 2 3 4. First it will not evaluate the
|
||
sequence of parameters 1 2 3 4. Second it makes these into a list
|
||
(1 2 3 4). It then binds 'l to this list and evaluates the
|
||
expression (eval(cons( '+ l))). This expression results in (eval
|
||
(+ 1 2 3 4)). Which is just the desired result 10.
|
||
|
||
(label a1 (lambda|nlambda l1 s1..sn)) {not in Franz}
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
This acts just like a lambda expression except that the body
|
||
is temporarily bound to the name a1 for evaluation of the body
|
||
s1. This allows recursive calls to the same body. The binding of
|
||
the body to the name a1 will be forgotten as soon as the
|
||
expression s1 terminates the recursion. For example:
|
||
|
||
(label LastElement (lambda(List)
|
||
(cond ((null (cdr List))(car List))
|
||
(t (LastElement (cdr List))))))
|
||
|
||
|
||
|
||
|
||
40
|
||
|
||
|
||
|
||
LIST EVALUATION CONTROL FUNCTIONS CONT'D
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
(lexpr (a1) s1 s2 .... sN)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
This function body form is similar to the nlambda form
|
||
except that all of its variable number of arguments are evaluated
|
||
and the args are accessed in a different manner. The second
|
||
element of the lexpr form must be a list of exactly one atom. The
|
||
remaining elements of the lexpr form represent bodies that are
|
||
evaluated one after the other. The result of evaluating a list
|
||
whose first element is an lexpr is just the value that results
|
||
from evaluating s1....sN in order in the context where a1 is
|
||
bound to the number of actual parameters, and the (arg), (setarg)
|
||
and (listify) functions behave as follows:
|
||
|
||
(arg [n1]) {see also (defun) for description of (arg?)}
|
||
~~~~~~~~~~
|
||
When in the context of an lexpr's evaluation, will return
|
||
either the number of arguments provided to the nearest enclosing
|
||
lexpr, or the nth argument indexed from 1 passed to the lexpr
|
||
depending on whether or not n1 is provided as a parameter. An
|
||
error occurs if n1 is less than 1 or greater than (arg).
|
||
|
||
(setarg n1 s1)
|
||
~~~~~~~~~~~~~~
|
||
When in the context of an lexpr's evaluation, will return
|
||
exactly s1. It has the side effect that future calls to (arg n1)
|
||
will return the value s1 for the duration of the current
|
||
enclosing lexpr evaluation. An error occurs if n1 is less than 1
|
||
or greater than (arg).
|
||
|
||
(listify n1)
|
||
~~~~~~~~~~~~
|
||
When in the context of an lexpr's evaluation, will return a
|
||
tail of the list of arguments that were passed to the nearest
|
||
enclosing lexpr. The head of this tail is either (arg n1) if n1
|
||
is positive, or (arg (+ (arg) n1 1)) if n is negative. If the
|
||
value of n1 does not correctly index a head within the actual
|
||
argument list, nil is returned.
|
||
|
||
Here is a small lexpr example which just sets some global
|
||
variables to allow us to see what went on inside. Again see
|
||
LISPcraft for a much better description.
|
||
|
||
-->((lexpr(n)
|
||
(setq a0 n a1 (arg 1) an (arg(arg)))
|
||
(listify -3)
|
||
) 'A 'B 'C 'D 'E 'F 'G )
|
||
(E F G)
|
||
-->a0
|
||
7
|
||
-->a1
|
||
A
|
||
-->an
|
||
G
|
||
|
||
|
||
41
|
||
|
||
|
||
|
||
LIST EVALUATION CONTROL FUNCTIONS CONT'D
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
(apply s1 l1)
|
||
~~~~~~~~~~~~~
|
||
The function s1 is evaluated in the context resulting from
|
||
binding its formal parameters to the values in l1. The result of
|
||
this evaluation is returned. Example:
|
||
|
||
-->(apply '(lambda(x y z)(* (+ x y) z)) '(2 3 4))
|
||
20
|
||
|
||
(cond l1 l2 ... ln)
|
||
~~~~~~~~~~~~~~~~~~~
|
||
The lists l1 ... ln are checked on by one. They are of the
|
||
form (s1 s2 .. sn). Cond evaluates the s1's one by one until it
|
||
finds one that does not eval to nil. It then evaluates the s2..sn
|
||
expressions one by one and returns the result of evaluating sn.
|
||
If all of the s1's (called guards) evaluate to nil, it returns
|
||
'nil. For example:
|
||
|
||
-->(cond ((equal '(a b c) (cdr '(x a b c))) 'yes)
|
||
(t 'opps))
|
||
yes
|
||
|
||
(eval s1)
|
||
~~~~~~~~~
|
||
Runs the LISP interpreter on the S-expression s1. It is like
|
||
removing a quote from the expression s1. For example:
|
||
|
||
-->(eval '(+ 2 4))
|
||
6
|
||
|
||
(mapcar s1 l1 l2 l3 .... ln) and (mapc s1 l1 ... ln)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
These functions will map the function s1 onto the parameter
|
||
list made by taking the car of each of l1...ln. It forms a list
|
||
of the results of the repeated application of s1 to the next
|
||
elements in the lists l1...ln. It stops when the list l1 runs out
|
||
of elements. Note that each of l1...ln should have the same
|
||
number of elements, although this condition is not checked for
|
||
and nil will be substituted if a list runs out of elements before
|
||
the others. Extra elements in any list are ignored. For example:
|
||
|
||
-->(mapcar '< '(10 20 30) '(11 19 30))
|
||
(t nil nil)
|
||
|
||
Which returns the results of (< 10 11) (< 20 19) and (< 30
|
||
30) as the list (t nil nil).
|
||
|
||
The function mapc operates in exactly the same way as mapcar
|
||
except that the result is just l1. No result list is returned.
|
||
Mapc is meant to be used to save a little memory when the side
|
||
effect is of interest, not the list of returned values from each
|
||
individual mapping.
|
||
|
||
|
||
|
||
42
|
||
|
||
|
||
|
||
LIST EVALUATION CONTROL FUNCTIONS CONT'D
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
(maplist s1 l1 l2 ... ln) and (map s1 l1 l2 ... ln)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
These functions are similar to mapc and mapcar except that
|
||
rather than taking successive cars down the lists l1...ln to make
|
||
argument lists, they take successive cdrs down the lists l1...ln
|
||
to make the arguement lists. Map does the same thing as maplist
|
||
but it does not construct a list of the results of each mapping,
|
||
rather it just returns l1. Like mapc, this is usefull when the
|
||
side effect is of greater interest than the result. For example:
|
||
|
||
-->(maplist 'cons '(a b) '(x y))
|
||
(((a b) x y) ((b)(y))
|
||
-->(maplist 'car '(a b c))
|
||
(a b c)
|
||
-->(defun silly(a b)(list a b))
|
||
-->(trace silly)
|
||
(silly)
|
||
-->(mapc silly '(a b) '(c d))
|
||
<enter> silly((a b) (c d))
|
||
<EXIT> silly ((a b) (c d))
|
||
<enter> silly((b)(d))
|
||
<EXIT> silly((b)(d))
|
||
(a b)
|
||
|
||
These examples operate as follows. The first example simply
|
||
cons'es the list (a b) to the list (x y) and then cons'es the
|
||
list (b) to the list (y). It then makes a list of these two
|
||
results and returns it (((a b) x y) and ((b) y). The second
|
||
applies car to successive cdr's of the list (a b c) ie it
|
||
evaluates (car (a b c)) then (car (b c)) then (car (c)) and makes
|
||
a list of the results (a b c) which it returns. The last example
|
||
demonstrates that mapc does the same thing as maplist except that
|
||
there is no list of results returned. Rather the first argument
|
||
list is returned. To demonstrate this the function 'silly which
|
||
makes a list of its two arguments was traced as it was mapped
|
||
accross the argument lists '(a b) and '(c d). This results in
|
||
silly being called with two lists as parameters the first time,
|
||
and the second time with the cdr's of the above two lists.
|
||
|
||
Note the functions mapcon and mapcan are NOT built into PC-
|
||
LISP but are available in PC-LISP.L as macros. The extra
|
||
functions mapcon and mapcan apply (nconc). Use them carefully.
|
||
(See the notes on (nconc) in the DANGEROUS FUNCTIONS section).
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
43
|
||
|
||
|
||
|
||
LIST EVALUATION CONTROL FUNCTIONS CONT'D
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
(defun a1 macro l1 s1 s2 ... sn)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
Macro is a special body, similar to nlambda except that it
|
||
may causes code replacement when it is evaluated. When a macro is
|
||
encountered the list (name arg1 arg2...) is bound to the
|
||
macro parameter l1. Name is the name of the macro and arg1..argn
|
||
are the arguments that were provided to it. Then the bodies of
|
||
the macro s1..sn are evaluated and the expression returned by the
|
||
last body is returned. Then depending on the value of displace-
|
||
macros and the type of the returned S-expression, the returned S-
|
||
expression may destructively replace the peice of code that
|
||
called it. If the value of displace-macros is nil (its default
|
||
value) or the type of the returned S-expression is not one that
|
||
can be replaced, no destructive substitution will occur. Next
|
||
regardless of whether the S-expression was substituted or not,
|
||
the S-expression is evaluated and the value returned. This all
|
||
sounds pretty compex, but in fact it is quite simple, here is an
|
||
example:
|
||
|
||
-->(defun first-elemet macro(l)(cons 'car (cdr l)))
|
||
first-element
|
||
-->(setq x '(first-element '(a b c)))
|
||
(first-element '(a b c))
|
||
-->(eval x)
|
||
a
|
||
-->x
|
||
(first-element '(a b c))
|
||
-->(setq displace-macros t)
|
||
t
|
||
-->(eval x)
|
||
a
|
||
-->x
|
||
(car '(a b c))
|
||
-->(eval x)
|
||
a
|
||
|
||
In the example above I have first declared a macro called
|
||
'first-element' which when run given a list parameter should
|
||
return the first element in the list. I could have done this
|
||
using a lambda expression but this would require parameter
|
||
binding etc every time I execute 'first-element'. Rather, what I
|
||
have chosen to do is to cause (first-element x) to be replaced by
|
||
the code (car x) everywhere it is encountered. Then future
|
||
execution of (first-element x) is just as costly as an execution
|
||
of (car x). Let's examine what I did above. First I declared a
|
||
macro which will take the parameter (first-element -stuff-) and
|
||
construct the code (car -stuff-). I then set x to be an
|
||
expression which when evaluated should give 'a. I then verify
|
||
this by evaluating x, sure enough it is 'a. I then look at the
|
||
code for x which has not changed. Now, I set the global variable
|
||
displace-macros to be non nil. What I should now expect is that
|
||
(eval x) will give the same answer, but with the side effect of
|
||
doing the code substitution so that future passes of the
|
||
|
||
|
||
44
|
||
|
||
|
||
|
||
LIST EVALUATION CONTROL FUNCTIONS CONT'D
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
expression bound to x will run much faster. This is the whole
|
||
reason for macros, they are not much use if they are expanded
|
||
every time, it is more work than a simple user defined lambda
|
||
expression call. Anyway after running x and looking at its
|
||
definition we can see that the code has indeed been substituted.
|
||
It is worth noting that unless you set displace-macros to be non
|
||
nil all your macros will be expanded every time they are
|
||
encountered. This is probably not what you want. You should set
|
||
displace-macros to be t to cause macros to behave properly. The
|
||
only reason I did not set displace-macros to be t by default is
|
||
that Franz does not.
|
||
|
||
Note, macros may return any type expression however some
|
||
expressions may not result in code substitution because of
|
||
internal problems with doing the substitution. In particular a
|
||
macro that directly returns an atom, hunk or string will never
|
||
result in code replacement, while a macro that returns a list,
|
||
fixnum, flonum or port can result in code replacement. Since code
|
||
replacement is a physical copying of one cell over another heap
|
||
space owning functions cannot be physically substituted because
|
||
their cells are unique. You should note however that these
|
||
limitations do not occur much in practice since usually a macro
|
||
will return a number or a list. For exampe a quoted atom is ok
|
||
because it is really the list (quote x). In any case PC-LISP
|
||
macros will always return the correct values regardless of
|
||
these substitution limitations.
|
||
|
||
Macro bodies can function in all contexts that an nlambda
|
||
body can function, however expansion, if it is to occur will only
|
||
happen when a macro is referred to by its atom name which was
|
||
defined by a defun, def or putd call. Using macro expressions
|
||
disembodied from a name does not however seem terribly useful.
|
||
|
||
(macroexpand s1)
|
||
~~~~~~~~~~~~~~~~
|
||
This function lets you see what the macro expansion of s1
|
||
looks like prior to evaluation and substitution. This function is
|
||
useful for debugging macro definitions and for controlling macro
|
||
evaluation when writing code that generates new code.
|
||
|
||
-->(macroexpand '(a b (first-element '(a b c)) x y z))
|
||
(a b (car '(a b c)) x y z)
|
||
|
||
Macroexpand will expand all macros in s1 but will not
|
||
expand lists that start with quote. The workings of macroexpand
|
||
are probably a little different than Franz although the results
|
||
should be pretty much the same. Note in particular that
|
||
macroexpand creates a new structure, it does not expand into the
|
||
existing structure as (eval) does during real macro expansion.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
45
|
||
|
||
|
||
|
||
LIST EVALUATION CONTROL FUNCTIONS CONT'D
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
(prog l1 s1.....sn)
|
||
~~~~~~~~~~~~~~~~~~~
|
||
Prog is a way of escaping the pure LISP applicative
|
||
programming environment. It allows you to evaluate a sequence of
|
||
S-expressions one after the other in true imperative style. It
|
||
allows you to use the functions (go..) and (return ..) to perform
|
||
the goto and return functions that imperative languages permit.
|
||
Prog operates as follows: The list l1 which is a list of atom
|
||
names is scanned and each atom is bound to nil at this scope
|
||
level. Next the S-expressions s1..sn are scanned once. If any of
|
||
s1..sn are atoms they are bound to the S-expression that follows
|
||
them. Next we start evaluating lists s1...sn ignoring the atoms
|
||
which are assumed to be labels. If after evaluation an S-
|
||
expression is of the form ($[|return|]$ Z) we unbind all the
|
||
atoms and labels and return the S-expression Z. If after
|
||
evaluation a list is of the form ($[|go|]$ Z) we alter our
|
||
evaluation to start next at Z. The functions (go) and (return)
|
||
will return the above mentioned special forms. If at any time we
|
||
reach sn, and it is not a go or a return, we simply unbind all of
|
||
l1 and the labels in s1...sn and return the result of evaluating
|
||
sn. Note that prog labels must be alpha or literal alpha atoms.
|
||
You are advised to keep the calls to go and return within the
|
||
lexical scope of the prog body and to ensure that the special
|
||
form returned is not absorbed by some higher level function.
|
||
|
||
-->(prog (List SumOfAtoms)
|
||
(setq List (hashtabstat))
|
||
(setq SumOfAtoms 0)
|
||
LOOP (cond ((null List) (return SumOfAtoms)))
|
||
(setq SumOfAtoms (+ (car List) SumOfAtoms))
|
||
(setq List (cdr List))
|
||
(go LOOP)
|
||
)
|
||
306
|
||
|
||
This peice of code operates as follows. First it creates two
|
||
local variables. Next it binds the variable List to the list of
|
||
hash bucket totals from the heap hash table. It then sets a sum
|
||
counter to 0. Next it checks the List variable to see if it is
|
||
nil. If so it returns the Sum Of all the Atoms. Otherwise it adds
|
||
the first fixnum in the list List to the running SumOfAtoms,
|
||
winds in the list List by one, and jumps to LOOP. Note also that
|
||
we can accomplish the same thing as the above prog with the much
|
||
simpler example which follows:
|
||
|
||
-->(eval (cons '+ (hashtabstat)))
|
||
306
|
||
|
||
If execution reaches the end of the prog without
|
||
encountering a (return), the last evaluated expression is
|
||
returned.
|
||
|
||
|
||
|
||
46
|
||
|
||
|
||
|
||
LIST EVALUATION CONTROL FUNCTIONS CONT'D
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
(caseq exp *l1* *l2* ... *lN*)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
Where l1..lN are of the form (key s1...sN) or ((key1 key2
|
||
..keyN) s1...sN). Will select one of a number of cases l1..lN.
|
||
The case will be selected if its key or one of the keys in a key
|
||
list are eq to 'exp'. The key 't will match anything. When a case
|
||
is selected, the expressions s1...sN are evaluated left to right
|
||
and the result of the last evaluation is returned. For example:
|
||
|
||
-->(caseq 'apple
|
||
(orange "orange")
|
||
(grape "green")
|
||
((stawberry cherry apple) "red"))
|
||
"red"
|
||
|
||
-->(caseq '|\n|
|
||
((a b c d e f g h i j k l m n o p q r s t u v w x y z)
|
||
'lowercase)
|
||
((A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
|
||
'uppercase)
|
||
(t 'other))
|
||
other
|
||
|
||
-->(caseq (length '(a b c d))
|
||
( 0 nil)
|
||
( 1 nil)
|
||
( 2 (setq x 2) (patom "length is 2\n") t)
|
||
( 3 (patom "length is 3\n") t)
|
||
( (4 5 6 7 8 9 10)
|
||
(patom "length is in 4..10\n") t))
|
||
length is in 4..10
|
||
t
|
||
-->
|
||
|
||
Given the choice between caseq and cond, pick caseq because
|
||
it is much faster than cond. This is because most of the work of
|
||
testing conditions is done in the interpreter.
|
||
|
||
(funcall s1 .... sN)
|
||
~~~~~~~~~~~~~~~~~~~~
|
||
Will apply the function s1 to arguments s2..sN and return
|
||
the result. This is equivalent to (apply s1 (list s2...sN)). The
|
||
function s1 must be present but the arguments s2 .. sN are
|
||
optional and depend on the number of arguments/discipline of s1.
|
||
|
||
-->(funcall 'car '(a b c))
|
||
a
|
||
-->(funcall 'cons 'a '(b c))
|
||
(a b c)
|
||
|
||
|
||
|
||
|
||
|
||
47
|
||
|
||
|
||
|
||
ERROR TRAPPING AND GENERATION
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
PC-LISP V3.00 provides the user with the ability to trap any
|
||
error with the exception of stack overflows (for technical
|
||
reasons). This is accomplished by the (errset) and (err)
|
||
functions. Similar control flow violations are provided for
|
||
general use via the (catch) and (throw) functions.
|
||
|
||
(errset *s1* [*s2*])
|
||
~~~~~~~~~~~~~~~~~~~~
|
||
Will evaluate its argument s2 THEN s1, if s1 results in any
|
||
kind of error (with the exception of a stack ovflow), then errset
|
||
will return the value nil. If the value of s2 is non nil then the
|
||
normal error message is printed at this time on the console. If
|
||
no error occurs in evaluating s1 then the result of evaluating s1
|
||
is made into a list and returned (to distinguish nil from (nil)).
|
||
If the error was generated by a call to (err s1) then the value
|
||
s1 is returned rather than nil. Any bindings that were made
|
||
between the time (errset) was called and the point of the error
|
||
will be undone. However, global bindings are not undone. The
|
||
number of (errset)s that can be nested is determined by the
|
||
amount of memory you have, there is no arbitrary fixed limit. For
|
||
example:
|
||
|
||
-->(errset (car (cdr (car (car 8]
|
||
--- error evaluating built in function [car] ---
|
||
nil
|
||
-->(errset (car 8) nil) ; car err msg not printed.
|
||
nil
|
||
-->(errset (atom 8)) ; no error
|
||
(8)
|
||
-->(errset (prog () l (go l)) nil) ; trap CTRL-BREAK.
|
||
nil
|
||
|
||
(err s1)
|
||
~~~~~~~~
|
||
Will 'throw' the value of s1 to the nearest enclosing
|
||
errset which will then return with the value s1. If no errset
|
||
encloses the evaluation of err then the break level is entered
|
||
and the message "--- user err ---" is displayed. For example:
|
||
|
||
-->(errset (+ 1 2 3 (err 'x)) nil)
|
||
x
|
||
-->(errset (+ 1 2 3 (err 'x)))
|
||
--- user err ---
|
||
x
|
||
-->(err 'x)
|
||
--- user err ---
|
||
er> ; now do an end of file CONTROL-Z
|
||
-->(errset (errset (+ 1 2 3 (err 'x)) nil))
|
||
(x)
|
||
--> ; the 2nd errset trapped, 1st did not.
|
||
|
||
|
||
|
||
|
||
48
|
||
|
||
|
||
|
||
NON STANDARD CONTROL FLOW FUNCTIONS
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
PC-LISP V3.00 provides a means of skipping the returns to
|
||
outer evaluations and 'throwing' a value up to an outer routine
|
||
to be returned by that routine. These two routines are called
|
||
(throw) and (catch) respectively. They operate in a manner
|
||
similar to (err) and (errset) but allow selective catching by
|
||
means of a tag assocaited with a thrown value.
|
||
|
||
(catch *s1* [*s2*])
|
||
~~~~~~~~~~~~~~~~~~~
|
||
Will evaluate s2 first, then s1. If during the evaluation of
|
||
s1 a call is made to (throw s3) the catch will return immediately
|
||
with the value s3. If s2 is provided it is interpreted as a tag,
|
||
or a list of tags (where a tag is a symbol). The catch is then
|
||
selective in the throws that it will catch. It will not catch the
|
||
thrown expression unless the tag argument provided to the throw
|
||
matches either the symbol s2, or one of the symbols in the list
|
||
s2. The symbol nil matches all tags. If the tag of the thrown
|
||
expression does not match the symbol s2, or is not present in the
|
||
list of symbols s2, the expression and tag will be throw up to
|
||
the next closest enclosing catch. If a throw arrives at the top
|
||
level having not been caught, the error handler will catch it and
|
||
display the message "--- no catch for this tag [xyz] ---". Where
|
||
xyz was the tag assocaited with the thrown expression. This error
|
||
like all other errors (with the exception of a stack overflow)
|
||
can be trapped by (errset). As with errset and err, all bindings
|
||
other than global bindings that were made between the time the
|
||
catch was called and the throw was called will be undone. As
|
||
with errset, there is no fixed limit to the depth of nesting.
|
||
|
||
(throw s1 [s2])
|
||
~~~~~~~~~~~~~~~
|
||
Will never return, it throws the expression s1 with tag s2
|
||
or nil if s2 is not provided up to the nearest enclosing (catch).
|
||
The catch will either catch the expression or throw it up to the
|
||
next enclosing (catch) depending on whether the tag s2 matches
|
||
the catch's tag or list of tags. A nil tag (the default) will
|
||
match any other tag. If the thrown expression is not caught by
|
||
any (catch) the error handler will catch it and generate the
|
||
error described for (catch) above. For example:
|
||
|
||
-->(catch (patom "hi" (throw 'x)))
|
||
x
|
||
-->(catch (patom "hi" (throw 'x)) 'MyTag1)
|
||
x
|
||
-->(catch (patom "hi" (throw 'x)) 'MyTag1) 'MyTag1)
|
||
x
|
||
-->(catch (patom "hi" (throw 'x)) 'MyTag1) '(a b MyTag1))
|
||
x
|
||
-->(catch (catch "hi" (patom (throw 'x 't1)) 't2) 't1)
|
||
x
|
||
-->(catch (patom "hi" (throw 'x))) 'MyTag1)
|
||
--- no catch for this tag [MyTag] ---
|
||
|
||
|
||
|
||
49
|
||
|
||
|
||
|
||
HUNKS
|
||
~~~~~
|
||
A hunk is just an array of 1 to 126 elements. The elements
|
||
may be any other type including hunks. With hunks it is possible
|
||
to create self referencial structures (see DANGEROUS FUNCTIONS).
|
||
A Hunks element storage space comes from the heap. Hunks like
|
||
strings and alpha print names are subject to compaction
|
||
relocation and reclaimation.
|
||
|
||
(hunk s1 s2 .... sN)
|
||
~~~~~~~~~~~~~~~~~~~~
|
||
Returns a newly created hunk of size N whose elements are
|
||
s1, s2 ... sN in that order. N must be in the range 1 to 126
|
||
inclusive. Note that a hunk is printed like a list but
|
||
is delimited by { } not (). Ie {s1 s2 ...sN}.
|
||
|
||
(cxr n1 H)
|
||
~~~~~~~~~~
|
||
Returns the n1'th element of hunk H indexed from 0. Hence n1
|
||
must be in the range 0 .. (hunksize H)-1.
|
||
|
||
(hunkp s1)
|
||
~~~~~~~~~~
|
||
Returns true if s1 is of type hunk, otherwise it returns
|
||
nil. Note this function has also been mentioned with the other
|
||
predicates.
|
||
|
||
(hunksize H)
|
||
~~~~~~~~~~~~
|
||
Returns a fixnum whose value is the size of the hunk. This
|
||
value is one larger than the largest index allowed into the hunk
|
||
by both cxr and rplacx. The size of a hunk is fixed at the time
|
||
of its creation and can never change throughout it's life.
|
||
|
||
(makhunk n1) or (makhunk (s1 s2 ...sN))
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
The first form returns a nil filled hunk of n1 elements.
|
||
Needless to say, n1 must be between 1 and 126 inclusive. The
|
||
second form is just identical to (hunk s1.....sN).
|
||
|
||
(rplacx n1 H s1)
|
||
~~~~~~~~~~~~~~~~
|
||
Returns the hunk H, however as a side effect element n1 of H
|
||
has been made (eq) to s1. In other words H[n1] = s1. Note that
|
||
this function like rplaca and rplacd allows you to create self
|
||
referencial structures.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
50
|
||
|
||
|
||
|
||
ARRAYS
|
||
~~~~~~
|
||
(array *a1* *a2* n1 ... nN)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
Allocates and returns a nil filled array whose name is a1
|
||
and whose dimensions are n1 x n2 x..nN. Parameter a2 is ignored
|
||
and is there for compatability with MacLisp arrays. There must be
|
||
at least one dimension. The total size of the array is only
|
||
limited by the amount of memory available. After a call to
|
||
(array a1..) the symbol a1 will behave like a function that
|
||
accesses the array using it's args as dimensions and optional
|
||
element to store in the array. (See next function).
|
||
|
||
(A [exp] n2... nN ) { A is an array name not a function }
|
||
~~~~~~~~~~~~~~~~~~~
|
||
If A is the name of an array created by (array a1 t n1...nN)
|
||
then this function will return the element corresponding to
|
||
indecies n1...nN. If exp is provided then the element
|
||
corresponding to indecies n1...nN will be set eq to exp. It is an
|
||
error not to provide legal indecies to an array.
|
||
|
||
(arraydims A) { A evaluates to an array name }
|
||
~~~~~~~~~~~~~
|
||
Returns a list whose car is t, and whose cdr is the list
|
||
n1...nN that was provided to (array) when the array A was
|
||
created. In other words returns a list whose car is the type of
|
||
the elements of the array, and whose cdr is a list of the
|
||
dimensions of the array.
|
||
|
||
(getlength A) { A must evaluate to an array name }
|
||
~~~~~~~~~~~~~
|
||
Returns a fixnum whose value is the size of the array. This
|
||
is computed as n1xn2xn3...xnN where n1...nN are the dimensions
|
||
provided in the (array) call that created the array A.
|
||
|
||
(getdata A) { A must evaluate to an array name }
|
||
~~~~~~~~~~~
|
||
Returns a hunk that is the root of the array tree used to
|
||
store the raw elements of A. Its structure is given in the
|
||
section on data types in this manual.
|
||
|
||
(listarray A [n1]) { A must evaluate to an array name }
|
||
~~~~~~~~~~~~~~~~~~
|
||
Returns a list of the elements in A. If n1 is provided then
|
||
the first n1 elements of A are placed into the returned list.
|
||
|
||
(fillarray A l1) { A must evaluate to an array name }
|
||
~~~~~~~~~~~~~~~~
|
||
Fills the array A with successive elements of l1. If l1 has
|
||
less elements than A, the last element in l1 is used to fill the
|
||
remainder of the elements in A.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
51
|
||
|
||
|
||
|
||
ARRAYS (CONT'D)
|
||
~~~~~~~~~~~~~~~~
|
||
Note that like hunks arrays allow us to create self
|
||
referential structures so watch out. Here is a short example of
|
||
how the array access functions work together. Note that (store)
|
||
is a macro included in PC-LISP.L. You should be able to type
|
||
these same statements and get the same results.
|
||
|
||
-->(array A1 t 20 100) ; A1 is a 20 x 100 array
|
||
array[2000] ; ie it has 2000 elements
|
||
-->(A1 0 0) ; get A1[0,0], it is nil
|
||
nil ; to start with.
|
||
-->(A1 "hello" 0 0) ; A1[0,0] = "hello"
|
||
"hello"
|
||
-->(store (A1 19 99) "there") ; macro A1[19,99]="there"
|
||
"there"
|
||
-->(A1 19 99)
|
||
"there"
|
||
-->(arraydims 'A1) ; get the dimensions of A1
|
||
(t 20 100) ; 20 x 100, any type elems
|
||
-->(getlength 'A1)
|
||
2000
|
||
-->(listarray 'A1 5) ; make list of first 5
|
||
("hello" nil nil nil nil)
|
||
-->(getd 'A1)
|
||
array[2000]
|
||
-->(type (getdata 'A1)) ; getdata give hunk tree.
|
||
hunk
|
||
|
||
Note that if you try to allocate an array that is too big
|
||
for the available memory you will get either an out of heap, or
|
||
out of cons cell error message followed by entry to the break
|
||
level. Try making your LISP_HEAP and/or your LISP_ALPH
|
||
environment variables smaller. (See also memory exhaustion).
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
52
|
||
|
||
|
||
|
||
DANGEROUS FUNCTIONS
|
||
~~~~~~~~~~~~~~~~~~~
|
||
The following two functions have potentially disasterous
|
||
results if used by unwary or inexperienced LISP programmers. The
|
||
third function is provided to make their use less dangerous.
|
||
|
||
(rplaca l1 s1)
|
||
~~~~~~~~~~~~~~
|
||
The cons cell l1 is physically altered so that its car is
|
||
(eq) to s1. That is the car pointer of l1 is set to point to s1.
|
||
The list l1 is returned. (l1 must not be nil).
|
||
|
||
(rplacd l1 s1)
|
||
~~~~~~~~~~~~~~
|
||
The cons cell l1 is physically altered so that its cdr is
|
||
(eq) to s1. That is the cdr pointer of l1 is set to point to s1.
|
||
The list l1 is returned. (l1 must not be nil).
|
||
|
||
(copy s1)
|
||
~~~~~~~~~
|
||
Returns a structure (equal) to s1 but made with new cons
|
||
cells. Note that only cons cells are copied, strings, atoms,
|
||
hunks etc are not copied.
|
||
|
||
Warning #1 - altering a cons cell allows you to create
|
||
structures that point (refer) to themselves. While this does not
|
||
cause a problem for the LISP interpreter or garbage collector it
|
||
does mean that many built in functions will either loop around
|
||
the structure infinitely or recurse until a stack overflows.
|
||
|
||
-->(setq x '(a b c d))
|
||
(a b c d)
|
||
-->(rplaca x x)
|
||
((((((((((((((((((((((((((((((((((((((((...............
|
||
-- stack overflow --
|
||
er>
|
||
|
||
Warning #2 - altering a cons cell can cause a million little
|
||
side effects that you did not count on. Consider carefully the
|
||
following example.
|
||
|
||
-->(defun FooBar(x) (cons x '(b c)))
|
||
FooBar
|
||
-->(setq z (FooBar 'a))
|
||
(a b c)
|
||
-->(rplaca (cdr z) 'GOTCHA!)
|
||
(GOTCAH! c)
|
||
-->(FooBar 'a)
|
||
(a GOTCHA! c)
|
||
|
||
What happened? The rplaca has modified the list that is a
|
||
constant in FooBar. Lists are not copied unless necessary and
|
||
building the list (a b c) did not require a copy of the constant
|
||
list (b c) to be made. Ie (cdr z) is eq to (b c) in FooBar.
|
||
|
||
|
||
|
||
53
|
||
|
||
|
||
|
||
DANGEROUS FUNCTIONS (CONT'D)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
(sort l1 s1)
|
||
~~~~~~~~~~~~
|
||
Destructively sorts the list l1 and returns it. The function
|
||
s1 is used to compare elements in the list. If s1 is nil the
|
||
function alphalessp is used. If s1 is non nil, it must be a
|
||
lambda expression taking two parameters and return t or nil. It
|
||
may also be the name of a built in function such as '< or '>.
|
||
etc. For example:
|
||
|
||
-->(sort '(john frank adam) nil)
|
||
(adam frank john)
|
||
|
||
-->(sort '(10 9 8 1 2 3) '<)
|
||
(1 2 3 8 9 10)
|
||
|
||
-->(sort '(10 9 8 1 2 3) '(lambda(x y)(not (< x y))))
|
||
(10 9 8 3 1 2) ; reverse of last example would be faster.
|
||
|
||
(sortcar l1 s1)
|
||
~~~~~~~~~~~~~~~
|
||
Destructively sorts the list l1 and returns it. The car of
|
||
each element in l1 is used as the key rather then the element
|
||
itself as in (sort) above. The function s1 is used to compare
|
||
elements. s1 is as in (sort). For example.
|
||
|
||
-->(sortcar '( (john smith) (frank jones) (adam west)) nil)
|
||
((adam west)(frank jones)(john smith))
|
||
|
||
Note that these functions are destructive, they alter the
|
||
actual list parameters passed to them. Either make sure you know
|
||
what you are doing, or use (copy l1) before passing the list as a
|
||
parameter to (sort) or (sortcar). Both of these functions use the
|
||
quicksort algorithm. Ie, they run in O(n*lg(n)) time. Note that
|
||
there is a limit to the length of list that you can sort imposed
|
||
by the size of the system stack. Sorting with s1=nil, is much
|
||
much faster than providing your own compare routine. If you want
|
||
the reverse ordering, sort using s1 = nil, then call (reverse)
|
||
don't do (sort l1 '(lambda(k1 k2)(not(alphalessp(k1 k2)))), it
|
||
will be much slower. Sorting with s1=nil also does not cause any
|
||
garbage collection whereas sorting with s1 not = nil may be
|
||
interrupted by garbage collection because of the overhead of
|
||
building a parameter list and calling the function.
|
||
|
||
(nconc l1 l2 ... ln)
|
||
~~~~~~~~~~~~~~~~~~~~
|
||
Similar to append except that the lists are joined together
|
||
destructively. The last cons cell in l1 is changed so that its
|
||
cdr points to l2, the last cons cell in l2 is changed to point to
|
||
l3 etc. Note that any nil parameters are ignored. Nconc returns
|
||
the constructed list or nil if all parameters are nil.
|
||
MSDOS BIOS CALLS FOR GRAPHICS OUTPUT
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
|
||
54
|
||
|
||
|
||
|
||
These functions allow you to perform BIOS level
|
||
graphics/character oriented I/O. They all result in an INT 10H.
|
||
This means that the graphics should be portable to most MSDOS
|
||
machines and should run under any windowing environment like
|
||
Topview or MSwindows. This is why they are so slow. Note that
|
||
they all return 't. They do not check to see if the INT call was
|
||
successful or if you have a graphics capability. You can crash
|
||
your system if you abuse these functions.
|
||
|
||
(#scrline# n1 n2 n3 n4 n5)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
Draws a line on the screen connecting (n1,n2) with the point
|
||
(n3,n4) using attribute n5. BIOS level I/O means this is slow but
|
||
does work on every MS-DOS machine I know of!
|
||
|
||
(#scrmde# n1) {ah=0, al=n1, INT 10H}
|
||
~~~~~~~~~~~~~
|
||
Sets the video mode to n1. Modes are positive numbers 0.....
|
||
Where (8 and 9) are high resolution for the Tandy2000 and I
|
||
suppose are high resolution modes on other machines that support
|
||
the (640 x 400) or greater graphics resolutions. These are all
|
||
listed in your hardware reference manual but basically they are:
|
||
0 = 40x25B&W, 1=40x25COL, 2=80x25B&W 3=80x25COL, 4 =320x200COL,
|
||
5=320x200B&W, 6=640x200B&W, 7=reserved, 8=640x400COL,
|
||
9=640x400B&W etc...? This is as of DOS 2.10. Also note that the
|
||
AT EGA Graphics Modes should also work with no problem. The value
|
||
of n1 is not checked. This allows for the unpredictably high
|
||
modes required by some machines.
|
||
|
||
(#scrsap# n1) {ah=5, al=n1, INT 10H}
|
||
~~~~~~~~~~~~~
|
||
Sets the active video page to n1. n1 should be between 0 and
|
||
8. This is valid for text modes only. Versions of MSDOS other
|
||
than 2.10 may not support this call.
|
||
|
||
(#scrspt# bh bl al) {ah=11,bh=bh,bl=bl,al=al,INT 10H}
|
||
~~~~~~~~~~~~~~~~~~~
|
||
Sets the color palette according to the value in bh. For
|
||
most BIOS compatable machines these are: If bh=0 it sets
|
||
background color bl. If bh=1 it sets the default palette to the
|
||
number 0 or 1 in BL. If bh=2 it sets a single palette entry where
|
||
bl is the palette entry number and al is the color value. See
|
||
your BIOS reference for the color values and additional info.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
55
|
||
|
||
|
||
|
||
MSDOS BIOS CALLS FOR GRAPHICS OUTPUT (CONT'D)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
(#scrscp# n1 n2 n3) {ah=2,bh=n1,dh=n2,dl=n3,INT 10H}
|
||
~~~~~~~~~~~~~~~~~~~
|
||
Sets the cursor position to be in page n1 at row n2 and in
|
||
column n3. Where 0 is the top row and 0 is leftmost col.
|
||
|
||
(#scrsct# n1 n2) {ah=1,ch=n1,cl=n2,INT 10H}
|
||
~~~~~~~~~~~~~~~~
|
||
Sets the cursor type to agree with the following: n1 bit
|
||
5 (0 = blink 1 = steady), bit 6 (0 = visible, 1 = invisible),
|
||
bits 4-0 = start line for cursor within character cell. n2 bits
|
||
4-0 = end line for cursor within character cell.
|
||
|
||
(#scrwdot# n1 n2 n3) {ah=12,cx=n1,dx=n2,al=n3,INT 10H}
|
||
~~~~~~~~~~~~~~~~~~~~
|
||
Write a dot (pixel). The pixel at row n1 and column n2 has
|
||
its color value XORed with the color attribute n3. Since the
|
||
color attributes vary from machine to machine you will have to
|
||
look up the correct values for this parameter in your BIOS guide.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
56
|
||
|
||
|
||
|
||
MEMORY EXHAUSTION
|
||
~~~~~~~~~~~~~~~~~
|
||
The memory is all used up when you get a message such as
|
||
"--- out of cons cells ---". Usually when this happens it is
|
||
because you are tying up memory somewhere but do not realize it.
|
||
The most common way to tie up memory is to execute an infinite
|
||
recursion such as (defun looper(n)(looper (+ n 1))). The stack
|
||
will of course overflow and YOUR BINDINGS WILL BE HELD FOR YOU!!
|
||
This means that ALL bindings are held. If you execute the above
|
||
program several times from the break level, 'er>', you will
|
||
eventually run out of CONS cells. They are all in use to hold the
|
||
values n, n+1, n+2,...... to the point of the first stack
|
||
overflow. Then n, n+1,.... to the point of the second overflow
|
||
and so on and so on. Eventually there is no more space left to
|
||
evaluate the function (looper). The solution is simple: If you
|
||
run an infinite recursion by mistake and are placed in the break
|
||
level, use the showstack to figure out where you are. Then use
|
||
the break level to examine variables etc. But before retrying
|
||
anything return to the top level. This will cause the held
|
||
bindings to be dropped and the cells will become reclaimable
|
||
ie (garbage and thus free). Consider the following session with
|
||
PC-LISP V3.00:
|
||
|
||
-->(defun looper(n)(looper (+ n 1))) ; infinite function
|
||
looper
|
||
-->(looper 0) ; run it from 0
|
||
-- Stack Overflow -- ; all n's saved!
|
||
er>n ; last value of n
|
||
588
|
||
er>(looper 0) ; another run will
|
||
-- Stack Overflow -- ; save more n's
|
||
er>(looper 0)
|
||
-- Stack Overflow --
|
||
er>(looper 0) ; another run won't
|
||
--- out of cons cells ---
|
||
er>
|
||
|
||
Note that the last (looper 0) call we made from the break
|
||
level was unable to complete because we ran out of memory. When
|
||
any of the three types of memory is exhausted a message is
|
||
printed and the break level is entered. In most cases it is
|
||
possible to continue by typing CONTROL-Z and ENTER or CONTROL-D
|
||
(if you are using UNIX) to return to the top level.
|
||
|
||
If you find that you are running out of heap space it may be
|
||
because you are keeping too many unused strings,symbols or hunks.
|
||
Or, you are trying to allocate an array that is too big for the
|
||
amount or configuration of your H and A LISP_MEM settings. In the
|
||
first case the solution is not to do things like (setq x
|
||
(oblist)). In the second case the solution is to adjust the
|
||
H option up and the A option down until the array can be
|
||
allocated. There is of course a practical limit to the size of
|
||
array that can be allocated on a 640K IBM-PC. A UNIX machine or
|
||
MS-DOS machine without the 640K limit will be restricted by the
|
||
hard limit of 75 allocatable blocks.
|
||
|
||
|
||
57
|
||
|
||
|
||
|
||
TECHNICAL INFORMATION
|
||
~~~~~~~~~~~~~~~~~~~~~
|
||
The interpreter is written 99% in C. The other 1 percent is
|
||
assember needed to trap things like stack overflows and handle
|
||
BIOS level graphics on an MS-DOS machines. The UNIX version
|
||
requires no extra assembly language. In total the program is
|
||
nearly 9000 lines of C and is easily ported to most UNIX machines
|
||
but requires a little assembler for most MS-DOS machines.
|
||
|
||
Memory is organized as follows. Alpha cells have fields for
|
||
a shallow stack of bindings, a pointer to heap space for the
|
||
print names, a pointer to any built in or user defined functions,
|
||
and a pointer to any property lists. Alpha cells are the largest
|
||
of all the cells and have their own fixed storage area. Heap
|
||
space which is just the space used for the print names of the
|
||
alpha cells and strings, and the element array for hunks may be
|
||
variable sized blocks of up to 254 bytes long. This is why a hunk
|
||
can have only 126 elements in PC-LISP. The rest of the cells used
|
||
by PC-LISP are all considered as one. This consists of the
|
||
flonum, fixnum, list, string, hunk and port cells. They have
|
||
their own contiguous slice of memory. This means that three
|
||
different contiguous types of memory are required. It is managed
|
||
in the following way. At start up time the percentages of memory
|
||
are read from the default settings or the environment variables
|
||
LISP_MEM. Next memory is allocated in 16K chunks up to either the
|
||
limit given by the B setting in the LISP_MEM variable, or until
|
||
either no memory is left or the hard limit of 75 blocks is
|
||
reached. These blocks are then kept track of in a large vector of
|
||
pointers. Next groups of these blocks are primed for use by
|
||
alpha,cell, or heap managers according to the A and H settings in
|
||
the LISP_MEM environment variable or the default of 1 each. These
|
||
managers handle the distribution and reclamation of memory in
|
||
their own block. The heap manager will perform compaction and
|
||
relocation to get free space. The alpha and cell managers will
|
||
perform mark and gather garbage collection to get space. The heap
|
||
manager may request mark and gather collection if there is a real
|
||
shortage of heap space prior to performing compaction.
|
||
|
||
Stack overflow detection is done by intercepting the call to
|
||
the MSC __chkstk() routine. And performing its usual function of
|
||
local storage allocation but when an overflow occurs temporarily
|
||
resets the stack, and them making a call to my own C stack
|
||
overflow routine. This then longjmps out of the error condition.
|
||
The UNIX version does not require this checking because an
|
||
internal stack (not the C stack) will always overflow first. The
|
||
opposite is always true of the MS-DOS version.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
58
|
||
|
||
|
||
|
||
TECHNICAL INFORMATION (CONT'D)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Control-BREAK detection is done via periodic testing of the
|
||
status in the evaluator main loop, and the read main loop. When a
|
||
break is detected control is transferred to the break handler
|
||
which prints a message and longjmps back to the mainline code.
|
||
The MS-DOS version must poll the break status because DOS is not
|
||
reentrant. The UNIX version also polls the break status but only
|
||
because it is forced to by the logic of the MS-DOS version.
|
||
CONTROL-C checking is done in the same way except that a CONTROL-
|
||
C will only be spotted on I/O so a looping non printing function
|
||
can only be stopped with CONTROL-BREAK. Note that CONTROL-BREAK
|
||
is INT 1BH and CONTROL-C is INT 23H on an MS-DOS machine.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
59
|
||
|
||
|
||
|
||
KNOWN BUGS OR LACKING FEATURES OF V2.16
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
-It is possible (but pretty unlikely) to run out of stack
|
||
space while garbage collecting. When this happens the garbage
|
||
collection is retried once but the error is unrecoverable. You
|
||
should treat this as a stack overflow CAUSED BY YOUR PROGRAM.
|
||
PC-LISP V3.00 uses a link inversion marking phase and thus uses a
|
||
small bounded amount of stack space for garbage collection. Note
|
||
that if the stack overflows on the second garbage collection
|
||
retry PC-LISP gives up. Memory will be corrupt and you should
|
||
quit because of the possibility of clobbering programs in RAM
|
||
other than PC-LISP, ie DOS. If you CTRL-Z out of the error, no
|
||
corruption will occur, but (exit) just could corrupt RAM if you
|
||
were very unlucky.
|
||
|
||
-Two special atoms with rather obscure names should never be
|
||
directly returned or manipulated in a prog. These are
|
||
$[|return|]$ and $[|go|]$. If you attempt to say print these from
|
||
within a prog, the print function will return them and this will
|
||
confuse the heck out of prog which uses them for internal
|
||
purposes. Because of this the (oblist) call does not return them.
|
||
Thus the only way they can get into your code is for you to enter
|
||
them directly. Since this is unlikely and I have warned you the
|
||
problem should not occur.
|
||
|
||
-You are not prevented from altering the binding of t. This
|
||
means that if you use t as a parameter or set/setq it to
|
||
something other than t you may cause some strange behaviour,
|
||
especially if you bind t to nil by accident. To limit this
|
||
problem (defun) and (def) will check their parameter list for t
|
||
or nil parameters before putd'ing the expression. (putd) however
|
||
does not check!
|
||
|
||
-Macros are slightly restricted in that only lists, fixnums
|
||
, flonums or ports can be substituted. This is a small difference
|
||
from Franz but one that would require significant performance
|
||
penalties to implement. Since not substituting these types is
|
||
less expensive than implementing substitution would be, I will
|
||
not implement this feature of Franz in a PC environment.
|
||
|
||
-Integer overflow/underflow is not trapped. The answers will
|
||
silently change sign leaving you to figure out why your program
|
||
does not work properly. These could be trapped but I have not
|
||
figured out the best way to do it yet.
|
||
|
||
-A symbol with bindings should not be given a function
|
||
definition and vise versa. This is because the binding of an atom
|
||
is deemed to be its function body if it has no real binding. This
|
||
is different from Franz and was done to simplify the evaluator.
|
||
This is not really a problem because programmers are used to
|
||
keeping function and variable names different.
|
||
|
||
|
||
|
||
|
||
|
||
60
|
||
|
||
|
||
|
||
KNOWN BUGS OR LACKING FEATURES OF V3.00 (CONT'D)
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
-The interpreter is slow. I am planning on introducing a
|
||
compiler which should speed things up significantly. The program
|
||
is slower than some of the commercially available interpreters
|
||
for 3 reasons. Mostly because it is a large model, many
|
||
commercial interpreters are small models. This makes it nearly 3
|
||
times slower, but gives it more usable memory. PC-LISP uses 32bit
|
||
integers which slow down many benchmarks. However, 32bit integers
|
||
are what Franz provides and compatability is more important to
|
||
me. Thirdly PC-LISP uses very little assembler as it is almost
|
||
entirely C. A reasonable speed up could be achieved by rewriting
|
||
one or two key procedures in assembler.
|
||
|
||
-Car and cdr will not access the first and second element of
|
||
a hunk as they do in Franz.
|
||
|
||
-You cannot create custom array accessing schemes. I am not
|
||
planning on introducing these features as they are probably used
|
||
pretty infrequently and would make the already slow array
|
||
accessing even slower.
|
||
|
||
-You cannot set the syntax of a character to anything other
|
||
than a read or splicing read macro. I may introduce this stuff
|
||
later on.
|
||
|
||
-Showstack does not print lists in compressed form
|
||
horizontally. The vertical compression <**> is however done.
|
||
|
||
-Circular structures may cause problems for certain built in
|
||
functions in particular you may not be able to abort the
|
||
evaluation either. They do not however cause any problem for
|
||
garbage collection and can be manipulated if you are careful.
|
||
|
||
-Depending on how much memory is free when PC-LISP is loaded
|
||
it is possible that the (load) and (read) will become slowed down
|
||
due to lack of buffer space for I/O. This happens very
|
||
infrequently but if you notice the slowdown you can fix it by
|
||
setting the LISP-MEM blocks value so that not all the free blocks
|
||
are allocated. See the (exec) command for instructions on how to
|
||
do this.
|
||
|
||
RE BUGS OR DESIRED ENHANCMENTS
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
Please let me know if you find a bug or if you have any
|
||
suggestions. I am always interested to hear other peoples
|
||
ideas and/or criticism.
|
||
|
||
Regards,
|
||
|
||
Peter Ashwood-Smith.
|
||
|
||
|
||
|
||
|
||
|
||
61
|
||
|