Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.2 9/18/84; site utcsri.UUCP Path: utzoo!utcsri!petera From: petera@utcsri.UUCP (Smith) Newsgroups: net.micro Subject: PC-LISP PACKAGE (article 6 of 13) Message-ID: <2655@utcsri.UUCP> Date: Sun, 27-Apr-86 14:44:34 EDT Article-I.D.: utcsri.2655 Posted: Sun Apr 27 14:44:34 1986 Date-Received: Sun, 27-Apr-86 16:12:59 EDT Distribution: net Organization: CSRI, University of Toronto Lines: 1204 *** REPLACE THIS LINE WITH YOUR MESSAGE *** [ line eater ] PC-LISP.DOC (part 1 of 2) ---------------------------- CUT HERE ------------------- A GUIDE TO THE PC-LISP INTERPRETER (V2.10) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By Peter Ashwood-Smith ~~~~~~~~~~~~~~~~~~~~~~ University of Toronto, ~~~~~~~~~~~~~~~~~~~~~ Ontario, Canada. ~~~~~~~~~~~~~~~ With thanks to Brian Robertson for the math functions April 15 1986, for Guylaine email: petera!utcsri or br!utcsri mail: Peter Ashwood-Smith #811, 120 St. Patrick St. Toronto, Ontario, Canada, M5T-2X7. phone: (416) 593-7574. 1 INTRODUCTION ~~~~~~~~~~~~ PC-LISP is a small implementation of LISP for ANY MS-DOS machine. 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 efficiencies sake. Version 2.10 has the following features. - Types fixnum, flonum, list, port, symbol, string and hunk. Function bodies lambda, nlambda and macro. - Full garbage collection of ALL types. - Compacting relocating heap management. - Shallow binding techniques for O(1) symbol value lookup. (Dynamic scoping). - Access to some MSDOS BIOS graphics routines. - Over 150 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. - Access to as much (non extended) memory as you've got and control over how this memory is spread among the various data types. - Will run in 256K PC/AT/XT or nearly any other MS-DOS machine. (It has run on every machine I have tried.) 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. Please do not request source unless you plan to use it. Thanks to Brian Robertson also of the University of Toronto Department of Computer Science for the math functions that he wrote for my otherwise excellent Lattice C V2.03 compiler which did not originally come with any. 2 A WARNING ~~~~~~~~~ As I mentioned previously this program was compiled with the Lattice C compiler, as such the program contains code to which Lattice Inc. holds a copyright. If you sell it I can only get angry but Lattice could take you to court. And, as with all software you use it at your own risk. I will not be held responsible for loss of any kind as a result of the correct or incorrect use of this program. A NOTE ~~~~~~ The rest of this manual assumes some knowledge of LISP, MSDOS 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. I recommend LISPcraft because it is the book I happen to use. 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. 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. You can then play with all the functions mentioned in LISPcraft. 3 EXAMPLE LOAD FILES AND THE PC-LISP.L FILE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Included with PC-LISP (V2.10) are a number of .L files. These include: PC-LISP.L, MATCH.L, TURTLE.L, DRAGON.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. Note that the video functions that are still experimental so use them for fun but don't rely on them. These 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. 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 it will look sharper at this (640x400) resolution. 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 as much memory as possible but by setting the LISP%MEM environment variable to an integer >= 3, PC-LISP will stop when this many 16K blocks have been allocated. These will be distributed to the three basic data types in percentages that you can specify via 2 environment variables. The default is that 5% of the memory will be allocated for alpha atoms. 5% will be allocated for heap space, and the rest for cons,port, fixnum, string, flonum and hunk cell types.If you set the environment variables LISP%HEAP and LISP%ALPH to an integer between 1 and 85 these will become the new percentages for the heap and alpha respectively, the rest going to cons, port,flonum, fixnum, hunk and string cells. Note that the percentages are only accurate to the nearest 16K boundary. In other words the set of 16K blocks are divided among the three types as closely to the percentages that you specify as possible. If the percentages that you specify are unreasonable PC-LISP will stop with an error message, otherwise PC-LISP will continue by giving back a very small amount of memory for use by the standard I/O routines. You can alter the amount given back by setting the environment variale LISP%KEEP to the amount you want to give back (See memory management). PC-LISP will then print the banner message, the total memory available and the actual percentages that are allocated to each object. Before processing the command line PC- LISP will look for a file called PC-LISP.L it will look 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 be loaded. Next PC-LISP will read the parameters on the command line. The usage is as follows. * PC-LISP [=nnnn] [file] The optional parameter =nnnn is the Lattice set stack size option. It is preset to 32K and cannot be set smaller. You may set it larger up to 64K if you wish. A 32K stack gives you about 466 recursive calls, 50K = 731 calls, 60K = 878 calls, and 64K = 936 calls. 8086 machines do not allow efficient stacks > 64K. 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 LISP top level prompt '-->'. Typing control-Z and ENTER (MS-DOS 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 ~~~~~~ You will now be in the LISP interpreter and can start to play with it. Basically it is expecting you to type an S- expression. Where an S-expression is an atom, or a list and: An atom may be one of four kinds. It may be an alpha atom , a number atom, a literal alpha atom or a string atom. An alpha atom is just a letter followed by letters/digits and certain special symbols. There may be no more than 254 characters in the alpha atom. To allow you to enter any text as an atom you may delimit the atom with |'s. These will define a literal alpha atom in which you may place any character between the delimiters (except | itself). A number atom is just as you might think an optional plus or minus sign followed by a sequence of digits, followed optionally by a radix point and more digits. Sorry, exponential notation is not supported. It should get into the next version. String atoms are delimited by double quotes like this "this is one string atom" they may not contain | or " in V2.10. A list is just a left ( followed by a of sequence of atoms or lists followed by a right ). A list may also be a sequence of atoms or lists followed by a '.' followed by an S-expression followed by a right parenthesis ). This is called a dotted pair and it means that the CAR and CDR of a list lie on either side of the dot. Note that a space on either side of the dot is essential syntactically. You may optionally place [ and ] in the list to represent meta-parenthesis. Basically the ] just closes all open lists up to the nearest [, or to the beginning of the list if no [ is present. Unlike Franz you may not nest [ ]. Here are some example legal lists. (But NOT legal Lisp commands!) (now is (the . time)) ; dotted pair (the . time) (1 now16 (is (the (time ] ; the ] closes all 4 ('s (car [quote(a b c d]) ; the ] closes 2 ('s to ] (ThisIsBiggerAtom012345678) ; Upper case is ok () ; empty list is equiv to 'nil nil ; is a list and an atom! (1 (-2.2 +3.333)) ; some numbers all floats! ((((((|all one atom|] ; spaces are part of lit atom! ("now" is "the" time) ; "'ed objects are strings! (a . (b . (c . (e)))) ; same thing as (a b c d e) (pc-lisp.l queens.l) ; two atoms, not dotted pairs! (pc-lisp . l) ; dotted because of spaces. Note that you are allowed to mix any number of spaces, line- feeds, carriage returns, form feeds, tabs etc. as long as they do not alter the delimitation of an atom/string/fixnum/flonum or dot. Comments may start at any point in a line and will continue until the end of the line as shown in the above example lists. Any number of comment starters ';' may harmlessly follow the first comment starter ';'. PC-LISP is insensitive to line length and will handle lines as long as MS-DOS will give it. 6 READ MACRO QUOTE ~~~~~~~~~~~~~~~~ PC-LISP supplies one read macro called 'quote' and written using the little ' symbol. (User read macros in later versions) This read macro is just a short hand way of writing the list (quote XX). Where XX is what follows the '. Here are some examples of what the read macro will do to your input before passing it to the evaluator. '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 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 required, and draws attention to 'data' in your expressions. User defined read macros will be added to a future version of PC-LISP. And backquote, splice etc. will be predefined. SIDEKICK AND OTHER CO-RESIDENT PROGRAMS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sidekick and others, should all work without any problem with PC-LISP. I highly recommend using one of these programs as it provides a way of editing your lists and then resubmitting them for evaluation. This is much more convenient than retyping the expression, or leaving the interpreter and reediting a load file. The existence of these programs is one reason why there is no editing facility provided in PC-LISP. Note also that the normal MS-DOS command line editing functions all work within PC- LISP. (See your DOS manual for details on command line editing). 7 SYNTAX ERRORS ~~~~~~~~~~~~~ When you enter a list which is not correctly nested the interpreter will return the wonderfully informative 'syntax error' message. 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. EVALUATING S-EXPRESSIONS ~~~~~~~~~~~~~~~~~~~~~~~~ An S-expression may be an atom or a list. If it is an atom the evaluation of it is its current binding. Strings, integers, hunks and floating point numbers all evaluate to themselves. Most atoms are not bound to begin with so just entering an atom will result in the error 'unbound atom'. Evaluating a list consists of calling the function named or given by the first element in the list with parameters given by the rest of the list. For example there is a function called '+' which takes any number of fixnum values and returns their sum. So: -->(+ 2 4 6 8) Would return the result of 2+4+6+8 ie 20. We can also compose these function calls by using list nesting. For example we can subtract 2+4 from 6+8 as follows: -->(- (+ 6 8) (+ 2 4)) We can also perform operations on other types of atoms. Suppose that we wanted to reverse the list (time flies like arrows). There is a built in function called 'reverse' that does just what we want. So we could try typing. -->(reverse (time flies like arrows)) But the interpreter will be confused! It does not know that 'time' is data and not a function taking arguments 'flies', 'like' and 'arrows'. We must use the function 'quote' which returns its arguments unevaluated, hence its name "quote". -->(reverse (quote (time flies like arrows))) 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 '(now is the time)) 8 EVALUATING S-EXPRESSIONS CONT'D ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This gives us the correct result without as much typing. You will note that the subtraction of 2+4 from 6+8 could also have been entered as: -->(- (+ '6 '8) (+ '2 '4)) 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. So a legal input to the interpreter could be: -->((lambda(x)(+ x 10)) 14) 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. So I can evaluate the following expression. -->((lambda(f x)(f (car x))) '(lambda(l)(car l)) '((hi))) 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 lambda expressions with multiple bodies, nlambda expressions with multiple bodies and labeled lambda and nlambda expressions. See the Built In Functions Section which follows for more details on lambda and nlambda. A slightly restricted macro form is also permitted. For information on macros see the MACRO section of the manual. 9 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 (hereafter referred to as CONTROL- BREAK). Note that this will only interrupt list evaluation, it will not interrupt garbage collection which continues to completion. So, if you hit CONTROL-BREAK or CONTROL-C 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 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. 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. 10 DATA TYPES IN PC-LISP ~~~~~~~~~~~~~~~~~~~~~ PC-LISP has the following data types, 32 bit integers, single precision floating point numbers, lists, ports for file I/O, alpha atoms, strings and hunks (up to 126 in length just one short of Franz!). The (type) function returns these atoms: fixnum - a 32 bit integer. flonum - a single 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). 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 in it, it is assumed to be a flonum even if there are no non zero digits following the radix point. Fixnums and flonums may appear the same when printed. The print function will output a flonum with no radix point if none is necessary, hence two numbers may look the same when printed but may be un (equal) because they have different types and hence different structures. 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. See HUNKS. 11 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 float atom parameters, type flonun. - 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. 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 same results shown. 12 PREDEFINED GLOBAL VARIABLES (ATOMS) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A number of atoms are globally prebound by PC-LISP. These variables are testable and settable by you but in some cases altering the bindings is highly inadvisable. Note that a binding can be inadvertently 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'. "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 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. "$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 occurred 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. 13 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 MIXED 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. 14 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 and fixnums you cannot use (eq) because they are not identical objects. In Franz (eq 1 1) returns t because of a space saving trick. You should not rely on this working in other LISPS including PC-LISP. (alphalessp h1 h2) ---> if (h1 ASCII before h2) 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 object) 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 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, 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. For example (zerop n) is faster than (= 0 n) because (zerop) like all functions that take number parameters is biased towards fixnums. 15 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 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 a1) 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 position (from 1) of first char in h2 in h1 or nil if this char does not occur in h1. (concat h1 .. hN) ---> Forms a new atom by concatenating all the strings or atoms. Or nil if if N = 0. (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. 16 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 all atom elements of l1. If l1 is (equal) to (n i l) implode returns nil. (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. (list s1 s2...sN) ---> a list with elements (s1 s2 ...sN) If N = 0 list returns nil. (member s1 l1) ---> If (s1 (equal) to a top level sub list of l1) this sublist, else nil. (memq s1 l1) ---> If (s1 (eq) to a top level sub list of l1) this sublist, 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*) ---> exactly s1 unevaled without changes. (reverse l1) ---> the list l1, reversed at top level. (type s1) ---> list,flonum,port,symbol, fixnum or hunk as determined by the type of the parameter s1. 17 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 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 or 'hunk. (stringp s1) ~~~~~~~~~~~~ Will return t if the S-expression s1 is of type string, otherwise it returns nil. (substring h1 n1 [n2]) ~~~~~~~~~~~~~~~~~~~~~~ If n1 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 referenced 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. 18 FILE I/O FUNCTIONS ~~~~~~~~~~~~~~~~~~ These functions perform simple list/atom and character I/O you must be careful when writing lists to files to terminate with a new line before closing the file. Otherwise they may cause problems for some MS-DOS editors etc. These functions operate on type 'port' which is returned by 'fileopen' and which when printed is just %file@nn% where 'file' is the name of associated port and nn is the file number 0..(20?). You cannot have more than 5 ports open to the same file at any one time, nor more than 20 ports open in total. (close p1) ~~~~~~~~~~ Closes the port p1 and returns t. Note that you must be careful to write a line feed (ascii 10) to the file before closing it in some cases. Certain MS-DOS text editors do not like files with very large line lengths. (fileopen h1 h2) ~~~~~~~~~~~~~~~~ Opens file whose name is h1 for mode h2 access. h1 should be a file name optionally including a path. h2 should be one of 'r, 'w, or'a meaning read, write or append respectively. The function if successful returns a port atom which will print as %file@nn%. If the function is not successful nil is returned. Fileopen does not look in any but the current directory for a relative path or file. Note devices like "con:" are allowed in place of file names. (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: 19 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]) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Will cause the S-expression s1 to be printed without delimiters 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 they will not be added when printing the list with patom. patom returns s1 while princ returns t. Strings print w/o quotes. (print s1 [p1]) ~~~~~~~~~~~~~~~ Will cause the S-expression s1 to be printed with delimiters if necessary on the output port p1, or on the standard output if no p1 parameter is given. All atoms that would require | | delimiting to be input, will be printed with | | delimiters around them. The expression s1 is returned. (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