[comp.binaries.amiga] scheme

ain@j.cc.purdue.edu (Patrick White) (08/09/88)

Submitted by:	qix@vx.lcs.mit.edu (Ed Puckett)
Summary:	statically scoped and properly tail-recursive dialect of lisp.
Poster Boy:	Patrick White	(ain@j.cc.purdue.edu)
Archive Name:	binaries/amiga/volume8/scheme.d.sh1.Z
tested
 
NOTES:
   I re-did the uuencode of the scheme program itself since it was done with
an older version of uuencode that used spaces instead of back-quotes.
   I don't know lisp nor scheme, so I didn't exhaustively test it.. I ran
it, it game me lots of error messages, and I managed to exit it -- I'd say
it works :-)
.
 
 
-- Pat White   (co-moderator comp.sources/binaries.amiga)
ARPA/UUCP: j.cc.purdue.edu!ain  BITNET: PATWHITE@PURCCVM  PHONE: (317) 743-8421
U.S.  Mail:  320 Brown St. apt. 406,    West Lafayette, IN 47906
[archives at: j.cc.purdue.edu.ARPA]
 
========================================
 
#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	README
#	SYNOPSIS
#	overview.doc
#	diff.doc
#	plans.doc
#	HOP.scm
#	dump.scm
# This archive created: Mon Aug  8 09:29:37 1988
# By:	Patrick White (PUCC Land, USA)
cat << \SHAR_EOF > README
README file for Scheme
Version 1.2  19-March-1988

This is the first distribution of a Scheme system for the Amiga (tm)
personal computer.  The following files are included:

    Scheme
    Scheme.info

    README
    SYNOPSIS
    diff.doc
    ext.doc
    overview.doc
    plans.doc

    HOP.scm
    dump.scm
    load-patches.scm
    repl.scm
    special-forms.scm
    streams.scm

Quoting from the "Revised^3 Report on the Algorithmic Language Scheme":

    Scheme is a statically scoped and properly tail-recursive
    dialect of the Lisp programming language invented by
    Guy Lewis Steele Jr. and Gerald Jay Sussman.

Individuals may freely use and distribute this program.  Those who find it
useful are encouraged (though by no means expected) to send a donation to
the author.  However, such donations will help speed future enhancements to
the system.

Amiga is a trademark of Commodore-Amiga Incorporated.

Send correspondence to:

    Ed Puckett
    MIT Branch PO
    PO Box 61
    Cambridge, MA  02139
    (617) 536-8876
    qix@oz.ai.mit.edu

SHAR_EOF
cat << \SHAR_EOF > SYNOPSIS
=============================
SYNOPSIS OF SYSTEM PROCEDURES
=============================

Scheme
Version 1.2
19-March-1988



--------------------------- primitives -----------------------------------------

command-name			+
command-line			+
startup-file			+

change-size			+ (change-size new-size)
extend-size			+ (extend-size amount-to-expand)
shrink-size			+ (shrink-size amount-to-shrink)
minimize-size			+ (minimize-size)
current-size			+ (current-size)

collect-garbage 		+ (collect-garbage)

call/cc 			+ (call/cc one-arg-proc)
dynamic-wind			+ (dynamic-wind entry-thunk body-thunk exit-thunk)
set-interrupt-flags!		+ (set-interrupt-flags! flags-mask affect-mask)
with-interrupt-mask		+ (with-interrupt-mask mask affect-mask thunk)
current-interrupt-flags 	+ (current-interrupt-flags)
current-interrupt-mask		+ (current-interrupt-mask)
error-context			+ (error-context error-handler-continuation body-thunk)
current-error-continuation	+ (current-error-continuation)
error				+ (error reason cause)
abort-system			+ (abort-system) or (abort-system exitval)

file-exists?			+ (file-exists? filename)
sleep				+ (sleep nticks)
call-system			+ (call-system command)

obj->rep			+ (obj->rep obj)
!rep->obj			+ (!rep->obj rep) WHERE rep = (tags . ref)
!storage-ref			+ (!storage-ref obj offset)
!storage-set!			+ (!storage-set! obj offset obj-to-store)
!storage-rep-ref		+ (!storage-rep-ref obj offset)
!storage-rep-set!		+ (!storage-rep-set! obj offset rep-to-store)
!byte-ref			+ (!byte-ref obj offset)
!byte-set!			+ (!byte-set! obj offset byte)

add-syntax!			+ (add-syntax! symbol two-arg-proc)
delete-syntax!			+ (delete-syntax! symbol)
get-syntax-definitions		+ (get-syntax-definitions)

nil
t

eval				+ (eval exp env)
apply				  (apply proc arglist)

eqv?				  (eqv? obj1 obj2)
eq?				  (eq? obj1 obj2)
equal?				  (equal? obj1 obj2)

memv				  (memv obj list)
memq				  (memq obj list)
member				  (member obj list)

assv				  (assv obj alist)
assq				  (assq obj alist)
assoc				  (assoc obj alist)

not				  (not obj)

pair?				  (pair? obj)
stream? 			+ (stream? obj)
environment?			+ (environment? obj)
procedure?			  (procedure? obj)
vector? 			  (vector? obj)
number? 			  (number? obj)
complex?			  (complex? obj)
real?				  (real? obj)
rational?			  (rational? obj)
integer?			  (integer? obj)
string? 			  (string? obj)
port?				  (port? obj)
symbol? 			  (symbol? obj)
eof-object?			  (eof-object? obj)

