-
Notifications
You must be signed in to change notification settings - Fork 5
/
pc-lisp.doc
3722 lines (2670 loc) · 164 KB
/
pc-lisp.doc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
A GUIDE TO THE PC-LISP INTERPRETER (V2.16)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
By Peter Ashwood-Smith
~~~~~~~~~~~~~~~~~~~~~~
University of Toronto,
~~~~~~~~~~~~~~~~~~~~~
Ontario, Canada.
~~~~~~~~~~~~~~~
Copyright (C) 1985,1986,1987 - Peter Ashwood-Smith
for my wife, Guylaine
email: petera!utcsri USENET
pashwood BIX
mail: Peter Ashwood-Smith
#811, 120 St. Patrick St.
Toronto, Ontario,
Canada,
M5T-2X7.
phone: (416) 593-7574.
Addresses valid April 87 then try Ottawa Ont.
(BIX address will stay the same)
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 2.16 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 V2.16 Copyright (C) 1986 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 (V2.16) 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 V2.16 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. V2.16'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