home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-02-01 | 144.7 KB | 3,714 lines |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 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
-
-