primitive?			+ (primitive? obj)
compound-procedure?		+ (compound-procedure? obj)
compiled?			+ (compiled? obj)
thunk?				+ (thunk? obj)
promise?			+ (promise? obj)
continuation?			+ (continuation? obj)


null?				  (null? obj)
empty-stream?			+ (empty-stream? obj)
char?				  (char? obj)
boolean?			  (boolean? obj)

length				  (length list)
append				  (append list1 ...)
reverse 			  (reverse list)
last-pair			  (last-pair list)
list-ref			  (list-ref list pos)
list-tail			  (list-tail list pos)
list				  (list) or (list obj1 ...)
cons				  (cons obj1 obj2)
car				  (car pair)
cdr				  (cdr pair)
caar				  (caar pair)
cadr				  (cadr pair)
cdar				  (cdar pair)
cddr				  (cddr pair)
caaar				  (caaar pair)
caadr				  (caadr pair)
cadar				  (cadar pair)
caddr				  (caddr pair)
cdaar				  (cdaar pair)
cdadr				  (cdadr pair)
cddar				  (cddar pair)
cdddr				  (cdddr pair)
caaaar				  (caaaar pair)
caaadr				  (caaadr pair)
caadar				  (caadar pair)
caaddr				  (caaddr pair)
cadaar				  (cadaar pair)
cadadr				  (cadadr pair)
caddar				  (caddar pair)
cadddr				  (cadddr pair)
cdaaar				  (cdaaar pair)
cdaadr				  (cdaadr pair)
cdadar				  (cdadar pair)
cdaddr				  (cdaddr pair)
cddaar				  (cddaar pair)
cddadr				  (cddadr pair)
cdddar				  (cdddar pair)
cddddr				  (cddddr pair)
set-car!			  (set-car! pair obj)
set-cdr!			  (set-cdr! pair obj)

force				+ (force promise)

the-empty-stream		+
head				+ (head stream)
tail				+ (tail stream)

call-with-input-file		  (call-with-input-file string one-arg-proc)
call-with-output-file		  (call-with-output-file string one-arg-proc)
input-port?			  (input-port? obj)
output-port?			  (output-port? obj)
current-input-port		  (current-input-port)
current-output-port		  (current-output-port)
with-input-from-file		  (with-input-from-file string thunk)
with-output-to-file		  (with-output-to-file string thunk)
open-input-file 		  (open-input-file string)
open-output-file		  (open-output-file string)
close-input-port		  (close-input-port port)
close-output-port		  (close-output-port port)
read				  (read) or (read port)
read-char			  (read-char) or (read-char port)
char-ready?			  (char-ready?) or (char-ready? port)
write				  (write obj) or (write obj port)
display 			  (display obj) or (display obj port)
newline 			  (newline) or (newline port)
write-char			  (write-char char) or (write-char char port)
load				  (load string)
read-raw-bytes			+ (read-raw-bytes size) or (read-raw-bytes size port)
write-raw-bytes 		+ (write-raw-bytes string) or (write-raw-bytes string port)

map				  (map proc list1 ...)
for-each			  (for-each proc list1 ...)

char->string			+ (char->string char)
string->char			+ (string->char string)
char=?				  (char=? char1 char2)
char-ci=?			  (char-ci=? char1 char2)
char<?				  (char<? char1 char2)
char-ci<?			  (char-ci<? char1 char2)
char>=? 			  (char>=? char1 char2)
char-ci>=?			  (char-ci>=? char1 char2)
char>?				  (char>? char1 char2)
char-ci>?			  (char-ci>? char1 char2)
char<=? 			  (char<=? char1 char2)
char-ci<=?			  (char-ci<=? char1 char2)

char-alphabetic?		  (char-alphabetic? char)
char-numeric?			  (char-numeric? char)
char-whitespace?		  (char-whitespace? char)
char-upper-case?		  (char-upper-case? char)
char-lower-case?		  (char-lower-case? char)
char-upcase			  (char-upcase char)
char-downcase			  (char-downcase char)
char->integer			  (char->integer char)
integer->char			  (integer->char number)

string=?			  (string=? string1 string2)
string-ci=?			  (string-ci=? string1 string2)
string<?			  (string<? string1 string2)
string-ci<?			  (string-ci<? string1 string2)
string>=?			  (string>=? string1 string2)
string-ci>=?			  (string-ci>=? string1 string2)
string>?			  (string>? string1 string2)
string-ci>?			  (string-ci>? string1 string2)
string<=?			  (string<=? string1 string2)
string-ci<=?			  (string-ci<=? string1 string2)

string-copy			  (string-copy string)
string-append			  (string-append) or (string-append string1 ...)
string->list			  (string->list string)
list->string			  (list->string char-list)
make-string			  (make-string length) or (make-string length fill-char)
string-length			  (string-length string)
string-ref			  (string-ref string pos)
string-set!			  (string-set! string pos char)
string-fill!			  (string-fill! string char)
substring			  (substring string start end+1)

vector->list			  (vector->list vector)
list->vector			  (list->vector list)
vector				  (vector) or (vector obj1 ...)
make-vector			  (make-vector length) or (make-vector length fill-obj)
vector-length			  (vector-length vector)
vector-ref			  (vector-ref vector pos)
vector-set!			  (vector-set! vector pos obj)
vector-fill!			  (vector obj)

string->symbol			  (string->symbol string)
symbol->string			  (symbol->string symbol)

