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