exact->inexact			  (exact->inexact number)
inexact->exact			  (inexact->exact number)
exact?				  (exact? obj)
inexact?			  (inexact? obj)
odd?				  (odd? obj)
even?				  (even? obj)
zero?				  (zero? obj)
positive?			  (positive? obj)
negative?			  (negative? obj)

=				  (= number1 number2)
<				  (< number1 number2)
>=				  (>= number1 number2)
>				  (> number1 number2)
<=				  (<= number1 number2)

abs				  (abs number)
+				  (+ number1 number2)
-				  (- number1 number2)
*				  (* number1 number2)
/				  (/ number1 number2)
quotient			  (quotient number1 number2)
remainder			  (remainder number1 number2)
call-with-quotient&remainder	+ (call-with-quotient&remainder number1 number2 proc)
min				  (min n1 n2)
max				  (max n1 n2)

number->string			  (number->string number format)
string->number			  (string->number string exactness radix)



--------------------------- special forms --------------------------------------

cond				  (cond clause1 ...) or (cond ... (else exp1 ...))

if				  (if predicate consequent alternative)
set!				  (set! symbol exp)
define				  (define symbol exp) or (define (exp1 ...) exp)
begin				  (begin exp1 ...)

quote				  (quote exp)
quasiquote			  (quasiquote exp)
unquote 			  (unquote exp)
unquote-splicing		  (unquote-splicing exp)

let				  (let (binding1 ...) exp1 ...) or (let () exp1 ...)
letrec				  (letrec (binding1 ...) exp1 ...) or (letrec () exp1 ...)
fluid-let			+ (fluid-let (binding1 ...) exp1 ...) or (fluid-let () exp1 ...)
fluid				+ (fluid symbol)
lambda				  (lambda (symbol1 ...) exp1 ...) or (lambda () exp1 ...)
the-environment 		+ (the-environment)
make-environment		+ (make-environment exp1 ...)
access				+ (access symbol1 ... env)
delay				+ (delay exp1 ...)
cons-stream			+ (cons-stream obj1 obj2)
and				+ (and exp1 ...)
or				  (or exp1 ...)

SHAR_EOF
cat << \SHAR_EOF > overview.doc
======================
OVERVIEW OF THE SYSTEM
======================

Scheme
Version 1.2
19-March-1988


TOPICS
------
STARTING THE SYSTEM
INITIALIZATION FILE
GARBAGE COLLECTION AND MEMORY MANAGEMENT
INDEX TO EXAMPLE CODE
MORE INFORMATION ABOUT SCHEME
BUG REPORTS AND OTHER COMMUNICATION



================================================================================

STARTING THE SYSTEM
-------------------

The system may be run either from the Workbench (by double-clicking
its icon) or from the CLI (by issuing its name from the command line).

The system requires approximately 285k bytes to start, of which there must
be two contiguous blocks of 64k each.  Heap memory can be dynamically
resized after startup; the total memory usage can be reduced to a minimum
of approximately 185k.	The system's stacks (there are two in this
implementation) are allocated internally, so the task may be started with
AmigaDOS' default 4k stack.

Below, it is assumed that the Scheme system program is named "Scheme" and
resides in some directory accessible in your CLI Path.

Here is a quick example of running the system, starting from the CLI:

    1> Scheme is fun!
    Scheme
    Version 1.2  19-March-1988
    Implemented by Ed Puckett
    MIT Branch PO
    PO Box 61
    Cambridge, MA  02139


    :=> command-name
    Scheme
    :=> command-line
    is fun!

    :=> (write command-line)
    "is fun!\
    "#u

The ":=> " prompt is issued by the system read-eval-print-loop.  This loop
is interface with the Scheme system, and it does just what its name
implies: it reads an expression, evaluates it, prints the result and loops
back to read another expression.  (Some people colorfully refer to the
read-eval-print-loop as a "Listener".)

Two variables (among others) are introduced in the above example:
command-name and command-line.	These can be used by your programs to
determine if the system was started from the Workbench or from the CLI,
and to determine what arguments may have been issued on the CLI command line.

If run from the Workbench, the symbols command-name and command-line
will both be bound to empty strings.  From the CLI, they are bound to
respectively, the name of the executable file (usually "Scheme", unless
you rename it) and the remainder of the command line.

Notice that applying write to command-line's value showed us that it
includes a newline character.  The string representation of a newline is
the string escape character \ followed by a newline.  This is why the rest
of the string is on the next line.  The #u which appears after the closing
double quote is the return value from write.

We can also see the individual characters that comprise this string:

    :=> (write (string->list command-line))
    (#\i #\s #\space #\f #\u #\n #\! #\lf)#u

To leave the system, simply cause an end-of-file (by pressing CTRL-\) or
else call the procedure abort-system:

    :=> (abort-system)
    1>

See the section MORE INFORMATION ABOUT SCHEME for references to other
literature on Scheme.  Extensions particular to this implementation are
described in the file "ext.doc".



================================================================================

INITIALIZATION FILE
-------------------

Just prior to entering the read-eval-print-loop for the first time, the
system attempts to open the file "S:Scheme-init.scm".  If this file exists,
it is loaded before the first read-eval-print-loop prompt is issued.  You
may put any valid Scheme expressions into this file.

Some uses for the initialization file:

    * Load your own frequently-used procedures.

    * Parse the command line, and take action on it.  For example,
      you could evaluate each file specified on the command line and
      then exit.  If no files were given, then you could do the normal
      read-eval-print-loop.  This would be something like Unix's /bin/sh.

    * Start your own custom read-eval-print-loop.  See repl.scm for a
      crude example.



================================================================================

GARBAGE COLLECTION AND MEMORY MANAGEMENT
----------------------------------------

The garbage collector combines both mark/sweep and copying methods.
Storage for strings and ports is allocated outside the heap memory (via
AllocMem) and is collected in a mark/sweep fashion.  All other memory (such
as that for pairs and vectors) is allocated from the heap, and is collected
by the copying method.

The copying method is reasonably fast (this system collects about 13500
pairs/second), and takes time proportional to the current amount of storage
used (i.e., storage which is not currently garbage).

It has two major drawbacks.  One is that it destroys objects' locality of
reference.  The other is that only half of the allocated memory is ever
available for use.  This is because it maintains two equal-sized blocks
of memory, a working block and a free block.

The locality issue does not really affect the Amiga since no virtual
addressing is employed.  As for the amount of memory required, what the
heck, we can always add more memory (!).

Seriously though, the copying method's speed was its major attraction.
Typically, I see 1/4 second delays (for instance, in the middle of
printing) if I see them at all.  Only when a lot of internal structures are
allocated do the delays become very noticeable.

Strings contain no references to other objects on the heap, and it seemed a
shame to clog up the heap with them; each collection would move each string.

Ports, too, contain no heap references, but there was another reason for
collecting them with mark/sweep.  Since the mark/sweep scans unused as well
as used objects, unused ports are merely closed by the garbage collector
during the sweep phase.  (It is by no means impossible to detect unused
open ports in the heap, but the mark/sweep method seemed easier.)

Incidentally, the default error handler calls the garbage collector.  This
aids in the following circumstance.  Suppose you are loading a file (with
the primitive procedure "load"), and an error is detected in the file.  If
the port associated with the file from which you were reading were left
open, you would not be able to modify the file because it would be locked.
So the error handler cleans out the registers, removing load's reference to
the port, and calls the garbage collector which in turn closes the port,
thereby unlocking the file.

Changing Memory Size
--------------------
Several procedures are provided by which you can dynamically change the
size of the heap: extend-size, shrink-size, change-size and minimize-size.
(See "ext.doc" for detailed descriptions of these.)

These procedures start by garbage collecting into the free block
(extend-size skips this step).	Because the copying method compacts
storage, it is then possible to determine the minimum amount of memory that
the system must have.  Two new blocks of the requested size are now
allocated, and another collection is performed into one of the new blocks.
Now the old pair of blocks can be discarded, and the new ones installed in
their place.

These routines will fail if there is not enough memory available for both
the old and new pairs of blocks simultaneously.  Though a very conservative
measure, it seems too risky to trust that a freed block can be reallocated
if the new blocks can not be.

Finally, the heap management system will attempt to increase the size of
the heap if heap storage is requested and none is available.  It currently
adds increments of #x800 bytes (i.e. 256 pairs).  If the reallocation is
unsuccessful, the system will abort.  (This needs to be changed so that
you can at least decide!!!)

This naive strategy should instead attempt reallocation based on the amount
ultimately needed.  Each heap reallocation is time-consuming, and may
fragment the Amiga system memory so that further reallocations are
impossible.  However, out-of-memory is currently detected at a quite low
level, and it is tough to determine the ultimate need.

You may enjoy watching the memory on a graphically-displayed memory monitor
as the system is trying to allocate a lot of memory.  You can cause this
by calling minimize-size and then appending a long list to another list.
Though it's probably not something you want to happen when you're doing
real work, it's fun to watch this memory "shell game".



================================================================================

INDEX TO EXAMPLE CODE
---------------------

HOP.scm
-------
This file contains useful higher-order procedures which I load into my
system during initialization.  For example, repeat is useful for testing
something repeatedly:

    :=> (repeat
	  (lambda ()
	    (do-test)		; some time-consuming test
	    (display "."))      ; progress indicator
	  10)
    ..........#u

The procedure "filter" allows you to select elements from a list for which
a predicate returns true:

    :=> (filter even? '(0 1 2 3 4 5 6 7 8 9))
    (0 2 4 6 8)

The procedure "repeated" forms the n-fold composition of a function.
Here it is used to define multiplication in terms of addition:

    :=> (define (mult m n)
	  ((repeated (lambda (x) (+ n x)) m) 0))
    mult

    :=> (mult 200 340)
    68000



dump.scm
--------

This file has many procedures and definitions pertinent to the system
internals.  Careless use of these functions will probably cause a crash!
That's why they're so fun!

The representation of many objects is noted in this file, and procedures
for dumping things like environments are defined.



load-patches.scm
----------------

This file shows examples of how you may modify existing procedures.
Its main functions are "cd" and "load".

Use "cd" to change the current directory.  Then, "load" will prepend this
name when relative pathnames are given to it:

    :=> (cd "Scheme:tests")
    #u

    :=> (cd)
    Scheme:tests/	; with no arguments, returns current directory

    :=> (load "xxx")
    *** ERROR: could-not-open-port
    Scheme:tests/xxx

    :=> (load "test-cwcc.scm")
    #u

    :=> (load "test-tests.scm" "test-dwind.scm")
    #u			; more than one file can be specified; loaded in order

    :=> (load)
    #u			; reloads last specified list of files



repl.scm
--------
This file defines a custom read-eval-print-loop.  To start it, apply the
function repl to a read function, an eval function and a print function.
Typical choices for these are read, eval and display:

    :=> (repl read eval display)

    1=> (repl read eval display)

    2=> <eof>

    1=>

Each time it is invoked, it increases the level number displayed in the
prompt.  Each time you exit a level, the number is decremented.
These repl's execute in their own environments.  When you leave
one, all of its definitions are lost.

Commands can be easily passed to AmigaDOS.  Simply enter the command
prefixed with the character ~.	It is not necessary to separate the ~ and
the rest of the command, and you needn't enter parentheses.  Everything up
to the end of the line will be sent to AmigaDOS.

    1=> ~dir S:
      .edrc			       Scheme-init.scm
      Startup-Sequence

    1=>

These read-eval-print-loops install their own error handler:

    1=> )
    Yeeow!  #u
    Illegal right parenthesis
    Interrupt flags/mask: #x0/#x2

    1=>

If you install your own error handler, you don't fall out of your subsystem
just because you get an error.

All in all, the code in repl.scm is a hack-job.  It represents a lot of
experimentation (actually playing) with the system.  For example, it would
be much better to dynamically-wind the level number rather than using a
global variable.  But it provides a decent example of fun things that you
can do.



special-forms.scm
-----------------
This file gives an example of how to add new special forms to the system
via the primitive procedure add-syntax!.  The special forms "let*" and
"case" are defined.  Other special-forms manipulation procedures are
delete-syntax! and get-syntax-definitions.



streams.scm
-----------
Streams are essentially lists whose "cdr" is delayed, i.e., it is not
evaluated until its value is required, and then that value is stored for
future accesses.

This file shows example streams functions.  After loading it, try the
following:

    (display-stream ones)
    (display-stream natural-numbers)
    (display-stream fibonacci-numbers)
    (display-stream prime-numbers)

These all generate infinite streams.  You can stop these by pressing CTRL-C.
If you display the prime numbers twice, you will observe memoization in
action -- the previously computed part of the stream come back quite
quickly.

These functions use LOTS of memory if you display long portions of the
streams.  The system will abort if it runs out of memory.



================================================================================

MORE INFORMATION ABOUT SCHEME
-----------------------------

Two wonderful sources of information on Scheme are:

    The book "Structure and Interpretation of Computer Programs"
    by Harold Abelson and Gerald Jay Sussman with Julie Sussman.
    Published by The MIT Press, Cambridge, Massachusetts.

    The "Revised^3 Report on the Algorithmic Language Scheme"
    Editors: Jonathan Rees and William Clinger.
    MIT Artificial Intelligence Laboratory Memo 848a.

The bibliographies of these will direct you to a wealth of information.



================================================================================

BUG REPORTS AND OTHER COMMUNICATION
-----------------------------------
If you find bugs, please let me know.  If possible, send code which will
reproduce the bug, or at least a description of how the bug might be
reproduced.  If the system crashes, it would help to know the GURU
MEDITATION NUMBER and information about your system configuration.
Crashes are most likely the result of the garbage collector getting hold of
some untagged datum.

Suggestions and/or comments are welcome.
Have fun!

Ed Puckett
MIT Branch PO
PO Box 61
Cambridge, MA  02139
(617) 536-8876
qix@oz.ai.mit.edu

SHAR_EOF
cat << \SHAR_EOF > diff.doc
================================
DIFFERENCES, ANNOYANCES AND BUGS
================================

Scheme
Version 1.2
19-March-1988

The following is a list of implementation details and trouble spots.
Not everything mentioned is a bug, but there are bugs here!



================================================================================



call-with-current-continuation
------------------------------
The procedure call-with-current-continuation is named call/cc



Dynamically-Wound I/O
---------------------
The I/O procedures with-input-from-file and with-output-to-file, use
dynamic-wind to ensure that their files are always open within their
context, and are closed when control passes out of their context.

The procedures load, call-with-input-file and call-with-output-file are not
dynamically-wound.  Thus, the file will be closed upon normal exit from
these procedures' contexts, but not necessarily otherwise. (The garbage
collector closes files after all references to them go away.) Once the file
is closed, nonlocal entries (via continuations passed out) will encounter
an I/O error; the file is not automatically reopened.

EXAMPLE

    :=> (define xxx
	  (call/cc
	    (lambda (return)
	      (with-output-to-file "CON:10/10/300/100/Wound-IO"
		(lambda ()
		  (write (call/cc (lambda (later) (return later))))
		  (sleep 50) )))))
    xxx
    ; The window opens, then closes as soon as the
    ; continuation named "later" is thrown out.

    :=> (define the-cont xxx)
    the-cont

    :=> (the-cont "Hello again!")
    xxx

    ; The window re-opens and the string "Hello again! is printed in it.
    ; Approximately 1 second later, the window closes and xxx is
    ; (mysteriously) re-defined.  The window will re-open and close
    ; in the same way each time the-cont is applied.



delay
-----
The primitive procedure delay accepts more than one expression.
(delay exp1 ...) is equivalent to (delay (begin exp1 ...))



Case-Sensitivity
----------------
Character case is significant in symbols.  The standard specifies that
symbol names should be converted to the system-preferred case when read.
In this implementation, your symbol names are left in the case in which
they were entered.

All keywords (e.g., define) are recognized as the lower-case version of
their symbols.	This also includes format symbols used by number->string.

Character specifiers and number prefixes, on the other hand, are
case-insensitive.  This is according to the standard.

EXAMPLES

    (DEFINE a 1)	  --> ERROR	; (assuming DEFINE is not a procedure)
    (eq? 'Abc 'Abc)       --> #t        ; according to standard
    (eq? 'Abc 'aBc)       --> #f        ; not according to standard
    (eq? #\Space #\space) --> #t	; according to standard
    (= #X12 #x12)	  --> #t	; according to standard

I prefer case-sensitivity.  However, this should be switchable; it
currently is not.  The next release will almost certainly have this fixed.



Syntax
------
The non-essential special forms "let*", "case" and "do" are not directly
implemented.  However, add-syntax! provides a means of adding new special
forms, and the file "special-forms.scm" provides definitions for "let*"
and "case".

Special forms currently are not careful about syntax errors which do not
prevent necessary parts of the form to be accessed.  So while (let ((a)) a)
will cause a syntax error, (if a b c d e f g h) will not.



Internal Definitions
--------------------
Internal definitions do not behave exactly as an equivalent letrec. Instead
of first establishing all symbols which will be introduced into a new block
and then evaluating the block's expressions, internal defines are merely
evaluated as encountered.

This will be fixed, but for now it should not cause problems with "normal"
code.  Unlike letrec, a variable will be unbound (rather than merely
uninitialized) until its definition is evaluated.



Immutable Data
--------------
There is no concept of immutable data in the system.  Instead, private data
passed out of the system internals is copied.  For example, even though the
system allows (string-set! (symbol->string 'abc) 0 #\x), this will not
affect the symbol abc.



Out-Of-Memory
-------------
If the system runs out of heap space and cannot allocate more, it will
panic and abort (and without so much as a message!).  This needs to be
fixed, but it will take some doing to clean up after such an error.  Still,
it should at least give you the satisfaction of clicking a requester, even
if you are still going to lose the stuff currently in the system!



Tags and the 68020
------------------
The tags for Scheme object references are stored in bits 31:24.  On the
68000 and 68010, these could be left even during pointer dereferencing
since these bits are not used by the processor in address calculations.
However, the 68020 does use these bits.

In the system, no pointer is ever dereferenced without first stripping its
tags.  This therefore is compatible with the 68020.  However, I assume that
any pointer returned by AllocMem will always be "boxable", i.e., bits 31:24
will all be 0.	I don't know if this conflicts with any plans for future
versions of the Amiga or not.

As the system stands, it never verifies that AllocMem returned a boxable
pointer (it never needs to on the A1000).  However, it should do this and
either retry or abort if it gets a "bad" pointer.



number->string
--------------
Only the formats "int" and "heur" are recognized, and these are treated
equivalently.  In addition, the modifiers "exactness" and "radix" may be
supplied.

Number->string improperly allows more than one modifier of the same kind to
appear.  Settings caused by the modifiers shadow those caused by earlier
modifiers in the same format.  This can cause a misleading conversion:

    :=> (number->string #x12 '(int (radix o e) (radix x s)))
    "#o12"

number->string has a strange representation for the system's most-negative
number.  Try this:

    :=> (- -#x7FFFFFFF 1)



Numbers
-------
Only signed 32-bit numbers are supported.  Numbers from -#x7FFFFFFF to
#x7FFFFFFF may be input.  The most-negative number -#x100000000 cannot be
input, but is nevertheless internally represented.

Exactness is supported.

Only the binary versions of procedures such as + and < are provided.
You may extend these procedures to allow arbitrary numbers of arguments
by redefining them.  However, this will all eventually migrate down into
the primitives.

I hope to get support for bignums and rationals soon....

The following (nonessential) numeric functions have been omitted:

	modulo		; but it can be defined in terms of remainder
	numerator
	denominator
	gcd
	lcm
	floor
	ceiling
	truncate
	round
	rationalize
	exp
	log
	sin
	cos
	tan
	asin
	acos
	atan
	sqrt
	expt
	make-rectangular
	make-polar
	real-part
	imag-part
	magnitude
	angle



Interrupts and Errors in read
-----------------------------
While a call to read waits, interrupts will not be acted upon.	Therefore,
while at the ":=> " prompt, pressing CTRL-C causes no immediately
observable behavior.  Entering some object (representation) at this point
will cause the interrupt to be recognized by the interpreter.

The input stream is not flushed when an error occurs.  Therefore, if you
type ))))))))) at the prompt, a long sequence of errors ensues.

Read errors (like illegal right parentheses) cause a string to be thrown to
the error handler as the reason.  This should be changed to a symbol in
order to make it easier for debuggers to recognize error types.  Other
system-detected errors throw symbols.



eqv?
----
The predicate eqv? (and thus its associated procedures memv and assv) is no
smarter than eq?.



Other Omitted Procedures
------------------------
transcript-on
transcript-off

SHAR_EOF
cat << \SHAR_EOF > plans.doc
===========================
FUTURE PLANS FOR THE SYSTEM
===========================

Scheme
Version 1.2
19-March-1988


TOPICS
------
NUMBERS
DUMP-OBJ
COMPILER
INTERFACES



================================================================================

NUMBERS
-------
I hope to extend the number system soon.  Bignums and rational numbers are
high priority on my list.  However, floating point numbers will eventually
be needed; they are probably the best way to implement transcendental
functions.

The hardest part (or at least the part I'm the least enthusiastic about) is
the number <-> string conversion functions.  These will be tough.



================================================================================

DUMP-OBJ
--------
Something I'm quite excited about implementing is a dump-obj function. This
would dump some representation of a Scheme object and all of its associated
objects to a file.  Later, this file could be loaded back into the system.

To save the entire system, you would just do (dump-obj (the-environment))
from the top-level.  This would allow you to save your own customized
versions of Scheme, without loading in a lot of initialization each time.

The process would be a lot like a garbage collection, except that addresses
would have to be marked in a relocation table, and things like symbols'
names would have to be noted as well.  There are also some sticky issues,
like what does one do with open ports?



================================================================================

COMPILER
--------
There are provisions for compiled functions in the interpreter.  In fact, I
have code (sans code generators) for a couple of compilers now. Some issues
are:

    * How should relocation be handled so that compiled code will interact
      well with things like dump-obj and the garbage collector?

    * Should the compiler produce an intermediate file such as would be
      produced by dump-obj?



================================================================================

INTERFACES
----------
It would be nice to provide external library interfaces through functions
packaged in their own environments.  For example, you could load the
AmigaDOS environment and then access functions out of that environment.

There are some problems, though.  I don't think it would be a great idea
to start adding a bunch of new object types into the system; it makes
garbage collection and dump-obj harder than ever.

Another possibility is to provide interface through AmigaDOS handlers.	Then
system interfaces could be gained through the existing filesystem interface.
(How about a GFX: handler?)  But, there are probably some things that
just wouldn't work well as handlers.

SHAR_EOF
cat << \SHAR_EOF > HOP.scm
;;; HOP.scm

;;;
;;; Useful Higher-Order Procedures
;;;

(define (filter predicate source-list)
  (cond ((null? source-list) '())
	((predicate (car source-list))
	 (cons (car source-list)
	       (filter predicate (cdr source-list)) ))
	(else
	 (filter predicate (cdr source-list)) )))

(define (repeat thunk n)
  (if (< n 1)
      #u
      (begin
	(thunk)
	(repeat thunk (- n 1)) )))

(define (repeated f n)
  (if (< n 1)
      (lambda (x) x)
      (lambda (x) ((repeated f (- n 1)) (f x))) ))



;;; EOF HOP.scm

SHAR_EOF
cat << \SHAR_EOF > dump.scm
;;; dump.scm

;;; (Use of this file may be hazardous to your garbage collector!!!)

;;;
;;; Object TAGS are in bits 31:28
;;;

(define TAG_PAIR		#x00)	; cdr offset = 0, car offset = 4
(define TAG_STREAM		#x01)	; pair underneath
(define TAG_ENV 		#x02)	; pair underneath
(define TAG_CLOSURE		#x03)	; pair underneath
(define TAG_VECTOR		#x04)	; size, byte_size, ref, ref, ...
(define TAG_NUMBER		#x05)	; constant or pointer to rawdata in 23:0
(define TAG_STRING		#x06)	; size, char, char, ...
(define TAG_PORT		#x07)	; subtagged, value stored in bits 23:0
(define TAG_SMALLCONST		#x08)	; small constants -- see below
(define TAG_SYMBOL		#x09)	; index into symbol list
(define TAG_EOF_OBJECT		#x0A)	; point ptr in 24:0, but don't reuse it
(define TAG_ENIGMA		#x0B)	; for "boxed" values; value in bits 23:0
(define TAG_STORAGE		#x0C)	; "scrap" memory -- unspecified format
; --- no tag $0D
; --- no tag $0E
(define TAG_RAWDATA		#x0F)	; raw data in heap; size in bits 23:0

(define TAG_IMPOSSIBLE		TAG_SMALLCONST) ; marks gc forwarding pointers
(define TAG_UNIT		TAG_SMALLCONST)
(define TAG_EMPTY_LIST		TAG_SMALLCONST)
(define TAG_EMPTY_STREAM	TAG_SMALLCONST)
(define TAG_CHARACTER		TAG_SMALLCONST) ; character in bits 7:0
(define TAG_BOOLEAN		TAG_SMALLCONST)
(define TAG_STACK_MARKER	TAG_SMALLCONST) ; don't store this anywhere!!!


;;;
;;; Subtags are in bits 27:24
;;;

(define SC_SUBTAG_IMPOSSIBLE	#x00)
(define SC_SUBTAG_UNIT		#x01)
(define SC_SUBTAG_EMPTY_LIST	#x02)
(define SC_SUBTAG_EMPTY_STREAM	#x03)
(define SC_SUBTAG_CHARACTER	#x04)
(define SC_SUBTAG_BOOLEAN	#x05)
(define SC_SUBTAG_STACK_MARKER	#x06)

(define CL_SUBTAG_UNSPECIFIED	#x00)
(define CL_SUBTAG_PRIMITIVE	#x01)
(define CL_SUBTAG_PROCEDURE	#x02)
(define CL_SUBTAG_COMPILED	#x03)
(define CL_SUBTAG_THUNK 	#x04)
(define CL_SUBTAG_PROMISE	#x05)
(define CL_SUBTAG_CONTINUATION	#x06)

(define NUM_SUBTAG_CONSTANT	#x00)	; signed value stored in bits 15:0
(define NUM_SUBTAG_FIXNUM	#x01)	; pointer to rawdata holding a longword
(define NUM_SUBTAG_RATNUM	#x02)	; not currently used
(define NUM_SUBTAG_BIGNUM	#x03)	;  "     "       "
(define NUM_SUBTAG_FLONUM	#x04)	;  "     "       "
(define NUM_SUBTAG_NUMBER_PAIR	#x05)	;  "     "       "


(define tag-name
  (vector
    'TAG_PAIR
    'TAG_STREAM
    'TAG_ENV
    'TAG_CLOSURE
    'TAG_VECTOR
    'TAG_NUMBER
    'TAG_STRING
    'TAG_PORT
    'TAG_SMALLCONST
    'TAG_SYMBOL
    'TAG_EOF_OBJECT
    'TAG_ENIGMA
    'TAG_STORAGE
    '*UNKNOWN-TAG*
    '*UNKNOWN-TAG*
    'TAG_RAWDATA))



(define closure? procedure?)

(define (call-with-tag&subtag&ref obj proc)
  (let ((rep (obj->rep obj)))
    (call-with-quotient&remainder (car rep) #x10
      (lambda (tag subtag)
	(proc tag subtag (cdr rep))))))

(define (dump-obj-rep obj)
  (call-with-tag&subtag&ref obj
    (lambda (tag subtag ref)
      (list tag (vector-ref tag-name tag) subtag ref))))


(define (error-if-not-closure obj)
  (if (not (closure? obj))
      (error "object not a closure" obj)))



(define (closure-params closure)
  (error-if-not-closure closure)
  (dump-obj-rep (!storage-ref closure 1)))



(define (compound-procedure-params proc)
  (if (compound-procedure? proc)
      (!storage-ref proc 1)
      (error "object not a compound procedure" proc)))

(define (compound-procedure-body proc)
  (if (compound-procedure? proc)
      (car (!storage-ref proc 0))
      (error "object not a compound procedure" proc)))

(define (compound-procedure-env proc)
  (if (compound-procedure? proc)
      (cadr (!storage-ref proc 0))
      (error "object not a compound procedure" proc)))



;-------------------------------------------------------------------------------
;
; CONTINUATION STRUCTURE
; ----------------------
;
; (#u body-code env-stuff . pspec)
;
;      pspec:1 (boxed)	(parameter specifier: exactly 1 argument)
;  body-code:boxed pointer to 68k code
;  env-stuff:(Scheme_stack proc_stack state_point . ???)
;
;	      env:environment in which continuation was created
;    Scheme_stack:VECTOR containing the Scheme stack during creation
;      proc_stack:STORAGE containing the processor stack during creation
;     state_point:the state point during creation
;
; All the Scheme registers are pushed onto the Scheme_stack before the
; vector is created.
;
;-------------------------------------------------------------------------------
;
; STATE POINT STRUCTURE
; ---------------------
;
; (parent_state_point entry_thunk exit_thunk boxed_interrupt_mask . ???)
;
; Note that state points have no special tags; they are just lists.
; It is not currently intended that they be first-class (or even expressible)
; in the system.
;
;-------------------------------------------------------------------------------

(define (dump-continuation cont)
  (if (continuation? cont)
      (list (dump-obj-rep (car (!storage-ref cont 0)))
	    (cadr (!storage-ref cont 0)))
      (error "object not a continuation" cont)))

(define (dump-continuation-proc-stack cont)
  (let ((cont-dump (dump-continuation cont)))
    (let ((proc-stack (cadr (cadr cont-dump))))
      (let ((proc-stack-rep (!storage-rep-ref proc-stack 0)))
	(let ((byte-size (+ (* #x1000000 (car proc-stack-rep)) (cdr proc-stack-rep))))
	  (let ((n-items (inexact->exact (/ byte-size 4))))
	    (define (dump-items i item-list)
	      (if (< i 1)
		  item-list
		  (dump-items (- i 1) (cons (!storage-rep-ref proc-stack i) item-list))))
	    (dump-items n-items '()) ))))))

(define (dump-continuation-Scheme-stack cont)
  (let ((cont-dump (dump-continuation cont)))
    (let ((Scheme-stack (car (cadr cont-dump))))
      Scheme-stack)))



;-------------------------------------------------------------------------------
;
; ENVIRONMENT STRUCTURE
; ---------------------
;      env
;	 \
;	  OO
;	 /  \
;	/    \		  To find a binding, search each frame in var-list from
; var-list  val-list	  frame0 for the desired symbol.  If found at frame F,
;			  binding B, then find its value in frame F, binding B
;			  of val-list.
; var-list
;   -or-
; val-list
;	\
;	 OO-----OO-- - - --OO-nil
;	 |	|	   |
;	 |	|	   |
;      frame0 frame1	 frameJ-1
;
;  frameI
;     \
;      OO-----OO-- - - --OO-nil
;      |      | 	 |
;      |      | 	 |
;    var0   var1       varK-1
;    -or-   -or-       -or-
;    val0   val1       valK-1
;
;-------------------------------------------------------------------------------

(define (dump-env-var-frame-list env)
  (if (environment? env)
      (!storage-ref env 1)
      (error "object not an environment" env)))

(define (dump-env-val-frame-list env)
  (if (environment? env)
      (!storage-ref env 0)
      (error "object not an environment" env)))

(define (dump-env env)
  (cons (dump-env-var-frame-list env)
	(dump-env-val-frame-list env)))

(define (dump-env-bindings env)
  (map (lambda (var-list val-list)
	 (map (lambda (var val)
		(cons var val))
	      var-list
	      val-list))
       (dump-env-var-frame-list env)
       (dump-env-val-frame-list env)))



;;; EOF dump.scm

SHAR_EOF
#	End of shell archive
exit 0