diff options
author | Bryan Newbold <bnewbold@robocracy.org> | 2017-02-20 00:05:25 -0800 |
---|---|---|
committer | Bryan Newbold <bnewbold@robocracy.org> | 2017-02-20 00:05:25 -0800 |
commit | 8ffbc2df0fde83082610149d24e594c1cd879f4a (patch) | |
tree | a2be9aad5101c5e450ad141d15c514bc9c2a2963 | |
download | slib-upstream/2a6.tar.gz slib-upstream/2a6.zip |
Import Upstream version 2a6upstream/2a6
-rw-r--r-- | ANNOUNCE | 70 | ||||
-rw-r--r-- | ChangeLog | 1094 | ||||
-rw-r--r-- | FAQ | 216 | ||||
-rw-r--r-- | Makefile | 229 | ||||
-rw-r--r-- | README | 220 | ||||
-rw-r--r-- | Template.scm | 267 | ||||
-rw-r--r-- | alist.scm | 66 | ||||
-rw-r--r-- | alistab.scm | 227 | ||||
-rw-r--r-- | array.scm | 279 | ||||
-rw-r--r-- | arraymap.scm | 76 | ||||
-rw-r--r-- | batch.scm | 417 | ||||
-rw-r--r-- | break.scm | 151 | ||||
-rw-r--r-- | chap.scm | 150 | ||||
-rw-r--r-- | charplot.scm | 142 | ||||
-rw-r--r-- | chez.init | 266 | ||||
-rw-r--r-- | cltime.scm | 74 | ||||
-rw-r--r-- | collect.scm | 236 | ||||
-rw-r--r-- | comlist.scm | 326 | ||||
-rw-r--r-- | comparse.scm | 92 | ||||
-rw-r--r-- | dbrowse.scm | 98 | ||||
-rw-r--r-- | dbutil.scm | 222 | ||||
-rw-r--r-- | debug.scm | 78 | ||||
-rw-r--r-- | defmacex.scm | 96 | ||||
-rw-r--r-- | dwindtst.scm | 80 | ||||
-rw-r--r-- | dynamic.scm | 75 | ||||
-rw-r--r-- | dynwind.scm | 74 | ||||
-rw-r--r-- | elk.init | 281 | ||||
-rw-r--r-- | factor.scm | 149 | ||||
-rw-r--r-- | fluidlet.scm | 45 | ||||
-rw-r--r-- | format.scm | 1678 | ||||
-rw-r--r-- | formatst.scm | 647 | ||||
-rw-r--r-- | gambit.init | 219 | ||||
-rw-r--r-- | genwrite.scm | 264 | ||||
-rw-r--r-- | getopt.scm | 80 | ||||
-rw-r--r-- | hash.scm | 153 | ||||
-rw-r--r-- | hashtab.scm | 79 | ||||
-rw-r--r-- | lineio.scm | 50 | ||||
-rw-r--r-- | logical.scm | 150 | ||||
-rw-r--r-- | macrotst.scm | 54 | ||||
-rw-r--r-- | macscheme.init | 265 | ||||
-rw-r--r-- | macwork.scm | 126 | ||||
-rw-r--r-- | makcrc.scm | 86 | ||||
-rw-r--r-- | mbe.scm | 362 | ||||
-rw-r--r-- | mitcomp.pat | 1466 | ||||
-rw-r--r-- | mitscheme.init | 254 | ||||
-rw-r--r-- | modular.scm | 158 | ||||
-rw-r--r-- | mulapply.scm | 28 | ||||
-rw-r--r-- | mularg.scm | 10 | ||||
-rw-r--r-- | mwdenote.scm | 273 | ||||
-rw-r--r-- | mwexpand.scm | 548 | ||||
-rw-r--r-- | mwsynrul.scm | 343 | ||||
-rw-r--r-- | obj2str.scm | 61 | ||||
-rw-r--r-- | object.scm | 97 | ||||
-rw-r--r-- | paramlst.scm | 215 | ||||
-rw-r--r-- | plottest.scm | 47 | ||||
-rw-r--r-- | pp.scm | 12 | ||||
-rw-r--r-- | ppfile.scm | 70 | ||||
-rw-r--r-- | primes.scm | 181 | ||||
-rw-r--r-- | printf.scm | 278 | ||||
-rw-r--r-- | priorque.scm | 141 | ||||
-rw-r--r-- | process.scm | 68 | ||||
-rw-r--r-- | promise.scm | 29 | ||||
-rw-r--r-- | qp.scm | 149 | ||||
-rw-r--r-- | queue.scm | 72 | ||||
-rw-r--r-- | r4rsyn.scm | 542 | ||||
-rw-r--r-- | randinex.scm | 99 | ||||
-rw-r--r-- | random.scm | 101 | ||||
-rw-r--r-- | ratize.scm | 13 | ||||
-rw-r--r-- | rdms.scm | 598 | ||||
-rw-r--r-- | recobj.scm | 54 | ||||
-rw-r--r-- | record.scm | 211 | ||||
-rw-r--r-- | repl.scm | 92 | ||||
-rw-r--r-- | report.scm | 116 | ||||
-rw-r--r-- | require.scm | 348 | ||||
-rw-r--r-- | root.scm | 149 | ||||
-rw-r--r-- | sc2.scm | 66 | ||||
-rw-r--r-- | sc4opt.scm | 53 | ||||
-rw-r--r-- | sc4sc3.scm | 35 | ||||
-rw-r--r-- | scaexpp.scm | 2956 | ||||
-rw-r--r-- | scaglob.scm | 32 | ||||
-rw-r--r-- | scainit.scm | 103 | ||||
-rw-r--r-- | scamacr.scm | 181 | ||||
-rw-r--r-- | scanf.scm | 351 | ||||
-rw-r--r-- | scaoutp.scm | 93 | ||||
-rw-r--r-- | scheme2c.init | 291 | ||||
-rw-r--r-- | scheme48.init | 239 | ||||
-rw-r--r-- | scmacro.scm | 119 | ||||
-rw-r--r-- | scmactst.scm | 160 | ||||
-rw-r--r-- | sierpinski.scm | 71 | ||||
-rw-r--r-- | slib.info | 153 | ||||
-rw-r--r-- | slib.info-1 | 1306 | ||||
-rw-r--r-- | slib.info-2 | 1193 | ||||
-rw-r--r-- | slib.info-3 | 859 | ||||
-rw-r--r-- | slib.info-4 | 1248 | ||||
-rw-r--r-- | slib.info-5 | 1536 | ||||
-rw-r--r-- | slib.info-6 | 1410 | ||||
-rw-r--r-- | slib.info-7 | 615 | ||||
-rw-r--r-- | slib.info-8 | 570 | ||||
-rw-r--r-- | slib.texi | 9058 | ||||
-rw-r--r-- | sort.scm | 154 | ||||
-rw-r--r-- | soundex.scm | 82 | ||||
-rw-r--r-- | stdio.scm | 7 | ||||
-rw-r--r-- | strcase.scm | 45 | ||||
-rw-r--r-- | strport.scm | 51 | ||||
-rw-r--r-- | strsrch.scm | 95 | ||||
-rw-r--r-- | struct.scm | 165 | ||||
-rw-r--r-- | structst.scm | 37 | ||||
-rw-r--r-- | structure.scm | 80 | ||||
-rw-r--r-- | syncase.sh | 146 | ||||
-rw-r--r-- | synchk.scm | 104 | ||||
-rw-r--r-- | synclo.scm | 748 | ||||
-rw-r--r-- | synrul.scm | 327 | ||||
-rw-r--r-- | t3.init | 425 | ||||
-rw-r--r-- | tek40.scm | 92 | ||||
-rw-r--r-- | tek41.scm | 147 | ||||
-rw-r--r-- | time.scm | 158 | ||||
-rw-r--r-- | trace.scm | 106 | ||||
-rw-r--r-- | tree.scm | 62 | ||||
-rw-r--r-- | trnscrpt.scm | 76 | ||||
-rw-r--r-- | tsort.scm | 46 | ||||
-rw-r--r-- | values.scm | 27 | ||||
-rw-r--r-- | vscm.init | 306 | ||||
-rw-r--r-- | withfile.scm | 82 | ||||
-rw-r--r-- | wttest.scm | 134 | ||||
-rw-r--r-- | wttree.scm | 784 | ||||
-rw-r--r-- | yasyn.scm | 201 |
126 files changed, 44217 insertions, 0 deletions
diff --git a/ANNOUNCE b/ANNOUNCE new file mode 100644 index 0000000..f34c063 --- /dev/null +++ b/ANNOUNCE @@ -0,0 +1,70 @@ +This message announces the availability of Scheme Library release +slib2a6. + +New in SLIB2a6: + + * structure.scm scaoutp.scm scamacr.scm scainit.scm scaglob.scm + scaexpp.scm: Added missing copyright notice and terms. + + * rbtest.scm rbtree.scm: removed for lack of copying permissions. + + * root.scm (newton:find-integer-root integer-sqrt newton:find-root + laguerre:find-root laguerre:find-root): added. + + * scanf.scm (stdio:scan-and-set): removed gratuitous char-downcase + by changing all (next-format-char) ==> (read-char format-port). + +SLIB is a portable scheme library meant to provide compatibiliy and +utility functions for all standard scheme implementations. + +SLIB includes initialization files for Chez, ELK 2.1, GAMBIT, +MacScheme, MITScheme, scheme->C, Scheme48, T3.1, and VSCM. SCM also +supports SLIB. + +Documentation includes a manifest, installation instructions, and +coding standards for the library. Documentation on each library +package is supplied. SLIB Documentation is online at: + + http://ftp-swiss.ai.mit.edu/~jaffer/SLIB.html + +SLIB is a portable Scheme library: + ftp-swiss.ai.mit.edu:pub/scm/slib2a6.tar.gz + prep.ai.mit.edu:pub/gnu/jacal/slib2a6.tar.gz + ftp.maths.tcd.ie:pub/bosullvn/jacal/slib2a6.tar.gz + ftp.cs.indiana.edu:/pub/scheme-repository/imp/slib2a6.tar.gz + +SLIB-PSD is a portable debugger for Scheme (requires emacs editor): + ftp-swiss.ai.mit.edu:pub/scm/slib-psd1-3.tar.gz + prep.ai.mit.edu:pub/gnu/jacal/slib-psd1-3.tar.gz + ftp.maths.tcd.ie:pub/bosullvn/jacal/slib-psd1-3.tar.gz + ftp.cs.indiana.edu:/pub/scheme-repository/utl/slib-psd1-3.tar.gz + +SLIB-SCHELOG is an embedding of Prolog in Scheme: + ftp-swiss.ai.mit.edu:pub/scm/slib-schelog.tar.gz + prep.ai.mit.edu:pub/gnu/jacal/slib-schelog.tar.gz + ftp.maths.tcd.ie:pub/bosullvn/jacal/slib-schelog.tar.gz + ftp.cs.indiana.edu:/pub/scheme-repository/utl/slib-schelog.tar.gz + +Programs for printing and viewing TexInfo documentation (which SLIB +has) come with GNU Emacs or can be obtained via ftp from: +prep.ai.mit.edu:pub/gnu/texinfo-3.1.tar.gz + +Files in these directories are compressed with patent-free gzip (no +relation to zip). The program to uncompress them is available from + prep.ai.mit.edu:pub/gnu/gzip-1.2.4.tar + prep.ai.mit.edu:pub/gnu/gzip-1.2.4.shar + prep.ai.mit.edu:pub/gnu/gzip-1.2.4.msdos.exe + + ftp ftp-swiss.ai.mit.edu (anonymous) + bin + cd pub/scm + get slib2a6.tar.gz +or + ftp prep.ai.mit.edu (anonymous) + cd pub/gnu/jacal + bin + get slib2a6.tar.gz + + `slib2a6.tar.gz' is a compressed tar file of a Scheme Library. + +Remember to use binary mode when transferring the *.tar.gz files. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..977f23e --- /dev/null +++ b/ChangeLog @@ -0,0 +1,1094 @@ +Fri Jul 19 11:24:45 1996 Aubrey Jaffer <jaffer@jacal.bertronics> + + * structure.scm scaoutp.scm scamacr.scm scainit.scm scaglob.scm + scaexpp.scm: Added missing copyright notice and terms. + +Thu Jul 18 17:37:14 1996 Aubrey Jaffer <jaffer@jacal.bertronics> + + * rbtest.scm rbtree.scm: removed for lack of copying permissions. + +Wed Jun 5 00:22:33 1996 Aubrey Jaffer <jaffer@jacal.bertronics> + + * root.scm (newton:find-integer-root integer-sqrt newton:find-root + laguerre:find-root laguerre:find-root): added. + +Wed May 15 09:59:00 1996 Aubrey Jaffer <jaffer@jacal.bertronics> + + * scanf.scm (stdio:scan-and-set): removed gratuitous char-downcase + by changing all (next-format-char) ==> (read-char format-port). + +Tue Apr 9 19:22:40 1996 Aubrey Jaffer <jaffer@jacal.bertronics> + + * slib2a5 released. + + * mwtest.scm: removed from distribution for lack of copyright + info. + + * batch.scm (batch:apply-chop-to-fit): added + (batch:try-system): renamed from batch:system. + (batch:system): now signals error if line length over limit or + system calls fail. + +Sun Aug 20 19:20:35 1995 Gary Leavens <leavens@cs.iastate.edu> + + * struct.scm (check-define-record-syntax check-variant-case-syntax): + + For using the file "struct.scm" with the EOPL book, one has to + make 2 corrections. To correct it, there are two places where "-" + has to be replaced by "->" as in the code below... + +Sat Apr 6 14:31:19 1996 Aubrey Jaffer <jaffer@jacal.bertronics> + + * batch.scm (must-be-first must-be-last): added. + + * paramlst.scm (check-parameters): made error message more + informative. + +Mon Mar 18 08:46:36 1996 Aubrey Jaffer <jaffer@jacal.bertronics> + + * modular.scm (modular:*): non-bignum symmetric modulus case was + dividing by 0. Algorithm still needs to be fixed. + +Mon Mar 13 00:41:00 1996 Aubrey Jaffer <jaffer@jacal.bertronics> + + * slib2a4 released. + +Sat Mar 9 21:36:19 1996 Mikael Djurfeldt <mdj@nada.kth.se> + + * tsort.scm (topological-sort): Added. + +Fri Mar 8 19:25:52 1996 Aubrey Jaffer <jaffer@jacal.bertronics> + + * printf.scm: Removed use of string-ports. Cleaned up error + handling. + +Tue Mar 5 14:30:09 1996 Aubrey Jaffer <jaffer@jacal.bertronics> + + * printf.scm (%a %A): General scheme output specifier added. + +Mon Feb 19 15:48:06 1996 Aubrey Jaffer <jaffer@jacal.bertronics> + + * scanf.scm (stdio:scan-and-set): Removed flush-whitespace from + all conversion specifications per suggestion from + oleg@mozart.compsci.com (Oleg Kiselyov). + +Sat Feb 3 00:02:06 1996 Oleg Kiselyov (oleg@ponder.csci.unt.edu) + + * strsrch.scm (string-index substring? find-string-from-port?): added. + +Mon Jan 29 23:56:33 1996 Aubrey Jaffer <jaffer@jacal.bertronics> + + * printf.scm (stdio:iprintf): Rewrote for Posix compliance (+ + extensions which are both BSD and GNU). + +Sat Jan 27 09:55:03 1996 Aubrey Jaffer <jaffer@jacal.bertronics> + + * FAQ: printf vs. format explained. + + * printf.scm: renamed from "stdio.scm". (require 'printf) now + brings in "printf.scm". + +Sun Jan 14 21:00:17 1996 Aubrey Jaffer <jaffer@jacal.bertronics> + + * scanf.scm: Rewrote from scratch. + +Mon Oct 9 22:48:58 1995 Aubrey Jaffer (jaffer@jacal) + + * modular.scm (modular:invertable?): added. + +Wed Sep 27 10:01:04 1995 Aubrey Jaffer (jaffer@jacal) + + * debug.scm: augmented, reorganized, and split. + (print): removed. + + * break.scm: created. + + * qp.scm: created. + +Sun Sep 24 22:23:19 1995 Aubrey Jaffer (jaffer@jacal) + + * require.scm (*catalog*): test.scm removed. + +Sun Sep 17 21:32:02 1995 Aubrey Jaffer (jaffer@jacal) + + * modular.scm: rewritten so that if modulus is: + positive? -- work as before (Z_modulus) + zero? -- perform integer operations (Z) + negative? -- perform operations using symmetric + representation (Z_(1-2*modulus)) + (symmetric:modulus modulus->integer modular:normalize): added. + (modular:*): not completed for fixnum-only implementations. + +Sat Sep 9 16:53:22 1995 Aubrey Jaffer (jaffer@jacal) + + * slib.texi (Legacy): added for t, nil, last-pair, and identity, + which are now required of all implementations. + +Mon Aug 28 00:42:29 1995 Aubrey Jaffer (jaffer@jacal) + + * require.scm (require:feature->path require:provided? + require:require): cleaned up. feature->path now returns a path, + whether the module is loaded or not. + +Sun Aug 27 11:05:19 1995 Aubrey Jaffer (jaffer@jacal) + + * genwrite.scm (generic-write): Fixed "obj2str" + OBJECT->LIMITED-STRING non-terminating wr-lst for cases like + (set-car! foo foo). + + * obj2str.scm (object->limited-string): uncommented. + +Sun Aug 20 17:10:40 1995 Stephen Adams <adams@martigny.ai.mit.edu> + + * wttest.scm wttree.scm: Weight Balanced Trees added. + +Sun Aug 20 16:06:20 1995 Dave Love <d.love@dl.ac.uk> + + * tree.scm yasyn.scm collect.scm: Uppercase identifiers changed to + lower case for compatability with case sensitive implementations. + +Sat Aug 19 21:27:55 1995 Aubrey Jaffer (jaffer@jacal) + + * arraymap.scm (array-copy!): added. + + * primes.scm (primes:primes< primes:primes>): primes:primes split + into ascending and descending versions. + +Sun Jul 16 22:44:36 1995 Aubrey Jaffer (jaffer@jacal) + + * makcrc.scm (make-port-crc): added. POSIX.2 checksums. + +Mon Jun 12 16:20:54 1995 Aubrey Jaffer (jaffer@jacal) + + * synclo.scm (internal-syntactic-environment + top-level-syntactic-environment): replaced call to alist-copy. + + * require.scm (*catalog*): 'schelog, 'primes, and 'batch added. + 'prime renamed to 'factor. + + From: mhc@edsdrd.eds.com (Michael H Coffin) + * primes.scm (primes probably-prime?): added. prime.scm renamed + to factor.scm. + +Fri Mar 24 23:35:25 1995 Matthew McDonald <mafm@cs.uwa.edu.au> + + * struct.scm (define-record): added field-setters. + +Sun Jun 11 23:36:55 1995 Aubrey Jaffer (jaffer@jacal) + + * batch.scm: added + + * Makefile (schelogfiles): SLIB schelog distribution created. + +Mon Apr 17 15:57:32 1995 Aubrey Jaffer (jaffer@jacal) + + * comlist.scm (coerce type-of): added. + + * debug.scm (debug:qp): with *qp-width* of 0 just `write's. + + * paramlst.scm (getopt->parameter-list): Now accepts long-named + options. Now COERCEs according to types. + +Sat Apr 15 23:15:26 1995 Aubrey Jaffer (jaffer@jacal) + + * require.scm (require:feature->path): Returns #f instead of + string if feature not in *catalog* or *modules*. + +Sun Mar 19 22:26:52 1995 Aubrey Jaffer (jaffer@jacal) + + * getopt.scm (getopt-- argc argv optstring): added wrapper for + getopt which parses long-named-options. + +Tue Feb 28 21:12:14 1995 Aubrey Jaffer (jaffer@jacal) + + * paramlst.scm (parameter-list-expand expanders parms): added. + +Mon Feb 27 17:23:54 1995 Aubrey Jaffer (jaffer@jacal) + + * report.scm (dbutil:print-report): added. + + * comparse.scm (read-command): added. Reads from a port and + returns a list of strings: the arguments (and options). + +Sat Feb 25 01:05:25 1995 Aubrey Jaffer (jaffer@jacal) + + * repl.scm (repl:repl): Added loop, conditional on CHAR-READY? + being PROVIDED?, which reads through trailing white-space. + +Sun Feb 5 16:34:03 1995 Aubrey Jaffer (jaffer@jacal) + + * paramlst.scm ((make-parameter-list parameter-names)): + ((fill-empty-parameters defaults parameter-list)): + ((check-parameters checks parameter-list)): + ((parameter-list->arglist positions arities parameter-list)): + ((parameter-list-ref parameter-list i)): + ((adjoin-parameters! parameter-list parameters)): + Procedures for making, merging, defaulting, checking and + converting `parameter lists' (named parameters). + ((getopt->parameter-list argc argv optnames arities aliases)): + ((getopt->arglist argc argv optnames positions + arities defaults checks aliases)): + Procedures for converting options and arguments processed by + getopt to parameter-list or arglist form. + + * dbutil.scm ((make-command-server rdb command-table)): added + procedure which calls commands and processes parameters. + + * rdms.scm ((make-relational-system base)): add-domain and + delete-domain commands moved to "dbutil.scm" (create-database). + +Fri Feb 3 11:07:46 1995 Aubrey Jaffer (jaffer@jacal) + + * debug.scm (debug:tracef debug:untracef): removed (duplicates of + code in "trace.scm"). + (trace-all): utility to trace all defines in a file added. + +Thu Jan 19 00:26:14 1995 Aubrey Jaffer (jaffer@jacal) + + * logical.scm (logbit? logtest): added. + +Sun Jan 15 20:38:42 1995 Aubrey Jaffer (jaffer@jacal) + + * dbutil.scm (dbutil:create-database)): Added parameter + description tables for "commands". + + * require.scm (software-type): standardize msdos -> ms-dos. + +Mon Jan 2 10:26:45 1995 Aubrey Jaffer (jaffer@jacal) + + * comlist.scm (comlist:atom?): renamed from comlist:atom. + + * scheme48.init (char->integer integer->char): Now use integers in + the range 0 to 255. Fixed several other problems. + (modulo): Worked around negative modulo bug. + + * Makefile (slib48): `make slib48' loads "scheme48.init", `,dump's + a scheme48 image file, and creates an `slib48' shell script to + invoke it. + + * hash.scm (hash:hash-number): no longer does inexact->exact to + exacts, etc. + + * trnscrpt.scm (read): no longer transcripts eof-objects. + + From: johnm@vlibs.com (John Gerard Malecki) + * priorque.scm (heap:heapify): internal defines incorrectly + dependent on order-of-eval replaced with let*. + +Thu Dec 22 13:28:16 1994 Aubrey Jaffer (jaffer@jacal) + + * dbutil.scm (open-database! open-database create-database): This + enhancement wraps a utility layer on `relational-database' which + provides: + * Automatic loading of the appropriate base-table package when + opening a database. + * Automatic execution of initialization commands stored in + database. + * Transparent execution of database commands stored in + `*commands*' table in database. + +Wed Dec 21 22:53:57 1994 Aubrey Jaffer (jaffer@jacal) + + * rdms.scm (make-relational-system base): Now more careful about + protecting read-only databases. + +Mon Dec 19 00:06:36 1994 Aubrey Jaffer (jaffer@jacal) + + * dbutil.scm (dbutil:define-tables): added utility which provides: + Data definition from Scheme lists for any SLIB + relational-database. + +Sat Dec 17 12:10:02 1994 Aubrey Jaffer (jaffer@jacal) + + * alistab.scm rdms.scm (make-getter row-eval): evaluation of + `expression' fields no longer done when retrieved from base + tables (which made copying of many tables impossible). + + * alistab.scm + (write-base): rewrote to not use pretty-print. + + * sc3.scm: removed (only contained last-pair, t, and nil). + + * Template.scm scheme48.init vscm.init (last-pair t nil): added. + +Thu Dec 8 00:02:18 1994 Aubrey Jaffer (jaffer@jacal) + + * mularg.scm pp.scm ratize.scm: copyright line removed from files + (still lacking terms) less than 12 lines. + + From: johnm@vlibs.com (John Gerard Malecki) + * sort.scm (sort:sort!): long standing bug in sort! with vector + argument fixed. + +Thu Dec 1 17:10:24 1994 Aubrey Jaffer (jaffer@jacal) + + * *.scm: Most missing copyright notices supplied. + +Sun Nov 27 23:57:41 1994 Aubrey Jaffer (jaffer@jacal) + + * rdms.scm (make-relational-system base): now checks field types + when table is opened. Domains table now has foreign-table field. + (for-each-row): ordered for-each function added. + * alistab.scm (ordered-for-each-key supported-key-type?): added. + +Thu Oct 27 12:20:41 1994 Tom Tromey <tromey@drip.colorado.edu> + + * priorque.scm: Renamed everything to conform to coding standards + and updated docs. Changed names: heap-extract-max to + heap-extract-max!, heap-insert to heap-insert! and heap-size to + heap-length. + +Sat Nov 26 22:52:31 1994 Aubrey Jaffer (jaffer@jacal) + + * Template.scm *.init (identity): Now required; moved from + "comlist.scm". + + * alistab.scm (alist-table): Converted to representing rows as + lists. Non-row operations removed. + + * rdms.scm (make-relational-system base): Most individual column + operations removed. Only get and get* remain. Row operations + renamed. Row inserts and updates distinguished. + +Tue Nov 15 16:37:16 1994 Aubrey Jaffer (jaffer@jacal) + + * rdms.scm (make-relational-system base): Generalized database + system inspired by the Relational Model. + + * alistab.scm (alist-table): Base table implementation suitable + for small databases and testing rdms.scm. + +Tue Oct 25 22:36:01 1994 Aubrey Jaffer (jaffer@jacal) + + From: Tommy Thorn <Tommy.Thorn@irisa.fr> + * chez.init (scheme-implementation-version): fixed (changed to "?"). + (library-vicinity): The definition of library-vicinity used + getenv, which was defined later. + (slib:chez:quit): The definition of slib:chez:quit was illegal. + Fixed. + (chez:merge!): had a typo. + (defmacro:load): (require 'struct) didn't work, because defmacro:load + doesn't add suffix. Workaround: defmacro:load and macro:load is + the same as slib:load-source. + +Wed Oct 19 11:44:12 1994 Aubrey Jaffer (jaffer@jacal) + + * require.scm time.scm cltime.scm (difftime offset-time): added to + allow 'posix-time functions to work with a non-numeric type + returned by (current-time). + +Tue Aug 2 10:44:32 1994 Aubrey Jaffer (jaffer@jacal) + + * repl.scm (repl:top-level repl:repl): Multiple values at top + level now print nicely. + +Sun Jul 31 21:39:54 1994 Aubrey Jaffer (jaffer@jacal) + + * cltime.scm (get-decoded-time get-universal-time + decode-universal-time encode-universal-time): + Common-Lisp time conversion routines created. + + * time.scm (*timezone* tzset gmtime localtime mktime asctime ctime): + Posix time conversion routines created. + +Mon Jul 11 14:16:44 1994 Aubrey Jaffer (jaffer@jacal) + + * Template.scm mitscheme.init scheme2c.init t3.init (*features*): + trace added. + +Fri Jul 8 11:02:34 1994 Aubrey Jaffer (jaffer@jacal) + + * chap.scm ((chap:string<? s1 s2) (chap:next-string s)): Functions + for "chapter ordering" of strings. + +Mon Jun 20 22:36:44 1994 Aubrey Jaffer (jaffer@jacal) + + * slib.texi (R4RS Macros section): added. + + From: jjb@isye.gatech.edu (John Bartholdi) + * sierpinski.scm (MAKE-SIERPINSKI-INDEXER): added. + * soundex.scm (SOUNDEX): added. + + From: hugh@cosc.canterbury.ac.nz (Hugh Emberson) + * mwexpand.scm ((mw:quasiquote exp env)): Fixed bug which occured + when mw:quasiquote expanded things like `(1 2 3 . ,(+ 1 a)). I + added support for vectors in quasiquotes while I was there. + +Sun Jun 19 00:37:09 1994 Aubrey Jaffer (jaffer@jacal) + + * defmacex.scm ((defmacro:expand* e)): fixed problem with varargs + define. + +Sat Jun 18 13:08:33 1994 Aubrey Jaffer (jaffer@jacal) + + * randinex.scm ((random:size-float l x)): no longer assumes that + inexact numbers have finite precision, which is not necessarily + true (pointed out by jar@ai.mit.edu). Limits size to 4. + +Mon Jun 6 00:46:48 1994 Aubrey Jaffer (jaffer@jacal) + + * trace.scm (trace untrace): created. + (debug:tracef debug:untracef): moved from debug.scm + +Sun May 22 23:44:03 1994 Aubrey Jaffer (jaffer@jacal) + + * yasyn.scm: replaces yasos.scm + +Sat May 21 22:28:01 1994 Aubrey Jaffer (jaffer@jacal) + + * comlist.scm ((comlist:has-duplicates? lst)): added. + +Mon May 16 13:40:18 1994 Aubrey Jaffer (jaffer@jacal) + + From: jjb@isye.gatech.edu (John Bartholdi) + * macscheme.init (slib:exit): fixed. Version set to 4.2. + +Wed Apr 27 00:48:54 1994 Aubrey Jaffer (jaffer@jacal) + + From: jjb@isye.gatech.edu (John Bartholdi) + * scanf.scm (scanf fscanf sscanf): created. + +Thu Apr 14 12:59:41 1994 Aubrey Jaffer (jaffer@jacal) + + From: pegelow@moorea.uni-muenster.de (Ulrich Pegelow) + * mbe.scm (hyg:tag-do): Scoping was wrong. The region of binding + of a <variable> did not include the <step> expression and the + <test> expression, instead it incorrectly included the <init> + expression. (rf. R4RS, 4.2.4) + (hyg:tag-lambda): the body of a lambda expression should be + generated using hyg:tag-generic instead of hyg:tag-vanilla. This + allows expressions within lambda to behave hygienically. + (hyg:tag-let): extended to support `named let'. + +Sun Apr 10 00:22:04 1994 Aubrey Jaffer (jaffer@jacal) + + * README: INSTALLATION INSTRUCTIONS greatly improved. + * Template.scm *.init: Path configurations move to top of files + for easier installation. + + * FAQ: File of Frequently Asked Questions and answers added. + +Sat Apr 9 21:28:46 1994 Aubrey Jaffer (jaffer@jacal) + + * slib.texi (Vicinity): scheme-file-suffix removed. Use + slib:load or slib:load-source instead. + +Wed Apr 6 00:55:16 1994 Aubrey Jaffer (jaffer@jacal) + + * require.scm (slib:report): + (slib:report-version): + (slib:report-locations): added to display SLIB configuration + information. + +Mon Apr 4 08:48:37 1994 Aubrey Jaffer (jaffer@jacal) + + * Template.scm *.init (slib:exit): added. + +Fri Apr 1 14:36:46 1994 Aubrey Jaffer (jaffer@jacal) + + * Makefile (intro): Added idiot message for those who make. + Cleaned up and reorganized Makefile. + +Wed Mar 30 00:28:30 1994 Aubrey Jaffer (jaffer@jacal) + + * Template.scm *.init ((slib:eval-load <pathname> evl)): created + to service all macro loads. + + From: whumeniu@datap.ca (Wade Humeniuk) + * recobj.scm yasyn.scm: added. These implement RECORDS and + YASOS using object.scm object system. + +Sun Mar 6 01:10:53 1994 Aubrey Jaffer (jaffer@jacal) + + From: barnett@armadillo.urich.edu (Lewis Barnett) + * gambit.init (implementation-vicinity library-vicinity): Relative + pathnames for Slib in MacGambit. + + From: lucier@math.purdue.edu (Brad Lucier) + * random.scm (random:random random:chunks/float): fixed off-by-one + and slop errors. + +Thu Mar 3 23:06:41 1994 Aubrey Jaffer (jaffer@jacal) + + From: lutzeb@cs.tu-berlin.de (Dirk Lutzebaeck) + * format.scm slib.texi: Format 3.0. + * format's configuration is rearranged to fit only into SLIB. All + implementation dependent configurations are done in the SLIB init files + * format's output routines rely on call-with-output-string now if + output to a string is desired + * The floating point formatting code (formatfl.scm) moved into + format.scm so that there is only one source code file; this + eliminates the configuration of the load path for the former + formatfl.scm and the unspecified scope of the load primitive + * floating point formatting doesn't use any floating point operation or + procedure except number->string now; all formatting is now based + solely on string, character and integer manipulations + * major rewrite of the floating point formatting code; use global + buffers now + * ~f,~e,~g, ~$ may use also number strings as an argument + * ~r, ~:r, ~@r, ~:@r roman numeral, and ordinal and cardinal + English number printing added (from dorai@cs.rice.edu) + * ~a has now a working `colinc' parameter + * ~t tabulate directive implemented + * ~/ gives a tabulator character now (was ~T in version < 2.4) + * ~& fresh line directive implemented + * ~@d, ~@b, ~@o and ~@x now has the CL meaning (plus sign printed) + automatic prefixing of radix representation is removed + * ~i prints complex numbers as ~f~@fi with passed parameters + * ~:c prints control characters like emacs (eg. ^C) and 8bit characters + as an octal number + * ~q gives information and copyright notice on this format implementation + ~:q gives format:version + * case type of symbol conversion can now be forced (see + format:symbol-case-conv in format.scm) + * case type of the representation of internal objects can now be + forced (see format:iobj-case-conv format.scm) + * format error messages are now printed on the current error port + if available by the implementation + * format now accepts a number as a destination port; the output + is then always directed to the current error port if available by + the implementation + * if format's destination is a string it is regarded as a format string now + and output is the current output port; this is a contribution to + Scheme->C to use format with the runtime system; the former semantics + to append tothe destination string is given up + * obj->string syntax change and speedup + * tested with scm4d, Elk 2.2, MIT Scheme 7.1, Scheme->C 01Nov91 + + +Wed Mar 2 13:16:37 1994 Aubrey Jaffer (jaffer@jacal) + + From: Matthias Blume <blume@cs.Princeton.EDU> + * vscm.init: added. + +Fri Feb 18 23:51:41 1994 Aubrey Jaffer (jaffer@jacal) + + From: jjb@isye.gatech.edu (John Bartholdi) + * macscheme.init: added. + +Thu Feb 17 01:19:47 1994 Aubrey Jaffer (jaffer@jacal) + + * ppfile.scm ((pprint-filter-file inport filter outport)): added. + Useful for pre-expanding macros. Preserves top-level comments. + +Wed Feb 16 12:44:34 1994 Aubrey Jaffer (jaffer@jacal) + + From: dorai@cs.rice.edu (Dorai Sitaram) + * mbe.scm: Macro by Example define-syntax using defmacro. + +Tue Feb 15 17:18:56 1994 Aubrey Jaffer (jaffer@jacal) + + From: whumeniu@datap.ca (Wade Humeniuk) + * object.scm: Macroless Object System + +Mon Feb 14 00:48:18 1994 Aubrey Jaffer (jaffer@jacal) + + * defmacex.scm (defmacro:expand*): replaces "defmacro.scm". Other + defmacro functions now supported in all implementations. + +Sun Feb 13 12:38:39 1994 Aubrey Jaffer (jaffer@jacal) + + * defmacro.scm (defmacro:macroexpand*): now expands quasiquotes + correctly. + +Sat Feb 12 21:23:56 1994 Aubrey Jaffer (jaffer@jacal) + + * hashtab.scm ((predicate->hash pred)): moved from hash.scm. + +Tue Feb 8 01:07:00 1994 Aubrey Jaffer (jaffer@jacal) + + * Template.scm *.init (slib:load-source slib:load-compiled + slib:load): support for loading compiled modules added. + Dependence on SCHEME-FILE-SUFFIX removed. + + * require.scm (require:require): Added support for 'source and + 'compiled features. + +Sat Feb 5 00:19:38 1994 Aubrey Jaffer (jaffer@jacal) + + * stdio.scm ((stdio:sprintf)): Now truncates printing if you run + out of string. + +Fri Feb 4 00:54:14 1994 Aubrey Jaffer (jaffer@jacal) + + From: pk@kaulushaikara.cs.tut.fi (Kellom{ki Pertti) + * (psd/primitives.scm): Here is a patch removing some problems + with psd-1.1, especially when used with Scheme 48. Thanks to + Jonathan Rees for poiting them out. The patch fixes two problems: + references to an unused variable *psd-previous-line*, and the + correct number of arguments to write-char. + +Fri Jan 14 00:37:19 1994 Aubrey Jaffer (jaffer@jacal) + + * require.scm (require:require): Now supports (feature . + argument-list) associations. + +Sat Nov 13 22:07:54 1993 (jaffer at jacal) + + * slib.info (Structures): added. Bug - struct.scm and + structure.scm do not implement the same macros. + +Mon Nov 1 22:17:01 1993 (jaffer at jacal) + + * array.scm (array-dimensions array-rank array-in-bounds?): + added. + +Sat Oct 9 11:54:54 1993 (jaffer at jacal) + + * require.scm (*catalog* portable-scheme-debugger): support added + for psd subdirectory. + +Tue Sep 21 11:48:26 1993 Aubrey Jaffer (jaffer at wbtree) + + * Makefile (lineio.scm rbtree.scm rbtest.scm scmacro.scm + sc4sc3.scm scaespp.scm scaglob.scm scainit.scm scamacr.scm + scaoutp.scm strcase.scm): hyphens removed from names. + +Mon Sep 20 00:57:29 1993 (jaffer at jacal) + + * arraymap.scm (array-map! array-for-each array-indexes): added. + +Sun Sep 19 19:20:49 1993 (jaffer at jacal) + + * require.scm (require:feature->path require:require *catalog*): + associations of the form (symbol1 . symbol2) in *catalog* look up + symbol2 whenever symbol1 is specified. + +Mon Sep 13 22:12:00 1993 (jaffer at jacal) + + From: sperber@provence.informatik.uni-tuebingen.de (Michael Sperber) + * elk.init: updated to ELK version 2.1. + +Sat Sep 11 21:17:45 1993 (jaffer at jacal) + + * hashtab.scm (hash-for-each): fixed and documented (also + documented alist.scm). + +Fri Sep 10 15:57:50 1993 (jaffer at jacal) + + * getopt.scm (getopt *optind* *optarg*): added. + +Tue Sep 7 23:57:40 1993 (jaffer at jacal) + + * slib1d3 released. + * comlist.scm: prefixed all functions with "comlist:". + +Tue Aug 31 23:59:28 1993 (jaffer at jacal) + + * Template.scm *.init (output-port-height): added. + +Wed May 26 00:00:51 1993 Aubrey Jaffer (jaffer at camelot) + + * hashtab.scm (hash-map hash-for-each): added. + * alist.scm (alist-map alist-for-each): added. + +Tue May 25 22:49:01 1993 Aubrey Jaffer (jaffer at camelot) + + * comlist.scm (delete delete-if atom): renamed as in common lisp. + * comlist.scm (delete-if-not): added. + * tree.scm: moved tree functions out of comlist.scm + +Mon May 24 10:28:22 1993 Aubrey Jaffer (jaffer at camelot) + + From: hanche@ams.sunysb.edu (Harald Hanche-Olsen) + * modular.scm: improvements and fixed bug in modular:expt. + +Fri May 14 01:26:44 1993 Aubrey Jaffer (jaffer at camelot) + + * slib1d2 released. + + From: Dave Love <d.love@daresbury.ac.uk> + * comlist.scm: added some tree functions. + * yasos.scm collect.scm: fixed name conflicts and documentation. + +Tue May 11 01:22:40 1993 Aubrey Jaffer (jaffer at camelot) + + * eval.scm: removed because all *.init files support it. + + * hash.scm: made all hash functions case-insensitive. Equal + inexact and exact numbers now hash to the same code. + + From: eigenstr@falstaff.cs.rose-hulman.edu: + * slib.texi: revised. + +Sun May 9 01:43:11 1993 Aubrey Jaffer (jaffer at camelot) + + From: kend@newton.apple.com (Ken Dickey) + * macwork.scm mwexpand.scm mwdenote.scm mwsynrul.scm: Macros no + longer expand builtin Scheme forms. + + From: William Clinger <will@skinner.cs.uoregon.edu> + * macwork.scm mwexpand.scm mwdenote.scm mwsynrul.scm: Macros that + work added. + +Sat May 1 23:55:42 1993 Aubrey Jaffer (jaffer at montreux) + + * random.scm (random:random): sped up for exact arguments. + +Wed Apr 28 00:24:36 1993 Aubrey Jaffer (jaffer at camelot) + + From: lutzeb@flp.cs.tu-berlin.de (Dirk Lutzebaeck) + * format.scm formatfl.scm formatst.scm slib.texi: Format 2.3. + * implemented floating point support ~F,~E,~G,~$ + * automatic detection if the scheme interpreter support flonums. + * the representation of internal objects can be selected to be + #<...> or #[...] or other forms + * new/redefintion of configuration variables format:abort, + format:floats, format:formatfl-path, format:iobj-pref, format:iobj-post + * added string-index + * added MIT Scheme 7.1 custom types + * for efficiencies reasons the error continuation is only used if + format:abort is not available + * improved error presentation and error handling + * tested with scm4b/c, Elk 2.0, MIT Scheme 7.1, Scheme->C 01Nov91, + UMB Scheme 2.5/2.10 + +Sun Apr 25 22:40:45 1993 Aubrey Jaffer (jaffer at camelot) + + From: Dave Love <d.love@daresbury.ac.uk> + * scheme2c.init: corrections and portability improvements. + * yasos.scm collect.scm: +These correct the scheme2c.init and a couple of other things as well as +hiding some non-exported definitions and removing an example from +collect.scm to the manual. + +Sat Apr 3 00:48:13 1993 Aubrey Jaffer (jaffer at camelot) + + From: eigenstr@cs.rose-hulman.edu (Todd R. Eigenschink) + * slib.texi: created. + +Thu Mar 25 01:47:38 1993 Aubrey Jaffer (jaffer at camelot) + + From: hanche@ams.sunysb.edu (Harald Hanche-Olsen) + * sca-init.scm sca-glob.scm sca-macr.scm sca-outp.scm + sca-expp.scm: syntax-case macros added. + +Wed Mar 24 23:12:49 1993 Aubrey Jaffer (jaffer at camelot) + + * comlist.scm (some every notany notevery): Now accept multiple + arguments. NOTANY added. + +Wed Mar 3 01:19:11 1993 Aubrey Jaffer (jaffer at camelot) + + From: "Dan Friedman" <dfried@cs.indiana.edu> + * struct.scm structst.scm: added. + +Tue Mar 2 00:28:00 1993 Aubrey Jaffer (jaffer at camelot) + + * obj2str (object->string): now handles symbols and number without + going to string-port. + +Sun Feb 28 22:22:50 1993 Aubrey Jaffer (jaffer at camelot) + + * all files with Jaffer copyright: Now have explicit conditions + for use and copying. + +Fri Feb 26 00:29:18 1993 Aubrey Jaffer (jaffer at camelot) + + * obj2str: redefined in terms of string ports. + + * pp2str: eliminated. + +Mon Feb 22 17:21:21 1993 Aubrey Jaffer (jaffer at camelot) + + From: dorai@cs.rice.edu (Dorai Sitaram) + * strport.scm: string ports. + + From: Alan@LCS.MIT.EDU (Alan Bawden) + * array.scm: functions which implement arrays. + +Wed Feb 17 00:18:57 1993 Aubrey Jaffer (jaffer at camelot) + + * repl.scm: split off from sc-macro.scm. + + * eval.scm *.init Template.scm (eval!): eliminated. + + From: dorai@cs.rice.edu (Dorai Sitaram) + * defmacro.scm: added. Chez, elk, mitscheme, scheme2c, and scm + support. + +Tue Feb 16 00:23:07 1993 Aubrey Jaffer (jaffer at camelot) + + * require.doc (output-port-width current-error-port tmpnam + file-exists? delete-file force-output char-code-limit + most-positive-fixnum slib:tab slib:form-feed error):descriptions + added. + + * *.init (tmpnam): now supported by all. + + From: dorai@cs.rice.edu (Dorai Sitaram) + * chez.init elk.init mitscheme.init scheme2c.init (defmacro macro? + macro-expand): added. + +Mon Feb 15 00:51:22 1993 Aubrey Jaffer (jaffer at camelot) + + * Template.scm *.init (file-exists? delete-file): now defined for + all implementations. + +Sat Feb 13 23:40:22 1993 Aubrey Jaffer (jaffer at camelot) + + * chez.init (slib:error): output now directed to + (current-error-port). + +Thu Feb 11 01:23:25 1993 Aubrey Jaffer (jaffer at camelot) + + * withfile.scm (with-input-from-file with-output-from-file): now + close file on thunk return. + + * *.init (current-error-port): added. + +Wed Feb 10 17:57:15 1993 Aubrey Jaffer (jaffer at camelot) + + * mitscheme.init (values dynamic-wind): added to *features*. + + From: mafm@cs.uwa.edu.au (Matthew MCDONALD) + * mitcomp.pat: added patch file of definitions for compiling SLIB + with MitScheme. + +Tue Feb 9 10:49:12 1993 Aubrey Jaffer (jaffer at camelot) + + From: jt@linus.mitre.org (F. Javier Thayer) + * t3.init: additions and corrections. + +Mon Feb 8 20:27:18 1993 Aubrey Jaffer (jaffer at camelot) + + From: dorai@cs.rice.edu (Dorai Sitaram) + * chez.init: added. + +Wed Feb 3 23:33:49 1993 Aubrey Jaffer (jaffer at camelot) + + * sc-macro.scm (macro:repl): now prints error message for errors. + +Mon Feb 1 22:22:17 1993 Aubrey Jaffer (jaffer at camelot) + + * logical.scm (logor): changed to logior to be compatible with + common Lisp. + +Fri Jan 29 17:15:03 1993 Aubrey Jaffer (jaffer at camelot) + + From: jt@linus.mitre.org (F. Javier Thayer) + * t3.init: modified so it passes most of SCM/test.scm. + +Sun Jan 24 00:18:13 1993 Aubrey Jaffer (jaffer at camelot) + + * comlist.scm (intersection): added. + +Wed Jan 13 19:01:11 1993 Aubrey Jaffer (jaffer at camelot) + + * debug.scm: (debug:qp): needed to shadow quotient. + +Sat Jan 9 13:44:44 1993 Aubrey Jaffer (jaffer at camelot) + + * rb-tree.scm: changed use of '() and NULL? to #f and NOT. + + * rb-tree.scm (rb-insert! rb-delete!) added ! to names. + +Fri Jan 8 01:17:16 1993 Aubrey Jaffer (jaffer at camelot) + + * rb-tree.doc: added. + + From: pgs@ai.mit.edu (Patrick Sobalvarro) + * rb-tree.scm rbt-test.scm: code for red-black trees added. + +Tue Jan 5 14:57:02 1993 Aubrey Jaffer (jaffer at camelot) + + From: lutzeb@cs.tu-berlin.de (Dirk Lutzebaeck) + * format.scm formatst.scm format.doc: version 2.2 + * corrected truncation for fixed fields by negative field parameters + inserted a '<' or a '>' when field length was equal to object string + length + * changed #[...] outputs to #<...> outputs to be conform to SCM's + display and write functions + * changed #[non-printable-object] output to #<unspecified> + * ~:s and ~:a print #<...> messages in strings "#<...>" so that the + output can always be processed by (read) + * changed implementation dependent part: to configure for various scheme + systems define the variable format:scheme-system + * format:version is a variable returning the format version in a string + * format:custom-types allows to use scheme system dependent predicates + to identify the type of a scheme object and its proper textual + representation + * tested with scm4a14, Elk 2.0 + +Tue Dec 22 17:36:23 1992 Aubrey Jaffer (jaffer at camelot) + + * Template.scm *.init (char-code-limit): added. + + * debug.scm (qp): qp-string had bug when printing short strings + when room was less than 3. + + * random.scm (random:size-int): now takes most-positive-fixnum + into account. + +Wed Nov 18 22:59:34 1992 Aubrey Jaffer (jaffer at camelot) + + From: hanche@ams.sunysb.edu (Harald Hanche-Olsen) + * randinex.scm (random:normal-vector! random:normal + random:solid-sphere random:hollow-sphere): new versions fix bug. + +Tue Nov 17 14:00:15 1992 Aubrey Jaffer (jaffer at Ivan) + + * str-case.scm (string-upcase string-downcase string-capitalize + string-upcase! string-downcase! string-capitalize!): moved from + format.scm. + +Fri Nov 6 01:09:38 1992 Aubrey Jaffer (jaffer at Ivan) + + * require.scm (require): uses base:load instead of load. + + * sc-macro.scm (macro:repl): now uses dynamic-wind. + +Mon Oct 26 13:21:04 1992 Aubrey Jaffer (jaffer at Ivan) + + * comlist.scm (nthcdr last) added. + +Sun Oct 25 01:50:07 1992 Aubrey Jaffer (jaffer at Ivan) + + * line-io.scm: created + +Mon Oct 19 12:53:01 1992 Aubrey Jaffer (jaffer at camelot) + + From: dorai@cs.rice.edu + * fluidlet.scm: FLUID-LET that works. + +Thu Oct 8 22:17:01 1992 Aubrey Jaffer (jaffer at camelot) + + From: Robert Goldman <rpg@rex.cs.tulane.edu> + * mitscheme.init: improvements. + +Sun Oct 4 11:37:57 1992 Aubrey Jaffer (jaffer at camelot) + + * values.scm values.doc: Documentation rewritten and combined + into values.scm + +Thu Oct 1 23:29:43 1992 Aubrey Jaffer (jaffer at Ivan) + + * sc-macro.scm sc-macro.doc: documentation improved and moved into + sc-macro.doc. + +Mon Sep 21 12:07:13 1992 Aubrey Jaffer (jaffer at Ivan) + + * sc-macro.scm (macro:load): now sets and restores *load-pathname*. + + * eval.scm (slib:eval!): (program-vicinity) now correct during + evaluation. + + * Template.scm, *.init: i/o-redirection changed to with-file. + *features* documentation changed. + + From: Stephen J Bevan <bevan@computer-science.manchester.ac.uk> + * t3.init: new. Fixes problems with require, substring, and + <,>,<=,>= with more than 2 arguments. + +Fri Sep 18 00:10:57 1992 Aubrey Jaffer (jaffer at Ivan) + + From andrew@astro.psu.edu Wed Sep 16 17:58:21 1992 + * dynamic.scm: added. + + From raible@nas.nasa.gov Thu Sep 17 22:28:25 1992 + * fluidlet.scm: added. + +Sun Sep 13 23:08:46 1992 Aubrey Jaffer (jaffer at Ivan) + + * sc-macro.scm (macro:repl): moved (require 'debug) into syntax-error. + + * dynwind.scm, withfile.scm, trnscrpt.scm: created. + + From kend@data.rain.com Sun Sep 13 21:26:59 1992 + * collect.scm: created. + * oop.scm => yasos.scm: updated. + * oop.doc: removed. + + From: Stephen J. Bevan <bevan@cs.man.ac.uk> 19920912 + * elk.init: created + +Tue Jul 14 11:42:57 1992 Aubrey Jaffer (jaffer at Ivan) + + * tek41.scm tek40.scm: added. + +Tue Jul 7 00:55:58 1992 Aubrey Jaffer (jaffer at Ivan) + + * record.scm record.doc (record-sub-predicate): added. + + * sc-macro.scm (macro:repl): syntax-errors now return into + macro:repl. + + * debug.scm (qp): removed (newline). Added qpn (qp with newline). + +Sun Jun 14 22:57:32 1992 Aubrey Jaffer (jaffer at Ivan) + + * slib1b8 released. + +Sat Jun 13 17:01:41 1992 Aubrey Jaffer (jaffer at Ivan) + + * alist.scm ppfile.scm: added. + + * hash.scm hashtab.scm scheme48.init: added. + + * sc-macro.scm (macro:repl): created. macro:load now uses + eval:eval!. + + * eval.scm (eval:eval!) created and eval done in terms of it. + + * prime.scm (prime:prime?) fixed misplaced parenthesis. + +Wed May 27 16:13:17 1992 Aubrey Jaffer (jaffer at Ivan) + + From: "Chris Hanson" <cph@martigny.ai.mit.edu> + * synrul.scm (generate-match): fixed for CASE syntax. + +Wed May 20 00:25:40 1992 Aubrey Jaffer (jaffer at Ivan) + + * slib1b6 released. + + * Template.scm gambit.init mitscheme.init scheme2c.init: + rearranged *features*. + +Tue May 19 22:51:28 1992 Aubrey Jaffer (jaffer at Ivan) + + * scmactst.scm: test cases fixed. + + From: "Chris Hanson" <cph@martigny.ai.mit.edu> + * r4syn.scm (make-r4rs-primitive-macrology): TRANSFORMER added + back in. + + * require.scm (load): load now passes through additional + arguments to *old-load*. + +Mon May 18 00:59:36 1992 Aubrey Jaffer (jaffer at Ivan) + + * mulapply.scm (apply): written. + + * record.scm record.doc (make-record-sub-type): added. + +Fri May 8 17:55:14 1992 Aubrey Jaffer (jaffer at Ivan) + + * process.scm: created, but not finished. + + From: hugh@ear.mit.edu (Hugh Secker-Walker) + * comlist.scm (nreverse make-list): non-recursive versions added. + + * sc2.scm (1+ -1+): versions which capture +. + + * mularg.scm (- /): created. + +Wed Apr 8 00:05:30 1992 Aubrey Jaffer (jaffer at Ivan) + + * require.scm sc-macro.scm (catalog): Now uses macro:load if + 'macro is part of catalog entry. + + From: Andrew Wilcox (awilcox@astro.psu.edu) + * queue.scm: created. + +Sun Mar 15 12:23:06 1992 Aubrey Jaffer (jaffer at Ivan) + + * comlist.scm (notevery): fixed. Now (not (every ..)). + + * eval.scm (eval:eval): renamed to slib:eval. + + * record.scm: replaced with version from From: david carlton + <carlton@husc.harvard.edu>. I changed updater => modifier, put + record-predicate into the rtd, and bummed code mercilessly. + + From: plogan@std.mentor.com (Patrick Logan) + * sc3.scm (last-pair): changed from testing null? to pair?. @@ -0,0 +1,216 @@ +FAQ (Frequently Asked Questions and answers) for SLIB Scheme Library (slib2a6). +Written by Aubrey Jaffer (jaffer@ai.mit.edu). + + INTRODUCTION AND GENERAL INFORMATION + +[] What is SLIB? + +SLIB is a portable scheme library meant to provide compatibiliy and +utility functions for all standard scheme implementations. + +[] What is Scheme? + +Scheme is a programming language in the Lisp family. + +[] Which implementations has SLIB been ported to? + +SLIB is currently supported by Chez, ELK 2.1, GAMBIT, MacScheme, +MITScheme, scheme->C, Scheme48, T3.1, SCM and VSCM + +[] How can I get SLIB? + +SLIB is available via ftp from: + ftp-swiss.ai.mit.edu:pub/scm/slib2a6.tar.gz + prep.ai.mit.edu:pub/gnu/jacal/slib2a6.tar.gz + ftp.maths.tcd.ie:pub/bosullvn/jacal/slib2a6.tar.gz + ftp.cs.indiana.edu:/pub/scheme-repository/imp/slib2a6.tar.gz + +SLIB is also included with SCM floppy disks. + +[] How do I install SLIB? + +Read the INSTALLATION INSTRUCTIONS in "slib/README". + +[] What are slib.texi and slib.info? + +"slib.texi" is the `texinfo' format documentation for SLIB. +"slib.info" is produced from "slib.texi" by either Gnu Emacs or the +program `makeinfo'. "slib.info" can be viewed using either Gnu Emacs +or `info' or a text editor. + +Programs for printing and viewing TexInfo documentation (which SLIB +has) come with GNU Emacs or can be obtained via ftp from: +prep.ai.mit.edu:pub/gnu/texinfo-3.1.tar.gz + +[] How often is SLIB released? + +SLIB was released 9 times in 1993. + +[] What is the latest version? + +The version as of this writing is slib2a6. + +[] What version am I using? + +The Version is in the first line of the files slib/FAQ, slib/ANNOUNCE, +and slib/README. If you have Scheme and SLIB running, type +(slib:report-version) + + SLIB INSTALLATION PROBLEMS + +[] When I load an SLIB initialization file for my Scheme + implementation, I get ERROR: Couldn't find "require.scm" + +Did you remember to set either the environment variable +SCHEME_LIBRARY_PATH or the library-vicinity in your initialization +file to the correct location? Make sure if you set only the +environment variable SCHEME_LIBRARY_PATH that your implementation +supports getenv. + +[] When I load an SLIB initialization file for my Scheme + implementation, I get ERROR: Couldn't find + "/usr/local/lib/slibrequire.scm" + +Notice that it is looking for "slibrequire.scm" rather than +"slib/require.scm". You need to put a trailing slash on either the +environment variable SCHEME_LIBRARY_PATH or in the library-vicinity in +your initialization file. + +[] SLIB used to work, but now I get ERROR: Couldn't find + "slib/require.scm". What happened? + +You changed directories and now the relative pathname +"slib/require.scm" no longer refers to the same directory. The +environment variable SCHEME_LIBRARY_PATH and library-vicinity in your +initialization file should be absolute pathnames. + +[] When I type (require 'macro) I get "ERROR: unbound variable: + require". + +You need to arrange to have your Scheme implementation load the +appropriate SLIB initialization file ("foo.init") before using SLIB. +If your implementation loads an initialization file on startup, you +can have it load the SLIB initialization file automatically. For +example (load "/usr/local/lib/slib/foo.init"). + +[] Why do I get a string-ref (or other) error when I try to load + or use SLIB. + +Check that the version of the Scheme implementation you are using +matches the version for which the SLIB initialization file was +written. There are some notes in the SLIB initialization files about +earlier versions. You may need to get a more recent version of your +Scheme implementation. + + USING SLIB PROCEDURES + +[] I installed SLIB. When I type (random 5) I get "ERROR: + unbound variable: random". Doesn't SLIB have a `random' + function? + +Before you can use most SLIB functions, the associated module needs to +be loaded. You do this by typing the line that appears at the top of +the page in slib.info (or slib.texi) where the function is documented. +In the case of random, the line is (require 'random). + +[] Why doesn't SLIB just load all the functions so I don't have + to type require statements? + +SLIB currently has more than 1 Megabyte of Scheme source code. Many +scheme implementations take unacceptably long to load 1 Megabyte of +source; some implementations cannot allocate enough storage. If you +use a package often, you can put the require statement in your Scheme +initialization file. Consult the manual for your Scheme +implementation to find out the initialization file's name. + +`Autoloads' will work with many Scheme implementations. You could put +the following in your initialization file: + (define (random . args) (require 'random) (apply random args)) + +I find that I only type require statements at top level when +debugging. I put require statements in my Scheme files so that the +appropriate modules are loaded automatically. + +[] Why does SLIB have PRINTF when it already has the more + powerful (CommonLisp) FORMAT? + +CommonLisp FORMAT does not support essential features which PRINTF +does. For instance, how do you format a signed 0 extended number? + + (format t "~8,'0,X~%" -3) ==> 000000-3 + +But printf gets it right: + + (printf "%08x\n" -3) ==> -0000003 + +How can one trunctate a non-numeric field using FORMAT? This feature +is essential for printing reports. The first 20 letters of a name is +sufficient to identify it. But if that name doesn't get trucated to +the desired length it can displace other fields off the page. Once +again, printf gets it right: + + (printf "%.20s\n" "the quick brown fox jumped over the lazy dog") + ==> the quick brown fox + +FORMAT also lacks directives for formatting date and time. printf +does not handle these directly, but a related function strftime does. + +[] Why doesn't SLIB:ERROR call FORMAT? + +Format does not provide a method to truncate fields. When an error +message contains non-terminating or large expressions, the essential +information of the message may be lost in the ensuing deluge. + +FORMAT as currently written in SLIB is not reentrant. Until this is +fixed exception handlers and errors which might occur while using +FORMAT cannot use it. + + MACROS + +[] Why are there so many macro implementations in SLIB? + +The R4RS committee specified only the high level pattern language in +the Revised^4 Report on Scheme and left to the free marketplace of +ideas the details of the low-level facility. Each macro package has a +different low-level facility. The low-level facilities are sometimes +needed because the high level pattern language is insufficiently +powerful to accomplish tasks macros are often written to do. + +[] Why are there both R4RS macros and Common-Lisp style defmacros + in SLIB? + +Most current Scheme implementations predate the adoption of the R4RS +macro specification. It turns out that all of the implementations +can support defmacro natively. + +[] I did (LOAD "slib/yasos.scm"). The error I get is "variable + define-syntax is undefined". + +The way to load the struct macro package is (REQUIRE 'YASOS). + +[] I did (REQUIRE 'YASOS). Now when I type (DEFINE-PREDICATE + CELL?) The error I get is "variable define-predicate is + undefined". + +If like most implementations, your Scheme does not natively support +R4RS macros you will need to install a macro-capable read-eval-print +loop. This is done by: + (require 'macro) ;already done if you did (require 'yasos) + (require 'repl) + (repl:top-level macro:eval) + +This is also true for Schemes which don't support DEFMACRO. The lines +in this case are: + (require 'repl) + (repl:top-level defmacro:eval) + +[] I always use R4RS macros. How can I avoid having to type + require statements every time I start Scheme? + +As is explained in the Repl entry in slib.info (or slib.texi): + + To have your top level loop always use macros, add any interrupt + catching lines and the following lines to your Scheme init file: + (require 'macro) + (require 'repl) + (repl:top-level macro:eval) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a2b8de7 --- /dev/null +++ b/Makefile @@ -0,0 +1,229 @@ +# Makefile for Scheme Library +# Copyright (C) 1991, 1992, 1993, 1994, 1995 Aubrey Jaffer. + +SHELL = /bin/sh +intro: + @echo + @echo "Welcome to SLIB. Read \"README\" and \"slib.info\" (or" + @echo "\"slib.texi\") to learn how to install and use SLIB." + @echo + @echo + -make slib.info + +srcdir=$(HOME)/slib/ +dvidir=../dvi/ +dvi: $(dvidir)slib.dvi +$(dvidir)slib.dvi: $(srcdir)slib.texi $(dvidir)slib.fn +# cd $(dvidir);texi2dvi $(srcdir)slib.texi + -(cd $(dvidir);texindex slib.??) + cd $(dvidir);tex $(srcdir)slib.texi +$(dvidir)slib.fn: + cd $(dvidir);tex $(srcdir)slib.texi +xdvi: $(dvidir)slib.dvi + xdvi $(dvidir)slib.dvi + +prefix = /usr/local +exec_prefix = $(prefix) +bindir = $(exec_prefix)/bin +libdir = $(exec_prefix)/lib +infodir = $(exec_prefix)/info +RUNNABLE = scheme48 +LIB = $(libdir)/$(RUNNABLE) +VM = scheme48vm +IMAGE = slib.image + +slib48: + (echo ,load `pwd`/scheme48.init; \ + echo "(define *args* '())"; \ + echo "(define (program-arguments) (cons \"$(VM)\" *args*))"; \ + echo ,dump $(LIB)/$(IMAGE); \ + echo ,exit) | scheme48 + (echo '#!/bin/sh'; \ + echo exec '$(LIB)/$(VM)' -i '$(LIB)/$(IMAGE)' \"\$$\@\") \ + > $(bindir)/slib48 + chmod +x $(bindir)/slib48 + +info: $(infodir)/slib.info + -make schelog-info +$(infodir)/slib.info: slib.texi + makeinfo slib.texi -o $(infodir)/slib.info + +infoz: $(infodir)/slib.info.gz + -make schelog-infoz +$(infodir)/slib.info.gz: $(infodir)/slib.info + -rm $(infodir)/slib.info*.gz + gzip $(infodir)/slib.info* + +#### Stuff for maintaining SLIB below #### + +VERSION = 2a6 +ver = $(VERSION) +ffiles = printf.scm format.scm genwrite.scm obj2str.scm pp.scm \ + ppfile.scm strcase.scm debug.scm trace.scm lineio.scm \ + strport.scm scanf.scm chap.scm qp.scm break.scm stdio.scm \ + strsrch.scm +lfiles = sort.scm comlist.scm tree.scm logical.scm random.scm tsort.scm +revfiles = sc4opt.scm sc4sc3.scm sc2.scm mularg.scm mulapply.scm \ + trnscrpt.scm withfile.scm dynwind.scm promise.scm values.scm +afiles = ratize.scm randinex.scm modular.scm primes.scm factor.scm \ + charplot.scm time.scm cltime.scm root.scm +bfiles = collect.scm fluidlet.scm struct.scm \ + object.scm recobj.scm yasyn.scm +# yasos.scm +scfiles = r4rsyn.scm scmacro.scm synclo.scm synrul.scm synchk.scm \ + repl.scm macwork.scm mwexpand.scm mwdenote.scm mwsynrul.scm +scafiles = scainit.scm scaglob.scm scamacr.scm scaoutp.scm scaexpp.scm \ + structure.scm +dfiles = defmacex.scm mbe.scm +efiles = record.scm dynamic.scm queue.scm process.scm \ + priorque.scm hash.scm hashtab.scm alist.scm \ + wttree.scm wttest.scm array.scm arraymap.scm \ + sierpinski.scm soundex.scm +rfiles = rdms.scm alistab.scm dbutil.scm paramlst.scm report.scm \ + batch.scm makcrc.scm dbrowse.scm comparse.scm getopt.scm +gfiles = tek40.scm tek41.scm +docfiles = ANNOUNCE README FAQ ChangeLog slib.texi +mfiles = Makefile require.scm Template.scm +ifiles = chez.init elk.init macscheme.init \ + mitscheme.init scheme2c.init scheme48.init gambit.init t3.init \ + vscm.init mitcomp.pat syncase.sh +tfiles = plottest.scm formatst.scm macrotst.scm scmactst.scm \ + dwindtst.scm structst.scm +sfiles = $(ffiles) $(lfiles) $(revfiles) $(afiles) $(scfiles) $(efiles) \ + $(rfiles) $(gfiles) $(scafiles) $(dfiles) +allfiles = $(docfiles) $(mfiles) $(ifiles) $(sfiles) $(tfiles) $(bfiles) + +makedev = make -f $(HOME)/makefile.dev +CHPAT=$(HOME)/bin/chpat +dest = $(HOME)/dist/ +temp/slib: $(allfiles) + -rm -rf temp + mkdir temp + mkdir temp/slib + ln $(allfiles) temp/slib + +infotemp/slib: slib.info + -rm -rf infotemp + mkdir infotemp + mkdir infotemp/slib + ln slib.info slib.info-* infotemp/slib + +distinfo: $(dest)slib.info.tar.gz +$(dest)slib.info.tar.gz: infotemp/slib + $(makedev) TEMP=infotemp/ DEST=$(dest) PROD=slib ver=.info tar.gz + rm -rf infotemp + +dist: $(dest)slib$(VERSION).tar.gz +$(dest)slib$(VERSION).tar.gz: temp/slib + $(makedev) DEST=$(dest) PROD=slib ver=$(VERSION) tar.gz +shar: slib.shar +slib.shar: temp/slib + $(makedev) PROD=slib shar +dclshar: slib.com +com: slib.com +slib.com: temp/slib + $(makedev) PROD=slib com +zip: slib.zip +slib.zip: temp/slib + $(makedev) PROD=slib zip +distzip: slib$(VERSION).zip +slib$(VERSION).zip: temp/slib + $(makedev) DEST=$(dest) PROD=slib ver=$(VERSION) zip + mv $(dest)slib$(VERSION).zip /c/scm/dist/ +pubzip: temp/slib + $(makedev) DEST=$(HOME)/pub/ PROD=slib zip + +diffs: pubdiffs +pubdiffs: temp/slib + $(makedev) DEST=$(HOME)/pub/ PROD=slib pubdiffs +distdiffs: temp/slib + $(makedev) DEST=$(dest) PROD=slib ver=$(ver) distdiffs +announcediffs: temp/slib + $(makedev) DEST=$(dest) PROD=slib ver=2a1 announcediffs + +psdfiles=COPYING.psd README.psd cmuscheme.el comint.el instrum.scm pexpr.scm \ + primitives.scm psd-slib.scm psd.el read.scm runtime.scm version.scm +psdocfiles=article.bbl article.tex manual.bbl manual.tex quick-intro.tex + +psdtemp/slib: + -rm -rf psdtemp + mkdir psdtemp + mkdir psdtemp/slib + mkdir psdtemp/slib/psd + cd psd; ln $(psdfiles) ../psdtemp/slib/psd + mkdir psdtemp/slib/psd/doc + cd psd/doc; ln $(psdocfiles) ../../psdtemp/slib/psd/doc + +psdist: $(dest)slib-psd.tar.gz +$(dest)slib-psd.tar.gz: psdtemp/slib + $(makedev) DEST=$(dest) PROD=slib ver=-psd tar.gz TEMP=psdtemp/ + +schelogfiles = copying schelog.doc schelog.scm schelog.texi +schelogexamples = bible.scm england2.scm holland.scm mapcol.scm \ + schelogt.scm england.scm games.scm houses.scm puzzle.scm \ + toys.scm + +schelogtemp/slib: + -rm -rf schelogtemp + mkdir schelogtemp + mkdir schelogtemp/slib + mkdir schelogtemp/slib/schelog + cd schelog; ln $(schelogfiles) ../schelogtemp/slib/schelog + mkdir schelogtemp/slib/schelog/examples + cd schelog/examples; ln $(schelogexamples) \ + ../../schelogtemp/slib/schelog/examples + +schelogdist: schelog-dist +schelog-dist: $(dest)slib-schelog.tar.gz +$(dest)slib-schelog.tar.gz: schelogtemp/slib + $(makedev) DEST=$(dest) PROD=slib ver=-schelog tar.gz TEMP=schelogtemp/ + +schelog-info: $(infodir)/schelog.info +$(infodir)/schelog.info: schelog/schelog.texi + makeinfo schelog/schelog.texi -o $(infodir)/schelog.info + +schelog-infoz: $(infodir)/schelog.info.gz +$(infodir)/schelog.info.gz: $(infodir)/schelog.info + -rm $(infodir)/schelog.info*.gz + gzip $(infodir)/schelog.info* + +schelog.dvi: $(dvidir)schelog.dvi +$(dvidir)schelog.dvi: $(srcdir)schelog/schelog.texi $(dvidir)schelog.fn +# cd $(dvidir);texi2dvi $(srcdir)schelog/schelog.texi + -(cd $(dvidir);texindex schelog.??) + cd $(dvidir);tex $(srcdir)schelog/schelog.texi +$(dvidir)schelog.fn: + cd $(dvidir);tex $(srcdir)schelog/schelog.texi +schelog-xdvi: $(dvidir)schelog.dvi + xdvi $(dvidir)schelog.dvi + +new: + $(CHPAT) slib$(VERSION) slib$(ver) ANNOUNCE FAQ ../scm/ANNOUNCE \ + ../jacal/ANNOUNCE ../wb/README ../wb/ANNOUNCE \ + /c/scm/dist/install.bat /c/scm/dist/makefile \ + ../public_html/README.html ../dist/README \ + ../public_html/SLIB.html ../public_html/JACAL.html \ + ../public_html/SCM.html ../public_html/Hobbit.html \ + /c/scm/dist/mkdisk.bat \ + ../scm/README ../scm/scm.texi + $(CHPAT) $(VERSION) $(ver) README slib.texi require.scm Makefile + +tagfiles = slib.texi $(mfiles) $(sfiles) $(bfiles) $(tfiles) +# README and $(ifiles) cause semgentation faults in ETAGS for Emacs version 19. +tags: $(tagfiles) + etags $(tagfiles) +test: $(sfiles) + scheme Template.scm $(sfiles) +rights: + scm -ladmin -e"(admin:check-all)" $(sfiles) $(tfiles) \ + $(bfiles) $(ifiles) +clean: + -rm -f *~ *.bak *.orig *.rej core a.out *.o \#* + -rm -rf *temp +distclean: realclean +realclean: + -rm -f *~ *.bak *.orig *.rej TAGS core a.out *.o \#* + -rm -f slib.info* slib.?? slib.??? + -rm -rf *temp +realempty: temp/slib + -rm -f $(allfiles) @@ -0,0 +1,220 @@ +This directory contains the distribution of Scheme Library slib2a3. +Slib conforms to Revised^4 Report on the Algorithmic Language Scheme +and the IEEE P1178 specification. Slib supports Unix and similar +systems, VMS, and MS-DOS. + +The maintainer can be reached at jaffer@ai.mit.edu. + + MANIFEST + + `README' is this file. It contains a MANIFEST, INSTALLATION + INSTRUCTIONS, and proposed coding standards. + `FAQ' Frequently Asked Questions and answers. + `ChangeLog' documents changes to slib. + `slib.texi' has documentation on library packages in TexInfo format. + + `Template.scm' Example configuration file. Copy and customize to + reflect your system. + `chez.init' is a configuration file for Chez Scheme. + `elk.init' is a configuration file for ELK 2.1 + `gambit.init' is a configuration file for Gambit Scheme. + `macscheme.init' is a configuration file for MacScheme. + `mitscheme.init' is a configuration file for MIT Scheme. + `mitcomp.pat' is a patch file which adds definitions to SLIB files + for the MitScheme compiler. + `scheme2c.init' is a configuration file for DEC's scheme->c. + `scheme48.init' is a configuration file for Scheme48. + `t3.init' is a configuration file for T3.1 in Scheme mode. + `vscm.init' is a configuration file for VSCM. + `require.scm' has code which allows system independent access to + the library files. + + `format.scm' has Common-Lisp style format. + `formatst.scm' has code to test format.scm + `pp.scm' has pretty-print. + `ppfile.scm' has pprint-file and pprint-filter-file. + `obj2str.scm' has object->string. + `strcase.scm' has functions for manipulating the case of strings. + `genwrite.scm' has a generic-write which is used by pp.scm, + pp2str.scm and obj2str.scm + `printf.scm' has printf, fprintf, and sprintf compatible with C. + `scanf.scm' has scanf, fscanf, and sscanf compatible by C. + `lineio' has line oriented input/output functions. + `qp.scm' has printer safe for circular structures. + `break.scm' has break and continue. + `trace.scm' has trace and untrace for tracing function execution. + `debug.scm' has handy higher level debugging aids. + `strport.scm' has routines for string-ports. + `strsrch.scm' search for chars or substrings in strings and ports. + + `alist.scm' has functions accessing and modifying association lists. + `hash.scm' defines hash, hashq, and hashv. + `hashtab.scm' has hash tables. + `sierpinski.scm' 2-dimensional coordinate hash. + `soundex.scm' English name hash. + `logical.scm' emulates 2's complement logical operations. + `random.scm' has random number generator compatible with Common Lisp. + `randinex.scm' has inexact real number distributions. + `primes.scm' has primes and probably-prime?. + `factor.scm' has factor. + `root.scm' has Newton's and Laguerre's methods for finding roots. + `charplot.scm' has procedure for plotting on character screens. + `plottest.scm' has code to test charplot.scm. + `tek40.scm' has routines for Tektronix 4000 series graphics. + `tek41.scm' has routines for Tektronix 4100 series graphics. + `getopt.scm' has posix-like getopt for parsing command line arguments. + `time.scm' has Posix time conversion routines. + `cltime.scm' has Common-Lisp time conversion routines. + `comparse.scm' has shell-like command parsing. + + `rdms.scm' has code to construct a relational database from a base + table implementation. + `alistab.scm' has association list base tables. + `dbutil.scm' has utilities for creating and manipulating relational + databases. + `dbrowse.scm' browses relational databases. + `paramlst.scm' has procedures for passing parameters by name. + `report.scm' prints database reports. + `batch.scm' Group and execute commands on various operating systems. + `makcrc.scm' Create Scheme procedure to calculate POSIX.2 checksums + or other CRCs. + + `record.scm' a MITScheme user-definable datatypes package + `promise.scm' has code from R4RS for supporting DELAY and FORCE. + + `repl.scm' has a read-eval-print-loop. + `defmacex.scm' has defmacro:expand*. + `mbe.scm' has "Macro by Example" define-syntax. + `scmacro.scm' is a syntactic closure R4RS macro package. + r4rsyn.scm, synclo.scm, synrul.scm have syntax definitions + and support. + `scmactst.scm' is code for testing SYNTACTIC CLOSURE macros. + `scainit.scm' is a syntax-case R4RS macro package. + scaglob.scm scamacr.scm scaoutp.scm scaexpp.scm have + syntax definitions and support. `syncase.sh' is a shell + script for producing the SLIB version from the original. + `macwork.scm' is a "Macros that work" package. + mwexpand.scm mwdenote.scm mwsynrul.scm have support. + `macrotst.scm' is code from R4RS for testing macros. + + `values.scm' is multiple values. + `queue.scm' has queues and stacks. + + `object.scm' is object oriented programming (using no macros). + `recobj.scm' is records implemented using object.scm. + `yasyn.scm' is a macro package implementing YASOS using object.scm. + + `yasos.scm' is object oriented programming (using R4RS macros). + `collect.scm' is collection operators (like CL sequences). + `priorque.scm' has code and documentation for priority queues. + `wttree.scm' has weight-balanced trees. + `wttest.scm' tests weight-balanced trees. + `process.scm' has multi-processing primitives. + `array.scm' has multi-dimensional arrays and sub-arrays. + `arraymap.scm' has array-map!, array-for-each, and array-indexes. + + `sort.scm' has sorted?, sort, sort!, merge, and merge!. + `tsort.scm' has topological-sort. + `comlist.scm' has many common list and mapping procedures. + `tree.scm' has functions dealing with trees. + `chap.scm' has functions which compare and create strings in + "chapter order". + + `sc4opt.scm' has optional rev4 procedures. + `sc4sc3.scm' has procedures to make a rev3 implementation run rev4 + code. + `sc2.scm' has rev2 procedures eliminated in subsequent versions. + `mularg.scm' redefines - and / to take more than 2 arguments. + `mulapply.scm' redefines apply to take more than 2 arguments. + `ratize.scm' has function rationalize from Revised^4 spec. + `trnscrpt.scm' has transcript-on and transcript-off from Revised^4 spec. + `withfile.scm' has with-input-from-file and with-output-to-file from R4RS. + `dynwind.scm' has proposed dynamic-wind from R5RS. + `dwindtst.scm' has routines for characterizing dynamic-wind. + `dynamic.scm' has proposed DYNAMIC data type. + `fluidlet.scm' has fluid-let syntax. + `struct.scm' has defmacros which implement RECORDS from the book: + "Essentials of Programming Languages". + `structure.scm' has syntax-case macros for the same. + `structst.scm' has test code for struct.scm. + + INSTALLATION INSTRUCTIONS + + Check the manifest in `README' to find a configuration file for your +Scheme implementation. Initialization files for most IEEE P1178 +compliant Scheme Implementations are included with this distribution. + + If the Scheme implementation supports `getenv', then the value of the +shell environment variable SCHEME_LIBRARY_PATH will be used for +`(library-vicinity)' if it is defined. Currently, Chez, Elk, +MITScheme, scheme->c, VSCM, and SCM support `getenv'. + + You should check the definitions of `software-type', +`scheme-implementation-version', `implementation-vicinity', and +`library-vicinity' in the initialization file. There are comments in +the file for how to configure it. + + Once this is done you can modify the startup file for your Scheme +implementation to `load' this initialization file. SLIB is then +installed. + + Multiple implementations of Scheme can all use the same SLIB +directory. Simply configure each implementation's initialization file +as outlined above. + + The SCM implementation does not require any initialization file as +SLIB support is already built in to SCM. See the documentation with +SCM for installation instructions. + + SLIB includes methods to create heap images for the VSCM and Scheme48 +implementations. The instructions for creating a VSCM image are in +comments in `vscm.init'. To make a Scheme48 image, `cd' to the SLIB +directory and type `make slib48'. This will also create a shell script +with the name `slib48' which will invoke the saved image. + + PORTING INSTRUCTIONS + + If there is no initialization file for your Scheme implementation, you +will have to create one. Your Scheme implementation must be largely +compliant with `IEEE Std 1178-1990' or `Revised^4 Report on the +Algorithmic Language Scheme' to support SLIB. + + `Template.scm' is an example configuration file. The comments inside +will direct you on how to customize it to reflect your system. Give +your new initialization file the implementation's name with `.init' +appended. For instance, if you were porting `foo-scheme' then the +initialization file might be called `foo.init'. + + Your customized version should then be loaded as part of your scheme +implementation's initialization. It will load `require.scm' from the +library; this will allow the use of `provide', `provided?', and +`require' along with the "vicinity" functions. The rest of the +library will then be accessible in a system independent fashion. + + Please mail new working configuration files to `jaffer@ai.mit.edu' so +that they can be included in the SLIB distribution. + + CODING STANDARDS + + All library packages are written in IEEE P1178 Scheme and assume that +a configuration file and `require.scm' package have already been +loaded. Other versions of Scheme can be supported in library packages +as well by using, for example, `(provided? 'rev3-report)' or `(require +'rev3-report)'. + + `require.scm' defines `*catalog*', an association list of module +names and filenames. When a new package is added to the library, an +entry should be added to `require.scm'. Local packages can also be +added to `*catalog*' and even shadow entries already in the table. + + The module name and `:' should prefix each symbol defined in the +package. Definitions for external use should then be exported by having +`(define foo module-name:foo)'. + + Submitted packages should not duplicate routines which are already in +SLIB files. Use `require' to force those features to be supported in +your package. Care should be taken that there are no circularities in +the `require's and `load's between the library packages. + + Documentation should be provided in Emacs Texinfo format if possible, +But documentation must be provided. diff --git a/Template.scm b/Template.scm new file mode 100644 index 0000000..a03b76b --- /dev/null +++ b/Template.scm @@ -0,0 +1,267 @@ +;"Template.scm" configuration template of *features* for Scheme -*-scheme-*- +; Copyright (C) 1991, 1992, 1993 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;; (software-type) should be set to the generic operating system type. +;;; UNIX, VMS, MACOS, AMIGA and MS-DOS are supported. + +(define (software-type) 'UNIX) + +;;; (scheme-implementation-type) should return the name of the scheme +;;; implementation loading this file. + +(define (scheme-implementation-type) 'Template) + +;;; (scheme-implementation-version) should return a string describing +;;; the version the scheme implementation loading this file. + +(define (scheme-implementation-version) "?") + +;;; (implementation-vicinity) should be defined to be the pathname of +;;; the directory where any auxillary files to your Scheme +;;; implementation reside. + +(define (implementation-vicinity) + (case (software-type) + ((UNIX) "/usr/local/src/scheme/") + ((VMS) "scheme$src:") + ((MS-DOS) "C:\\scheme\\"))) + +;;; (library-vicinity) should be defined to be the pathname of the +;;; directory where files of Scheme library functions reside. + +(define library-vicinity + (let ((library-path + (or + ;; Use this getenv if your implementation supports it. + (getenv "SCHEME_LIBRARY_PATH") + ;; Use this path if your scheme does not support GETENV + ;; or if SCHEME_LIBRARY_PATH is not set. + (case (software-type) + ((UNIX) "/usr/local/lib/slib/") + ((VMS) "lib$scheme:") + ((MS-DOS) "C:\\SLIB\\") + (else ""))))) + (lambda () library-path))) + +;;; *FEATURES* should be set to a list of symbols describing features +;;; of this implementation. Suggestions for features are: + +(define *features* + '( + source ;can load scheme source files + ;(slib:load-source "filename") +; compiled ;can load compiled files + ;(slib:load-compiled "filename") +; rev4-report ;conforms to +; rev3-report ;conforms to +; ieee-p1178 ;conforms to +; sicp ;runs code from Structure and + ;Interpretation of Computer + ;Programs by Abelson and Sussman. +; rev4-optional-procedures ;LIST-TAIL, STRING->LIST, + ;LIST->STRING, STRING-COPY, + ;STRING-FILL!, LIST->VECTOR, + ;VECTOR->LIST, and VECTOR-FILL! +; rev2-procedures ;SUBSTRING-MOVE-LEFT!, + ;SUBSTRING-MOVE-RIGHT!, + ;SUBSTRING-FILL!, + ;STRING-NULL?, APPEND!, 1+, + ;-1+, <?, <=?, =?, >?, >=? +; multiarg/and- ;/ and - can take more than 2 args. +; multiarg-apply ;APPLY can take more than 2 args. +; rationalize +; delay ;has DELAY and FORCE +; with-file ;has WITH-INPUT-FROM-FILE and + ;WITH-OUTPUT-FROM-FILE +; string-port ;has CALL-WITH-INPUT-STRING and + ;CALL-WITH-OUTPUT-STRING +; transcript ;TRANSCRIPT-ON and TRANSCRIPT-OFF +; char-ready? +; macro ;has R4RS high level macros +; defmacro ;has Common Lisp DEFMACRO +; eval ;SLIB:EVAL is single argument eval +; record ;has user defined data structures +; values ;proposed multiple values +; dynamic-wind ;proposed dynamic-wind +; ieee-floating-point ;conforms to + full-continuation ;can return multiple times +; object-hash ;has OBJECT-HASH + +; sort +; queue ;queues +; pretty-print +; object->string +; format +; trace ;has macros: TRACE and UNTRACE +; compiler ;has (COMPILER) +; ed ;(ED) is editor +; system ;posix (system <string>) + getenv ;posix (getenv <string>) +; program-arguments ;returns list of strings (argv) +; Xwindows ;X support +; curses ;screen management package +; termcap ;terminal description package +; terminfo ;sysV terminal description +; current-time ;returns time in seconds since 1/1/1970 + )) + +;;; (OUTPUT-PORT-WIDTH <port>) +(define (output-port-width . arg) 79) + +;;; (OUTPUT-PORT-HEIGHT <port>) +(define (output-port-height . arg) 24) + +;;; (CURRENT-ERROR-PORT) +(define current-error-port + (let ((port (current-output-port))) + (lambda () port))) + +;;; (TMPNAM) makes a temporary file name. +(define tmpnam (let ((cntr 100)) + (lambda () (set! cntr (+ 1 cntr)) + (string-append "slib_" (number->string cntr))))) + +;;; (FILE-EXISTS? <string>) +(define (file-exists? f) #f) + +;;; (DELETE-FILE <string>) +(define (delete-file f) #f) + +;;; FORCE-OUTPUT flushes any pending output on optional arg output port +;;; use this definition if your system doesn't have such a procedure. +(define (force-output . arg) #t) + +;;; CALL-WITH-INPUT-STRING and CALL-WITH-OUTPUT-STRING are the string +;;; port versions of CALL-WITH-*PUT-FILE. + +;;; CHAR-CODE-LIMIT is one greater than the largest integer which can +;;; be returned by CHAR->INTEGER. +(define char-code-limit 256) + +;;; MOST-POSITIVE-FIXNUM is used in modular.scm +(define most-positive-fixnum #x0FFFFFFF) + +;;; Return argument +(define (identity x) x) + +;;; If your implementation provides eval SLIB:EVAL is single argument +;;; eval using the top-level (user) environment. +;(define slib:eval eval) + +;;; If your implementation provides R4RS macros: +;(define macro:eval slib:eval) +;(define macro:load load) + +(define *defmacros* + (list (cons 'defmacro + (lambda (name parms . body) + `(set! *defmacros* (cons (cons ',name (lambda ,parms ,@body)) + *defmacros*)))))) +(define (defmacro? m) (and (assq m *defmacros*) #t)) + +(define (macroexpand-1 e) + (if (pair? e) (let ((a (car e))) + (cond ((symbol? a) (set! a (assq a *defmacros*)) + (if a (apply (cdr a) (cdr e)) e)) + (else e))) + e)) + +(define (macroexpand e) + (if (pair? e) (let ((a (car e))) + (cond ((symbol? a) + (set! a (assq a *defmacros*)) + (if a (macroexpand (apply (cdr a) (cdr e))) e)) + (else e))) + e)) + +(define gentemp + (let ((*gensym-counter* -1)) + (lambda () + (set! *gensym-counter* (+ *gensym-counter* 1)) + (string->symbol + (string-append "slib:G" (number->string *gensym-counter*)))))) + +(define base:eval slib:eval) +(define (defmacro:eval x) (base:eval (defmacro:expand* x))) +(define (defmacro:expand* x) + (require 'defmacroexpand) (apply defmacro:expand* x '())) + +(define (defmacro:load <pathname>) + (slib:eval-load <pathname> defmacro:eval)) + +(define (slib:eval-load <pathname> evl) + (if (not (file-exists? <pathname>)) + (set! <pathname> (string-append <pathname> (scheme-file-suffix)))) + (call-with-input-file <pathname> + (lambda (port) + (let ((old-load-pathname *load-pathname*)) + (set! *load-pathname* <pathname>) + (do ((o (read port) (read port))) + ((eof-object? o)) + (evl o)) + (set! *load-pathname* old-load-pathname))))) + +;;; define an error procedure for the library +;(define slib:error error) + +;;; define these as appropriate for your system. +(define slib:tab (integer->char 9)) +(define slib:form-feed (integer->char 12)) + +;;; Support for older versions of Scheme. Not enough code for its own file. +(define (last-pair l) (if (pair? (cdr l)) (last-pair (cdr l)) l)) +(define t #t) +(define nil #f) + +;;; Define these if your implementation's syntax can support it and if +;;; they are not already defined. + +;(define (1+ n) (+ n 1)) +;(define (-1+ n) (+ n -1)) +;(define 1- -1+) + +(define in-vicinity string-append) + +;;; Define SLIB:EXIT to be the implementation procedure to exit or +;;; return if exitting not supported. +(define slib:exit (lambda args #f)) + +;;; Here for backward compatability +(define scheme-file-suffix + (let ((suffix (case (software-type) + ((NOSVE) "_scm") + (else ".scm")))) + (lambda () suffix))) + +;;; (SLIB:LOAD-SOURCE "foo") should load "foo.scm" or with whatever +;;; suffix all the module files in SLIB have. See feature 'SOURCE. + +(define (slib:load-source f) (load (string-append f ".scm"))) + +;;; (SLIB:LOAD-COMPILED "foo") should load the file that was produced +;;; by compiling "foo.scm" if this implementation can compile files. +;;; See feature 'COMPILED. + +(define slib:load-compiled load) + +;;; At this point SLIB:LOAD must be able to load SLIB files. + +(define slib:load slib:load-source) + +(slib:load (in-vicinity (library-vicinity) "require")) diff --git a/alist.scm b/alist.scm new file mode 100644 index 0000000..65ddb22 --- /dev/null +++ b/alist.scm @@ -0,0 +1,66 @@ +;;;"alist.scm", alist functions for Scheme. +;;;Copyright (c) 1992, 1993 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(define (predicate->asso pred) + (cond ((eq? eq? pred) assq) + ((eq? = pred) assv) + ((eq? eqv? pred) assv) + ((eq? char=? pred) assv) + ((eq? equal? pred) assoc) + ((eq? string=? pred) assoc) + (else (lambda (key alist) + (let l ((al alist)) + (cond ((null? al) #f) + ((pred key (caar al)) (car al)) + (else (l (cdr al))))))))) + +(define (alist-inquirer pred) + (let ((assofun (predicate->asso pred))) + (lambda (alist key) + (let ((pair (assofun key alist))) + (and pair (cdr pair)))))) + +(define (alist-associator pred) + (let ((assofun (predicate->asso pred))) + (lambda (alist key val) + (let* ((pair (assofun key alist))) + (cond (pair (set-cdr! pair val) + alist) + (else (cons (cons key val) alist))))))) + +(define (alist-remover pred) + (lambda (alist key) + (cond ((null? alist) alist) + ((pred key (caar alist)) (cdr alist)) + ((null? (cdr alist)) alist) + ((pred key (caadr alist)) + (set-cdr! alist (cddr alist)) alist) + (else + (let l ((al (cdr alist))) + (cond ((null? (cdr al)) alist) + ((pred key (caadr al)) + (set-cdr! al (cddr al)) alist) + (else (l (cdr al))))))))) + +(define (alist-map proc alist) + (map (lambda (pair) (cons (car pair) (proc (car pair) (cdr pair)))) + alist)) + +(define (alist-for-each proc alist) + (for-each (lambda (pair) (proc (car pair) (cdr pair))) alist)) diff --git a/alistab.scm b/alistab.scm new file mode 100644 index 0000000..c8149bf --- /dev/null +++ b/alistab.scm @@ -0,0 +1,227 @@ +;;; "alistab.scm" database tables using association lists (assoc) +; Copyright 1994 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;; LLDB is (filename . alist-table) +;;; HANDLE is (table-name . TABLE) +;;; TABLE is an alist of (Primary-key . ROW) +;;; ROW is a list of non-primary VALUEs + +(define alist-table +(let ((catalog-id 0) + (resources '*base-resources*)) + +(define (make-base filename dim types) + (list filename + (list catalog-id) + (list resources (list 'free-id 1)))) + +(define (open-base infile writable) + (cons (if (input-port? infile) #f infile) + ((lambda (fun) + (if (input-port? infile) + (fun infile) + (call-with-input-file infile fun))) + read))) + +(define (write-base lldb outfile) + ((lambda (fun) + (cond ((output-port? outfile) (fun outfile)) + ((string? outfile) (call-with-output-file outfile fun)) + (else #f))) + (lambda (port) + (display (string-append + ";;; \"" outfile "\" SLIB alist-table database -*-scheme-*-") + port) + (newline port) (newline port) + (display "(" port) (newline port) + (for-each + (lambda (table) + (display " (" port) + (write (car table) port) (newline port) + (for-each + (lambda (row) + (display " " port) (write row port) (newline port)) + (cdr table)) + (display " )" port) (newline port)) + (cdr lldb)) + (display ")" port) (newline port) +; (require 'pretty-print) +; (pretty-print (cdr lldb) port) + (set-car! lldb (if (string? outfile) outfile #f)) + #t))) + +(define (sync-base lldb) + (cond ((car lldb) (write-base lldb (car lldb)) #t) + (else +;;; (display "sync-base: database filename not known") + #f))) + +(define (close-base lldb) + (cond ((car lldb) (write-base lldb (car lldb)) + (set-cdr! lldb #f) + (set-car! lldb #f) #t) + ((cdr lldb) (set-cdr! lldb #f) + (set-car! lldb #f) #t) + (else +;;; (display "close-base: database not open") + #f))) + +(define (make-table lldb dim types) + (let ((free-hand (open-table lldb resources 1 '(atom integer)))) + (and free-hand + (let* ((row (remover free-hand 'free-id)) + (id #f)) + (cond (row + (set! id (car row)) + ((make-putter 1 '(atom integer)) free-hand 'free-id + (list (+ 1 id))) + (set-cdr! lldb (cons (list id) (cdr lldb))) + id) + (else #f)))))) + +(define (open-table lldb base-id dim types) + (assoc base-id (cdr lldb))) + +(define (remover nalist key) + (let ((alist (cdr nalist))) + (cond ((null? alist) #f) + ((equal? key (caar alist)) + (set-cdr! nalist (cdr alist)) + (cdar alist)) + ((null? (cdr alist)) #f) + ((equal? key (caadr alist)) + (set! nalist (cdadr alist)) + (set-cdr! alist (cddr alist)) + nalist) + (else + (let l ((al (cdr alist))) + (cond ((null? (cdr al)) #f) + ((equal? key (caadr al)) + (set! nalist (caadr al)) + (set-cdr! al (cddr al)) + nalist) + (else (l (cdr al))))))))) + +(define (kill-table lldb base-id dim types) + (and (remover lldb base-id) #t)) + +(define handle->base-id car) +(define handle->alist cdr) +(define set-handle-alist! set-cdr!) + +(define (present? handle key) + (assoc key (handle->alist handle))) + +(define (make-putter prinum types) + (lambda (handle ckey restcols) + (let ((row (assoc ckey (handle->alist handle)))) + (cond (row (set-cdr! row restcols)) + (else (set-handle-alist! + handle (cons (cons ckey restcols) + (handle->alist handle)))))))) + +(define (make-getter prinum types) + (lambda (handle ckey) + (let ((row (assoc ckey (handle->alist handle)))) + (and row (cdr row))))) + +(define (make-list-keyifier prinum types) + (if (= 1 prinum) car list->vector)) + +(define (make-keyifier-1 type) + identity) + +(define (make-key->list prinum types) + (cond ((= 1 prinum) list) + (else vector->list))) + +(define (make-key-extractor primary-limit column-type-list index) + (if (= 1 primary-limit) identity + (let ((i (+ -1 index))) + (lambda (v) (vector-ref v i))))) + +(define (for-each-key handle operation) + (for-each (lambda (x) (operation (car x))) (handle->alist handle))) + +(define (map-key handle operation) + (map (lambda (x) (operation (car x))) (handle->alist handle))) + +(define (ordered-for-each-key handle operation) + (define (key->sortable k) + (cond ((number? k) k) + ((string? k) k) + ((symbol? k) (symbol->string k)) + ((vector? k) (map key->sortable (vector->list k))) + (else (slib:error "unsortable key" k)))) + ;; This routine assumes that the car of its operands are either + ;; numbers or strings (or lists of those). + (define (car-key-< x y) + (key-< (car x) (car y))) + (define (key-< x y) + (cond ((and (number? x) (number? y)) (< x y)) + ((number? x) #t) + ((number? y) #f) + ((string? x) (string<? x y)) + ((key-< (car x) (car y)) #t) + ((key-< (car y) (car x)) #f) + (else (key-< (cdr x) (cdr y))))) + (require 'sort) + (for-each operation + (map cdr (sort! (map (lambda (p) (cons (key->sortable (car p)) + (car p))) + (handle->alist handle)) + car-key-<)))) + +(define (supported-type? type) + (case type + ((base-id atom integer boolean string symbol expression) #t) + (else #f))) + +(define (supported-key-type? type) + (case type + ((atom integer symbol string) #t) + (else #f))) + + (lambda (operation-name) + (case operation-name + ((make-base) make-base) + ((open-base) open-base) + ((write-base) write-base) + ((sync-base) sync-base) + ((close-base) close-base) + ((make-table) make-table) + ((open-table) open-table) + ((kill-table) kill-table) + ((make-keyifier-1) make-keyifier-1) + ((make-list-keyifier) make-list-keyifier) + ((make-key->list) make-key->list) + ((make-key-extractor) make-key-extractor) + ((supported-type?) supported-type?) + ((supported-key-type?) supported-key-type?) + ((present?) present?) + ((make-putter) make-putter) + ((make-getter) make-getter) + ((delete) remover) + ((for-each-key) for-each-key) + ((map-key) map-key) + ((ordered-for-each-key) ordered-for-each-key) + ((catalog-id) catalog-id) + (else #f) + )) + )) diff --git a/array.scm b/array.scm new file mode 100644 index 0000000..3eecb7a --- /dev/null +++ b/array.scm @@ -0,0 +1,279 @@ +;;;;"array.scm" Arrays for Scheme +; Copyright (C) 1993 Alan Bawden +; +; Permission to copy this software, to redistribute it, and to use it +; for any purpose is granted, subject to the following restrictions and +; understandings. +; +; 1. Any copy made of this software must include this copyright notice +; in full. +; +; 2. Users of this software agree to make their best efforts (a) to +; return to me any improvements or extensions that they make, so that +; these may be included in future releases; and (b) to inform me of +; noteworthy uses of this software. +; +; 3. I have made no warrantee or representation that the operation of +; this software will be error-free, and I am under no obligation to +; provide any services, by way of maintenance, update, or otherwise. +; +; 4. In conjunction with products arising from the use of this material, +; there shall be no use of my name in any advertising, promotional, or +; sales literature without prior written consent in each case. +; +; Alan Bawden +; MIT Room NE43-510 +; 545 Tech. Sq. +; Cambridge, MA 02139 +; Alan@LCS.MIT.EDU + +(require 'record) + +;(declare (usual-integrations)) + +(define array:rtd + (make-record-type "Array" + '(indexer ; Must be a -linear- function! + shape ; Inclusive bounds: ((lower upper) ...) + vector ; The actual contents + ))) + +(define array:indexer (record-accessor array:rtd 'indexer)) +(define array-shape (record-accessor array:rtd 'shape)) +(define array:vector (record-accessor array:rtd 'vector)) + +(define array? (record-predicate array:rtd)) + +(define (array-rank obj) + (if (array? obj) (length (array-shape obj)) 0)) + +(define (array-dimensions ra) + (map (lambda (ind) (if (zero? (car ind)) (cadr ind) ind)) + (array-shape ra))) + +(define array:construct + (record-constructor array:rtd '(shape vector indexer))) + +(define (array:compute-shape specs) + (map (lambda (spec) + (cond ((and (integer? spec) + (< 0 spec)) + (list 0 (- spec 1))) + ((and (pair? spec) + (pair? (cdr spec)) + (null? (cddr spec)) + (integer? (car spec)) + (integer? (cadr spec)) + (<= (car spec) (cadr spec))) + spec) + (else (slib:error "array: Bad array dimension: " spec)))) + specs)) + +(define (make-array initial-value . specs) + (let ((shape (array:compute-shape specs))) + (let loop ((size 1) + (indexer (lambda () 0)) + (l (reverse shape))) + (if (null? l) + (array:construct shape + (make-vector size initial-value) + (array:optimize-linear-function indexer shape)) + (loop (* size (+ 1 (- (cadar l) (caar l)))) + (lambda (first-index . rest-of-indices) + (+ (* size (- first-index (caar l))) + (apply indexer rest-of-indices))) + (cdr l)))))) + +(define (make-shared-array array mapping . specs) + (let ((new-shape (array:compute-shape specs)) + (old-indexer (array:indexer array))) + (let check ((indices '()) + (bounds (reverse new-shape))) + (cond ((null? bounds) + (array:check-bounds array (apply mapping indices))) + (else + (check (cons (caar bounds) indices) (cdr bounds)) + (check (cons (cadar bounds) indices) (cdr bounds))))) + (array:construct new-shape + (array:vector array) + (array:optimize-linear-function + (lambda indices + (apply old-indexer (apply mapping indices))) + new-shape)))) + +(define (array:in-bounds? array indices) + (let loop ((indices indices) + (shape (array-shape array))) + (if (null? indices) + (null? shape) + (let ((index (car indices))) + (and (not (null? shape)) + (integer? index) + (<= (caar shape) index (cadar shape)) + (loop (cdr indices) (cdr shape))))))) + +(define (array:check-bounds array indices) + (or (array:in-bounds? array indices) + (slib:error "array: Bad indices for " array indices))) + +(define (array-ref array . indices) + (array:check-bounds array indices) + (vector-ref (array:vector array) + (apply (array:indexer array) indices))) + +(define (array-set! array new-value . indices) + (array:check-bounds array indices) + (vector-set! (array:vector array) + (apply (array:indexer array) indices) + new-value)) + +(define (array-in-bounds? array . indices) + (array:in-bounds? array indices)) + +; Fast versions of ARRAY-REF and ARRAY-SET! that do no error checking, +; and don't cons intermediate lists of indices: + +(define (array-1d-ref a i0) + (vector-ref (array:vector a) ((array:indexer a) i0))) + +(define (array-2d-ref a i0 i1) + (vector-ref (array:vector a) ((array:indexer a) i0 i1))) + +(define (array-3d-ref a i0 i1 i2) + (vector-ref (array:vector a) ((array:indexer a) i0 i1 i2))) + +(define (array-1d-set! a v i0) + (vector-set! (array:vector a) ((array:indexer a) i0) v)) + +(define (array-2d-set! a v i0 i1) + (vector-set! (array:vector a) ((array:indexer a) i0 i1) v)) + +(define (array-3d-set! a v i0 i1 i2) + (vector-set! (array:vector a) ((array:indexer a) i0 i1 i2) v)) + +; STOP! Do not read beyond this point on your first reading of +; this code -- you should simply assume that the rest of this file +; contains only the following single definition: +; +; (define (array:optimize-linear-function f l) f) +; +; Of course everything would be pretty inefficient if this were really the +; case, but it isn't. The following code takes advantage of the fact that +; you can learn everything there is to know from a linear function by +; simply probing around in its domain and observing its values -- then a +; more efficient equivalent can be constructed. + +(define (array:optimize-linear-function f l) + (let ((d (length l))) + (cond + ((= d 0) + (array:0d-c (f))) + ((= d 1) + (let ((c (f 0))) + (array:1d-c0 c (- (f 1) c)))) + ((= d 2) + (let ((c (f 0 0))) + (array:2d-c01 c (- (f 1 0) c) (- (f 0 1) c)))) + ((= d 3) + (let ((c (f 0 0 0))) + (array:3d-c012 c (- (f 1 0 0) c) (- (f 0 1 0) c) (- (f 0 0 1) c)))) + (else + (let* ((v (map (lambda (x) 0) l)) + (c (apply f v))) + (let loop ((p v) + (old-val c) + (coefs '())) + (cond ((null? p) + (array:Nd-c* c (reverse coefs))) + (else + (set-car! p 1) + (let ((new-val (apply f v))) + (loop (cdr p) + new-val + (cons (- new-val old-val) coefs))))))))))) + +; 0D cases: + +(define (array:0d-c c) + (lambda () c)) + +; 1D cases: + +(define (array:1d-c c) + (lambda (i0) (+ c i0))) + +(define (array:1d-0 n0) + (cond ((= 1 n0) +) + (else (lambda (i0) (* n0 i0))))) + +(define (array:1d-c0 c n0) + (cond ((= 0 c) (array:1d-0 n0)) + ((= 1 n0) (array:1d-c c)) + (else (lambda (i0) (+ c (* n0 i0)))))) + +; 2D cases: + +(define (array:2d-0 n0) + (lambda (i0 i1) (+ (* n0 i0) i1))) + +(define (array:2d-1 n1) + (lambda (i0 i1) (+ i0 (* n1 i1)))) + +(define (array:2d-c0 c n0) + (lambda (i0 i1) (+ c (* n0 i0) i1))) + +(define (array:2d-c1 c n1) + (lambda (i0 i1) (+ c i0 (* n1 i1)))) + +(define (array:2d-01 n0 n1) + (cond ((= 1 n0) (array:2d-1 n1)) + ((= 1 n1) (array:2d-0 n0)) + (else (lambda (i0 i1) (+ (* n0 i0) (* n1 i1)))))) + +(define (array:2d-c01 c n0 n1) + (cond ((= 0 c) (array:2d-01 n0 n1)) + ((= 1 n0) (array:2d-c1 c n1)) + ((= 1 n1) (array:2d-c0 c n0)) + (else (lambda (i0 i1) (+ c (* n0 i0) (* n1 i1)))))) + +; 3D cases: + +(define (array:3d-01 n0 n1) + (lambda (i0 i1 i2) (+ (* n0 i0) (* n1 i1) i2))) + +(define (array:3d-02 n0 n2) + (lambda (i0 i1 i2) (+ (* n0 i0) i1 (* n2 i2)))) + +(define (array:3d-12 n1 n2) + (lambda (i0 i1 i2) (+ i0 (* n1 i1) (* n2 i2)))) + +(define (array:3d-c12 c n1 n2) + (lambda (i0 i1 i2) (+ c i0 (* n1 i1) (* n2 i2)))) + +(define (array:3d-c02 c n0 n2) + (lambda (i0 i1 i2) (+ c (* n0 i0) i1 (* n2 i2)))) + +(define (array:3d-c01 c n0 n1) + (lambda (i0 i1 i2) (+ c (* n0 i0) (* n1 i1) i2))) + +(define (array:3d-012 n0 n1 n2) + (cond ((= 1 n0) (array:3d-12 n1 n2)) + ((= 1 n1) (array:3d-02 n0 n2)) + ((= 1 n2) (array:3d-01 n0 n1)) + (else (lambda (i0 i1 i2) (+ (* n0 i0) (* n1 i1) (* n2 i2)))))) + +(define (array:3d-c012 c n0 n1 n2) + (cond ((= 0 c) (array:3d-012 n0 n1 n2)) + ((= 1 n0) (array:3d-c12 c n1 n2)) + ((= 1 n1) (array:3d-c02 c n0 n2)) + ((= 1 n2) (array:3d-c01 c n0 n1)) + (else (lambda (i0 i1 i2) (+ c (* n0 i0) (* n1 i1) (* n2 i2)))))) + +; ND cases: + +(define (array:Nd-* coefs) + (lambda indices (apply + (map * coefs indices)))) + +(define (array:Nd-c* c coefs) + (cond ((= 0 c) (array:Nd-* coefs)) + (else (lambda indices (apply + c (map * coefs indices)))))) diff --git a/arraymap.scm b/arraymap.scm new file mode 100644 index 0000000..18ee64a --- /dev/null +++ b/arraymap.scm @@ -0,0 +1,76 @@ +;;;; "arraymap.scm", applicative routines for arrays in Scheme. +;;; Copyright (c) 1993 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'array) + +(define (array-map! ra0 proc . ras) + (define (ramap rshape inds) + (if (null? (cdr rshape)) + (do ((i (cadar rshape) (+ -1 i)) + (is (cons (cadar rshape) inds) + (cons (+ -1 i) inds))) + ((< i (caar rshape))) + (apply array-set! ra0 + (apply proc (map (lambda (ra) (apply array-ref ra is)) + ras)) + is)) + (let ((crshape (cdr rshape)) + (ll (caar rshape))) + (do ((i (cadar rshape) (+ -1 i))) + ((< i ll)) + (ramap crshape (cons i inds)))))) + (ramap (reverse (array-shape ra0)) '())) + +(define (array-for-each proc . ras) + (define (rafe rshape inds) + (if (null? (cdr rshape)) + (do ((i (caar rshape) (+ 1 i))) + ((> i (cadar rshape))) + (apply proc + (map (lambda (ra) + (apply array-ref ra (reverse (cons i inds)))) ras))) + (let ((crshape (cdr rshape)) + (ll (cadar rshape))) + (do ((i (caar rshape) (+ 1 i))) + ((> i ll)) + (rafe crshape (cons i inds)))))) + (rafe (array-shape (car ras)) '())) + +(define (shape->indexes shape) + (define ra0 (apply make-array '() shape)) + (define (ramap rshape inds) + (if (null? (cdr rshape)) + (do ((i (cadar rshape) (+ -1 i)) + (is (cons (cadar rshape) inds) + (cons (+ -1 i) inds))) + ((< i (caar rshape))) + (apply array-set! ra0 is is)) + (let ((crshape (cdr rshape)) + (ll (caar rshape))) + (do ((i (cadar rshape) (+ -1 i))) + ((< i ll)) + (ramap crshape (cons i inds)))))) + (ramap (reverse shape) '()) + ra0) + +(define (array-indexes ra) + (shape->indexes (array-shape ra))) + +(define (array-copy! source dest) + (array-map! dest identity source)) diff --git a/batch.scm b/batch.scm new file mode 100644 index 0000000..685dd3e --- /dev/null +++ b/batch.scm @@ -0,0 +1,417 @@ +;;; "batch.scm" Group and execute commands on various systems. +;Copyright (C) 1994, 1995 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'line-i/o) ;Just for write-line +(require 'parameters) +(require 'database-utilities) + +;;(define (batch parms op . args) ??) + +(define (batch:port parms) + (car (parameter-list-ref parms 'batch-port))) + +(define (batch:dialect parms) ; was batch-family + (car (parameter-list-ref parms 'batch-dialect))) + +(define (batch:line-length-limit parms) + (let ((bl (parameter-list-ref parms 'batch-line-length-limit))) + (cond (bl (car bl)) + (else (case (batch:dialect parms) + ((unix) 1023) + ((dos) 127) + ((vms) 1023) + ((system) 1023) + ((*unknown*) -1)))))) + +(define (batch-line parms str) + (let ((bp (parameter-list-ref parms 'batch-port)) + (ln (batch:line-length-limit parms))) + (cond ((not bp) (slib:error 'batch-line "missing batch-port parameter" + parms)) + ((>= (string-length str) ln) #f) + (else (write-line str (car bp)) #t)))) + +;;; add a Scheme batch-dialect? + +(define (batch:apply-chop-to-fit proc . args) + (define args-but-last (butlast args 1)) + (let loop ((fodder (car (last-pair args)))) + (let ((hlen (quotient (length fodder) 2))) + (cond ((apply proc (append args-but-last (list fodder)))) + ((not (positive? hlen)) + (slib:error 'batch:apply-chop-to-fit "can't split" + (cons proc (append args-but-last (list fodder))))) + (else (loop (nthcdr (+ 1 hlen) fodder)) + (loop (butlast fodder hlen))))))) + +(define (batch:system parms . strings) + (or (apply batch:try-system parms strings) + (slib:error 'batch:system 'failed strings))) + +(define (batch:try-system parms . strings) + (define port (batch:port parms)) + (set! strings (batch:flatten strings)) + (case (batch:dialect parms) + ((unix) (batch-line parms (apply string-join " " strings))) + ((dos) (batch-line parms (apply string-join " " strings))) + ((vms) (batch-line parms (apply string-join " " "$" strings))) + ((system) (write `(system ,(apply string-join " " strings)) port) + (newline port) + (zero? (system (apply string-join " " strings)))) + ((*unknown*) (write `(system ,(apply string-join " " strings)) port) + (newline port) + #f))) + +(define (batch:run-script parms . strings) + (case (batch:dialect parms strings) + ((unix) (batch:system parms strings name)) + ((dos) (batch:system parms strings name)) + ((vms) (batch:system parms (cons #\@ strings))) + ((system) (batch:system parms strings name)) + ((*unknown*) (batch:system parms strings name) + #f))) + +(define (batch:comment parms . lines) + (define port (batch:port parms)) + (set! lines (batch:flatten lines)) + (case (batch:dialect parms) + ((unix) (every (lambda (line) + (batch-line parms (string-append "# " line))) + lines)) + ((dos) (every (lambda (line) + (batch-line parms + (string-append + "rem" (if (equal? " " line) ".") line))) + lines)) + ((vms) (every (lambda (line) + (batch-line parms (string-append "$! " line))) + lines)) + ((system) (every (lambda (line) + (batch-line parms (string-append "; " line))) + lines)) + ((*unknown*) (for-each (lambda (line) + (batch-line parms (string-append ";;; " line)) + (newline port)) + lines) + #f))) + +(define (batch:lines->file parms file . lines) + (define port (batch:port parms)) + (set! lines (batch:flatten lines)) + (case (or (batch:dialect parms) '*unknown*) + ((unix) (batch-line parms (string-append "rm -f " file)) + (every + (lambda (string) + (batch-line parms (string-append "echo '" string "'>>" file))) + lines)) + ((dos) (batch-line parms (string-append "DEL " file)) + (every + (lambda (string) + (batch-line parms + (string-append "ECHO" (if (equal? "" string) "." " ") + string ">>" file))) + lines)) + ((vms) (and (batch-line parms (string-append "$DELETE " file)) + (batch-line parms (string-append "$CREATE " file)) + (batch-line parms (string-append "$DECK")) + (every (lambda (string) (batch-line parms string)) + lines) + (batch-line parms (string-append "$EOD")))) + ((system) (write `(delete-file ,file) port) (newline port) + (delete-file file) + (pretty-print `(call-with-output-file ,file + (lambda (fp) + (for-each + (lambda (string) (write-line string fp)) + ',lines))) + port) + (call-with-output-file file + (lambda (fp) (for-each (lambda (string) (write-line string fp)) + lines))) + #t) + ((*unknown*) + (write `(delete-file ,file) port) (newline port) + (pretty-print + `(call-with-output-file ,file + (lambda (fp) + (for-each + (lambda (string) + (write-line string fp)) + ,lines))) + port) + #f))) + +(define (batch:delete-file parms file) + (define port (batch:port parms)) + (case (batch:dialect parms) + ((unix) (batch-line parms (string-append "rm -f " file)) + #t) + ((dos) (batch-line parms (string-append "DEL " file)) + #t) + ((vms) (batch-line parms (string-append "$DELETE " file)) + #t) + ((system) (write `(delete-file ,file) port) (newline port) + (delete-file file)) ; SLIB provides + ((*unknown*) (write `(delete-file ,file) port) (newline port) + #f))) + +(define (batch:rename-file parms old-name new-name) + (define port (batch:port parms)) + (case (batch:dialect parms) + ((unix) (batch-line parms (string-join " " "mv -f" old-name new-name))) + ((dos) (batch-line parms (string-join " " "MOVE" "/Y" old-name new-name))) + ((vms) (batch-line parms (string-join " " "$RENAME" old-name new-name))) + ((system) (batch:extender 'rename-file batch:rename-file)) + ((*unknown*) (write `(rename-file ,old-name ,new-name) port) + (newline port) + #f))) + +(define (batch:call-with-output-script parms name proc) + (case (batch:dialect parms) + ((unix) ((cond ((string? name) + (lambda (proc) + (let ((ans (call-with-output-file name proc))) + (system (string-append "chmod +x " name)) + ans))) + ((output-port? name) (lambda (proc) (proc name))) + (else (lambda (proc) (proc (current-output-port))))) + (lambda (port) + (write-line "#!/bin/sh" port) + (cond + ((and (string? name) (provided? 'bignum)) + (require 'posix-time) + (write-line + (string-append + "# \"" name "\" build script created " + (ctime (current-time))) + port))) + (proc port)))) + + ((dos) ((cond ((string? name) + (lambda (proc) + (call-with-output-file (string-append name ".bat") proc))) + ((output-port? name) (lambda (proc) (proc name))) + (else (lambda (proc) (proc (current-output-port))))) + (lambda (port) + (cond + ((and (string? name) (provided? 'bignum)) + (require 'posix-time) + (write-line + (string-append + "rem " name + " build script created " + (ctime (current-time))) + port))) + (proc port)))) + + ((vms) ((cond ((string? name) + (lambda (proc) + (call-with-output-file (string-append name ".COM") proc))) + ((output-port? name) (lambda (proc) (proc name))) + (else (lambda (proc) (proc (current-output-port))))) + (lambda (port) + (cond + ((and (string? name) (provided? 'bignum)) + (require 'posix-time) + ;;(write-line + ;; "$DEFINE/USER SYS$OUTPUT BUILD.LOG" port) + (write-line + (string-append + "$! " name + " build script created " + (ctime (current-time))) + port))) + (proc port)))) + + ((system) ((cond ((string? name) + (lambda (proc) + (let ((ans (call-with-output-file name + (lambda (port) (proc name))))) + (system (string-append "chmod +x " name)) + ans))) + ((output-port? name) (lambda (proc) (proc name))) + (else (lambda (proc) (proc (current-output-port))))) + (lambda (port) + (cond + ((and (string? name) (provided? 'bignum)) + (require 'posix-time) + (write-line + (string-append + ";;; \"" name + "\" build script created " (ctime (current-time))) + port))) + (proc port)))) + + ((*unknown*) ((cond ((string? name) + (lambda (proc) + (let ((ans (call-with-output-file name + (lambda (port) (proc name))))) + (system (string-append "chmod +x " name)) + ans))) + ((output-port? name) (lambda (proc) (proc name))) + (else (lambda (proc) (proc (current-output-port))))) + (lambda (port) + (cond + ((and (string? name) (provided? 'bignum)) + (require 'posix-time) + (write-line + (string-append + ";;; \"" name + "\" build script created " (ctime (current-time))) + port))) + (proc port))) + #f))) + +;;; This little ditty figures out how to use a Scheme extension or +;;; SYSTEM to execute a command that is not available in the batch +;;; mode chosen. + +(define (batch:extender NAME BATCHER) + (lambda (parms . args) + (define port (batch:port parms)) + (cond + ((provided? 'i/o-extensions) ; SCM specific + (write `(,NAME ,@args) port) + (newline port) + (apply (slib:eval NAME) args)) + (else + (let ((pl (make-parameter-list (map car parms)))) + (adjoin-parameters! + pl (cons 'batch-dialect (os->batch-dialect + (parameter-list-ref parms 'platform)))) + (system + (call-with-output-string + (lambda (port) + (batch:call-with-output-script + port + (lambda (batch-port) + (define new-parms (copy-tree pl)) + (adjoin-parameters! new-parms (list 'batch-port batch-port)) + (apply BATCHER new-parms args))))))))))) + +(define (replace-suffix str old new) + (define (cs str) + (let* ((len (string-length str)) + (re (- len (string-length old)))) + (cond ((string-ci=? old (substring str re len)) + (string-append (substring str 0 re) new)) + (else + (slib:error 'replace-suffix "suffix doens't match:" + old str))))) + (if (string? str) (cs str) (map cs str))) + +(define (must-be-first firsts lst) + (append (remove-if-not (lambda (i) (member i lst)) firsts) + (remove-if (lambda (i) (member i firsts)) lst))) + +(define (must-be-last lst lasts) + (append (remove-if (lambda (i) (member i lasts)) lst) + (remove-if-not (lambda (i) (member i lst)) lasts))) + +(define (string-join joiner . args) + (if (null? args) "" + (apply string-append + (car args) + (map (lambda (s) (string-append joiner s)) (cdr args))))) + +(define (batch:flatten strings) + (apply + append (map + (lambda (obj) + (cond ((eq? "" obj) '()) + ((string? obj) (list obj)) + ((eq? #f obj) '()) + ((null? obj) '()) + ((list? obj) (batch:flatten obj)) + (else (slib:error 'batch:flatten "unexpected type" + obj "in" strings)))) + strings))) + +(define batch:platform (software-type)) +(cond ((and (eq? 'unix batch:platform) (provided? 'system)) + (let ((file-name (tmpnam))) + (system (string-append "uname > " file-name)) + (set! batch:platform (call-with-input-file file-name read)) + (delete-file file-name)))) + +(define batch:database #f) +(define (os->batch-dialect os) + ((((batch:database 'open-table) 'operating-system #f) + 'get 'os-family) os)) + +(define (batch:initialize! database) + (set! batch:database database) + (define-tables database + + '(batch-dialect + ((family atom)) + () + ((unix) + (dos) + (vms) + (system) + (*unknown*))) + + '(operating-system + ((name symbol)) + ((os-family batch-dialect)) + (;;(3b1 *unknown*) + (acorn *unknown*) + (aix unix) + (alliant *unknown*) + (amiga *unknown*) + (apollo unix) + (apple2 *unknown*) + (arm *unknown*) + (atari.st *unknown*) + (cdc *unknown*) + (celerity *unknown*) + (concurrent *unknown*) + (convex *unknown*) + (encore *unknown*) + (harris *unknown*) + (hp-ux unix) + (hp48 *unknown*) + (isis *unknown*) + (linux unix) + (mac *unknown*) + (masscomp unix) + (ms-dos dos) + (mips *unknown*) + (ncr *unknown*) + (newton *unknown*) + (next unix) + (novell *unknown*) + (os/2 dos) + (prime *unknown*) + (psion *unknown*) + (pyramid *unknown*) + (sequent *unknown*) + (sgi *unknown*) + (stratus *unknown*) + (sun-os unix) + (transputer *unknown*) + (unicos unix) + (unix unix) + (vms vms) + (*unknown* *unknown*) + ))) + + ((database 'add-domain) '(operating-system operating-system #f symbol #f)) + ) diff --git a/break.scm b/break.scm new file mode 100644 index 0000000..e6ba634 --- /dev/null +++ b/break.scm @@ -0,0 +1,151 @@ +;;;; "break.scm" Breakpoints for debugging in Scheme. +;;; Copyright (C) 1991, 1992, 1993, 1995 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'qp) + +;;;; BREAKPOINTS + +;;; Typing (init-debug) at top level sets up a continuation for +;;; breakpoint. When (breakpoint arg1 ...) is then called it returns +;;; from the top level continuation and pushes the continuation from +;;; which it was called on breakpoint:continuation-stack. If +;;; (continue) is later called, it pops the topmost continuation off +;;; of breakpoint:continuation-stack and returns #f to it. + +(define breakpoint:continuation-stack '()) + +(define debug:breakpoint + (let ((call-with-current-continuation call-with-current-continuation) + (apply apply) (qpn qpn) + (cons cons) (length length)) + (lambda args + (apply qpn "BREAKPOINT:" args) + (let ((ans + (call-with-current-continuation + (lambda (x) + (set! breakpoint:continuation-stack + (cons x breakpoint:continuation-stack)) + (debug:top-continuation + (length breakpoint:continuation-stack)))))) + (cond ((not (eq? ans breakpoint:continuation-stack)) ans)))))) + +(define debug:continue + (let ((null? null?) (car car) (cdr cdr)) + (lambda args + (cond ((null? breakpoint:continuation-stack) + (display "; no break to continue from") + (newline)) + (else + (let ((cont (car breakpoint:continuation-stack))) + (set! breakpoint:continuation-stack + (cdr breakpoint:continuation-stack)) + (if (null? args) (cont #f) + (apply cont args)))))))) + +(define debug:top-continuation + (if (provided? 'abort) + (lambda (val) (display val) (newline) (abort)) + (begin (display "; type (init-debug)") #f))) + +(define (init-debug) + (call-with-current-continuation + (lambda (x) (set! debug:top-continuation x)))) + +(define breakpoint debug:breakpoint) +(define bkpt debug:breakpoint) +(define continue debug:continue) + +(define debug:breakf + (let ((null? null?) ;These bindings are so that + (not not) ;breakf will not break on parts + (car car) (cdr cdr) ;of itself. + (eq? eq?) (+ +) (zero? zero?) (modulo modulo) + (apply apply) (display display) (breakpoint debug:breakpoint)) + (lambda (function . optname) +;;; (set! debug:indent 0) + (let ((name (if (null? optname) function (car optname)))) + (lambda args + (cond ((and (not (null? args)) + (eq? (car args) 'debug:unbreak-object) + (null? (cdr args))) + function) + (else + (breakpoint name args) + (apply function args)))))))) + +;;; the reason I use a symbol for debug:unbreak-object is so +;;; that functions can still be unbreaked if this file is read in twice. + +(define (debug:unbreakf function) +;;; (set! debug:indent 0) + (function 'debug:unbreak-object)) + +;;;;The break: functions wrap around the debug: functions to provide +;;; niceties like keeping track of breakd functions and dealing with +;;; redefinition. + +(require 'alist) +(define break:adder (alist-associator eq?)) +(define break:deler (alist-remover eq?)) + +(define *breakd-procedures* '()) +(define (break:breakf fun sym) + (cond ((not (procedure? fun)) + (display "WARNING: not a procedure " (current-error-port)) + (display sym (current-error-port)) + (newline (current-error-port)) + (set! *breakd-procedures* (break:deler *breakd-procedures* sym)) + fun) + (else + (let ((p (assq sym *breakd-procedures*))) + (cond ((and p (eq? (cdr p) fun)) + fun) + (else + (let ((tfun (debug:breakf fun sym))) + (set! *breakd-procedures* + (break:adder *breakd-procedures* sym tfun)) + tfun))))))) + +(define (break:unbreakf fun sym) + (let ((p (assq sym *breakd-procedures*))) + (set! *breakd-procedures* (break:deler *breakd-procedures* sym)) + (cond ((not (procedure? fun)) fun) + ((not p) fun) + ((eq? (cdr p) fun) + (debug:unbreakf fun)) + (else fun)))) + +(define breakf debug:breakf) +(define unbreakf debug:unbreakf) + +;;;; Finally, the macros break and unbreak + +(defmacro break xs + (if (null? xs) + `(begin ,@(map (lambda (x) `(set! ,x (break:breakf ,x ',x))) + (map car *breakd-procedures*)) + (map car *breakd-procedures*)) + `(begin ,@(map (lambda (x) `(set! ,x (break:breakf ,x ',x))) xs)))) +(defmacro unbreak xs + (if (null? xs) + (slib:eval + `(begin ,@(map (lambda (x) `(set! ,x (break:unbreakf ,x ',x))) + (map car *breakd-procedures*)) + '',(map car *breakd-procedures*))) + `(begin ,@(map (lambda (x) `(set! ,x (break:unbreakf ,x ',x))) xs)))) diff --git a/chap.scm b/chap.scm new file mode 100644 index 0000000..ed559c9 --- /dev/null +++ b/chap.scm @@ -0,0 +1,150 @@ +;;;; "chap.scm" Chapter ordering -*-scheme-*- +;;; Copyright 1992, 1993, 1994 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;; The CHAP: functions deal with strings which are ordered like +;;; chapters in a book. For instance, a_9 < a_10 and 4c < 4aa. Each +;;; section of the string consists of consecutive numeric or +;;; consecutive aphabetic characters. + +(define (chap:string<? s1 s2) + (let ((l1 (string-length s1)) + (l2 (string-length s2))) + (define (match-so-far i ctypep) + (cond ((>= i l1) (not (>= i l2))) + ((>= i l2) #f) + (else + (let ((c1 (string-ref s1 i)) + (c2 (string-ref s2 i))) + (cond ((char=? c1 c2) + (if (ctypep c1) + (match-so-far (+ 1 i) ctypep) + (delimited i))) + ((ctypep c1) + (if (ctypep c2) + (length-race (+ 1 i) ctypep (char<? c1 c2)) + #f)) + ((ctypep c2) #t) + (else + (let ((ctype1 (ctype c1))) + (cond + ((and ctype1 (eq? ctype1 (ctype c2))) + (length-race (+ 1 i) ctype1 (char<? c1 c2))) + (else (char<? c1 c2)))))))))) + (define (length-race i ctypep def) + (cond ((>= i l1) (if (>= i l2) def #t)) + ((>= i l2) #f) + (else + (let ((c1 (string-ref s1 i)) + (c2 (string-ref s2 i))) + (cond ((ctypep c1) + (if (ctypep c2) + (length-race (+ 1 i) ctypep def) + #f)) + ((ctypep c2) #t) + (else def)))))) + (define (ctype c1) + (cond + ((char-numeric? c1) char-numeric?) + ((char-lower-case? c1) char-lower-case?) + ((char-upper-case? c1) char-upper-case?) + (else #f))) + (define (delimited i) + (cond ((>= i l1) (not (>= i l2))) + ((>= i l2) #f) + (else + (let* ((c1 (string-ref s1 i)) + (c2 (string-ref s2 i)) + (ctype1 (ctype c1))) + (cond ((char=? c1 c2) + (if ctype1 (match-so-far (+ i 1) ctype1) + (delimited (+ i 1)))) + ((and ctype1 (eq? ctype1 (ctype c2))) + (length-race (+ 1 i) ctype1 (char<? c1 c2))) + (else (char<? c1 c2))))))) + (delimited 0))) + +(define chap:char-incr (- (char->integer #\2) (char->integer #\1))) + +(define (chap:inc-string s p) + (let ((c (string-ref s p))) + (cond ((char=? c #\z) + (string-set! s p #\a) + (cond ((zero? p) (string-append "a" s)) + ((char-lower-case? (string-ref s (+ -1 p))) + (chap:inc-string s (+ -1 p))) + (else + (string-append + (substring s 0 p) + "a" + (substring s p (string-length s)))))) + ((char=? c #\Z) + (string-set! s p #\A) + (cond ((zero? p) (string-append "A" s)) + ((char-upper-case? (string-ref s (+ -1 p))) + (chap:inc-string s (+ -1 p))) + (else + (string-append + (substring s 0 p) + "A" + (substring s p (string-length s)))))) + ((char=? c #\9) + (string-set! s p #\0) + (cond ((zero? p) (string-append "1" s)) + ((char-numeric? (string-ref s (+ -1 p))) + (chap:inc-string s (+ -1 p))) + (else + (string-append + (substring s 0 p) + "1" + (substring s p (string-length s)))))) + ((or (char-alphabetic? c) (char-numeric? c)) + (string-set! s p (integer->char + (+ chap:char-incr + (char->integer (string-ref s p))))) + s) + (else (slib:error "inc-string error" s p))))) + +(define (chap:next-string s) + (do ((i (+ -1 (string-length s)) (+ -1 i))) + ((or (negative? i) + (char-numeric? (string-ref s i)) + (char-alphabetic? (string-ref s i))) + (if (negative? i) (string-append s "0") + (chap:inc-string (string-copy s) i))))) + +;;; testing utilities +;(define (ns s1) (chap:next-string s1)) + +;(define (ts s1 s2) +; (let ((s< (chap:string<? s1 s2)) +; (s> (chap:string<? s2 s1))) +; (cond (s< +; (display s1) +; (display " < ") +; (display s2) +; (newline))) +; (cond (s> +; (display s1) +; (display " > ") +; (display s2) +; (newline))))) + +(define (chap:string>? s1 s2) (chap:string<? s2 s1)) +(define (chap:string>=? s1 s2) (not (chap:string<? s1 s2))) +(define (chap:string<=? s1 s2) (not (chap:string<? s2 s1))) diff --git a/charplot.scm b/charplot.scm new file mode 100644 index 0000000..2a2a49a --- /dev/null +++ b/charplot.scm @@ -0,0 +1,142 @@ +;;;; "charplot.scm", plotting on character devices for Scheme +;;; Copyright (C) 1992, 1993 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'sort) + +(define charplot:rows 24) +(define charplot:columns (output-port-width (current-output-port))) + +(define charplot:xborder #\_) +(define charplot:yborder #\|) +(define charplot:xaxchar #\-) +(define charplot:yaxchar #\:) +(define charplot:curve1 #\*) +(define charplot:xtick #\.) + +(define charplot:height (- charplot:rows 5)) +(define charplot:width (- charplot:columns 15)) + +(define (charplot:printn! n char) + (cond ((positive? n) + (write-char char) + (charplot:printn! (+ n -1) char)))) + +(define (charplot:center-print! str width) + (let ((lpad (quotient (- width (string-length str)) 2))) + (charplot:printn! lpad #\ ) + (display str) + (charplot:printn! (- width (+ (string-length str) lpad)) #\ ))) + +(define (scale-it z scale) + (if (and (exact? z) (integer? z)) + (quotient (* z (car scale)) (cadr scale)) + (inexact->exact (round (/ (* z (car scale)) (cadr scale)))))) + +(define (find-scale isize delta) + (if (inexact? delta) (set! isize (exact->inexact isize))) + (do ((d 1 (* d 10))) + ((<= delta isize) + (do ((n 1 (* n 10))) + ((>= (* delta 10) isize) + (list (* n (cond ((< (* delta 8) isize) 8) + ((< (* delta 6) isize) 6) + ((< (* delta 5) isize) 5) + ((< (* delta 4) isize) 4) + ((< (* delta 3) isize) 3) + ((< (* delta 2) isize) 2) + (else 1))) + d)) + (set! delta (* delta 10)))) + (set! isize (* isize 10)))) + +(define (charplot:iplot! data xlabel ylabel xmin xscale ymin yscale) + (define xaxis (- (scale-it ymin yscale))) + (define yaxis (- (scale-it xmin xscale))) + (charplot:center-print! ylabel 11) + (charplot:printn! (+ charplot:width 1) charplot:xborder) + (newline) + (set! data (sort! data (lambda (x y) (if (= (cdr x) (cdr y)) + (< (car x) (car y)) + (> (cdr x) (cdr y)))))) + (do ((ht (- charplot:height 1) (- ht 1))) + ((negative? ht)) + (let ((a (make-string (+ charplot:width 1) + (if (= ht xaxis) charplot:xaxchar #\ ))) + (ystep (if (= 1 (gcd (car yscale) 3)) 2 3))) + (string-set! a charplot:width charplot:yborder) + (if (< -1 yaxis charplot:width) (string-set! a yaxis charplot:yaxchar)) + (do () + ((or (null? data) (not (>= (cdar data) ht)))) + (string-set! a (caar data) charplot:curve1) + (set! data (cdr data))) + (if (zero? (modulo (- ht xaxis) ystep)) + (let* ((v (number->string (/ (* (- ht xaxis) (cadr yscale)) + (car yscale)))) + (l (string-length v))) + (if (> l 10) + (display (substring v 0 10)) + (begin + (charplot:printn! (- 10 l) #\ ) + (display v))) + (display charplot:yborder) + (display charplot:xaxchar)) + (begin + (charplot:printn! 10 #\ ) + (display charplot:yborder) + (display #\ ))) + (display a) (newline))) + (let* ((xstep (if (= 1 (gcd (car xscale) 3)) 10 12)) + (xstep/2 (quotient (- xstep 2) 2)) + (fudge (modulo yaxis xstep))) + (charplot:printn! 10 #\ ) (display charplot:yborder) + (charplot:printn! (+ 1 fudge) charplot:xborder) + (display charplot:yaxchar) + (do ((i fudge (+ i xstep))) + ((> (+ i xstep) charplot:width) + (charplot:printn! (modulo (- charplot:width (+ i 1)) xstep) + charplot:xborder)) + (charplot:printn! xstep/2 charplot:xborder) + (display charplot:xtick) + (charplot:printn! xstep/2 charplot:xborder) + (display charplot:yaxchar)) + (display charplot:yborder) (newline) + (charplot:center-print! xlabel (+ 12 fudge (- xstep/2))) + (do ((i fudge (+ i xstep))) + ((> (+ i xstep) charplot:width)) + (charplot:center-print! (number->string (/ (* (- i yaxis) (cadr xscale)) + (car xscale))) + xstep)) + (newline))) + +(define (charplot:plot! data xlabel ylabel) + (let* ((xmax (apply max (map car data))) + (xmin (apply min (map car data))) + (xscale (find-scale charplot:width (- xmax xmin))) + (ymax (apply max (map cdr data))) + (ymin (apply min (map cdr data))) + (yscale (find-scale charplot:height (- ymax ymin))) + (ixmin (scale-it xmin xscale)) + (iymin (scale-it ymin yscale))) + (charplot:iplot! (map (lambda (p) + (cons (- (scale-it (car p) xscale) ixmin) + (- (scale-it (cdr p) yscale) iymin))) + data) + xlabel ylabel xmin xscale ymin yscale))) + +(define plot! charplot:plot!) diff --git a/chez.init b/chez.init new file mode 100644 index 0000000..a91cce3 --- /dev/null +++ b/chez.init @@ -0,0 +1,266 @@ +;"chez.init" Initialization file for SLIB for Chez Scheme -*-scheme-*- +; Copyright (C) 1993 dorai@cs.rice.edu (Dorai Sitaram) +; Copyright (C) 1991, 1992, 1993 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;; (software-type) should be set to the generic operating system type. +;;; UNIX, VMS, MACOS, AMIGA and MS-DOS are supported. + +(define (software-type) 'UNIX) + +(define (scheme-implementation-type) 'Chez) + +;;; (scheme-implementation-version) should return a string describing +;;; the version the scheme implementation loading this file. + +(define (scheme-implementation-version) "?") + +(define implementation-vicinity + (lambda () "/usr/local/lib/scheme/")) + +;; library-vicinity is moved below the defination of getenv + +(define *features* + '( + source ;can load scheme source files + ;(slib:load-source "filename") + compiled ;can load compiled files + ;(slib:load-compiled "filename") + char-ready? + delay + dynamic-wind + fluid-let + format + full-continuation + getenv + ieee-p1178 + macro + multiarg/and- + multiarg-apply + pretty-print + random + random-inexact + rationalize + rev3-procedures + rev3-report + rev4-optional-procedures + rev4-report + sort + system + transcript + with-file + string-port + )) + +;R4RS define-syntax in terms of Chez's extend-syntax. +;Caveat: no let-syntax + +(extend-syntax (define-syntax syntax-rules) + ((define-syntax name (syntax-rules kwds . clauses)) + (extend-syntax (name . kwds) . clauses))) + +;DEFINED? +(define-syntax defined? + (syntax-rules () + ((defined? x) (or (bound? 'x) (get 'x '*expander*))))) + +;Chez's sort routines have the opposite parameter order to Slib's +(define chez:sort sort) +(define chez:sort! sort!) +(define chez:merge merge) +(define chez:merge! merge!) + +(define sort + (lambda (s p) + (chez:sort p s))) +(define sort! + (lambda (s p) + (chez:sort! p s))) +(define merge + (lambda (s1 s2 p) + (chez:merge p s1 s2))) +(define merge! + (lambda (s1 s2 p) + (chez:merge! p s1 s2))) + +;RENAME-FILE +(define rename-file + (lambda (src dst) + (system (string-append "mv " src " " dst)))) + +;OUTPUT-PORT-WIDTH +(define output-port-width (lambda arg 79)) + +;;; (OUTPUT-PORT-HEIGHT <port>) +(define (output-port-height . arg) 24) + +;;; (CURRENT-ERROR-PORT) +(define current-error-port + (let ((port (current-output-port))) + (lambda () port))) + +;;; (TMPNAM) makes a temporary file name. +(define tmpnam + (let ((cntr 100)) + (lambda () (set! cntr (+ 1 cntr)) + (let ((tmp (string-append "slib_" (number->string cntr)))) + (if (file-exists? tmp) (tmpnam) tmp))))) + +;GETENV +(provide-foreign-entries '("getenv")) +(define getenv + (foreign-procedure "getenv" + (string) string)) + +(define library-vicinity + (let ((library-path (or (getenv "SCHEME_LIBRARY_PATH") + "/usr/local/lib/slib/"))) + (lambda () library-path))) + +;FORCE-OUTPUT +(define force-output flush-output) + +;;; CALL-WITH-INPUT-STRING and CALL-WITH-OUTPUT-STRING are the string +;;; port versions of CALL-WITH-*PUT-FILE. +(define (call-with-output-string f) + (let ((outsp (open-output-string))) + (f outsp) + (let ((s (get-output-string outsp))) + (close-output-port outsp) + s))) + +(define (call-with-input-string s f) + (let* ((insp (open-input-string s)) + (res (f insp))) + (close-input-port insp) + res)) + +;CHAR-CODE-LIMIT +(define char-code-limit 256) + +;Chez's MOST-POSITIVE-FIXNUM is a thunk rather than a number +(if (procedure? most-positive-fixnum) + (set! most-positive-fixnum (most-positive-fixnum))) + +;;; Return argument +(define (identity x) x) + +(define slib:eval eval) + +(define-macro! defmacro z `(define-macro! ,@z)) + +(define (defmacro? m) (get m '*expander*)) + +(define macroexpand-1 eps-expand-once) + +(define (macroexpand e) + (if (pair? e) (let ((a (car e))) + (if (and (symbol? a) (getprop a '*expander*)) + (macroexpand (expand-once e)) + e)) + e)) + +(define gentemp + (let ((*gensym-counter* -1)) + (lambda () + (set! *gensym-counter* (+ *gensym-counter* 1)) + (string->symbol + (string-append "slib:G" (number->string *gensym-counter*)))))) + +(define defmacro:eval slib:eval) +(define macro:eval slib:eval) + +(define (slib:eval-load <pathname> evl) + (if (not (file-exists? <pathname>)) + (set! <pathname> (string-append <pathname> (scheme-file-suffix)))) + (call-with-input-file <pathname> + (lambda (port) + (let ((old-load-pathname *load-pathname*)) + (set! *load-pathname* <pathname>) + (do ((o (read port) (read port))) + ((eof-object? o)) + (evl o)) + (set! *load-pathname* old-load-pathname))))) + +;Chez's (FORMAT f . a) corresponds to Slib's (FORMAT #f f . a) + +(define chez:format format) +(define format + (lambda (where how . args) + (let ((str (apply chez:format how args))) + (cond ((not where) str) + ((eq? where #t) (display str)) + (else (display str where)))))) + +(define slib:error + (lambda args + (let ((port (current-error-port))) + (display "Error: " port) + (for-each (lambda (x) (display x port)) args) + (error #f "")))) + +(define slib:tab #\tab) +(define slib:form-feed #\page) + +;Chez's nil variable is bound to '() rather than #f + +(define nil #f) + +(define in-vicinity string-append) + +;;; Define SLIB:EXIT to be the implementation procedure to exit or +;;; return if exitting not supported. +(define slib:chez:quit + (let ([arg (call-with-current-continuation (lambda (x) x))]) + (cond [(procedure? arg) arg] + [arg (exit)] + [else (exit 1)]))) + +(define slib:exit + (lambda args + (cond ((null? args) (slib:chez:quit #t)) + ((eqv? #t (car args)) (slib:chez:quit #t)) + ((eqv? #f (car args)) (slib:chez:quit #f)) + ((zero? (car args)) (slib:chez:quit #t)) + (else (slib:chez:quit #f))))) + +;;; Here for backward compatability +;Note however that ".ss" is a common Chez file suffix +(define (scheme-file-suffix) ".scm") + +;;; (SLIB:LOAD-SOURCE "foo") should load "foo.scm" or with whatever +;;; suffix all the module files in SLIB have. See feature 'SOURCE. + +(define (slib:load-source f) (load (string-append f (scheme-file-suffix)))) + +;;; defmacro:load and macro:load also need the default suffix +(define defmacro:load slib:load-source) +(define macro:load slib:load-source) + +;;; (SLIB:LOAD-COMPILED "foo") should load the file that was produced +;;; by compiling "foo.scm" if this implementation can compile files. +;;; See feature 'COMPILED. + +(define slib:load-compiled load) + +;;; At this point SLIB:LOAD must be able to load SLIB files. + +(define slib:load slib:load-source) + +(slib:load (in-vicinity (library-vicinity) "require")) +;end chez.init diff --git a/cltime.scm b/cltime.scm new file mode 100644 index 0000000..248f638 --- /dev/null +++ b/cltime.scm @@ -0,0 +1,74 @@ +;;;; "cltime.scm" Common-Lisp time conversion routines. +;;; Copyright (C) 1994 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'values) +(require 'posix-time) + +(define (get-decoded-time) + (decode-universal-time (get-universal-time))) + +(define (get-universal-time) + (difftime (current-time) time:1900)) + +(define (decode-universal-time utime . tzarg) + (let* ((tz (if (null? tzarg) *timezone* (* 3600 (car tzarg)))) + (tv (time:split + (offset-time time:1900 utime) + (if (null? tzarg) time:daylight 0) + tz + (if (= tz *timezone*) (vector-ref time:tzname time:daylight) + "")))) + (values + (vector-ref tv 0) ;second [0..59] + (vector-ref tv 1) ;minute [0..59] + (vector-ref tv 2) ;hour [0..23] + (vector-ref tv 3) ;date [1..31] + (+ 1 (vector-ref tv 4)) ;month [1..12] + (+ 1900 (vector-ref tv 5)) ;year [0....] + (modulo (+ -1 (vector-ref tv 6)) 7);day-of-week [0..6] (0 is Monday) + (eqv? 1 (vector-ref tv 8)) ;daylight-saving-time? + (if (provided? 'inexact) + (inexact->exact (/ (vector-ref tv 9) 3600)) + (/ (vector-ref tv 9) 3600)) ;time-zone [-24..24] + ))) + +(define time:1900 (time:invert time:gmtime #(0 0 0 1 0 0 #f #f 0 0 "GMT"))) + +(define (encode-universal-time second minute hour date month year . tzarg) + (let* ((tz (if (null? tzarg) *timezone* + (* 3600 (car tzarg)))) + (tv (vector second + minute + hour + date + (+ -1 month) + (+ -1900 year) + #f ;ignored + #f ;ignored + (if (= tz *timezone*) time:daylight 0) + tz + (cond ((= tz *timezone*) + (vector-ref time:tzname time:daylight)) + ((zero? tz) "GMT") + (else "")) + ))) + (if (= tz *timezone*) (difftime (time:invert localtime tv) time:1900) + (difftime (offset-time (time:invert gmtime tv) tz) time:1900)))) + +(tzset) diff --git a/collect.scm b/collect.scm new file mode 100644 index 0000000..abdf209 --- /dev/null +++ b/collect.scm @@ -0,0 +1,236 @@ +;"collect.scm" Sample collection operations +; COPYRIGHT (c) Kenneth Dickey 1992 +; +; This software may be used for any purpose whatever +; without warrantee of any kind. +; AUTHOR Ken Dickey +; DATE 1992 September 1 +; LAST UPDATED 1992 September 2 +; NOTES Expository (optimizations & checks elided). +; Requires YASOS (Yet Another Scheme Object System). + +(require 'yasos) + +(define-operation (collect:collection? obj) + ;; default + (cond + ((or (list? obj) (vector? obj) (string? obj)) #t) + (else #f) +) ) + +(define (collect:empty? collection) (zero? (yasos:size collection))) + +(define-operation (collect:gen-elts <collection>) ;; return element generator + ;; default behavior + (cond ;; see utilities, below, for generators + ((vector? <collection>) (collect:vector-gen-elts <collection>)) + ((list? <collection>) (collect:list-gen-elts <collection>)) + ((string? <collection>) (collect:string-gen-elts <collection>)) + (else + (slib:error "Operation not supported: GEN-ELTS " (yasos:print obj #f))) +) ) + +(define-operation (collect:gen-keys collection) + (if (or (vector? collection) (list? collection) (string? collection)) + (let ( (max+1 (yasos:size collection)) (index 0) ) + (lambda () + (cond + ((< index max+1) + (set! index (collect:add1 index)) + (collect:sub1 index)) + (else (slib:error "no more keys in generator")) + ) ) ) + (slib:error "Operation not handled: GEN-KEYS " collection) +) ) + +(define (collect:do-elts <proc> . <collections>) + (let ( (max+1 (yasos:size (car <collections>))) + (generators (map collect:gen-elts <collections>)) + ) + (let loop ( (counter 0) ) + (cond + ((< counter max+1) + (apply <proc> (map (lambda (g) (g)) generators)) + (loop (collect:add1 counter)) + ) + (else 'unspecific) ; done + ) ) +) ) + +(define (collect:do-keys <proc> . <collections>) + (let ( (max+1 (yasos:size (car <collections>))) + (generators (map collect:gen-keys <collections>)) + ) + (let loop ( (counter 0) ) + (cond + ((< counter max+1) + (apply <proc> (map (lambda (g) (g)) generators)) + (loop (collect:add1 counter)) + ) + (else 'unspecific) ; done + ) ) +) ) + +(define (collect:map-elts <proc> . <collections>) + (let ( (max+1 (yasos:size (car <collections>))) + (generators (map collect:gen-elts <collections>)) + (vec (make-vector (yasos:size (car <collections>)))) + ) + (let loop ( (index 0) ) + (cond + ((< index max+1) + (vector-set! vec index (apply <proc> (map (lambda (g) (g)) generators))) + (loop (collect:add1 index)) + ) + (else vec) ; done + ) ) +) ) + +(define (collect:map-keys <proc> . <collections>) + (let ( (max+1 (yasos:size (car <collections>))) + (generators (map collect:gen-keys <collections>)) + (vec (make-vector (yasos:size (car <collections>)))) + ) + (let loop ( (index 0) ) + (cond + ((< index max+1) + (vector-set! vec index (apply <proc> (map (lambda (g) (g)) generators))) + (loop (collect:add1 index)) + ) + (else vec) ; done + ) ) +) ) + +(define-operation (collect:for-each-key <collection> <proc>) + ;; default + (collect:do-keys <proc> <collection>) ;; talk about lazy! +) + +(define-operation (collect:for-each-elt <collection> <proc>) + (collect:do-elts <proc> <collection>) +) + +(define (collect:reduce <proc> <seed> . <collections>) + (let ( (max+1 (yasos:size (car <collections>))) + (generators (map collect:gen-elts <collections>)) + ) + (let loop ( (count 0) ) + (cond + ((< count max+1) + (set! <seed> + (apply <proc> <seed> (map (lambda (g) (g)) generators))) + (loop (collect:add1 count)) + ) + (else <seed>) + ) ) +) ) + + + +;; pred true for every elt? +(define (collect:every? <pred?> . <collections>) + (let ( (max+1 (yasos:size (car <collections>))) + (generators (map collect:gen-elts <collections>)) + ) + (let loop ( (count 0) ) + (cond + ((< count max+1) + (if (apply <pred?> (map (lambda (g) (g)) generators)) + (loop (collect:add1 count)) + #f) + ) + (else #t) + ) ) +) ) + +;; pred true for any elt? +(define (collect:any? <pred?> . <collections>) + (let ( (max+1 (yasos:size (car <collections>))) + (generators (map collect:gen-elts <collections>)) + ) + (let loop ( (count 0) ) + (cond + ((< count max+1) + (if (apply <pred?> (map (lambda (g) (g)) generators)) + #t + (loop (collect:add1 count)) + )) + (else #f) + ) ) +) ) + + +;; MISC UTILITIES + +(define (collect:add1 obj) (+ obj 1)) +(define (collect:sub1 obj) (- obj 1)) + +;; Nota Bene: list-set! is bogus for element 0 + +(define (collect:list-set! <list> <index> <value>) + + (define (set-loop last this idx) + (cond + ((zero? idx) + (set-cdr! last (cons <value> (cdr this))) + <list> + ) + (else (set-loop (cdr last) (cdr this) (collect:sub1 idx))) + ) ) + + ;; main + (if (zero? <index>) + (cons <value> (cdr <list>)) ;; return value + (set-loop <list> (cdr <list>) (collect:sub1 <index>))) +) + +(add-setter list-ref collect:list-set!) ; for (setter list-ref) + + +;; generator for list elements +(define (collect:list-gen-elts <list>) + (lambda () + (if (null? <list>) + (slib:error "No more list elements in generator") + (let ( (elt (car <list>)) ) + (set! <list> (cdr <list>)) + elt)) +) ) + +;; generator for vector elements +(define (collect:make-vec-gen-elts <accessor>) + (lambda (vec) + (let ( (max+1 (yasos:size vec)) + (index 0) + ) + (lambda () + (cond ((< index max+1) + (set! index (collect:add1 index)) + (<accessor> vec (collect:sub1 index)) + ) + (else #f) + ) ) + ) ) +) + +(define collect:vector-gen-elts (collect:make-vec-gen-elts vector-ref)) + +(define collect:string-gen-elts (collect:make-vec-gen-elts string-ref)) + +;;; exports: + +(define collection? collect:collection?) +(define empty? collect:empty?) +(define gen-keys collect:gen-keys) +(define gen-elts collect:gen-elts) +(define do-elts collect:do-elts) +(define do-keys collect:do-keys) +(define map-elts collect:map-elts) +(define map-keys collect:map-keys) +(define for-each-key collect:for-each-key) +(define for-each-elt collect:for-each-elt) +(define reduce collect:reduce) ; reduce is also in comlist.scm +(define every? collect:every?) +(define any? collect:any?) + +;; --- E O F "collect.oo" --- ;; diff --git a/comlist.scm b/comlist.scm new file mode 100644 index 0000000..2c243fe --- /dev/null +++ b/comlist.scm @@ -0,0 +1,326 @@ +;;"comlist.scm" Implementation of COMMON LISP list functions for Scheme +; Copyright (C) 1991, 1993, 1995 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;; Some of these functions may be already defined in your Scheme. +;;; Comment out those definitions for functions which are already defined. + +;;;; LIST FUNCTIONS FROM COMMON LISP + +;;;From: hugh@ear.mit.edu (Hugh Secker-Walker) +(define (comlist:make-list k . init) + (set! init (if (pair? init) (car init))) + (do ((k k (+ -1 k)) + (result '() (cons init result))) + ((<= k 0) result))) + +(define (comlist:copy-list lst) (append lst '())) + +(define (comlist:adjoin e l) (if (memq e l) l (cons e l))) + +(define (comlist:union l1 l2) + (cond ((null? l1) l2) + ((null? l2) l1) + (else (comlist:union (cdr l1) (comlist:adjoin (car l1) l2))))) + +(define (comlist:intersection l1 l2) + (cond ((null? l1) l1) + ((null? l2) l2) + ((memv (car l1) l2) (cons (car l1) (comlist:intersection (cdr l1) l2))) + (else (comlist:intersection (cdr l1) l2)))) + +(define (comlist:set-difference l1 l2) + (cond ((null? l1) l1) + ((memv (car l1) l2) (comlist:set-difference (cdr l1) l2)) + (else (cons (car l1) (comlist:set-difference (cdr l1) l2))))) + +(define (comlist:position obj lst) + (letrec ((pos (lambda (n lst) + (cond ((null? lst) #f) + ((eqv? obj (car lst)) n) + (else (pos (+ 1 n) (cdr lst))))))) + (pos 0 lst))) + +(define (comlist:reduce-init p init l) + (if (null? l) + init + (comlist:reduce-init p (p init (car l)) (cdr l)))) + +(define (comlist:reduce p l) + (cond ((null? l) l) + ((null? (cdr l)) (car l)) + (else (comlist:reduce-init p (car l) (cdr l))))) + +(define (comlist:some pred l . rest) + (cond ((null? rest) + (let mapf ((l l)) + (and (not (null? l)) + (or (pred (car l)) (mapf (cdr l)))))) + (else (let mapf ((l l) (rest rest)) + (and (not (null? l)) + (or (apply pred (car l) (map car rest)) + (mapf (cdr l) (map cdr rest)))))))) + +(define (comlist:every pred l . rest) + (cond ((null? rest) + (let mapf ((l l)) + (or (null? l) + (and (pred (car l)) (mapf (cdr l)))))) + (else (let mapf ((l l) (rest rest)) + (or (null? l) + (and (apply pred (car l) (map car rest)) + (mapf (cdr l) (map cdr rest)))))))) + +(define (comlist:notany pred . ls) (not (apply comlist:some pred ls))) + +(define (comlist:notevery pred . ls) (not (apply comlist:every pred ls))) + +(define (comlist:find-if t l) + (cond ((null? l) #f) + ((t (car l)) (car l)) + (else (comlist:find-if t (cdr l))))) + +(define (comlist:member-if t l) + (cond ((null? l) #f) + ((t (car l)) l) + (else (comlist:member-if t (cdr l))))) + +(define (comlist:remove p l) + (cond ((null? l) l) + ((eqv? p (car l)) (comlist:remove p (cdr l))) + (else (cons (car l) (comlist:remove p (cdr l)))))) + +(define (comlist:remove-if p l) + (cond ((null? l) l) + ((p (car l)) (comlist:remove-if p (cdr l))) + (else (cons (car l) (comlist:remove-if p (cdr l)))))) + +(define (comlist:remove-if-not p l) + (cond ((null? l) l) + ((p (car l)) (cons (car l) (comlist:remove-if-not p (cdr l)))) + (else (comlist:remove-if-not p (cdr l))))) + +(define comlist:nconc + (if (provided? 'rev2-procedures) append! + (lambda args + (cond ((null? args) '()) + ((null? (cdr args)) (car args)) + ((null? (car args)) (apply comlist:nconc (cdr args))) + (else + (set-cdr! (last-pair (car args)) + (apply comlist:nconc (cdr args))) + (car args)))))) + +;;;From: hugh@ear.mit.edu (Hugh Secker-Walker) +(define (comlist:nreverse rev-it) +;;; Reverse order of elements of LIST by mutating cdrs. + (cond ((null? rev-it) rev-it) + ((not (list? rev-it)) + (slib:error "nreverse: Not a list in arg1" rev-it)) + (else (do ((reved '() rev-it) + (rev-cdr (cdr rev-it) (cdr rev-cdr)) + (rev-it rev-it rev-cdr)) + ((begin (set-cdr! rev-it reved) (null? rev-cdr)) rev-it))))) + +(define (comlist:butlast lst n) + (letrec ((l (- (length lst) n)) + (bl (lambda (lst n) + (cond ((null? lst) lst) + ((positive? n) + (cons (car lst) (bl (cdr lst) (+ -1 n)))) + (else '()))))) + (bl lst (if (negative? n) + (slib:error "negative argument to butlast" n) + l)))) + +(define (comlist:nthcdr n lst) + (if (zero? n) lst (comlist:nthcdr (+ -1 n) (cdr lst)))) + +(define (comlist:last lst n) + (comlist:nthcdr (- (length lst) n) lst)) + +;;;; CONDITIONALS + +(define (comlist:and? . args) + (cond ((null? args) #t) + ((car args) (apply comlist:and? (cdr args))) + (else #f))) + +(define (comlist:or? . args) + (cond ((null? args) #f) + ((car args) #t) + (else (apply comlist:or? (cdr args))))) + +; Checks to see if a list has any duplicates. +(define (comlist:has-duplicates? lst) + (cond ((null? lst) #f) + ((member (car lst) (cdr lst)) #t) + (else (comlist:has-duplicates? (cdr lst))))) + +(define (comlist:list* x . y) + (define (list*1 x) + (if (null? (cdr x)) + (car x) + (cons (car x) (list*1 (cdr x))))) + (if (null? y) + x + (cons x (list*1 y)))) + +(define (comlist:atom? a) + (not (pair? a))) + +(define (type-of obj) + (cond + ((null? obj) 'null) + ((boolean? obj) 'boolean) + ((char? obj) 'char) + ((number? obj) 'number) + ((string? obj) 'string) + ((symbol? obj) 'symbol) + ((input-port? obj) 'port) + ((output-port? obj) 'port) + ((procedure? obj) 'procedure) + ((eof-object? obj) 'eof-object) + ((list? obj) 'list) + ((pair? obj) 'pair) + ((and (provided? 'array) (array? obj)) 'array) + ((and (provided? 'record) (record? obj)) 'record) + ((vector? obj) 'vector) + (else '?))) + +(define (coerce obj result-type) + (define (err) (slib:error 'coerce "couldn't" obj '-> result-type)) + (define obj-type (type-of obj)) + (cond + ((eq? obj-type result-type) obj) + (else + (case obj-type + ((char) (case result-type + ((number) (char->integer obj)) + ((string) (string obj)) + ((symbol) (string->symbol (string obj))) + ((list) (list obj)) + ((vector) (vector obj)) + (else (err)))) + ((number) (case result-type + ((char) (integer->char obj)) + ((atom) obj) + ((string) (number->string obj)) + ((symbol) (string->symbol (number->string obj))) + ((list) (string->list (number->string obj))) + ((vector) (list->vector (string->list (number->string obj)))) + (else (err)))) + ((string) (case result-type + ((char) (if (= 1 (string-length obj)) (string-ref obj 0) + (err))) + ((atom) (or (string->number obj) (string->symbol obj))) + ((number) (or (string->number obj) (err))) + ((symbol) (string->symbol obj)) + ((list) (string->list obj)) + ((vector) (list->vector (string->list obj))) + (else (err)))) + ((symbol) (case result-type + ((char) (coerce (symbol->string obj) 'char)) + ((number) (coerce (symbol->string obj) 'number)) + ((string) (symbol->string obj)) + ((atom) obj) + ((list) (string->list (symbol->string obj))) + ((vector) (list->vector (string->list (symbol->string obj)))) + (else (err)))) + ((list) (case result-type + ((char) (if (and (= 1 (length obj)) + (char? (car obj))) + (car obj) + (err))) + ((number) (or (string->number (list->string obj)) (err))) + ((string) (list->string obj)) + ((symbol) (string->symbol (list->string obj))) + ((vector) (list->vector obj)) + (else (err)))) + ((vector) (case result-type + ((char) (if (and (= 1 (vector-length obj)) + (char? (vector-ref obj 0))) + (vector-ref obj 0) + (err))) + ((number) (or (string->number (coerce obj string)) (err))) + ((string) (list->string (vector->list obj))) + ((symbol) (string->symbol (coerce obj string))) + ((list) (list->vector obj)) + (else (err)))) + (else (err)))))) + +(define (comlist:delete obj list) + (let delete ((list list)) + (cond ((null? list) '()) + ((equal? obj (car list)) (delete (cdr list))) + (else + (set-cdr! list (delete (cdr list))) + list)))) + +(define (comlist:delete-if pred list) + (let delete-if ((list list)) + (cond ((null? list) '()) + ((pred (car list)) (delete-if (cdr list))) + (else + (set-cdr! list (delete-if (cdr list))) + list)))) + +(define (comlist:delete-if-not pred list) + (let delete-if ((list list)) + (cond ((null? list) '()) + ((not (pred (car list))) (delete-if (cdr list))) + (else + (set-cdr! list (delete-if (cdr list))) + list)))) + +;;; exports + +(define make-list comlist:make-list) +(define copy-list comlist:copy-list) +(define adjoin comlist:adjoin) +(define union comlist:union) +(define intersection comlist:intersection) +(define set-difference comlist:set-difference) +(define position comlist:position) +(define reduce-init comlist:reduce-init) +(define reduce comlist:reduce) ; reduce is also in collect.scm +(define some comlist:some) +(define every comlist:every) +(define notevery comlist:notevery) +(define notany comlist:notany) +(define find-if comlist:find-if) +(define member-if comlist:member-if) +(define remove comlist:remove) +(define remove-if comlist:remove-if) +(define remove-if-not comlist:remove-if-not) +(define nconc comlist:nconc) +(define nreverse comlist:nreverse) +(define butlast comlist:butlast) +(define nthcdr comlist:nthcdr) +(define last comlist:last) +(define and? comlist:and?) +(define or? comlist:or?) +(define has-duplicates? comlist:has-duplicates?) + +(define delete-if-not comlist:delete-if-not) +(define delete-if comlist:delete-if) +(define delete comlist:delete) +(define comlist:atom comlist:atom?) +(define atom comlist:atom?) +(define atom? comlist:atom?) +(define list* comlist:list*) diff --git a/comparse.scm b/comparse.scm new file mode 100644 index 0000000..add47c8 --- /dev/null +++ b/comparse.scm @@ -0,0 +1,92 @@ +;;; "comparse.scm" Break command line into arguments. +;Copyright (C) 1995 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;;; This is a simple command-line reader. It could be made fancier +;;; to handle lots of `shell' syntaxes. + +(require 'string-port) +(define (read-command . port) + (define argv '()) + (define obj "") + (define chars '()) + (define eof #f) + (define readc (lambda () (read-char port))) + (define peekc (lambda () (peek-char port))) + (define s-expression + (lambda () + (splice-arg (call-with-output-string + (lambda (p) (display (slib:eval (read port)) p)))))) + (define (backslash goto) + (readc) + (cond ((char=? #\newline (peekc)) (readc) (goto (peekc))) + (else (set! chars (cons (readc) chars)) + (build-token (peekc))))) + (define loop + (lambda (c) + (case c + ((#\\) (backslash loop)) + ((#\") (splice-arg (read port))) + ((#\( #\') (s-expression)) + ((#\#) + (do ((c (readc) (readc))) + ((or (eof-object? c) (char=? #\newline c) c)))) + ((#\; #\newline) (readc)) + (else + (cond ((eof-object? c) c) + ((char-whitespace? c) (readc) (loop (peekc))) + (else (build-token c))))))) + (define splice-arg + (lambda (arg) + (set! obj (string-append obj (list->string (reverse chars)) arg)) + (set! chars '()) + (build-token (peekc)))) + (define build-token + (lambda (c) + (case c + ((#\") (splice-arg (read port))) + ((#\() (s-expression)) + ((#\\) (backslash build-token)) + ((#\newline #\;) + (readc) + (set! argv (cons (string-append + obj (list->string (reverse chars))) + argv))) + (else + (cond ((or (eof-object? c) + (char-whitespace? c)) + (readc) + (set! argv (cons (string-append + obj (list->string (reverse chars))) + argv)) + (set! obj "") + (set! chars '()) + (loop (peekc))) + (else (set! chars (cons (readc) chars)) + (build-token (peekc)))))))) + (set! port + (cond ((null? port) (current-input-port)) + ((= 1 (length port)) (car port)) + (else + (slib:error + 'read-command-line + "Wrong Number of ARGs:" + port)))) + (let ((c (loop (peekc)))) + (cond ((and (null? argv) (eof-object? c)) c) + (else (reverse argv))))) diff --git a/dbrowse.scm b/dbrowse.scm new file mode 100644 index 0000000..aaa4635 --- /dev/null +++ b/dbrowse.scm @@ -0,0 +1,98 @@ +;;; "dbrowse.scm" relational-database-browser +; Copyright 1996 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'database-utilities) +(require 'printf) + +(define browse:db #f) + +(define (browse . args) + (define table-name '*catalog-data*) + (cond ((null? args)) + ((procedure? (car args)) + (set! browse:db (car args)) + (set! args (cdr args))) + ((string? (car args)) + (set! browse:db (open-database (car args))) + (set! args (cdr args)))) + (cond ((null? args)) + (else (set! table-name (car args)))) + (let* ((open-table (browse:db 'open-table)) + (catalog (and open-table (open-table '*catalog-data* #f)))) + (cond ((not catalog) + (slib:error 'browse "could not open catalog")) + ((eq? table-name '*catalog-data*) + (browse:display-dir '*catalog-data* catalog)) + (else + (let ((table (open-table table-name #f))) + (cond (table (browse:display-table table-name table) + (table 'close-table)) + (else (slib:error 'browse "could not open table" + table-name)))))))) + +(define (browse:display-dir table-name table) + (printf "%s Tables: +" table-name) + ((table 'for-each-row) + (lambda (row) + (printf " %s +" + (car row))))) + +(define (browse:display-table table-name table) + (let* ((width 18) + (dw (string-append "%-" (number->string width))) + (dwp (string-append "%-" (number->string width) "." + (number->string (+ -1 width)))) + (dwp-string (string-append dwp "s")) + (dwp-any (string-append dwp "a")) + (dw-integer (string-append dw "d")) + (underline (string-append (make-string (+ -1 width) #\=) " ")) + (form "")) + (printf "Table: %s +" table-name) + (for-each (lambda (name) (printf dwp-string name)) + (table 'column-names)) + (newline) + (for-each (lambda (foreign) (printf dwp-any foreign)) + (table 'column-foreigns)) + (newline) + (for-each (lambda (domain) (printf dwp-string domain)) + (table 'column-domains)) + (newline) + (for-each (lambda (type) + (case type + ((integer number uint base-id) + (set! form (string-append form dw-integer))) + ((boolean domain expression atom) + (set! form (string-append form dwp-any))) + ((string symbol) + (set! form (string-append form dwp-string))) + (else (slib:error 'browse:display-table "unknown type" type))) + (printf dwp-string type)) + (table 'column-types)) + (newline) + (set! form (string-append form " +")) + (for-each (lambda (domain) (printf underline)) + (table 'column-domains)) + (newline) + ((table 'for-each-row) + (lambda (row) + (apply printf form row))))) diff --git a/dbutil.scm b/dbutil.scm new file mode 100644 index 0000000..ffaaf9d --- /dev/null +++ b/dbutil.scm @@ -0,0 +1,222 @@ +;;; "dbutil.scm" relational-database-utilities +; Copyright 1994, 1995 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'relational-database) + +(define (db:base-type path) + 'alist-table) ; currently the only one. + +(define (dbutil:wrap-command-interface rdb) + (and rdb + (let* ((rdms:commands ((rdb 'open-table) '*commands* #f)) + (command:get + (and rdms:commands (rdms:commands 'get 'procedure)))) + (and command:get + (letrec ((wdb (lambda (command) + (let ((com (command:get command))) + (cond (com ((slib:eval com) wdb)) + (else (rdb command))))))) + (let ((init (wdb '*initialize*))) + (if (procedure? init) init wdb))))))) + +(define (dbutil:open-database! path . arg) + (let ((type (if (null? arg) (db:base-type path) (car arg)))) + (require type) + (dbutil:wrap-command-interface + (((make-relational-system (slib:eval type)) 'open-database) + path #t)))) + +(define (dbutil:open-database path . arg) + (let ((type (if (null? arg) (db:base-type path) (car arg)))) + (require type) + (dbutil:wrap-command-interface + (((make-relational-system (slib:eval type)) 'open-database) + path #f)))) + +(define (dbutil:create-database path type) + (require type) + (let ((rdb (((make-relational-system (slib:eval type)) 'create-database) + path))) + (dbutil:define-tables + rdb + '(parameter-arity + ((name symbol)) + ((predicate? expression) + (procedure expression)) + ((single (lambda (a) (and (pair? a) (null? (cdr a)))) car) + (optional + (lambda (lambda (a) (or (null? a) (and (pair? a) (null? (cdr a)))))) + identity) + (boolean + (lambda (a) (or (null? a) + (and (pair? a) (null? (cdr a)) (boolean? (car a))))) + (lambda (a) (if (null? a) #f (car a)))) + (nary (lambda (a) #t) identity) + (nary1 (lambda (a) (not (null? a))) identity)))) + (for-each (((rdb 'open-table) '*domains-data* #t) 'row:insert) + '((parameter-list *catalog-data* #f symbol #f) + (parameter-name-translation *catalog-data* #f symbol #f) + (parameter-arity parameter-arity #f symbol #f))) + (dbutil:define-tables + rdb + '(*parameter-columns* + *columns* + *columns* + ((1 #t index #f uint) + (2 #f name #f symbol) + (3 #f arity #f parameter-arity) + (4 #f domain #f domain) + (5 #f default #f expression) + (6 #f expander #f expression) + (7 #f documentation #f string))) + '(no-parameters + *parameter-columns* + *parameter-columns* + ()) + '(no-parameter-names + ((name string)) + ((parameter-index uint)) + ()) + '(*commands* + ((name symbol)) + ((parameters parameter-list) + (parameter-names parameter-name-translation) + (procedure expression) + (documentation string)) + ((domain-checker + no-parameters + no-parameter-names + (lambda (rdb) + (let* ((ro:domains ((rdb 'open-table) '*domains-data* #f)) + (ro:get-dir (ro:domains 'get 'domain-integrity-rule)) + (ro:for-tab (ro:domains 'get 'foreign-table))) + (lambda (domain) + (let ((fkname (ro:for-tab domain)) + (dir (slib:eval (ro:get-dir domain)))) + (cond (fkname (let* ((fktab ((rdb 'open-table) fkname #f)) + (p? (fktab 'get 1))) + (cond (dir (lambda (e) (and (dir e) (p? e)))) + (else p?)))) + (else dir)))))) + "return procedure to check given domain name") + + (add-domain + no-parameters + no-parameter-names + (lambda (rdb) + (((rdb 'open-table) '*domains-data* #t) 'row:insert)) + "given the row describing it, add a domain") + + (delete-domain + no-parameters + no-parameter-names + (lambda (rdb) + (((rdb 'open-table) '*domains-data* #t) 'row:remove)) + "given its name, delete a domain")))) + (dbutil:wrap-command-interface rdb))) + +(define (make-command-server rdb command-table) + (let* ((comtab ((rdb 'open-table) command-table #f)) + (names (comtab 'column-names)) + (row-ref (lambda (row name) (list-ref row (position name names)))) + (comgetrow (comtab 'row:retrieve))) + (lambda (comname command-callback) + (let* ((command:row (comgetrow comname)) + (parameter-table ((rdb 'open-table) + (row-ref command:row 'parameters) #f)) + (parameter-names + ((rdb 'open-table) (row-ref command:row 'parameter-names) #f)) + (comval ((slib:eval (row-ref command:row 'procedure)) rdb)) + (options ((parameter-table 'get* 'name))) + (positions ((parameter-table 'get* 'index))) + (arities ((parameter-table 'get* 'arity))) + (defaults (map slib:eval ((parameter-table 'get* 'default)))) + (domains ((parameter-table 'get* 'domain))) + (types (map (((rdb 'open-table) '*domains-data* #f) 'get 'type-id) + domains)) + (dirs (map (rdb 'domain-checker) domains)) + (aliases + (map list ((parameter-names 'get* 'name)) + (map (parameter-table 'get 'name) + ((parameter-names 'get* 'parameter-index)))))) + (command-callback comname comval options positions + arities types defaults dirs aliases))))) + +(define (dbutil:define-tables rdb . spec-list) + (define new-tables '()) + (define dom:typ (((rdb 'open-table) '*domains-data* #f) 'get 4)) + (define create-table (rdb 'create-table)) + (define open-table (rdb 'open-table)) + (define table-exists? (rdb 'table-exists?)) + (define (check-domain dname) + (cond ((dom:typ dname)) + ((member dname new-tables) + (let* ((ftab (open-table + (string->symbol + (string-append "desc:" (symbol->string dname))) + #f))) + ((((rdb 'open-table) '*domains-data* #t) 'row:insert) + (list dname dname #f + (dom:typ ((ftab 'get 'domain-name) 1)) #f)))))) + (define (define-table name prikeys slots data) + (cond + ((table-exists? name) + (let* ((tab (open-table name #t)) + (row:update (tab 'row:update))) + (for-each row:update data))) + ((and (symbol? prikeys) (eq? prikeys slots)) + (cond ((not (table-exists? slots)) + (slib:error "Table doesn't exist:" slots))) + (set! new-tables (cons name new-tables)) + (let* ((tab (create-table name slots)) + (row:insert (tab 'row:insert))) + (for-each row:insert data) + ((tab 'close-table)))) + (else + (let* ((descname + (string->symbol (string-append "desc:" (symbol->string name)))) + (tab (create-table descname)) + (row:insert (tab 'row:insert)) + (j 0)) + (set! new-tables (cons name new-tables)) + (for-each (lambda (des) + (set! j (+ 1 j)) + (check-domain (cadr des)) + (row:insert (list j #t (car des) + (if (null? (cddr des)) #f (caddr des)) + (cadr des)))) + prikeys) + (for-each (lambda (des) + (set! j (+ 1 j)) + (check-domain (cadr des)) + (row:insert (list j #f (car des) + (if (null? (cddr des)) #f (caddr des)) + (cadr des)))) + slots) + ((tab 'close-table)) + (set! tab (create-table name descname)) + (set! row:insert (tab 'row:insert)) + (for-each row:insert data) + ((tab 'close-table)))))) + (for-each (lambda (spec) (apply define-table spec)) spec-list)) + +(define create-database dbutil:create-database) +(define open-database! dbutil:open-database!) +(define open-database dbutil:open-database) +(define define-tables dbutil:define-tables) diff --git a/debug.scm b/debug.scm new file mode 100644 index 0000000..08406a9 --- /dev/null +++ b/debug.scm @@ -0,0 +1,78 @@ +;;;; "debug.scm" Utility functions for debugging in Scheme. +;;; Copyright (C) 1991, 1992, 1993, 1995 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'trace) +(require 'break) + +(define (for-each-top-level-definition-in-file file proc) + (call-with-input-file + file + (lambda + (port) + (letrec + ((walk + (lambda (exp) + (cond + ((not (and (pair? exp) (list? exp)))) + ((not (symbol? (car exp)))) + (else + (case (car exp) + ((begin) (for-each walk (cdr exp))) + ((cond) (for-each + (lambda (exp) + (for-each walk + (if (list? (car exp)) exp (cdr exp)))) + (cdr exp))) + ((if) (for-each + walk + (if (list? (cadr exp)) (cdr exp) (cddr exp)))) + ((defmacro define-syntax) "should do something clever here") + ((define) + (proc exp)))))))) + (do ((form (read port) (read port))) + ((eof-object? form)) + (walk form)))))) + +(define (for-each-top-level-defined-procedure-symbol-in-file file proc) + (letrec ((get-defined-symbol + (lambda (form) + (if (pair? form) + (get-defined-symbol (car form)) + form)))) + (for-each-top-level-definition-in-file + file + (lambda (form) (let ((sym (get-defined-symbol (cadr form)))) + (cond ((procedure? (slib:eval sym)) + (proc sym)))))))) + +(define (debug:trace-all file) + (for-each-top-level-defined-procedure-symbol-in-file + file + (lambda (sym) + (slib:eval `(set! ,sym (trace:tracef ,sym ',sym)))))) + +(define trace-all debug:trace-all) + +(define (debug:break-all file) + (for-each-top-level-defined-procedure-symbol-in-file + file + (lambda (sym) + (slib:eval `(set! ,sym (break:breakf ,sym ',sym)))))) + +(define break-all debug:break-all) diff --git a/defmacex.scm b/defmacex.scm new file mode 100644 index 0000000..bdaf020 --- /dev/null +++ b/defmacex.scm @@ -0,0 +1,96 @@ +;;;"defmacex.scm" defmacro:expand* for any Scheme dialect. +;;;Copyright 1993-1994 Dorai Sitaram and Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;;expand thoroughly, not just topmost expression. While expanding +;;;subexpressions, the primitive forms quote, lambda, set!, let/*/rec, +;;;cond, case, do, quasiquote: need to be destructured properly. (if, +;;;and, or, begin: don't need special treatment.) + +(define (defmacro:iqq e depth) + (letrec + ((map1 (lambda (f x) + (if (pair? x) (cons (f (car x)) (map1 f (cdr x))) + x))) + (iqq (lambda (e depth) + (if (pair? e) + (case (car e) + ((quasiquote) (list (car e) (iqq (cadr e) (+ 1 depth)))) + ((unquote unquote-splicing) + (list (car e) (if (= 1 depth) + (defmacro:expand* (cadr e)) + (iqq (cadr e) (+ -1 depth))))) + (else (map1 (lambda (e) (iqq e depth)) e))) + e)))) + (iqq e depth))) + +(define (defmacro:expand* e) + (if (pair? e) + (let* ((c (macroexpand-1 e))) + (if (not (eq? e c)) + (defmacro:expand* c) + (case (car e) + ((quote) e) + ((quasiquote) (defmacro:iqq e 0)) + ((lambda define set!) + (cons (car e) (cons (cadr e) (map defmacro:expand* (cddr e))))) + ((let) + (let ((b (cadr e))) + (if (symbol? b) ;named let + `(let ,b + ,(map (lambda (vv) + `(,(car vv) + ,(defmacro:expand* (cadr vv)))) + (caddr e)) + ,@(map defmacro:expand* + (cdddr e))) + `(let + ,(map (lambda (vv) + `(,(car vv) + ,(defmacro:expand* (cadr vv)))) + b) + ,@(map defmacro:expand* + (cddr e)))))) + ((let* letrec) + `(,(car e) ,(map (lambda (vv) + `(,(car vv) + ,(defmacro:expand* (cadr vv)))) + (cadr e)) + ,@(map defmacro:expand* (cddr e)))) + ((cond) + `(cond + ,@(map (lambda (c) + (map defmacro:expand* c)) + (cdr e)))) + ((case) + `(case ,(defmacro:expand* (cadr e)) + ,@(map (lambda (c) + `(,(car c) + ,@(map defmacro:expand* (cdr c)))) + (cddr e)))) + ((do) + `(do ,(map + (lambda (initsteps) + `(,(car initsteps) + ,@(map defmacro:expand* + (cdr initsteps)))) + (cadr e)) + ,(map defmacro:expand* (caddr e)) + ,@(map defmacro:expand* (cdddr e)))) + (else (map defmacro:expand* e))))) + e)) diff --git a/dwindtst.scm b/dwindtst.scm new file mode 100644 index 0000000..8d64800 --- /dev/null +++ b/dwindtst.scm @@ -0,0 +1,80 @@ +;;;; "dwindtst.scm", routines for characterizing dynamic-wind. +;Copyright (C) 1992 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'dynamic-wind) + +(define (dwtest n) + (define cont #f) + (display "testing escape from thunk") (display n) (newline) + (display "visiting:") (newline) + (call-with-current-continuation + (lambda (x) (set! cont x))) + (if n + (dynamic-wind + (lambda () + (display "thunk1") (newline) + (if (eqv? n 1) (let ((ntmp n)) + (set! n #f) + (cont ntmp)))) + (lambda () + (display "thunk2") (newline) + (if (eqv? n 2) (let ((ntmp n)) + (set! n #f) + (cont ntmp)))) + (lambda () + (display "thunk3") (newline) + (if (eqv? n 3) (let ((ntmp n)) + (set! n #f) + (cont ntmp))))))) +(define (dwctest n) + (define cont #f) + (define ccont #f) + (display "creating continuation thunk") (newline) + (display "visiting:") (newline) + (call-with-current-continuation + (lambda (x) (set! cont x))) + (if n (set! n (- n))) + (if n + (dynamic-wind + (lambda () + (display "thunk1") (newline) + (if (eqv? n 1) (let ((ntmp n)) + (set! n #f) + (cont ntmp)))) + (lambda () + (call-with-current-continuation + (lambda (x) (set! ccont x))) + (display "thunk2") (newline) + (if (eqv? n 2) (let ((ntmp n)) + (set! n #f) + (cont ntmp)))) + (lambda () + (display "thunk3") (newline) + (if (eqv? n 3) (let ((ntmp n)) + (set! n #f) + (cont ntmp)))))) + (cond + (n + (set! n (- n)) + (display "testing escape from continuation thunk") (display n) (newline) + (display "visiting:") (newline) + (ccont #f)))) + +(dwtest 1) (dwtest 2) (dwtest 3) +(dwctest 1) (dwctest 2) (dwctest 3) diff --git a/dynamic.scm b/dynamic.scm new file mode 100644 index 0000000..937f93e --- /dev/null +++ b/dynamic.scm @@ -0,0 +1,75 @@ +; "dynamic.scm", DYNAMIC data type for Scheme +; Copyright 1992 Andrew Wilcox. +; +; You may freely copy, redistribute and modify this package. + +(require 'record) +(require 'dynamic-wind) + +(define dynamic-environment-rtd + (make-record-type "dynamic environment" '(dynamic value parent))) +(define make-dynamic-environment + (record-constructor dynamic-environment-rtd)) +(define dynamic-environment:dynamic + (record-accessor dynamic-environment-rtd 'dynamic)) +(define dynamic-environment:value + (record-accessor dynamic-environment-rtd 'value)) +(define dynamic-environment:set-value! + (record-modifier dynamic-environment-rtd 'value)) +(define dynamic-environment:parent + (record-accessor dynamic-environment-rtd 'parent)) + +(define *current-dynamic-environment* #f) +(define (extend-current-dynamic-environment dynamic obj) + (set! *current-dynamic-environment* + (make-dynamic-environment dynamic obj + *current-dynamic-environment*))) + +(define dynamic-rtd (make-record-type "dynamic" '())) +(define make-dynamic + (let ((dynamic-constructor (record-constructor dynamic-rtd))) + (lambda (obj) + (let ((dynamic (dynamic-constructor))) + (extend-current-dynamic-environment dynamic obj) + dynamic)))) + +(define dynamic? (record-predicate dynamic-rtd)) +(define (guarantee-dynamic dynamic) + (or (dynamic? dynamic) + (slib:error "Not a dynamic" dynamic))) + +(define dynamic:errmsg + "No value defined for this dynamic in the current dynamic environment") + +(define (dynamic-ref dynamic) + (guarantee-dynamic dynamic) + (let loop ((env *current-dynamic-environment*)) + (cond ((not env) + (slib:error dynamic:errmsg dynamic)) + ((eq? (dynamic-environment:dynamic env) dynamic) + (dynamic-environment:value env)) + (else + (loop (dynamic-environment:parent env)))))) + +(define (dynamic-set! dynamic obj) + (guarantee-dynamic dynamic) + (let loop ((env *current-dynamic-environment*)) + (cond ((not env) + (slib:error dynamic:errmsg dynamic)) + ((eq? (dynamic-environment:dynamic env) dynamic) + (dynamic-environment:set-value! env obj)) + (else + (loop (dynamic-environment:parent env)))))) + +(define (call-with-dynamic-binding dynamic obj thunk) + (let ((out-thunk-env #f) + (in-thunk-env (make-dynamic-environment + dynamic obj + *current-dynamic-environment*))) + (dynamic-wind (lambda () + (set! out-thunk-env *current-dynamic-environment*) + (set! *current-dynamic-environment* in-thunk-env)) + thunk + (lambda () + (set! in-thunk-env *current-dynamic-environment*) + (set! *current-dynamic-environment* out-thunk-env))))) diff --git a/dynwind.scm b/dynwind.scm new file mode 100644 index 0000000..9212422 --- /dev/null +++ b/dynwind.scm @@ -0,0 +1,74 @@ +; "dynwind.scm", wind-unwind-protect for Scheme +; Copyright (c) 1992, 1993 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;This facility is a generalization of Common Lisp `unwind-protect', +;designed to take into account the fact that continuations produced by +;CALL-WITH-CURRENT-CONTINUATION may be reentered. + +; (dynamic-wind <thunk1> <thunk2> <thunk3>) procedure + +;The arguments <thunk1>, <thunk2>, and <thunk3> must all be procedures +;of no arguments (thunks). + +;DYNAMIC-WIND calls <thunk1>, <thunk2>, and then <thunk3>. The value +;returned by <thunk2> is returned as the result of DYNAMIC-WIND. +;<thunk3> is also called just before control leaves the dynamic +;context of <thunk2> by calling a continuation created outside that +;context. Furthermore, <thunk1> is called before reentering the +;dynamic context of <thunk2> by calling a continuation created inside +;that context. (Control is inside the context of <thunk2> if <thunk2> +;is on the current return stack). + +;;;WARNING: This code has no provision for dealing with errors or +;;;interrupts. If an error or interrupt occurs while using +;;;dynamic-wind, the dynamic environment will be that in effect at the +;;;time of the error or interrupt. + +(define dynamic:winds '()) + +(define (dynamic-wind <thunk1> <thunk2> <thunk3>) + (<thunk1>) + (set! dynamic:winds (cons (cons <thunk1> <thunk3>) dynamic:winds)) + (let ((ans (<thunk2>))) + (set! dynamic:winds (cdr dynamic:winds)) + (<thunk3>) + ans)) + +(define call-with-current-continuation + (let ((oldcc call-with-current-continuation)) + (lambda (proc) + (let ((winds dynamic:winds)) + (oldcc + (lambda (cont) + (proc (lambda (c2) + (dynamic:do-winds winds (- (length dynamic:winds) + (length winds))) + (cont c2))))))))) + +(define (dynamic:do-winds to delta) + (cond ((eq? dynamic:winds to)) + ((negative? delta) + (dynamic:do-winds (cdr to) (+ 1 delta)) + ((caar to)) + (set! dynamic:winds to)) + (else + (let ((from (cdar dynamic:winds))) + (set! dynamic:winds (cdr dynamic:winds)) + (from) + (dynamic:do-winds to (+ -1 delta)))))) diff --git a/elk.init b/elk.init new file mode 100644 index 0000000..f6dded0 --- /dev/null +++ b/elk.init @@ -0,0 +1,281 @@ +;;;"elk.init" Initialisation file for SLIB for ELK 2.1 -*- Scheme -*- +;;; Copyright (C) 1991, 1992, 1993 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +; No guarantees are given about the correctness of any of the +; choices made below. Only enough work was done to get the require +; mechanism to work correctly. +; +; Stephen J. Bevan <bevan@cs.man.ac.uk> 19920912 modified by Mike +; Sperber to work correctly with statically-linked Elk and slib1d. Be +; sure to change the library vicinities according to your local +; configuration. If you're running MS-DOS (which is possible since +; 2.1), you probably have to change this file to make everything work +; correctly. + +;;; (software-type) should be set to the generic operating system type. +;;; UNIX, VMS, MACOS, AMIGA and MS-DOS are supported. + +(define (software-type) 'UNIX) + +;;; (scheme-implementation-type) should return the name of the scheme +;;; implementation loading this file. + +(define (scheme-implementation-type) 'Elk) + +;;; (scheme-implementation-version) should return a string describing +;;; the version the scheme implementation loading this file. + +(define (scheme-implementation-version) "?2.1") + +;;; (implementation-vicinity) should be defined to be the pathname of +;;; the directory where any auxillary files to your Scheme +;;; implementation reside. + +(define (implementation-vicinity) + (case (software-type) + ((UNIX) "/usr/local/lib/elk-2.1/scm/") + ((VMS) "scheme$src:") + ((MS-DOS) "C:\\scheme\\"))) + +;;; (library-vicinity) should be defined to be the pathname of the +;;; directory where files of Scheme library functions reside. + +(define library-vicinity + (let ((library-path + (or (getenv "SCHEME_LIBRARY_PATH") + ;; Uses this path if SCHEME_LIBRARY_PATH is not defined. + (case (software-type) + ((UNIX) "/usr/local/lib/slib/") + ((VMS) "lib$scheme:") + ((MS-DOS) "C:\\SLIB\\") + (else ""))))) + (lambda () library-path))) + +;;; *features* should be set to a list of symbols describing features +;;; of this implementation. Suggestions for features are: + +(define *features* + '( + source ;can load scheme source files + ;(slib:load-source "filename") + compiled ;can load compiled files + ;(slib:load-compiled "filename") + rev4-report + ieee-p1178 + sicp + rev4-optional-procedures + rev3-procedures + rev2-procedures + multiarg/and- + multiarg-apply + delay + transcript + full-continuation + sort + format + system + getenv + program-arguments + string-port + )) + +;------------ + +(define program-arguments + (lambda () + (cons "undefined-program-name" (command-line-args)))) + +; EXACT? appears to always return #f which isn't very useful. +; Approximating it with INTEGER? at least means that some +; of the code in the library will work correctly + +(define exact? integer?) ; WARNING: redefining EXACT? + +(define (inexact? arg) + (not (exact? arg))) + +;;; (TMPNAM) makes a temporary file name. +(define tmpnam + (let ((cntr 100)) + (lambda () (set! cntr (+ 1 cntr)) + (let ((tmp (string-append "slib_" (number->string cntr)))) + (if (file-exists? tmp) (tmpnam) tmp))))) + +(require 'unix) + +; Pull in GENTENV and SYSTEM + +;;; (FILE-EXISTS? <string>) already here. + +;;; (DELETE-FILE <string>) +(define (delete-file f) (system (string-append "rm " f))) + +;------------ + +;;; (OUTPUT-PORT-WIDTH <port>) +(define (output-port-width . arg) 79) + +;;; (OUTPUT-PORT-HEIGHT <port>) +(define (output-port-height . arg) 24) + +;;; (CURRENT-ERROR-PORT) +;;; is already defined in Elk 2.1 + +;;; FORCE-OUTPUT flushes any pending output on optional arg output port +;;; use this definition if your system doesn't have such a procedure. +(define force-output flush-output-port) + +;;; CALL-WITH-INPUT-STRING and CALL-WITH-OUTPUT-STRING are the string +;;; port versions of CALL-WITH-*PUT-FILE. +(define (call-with-output-string f) + (let ((outsp (open-output-string))) + (f outsp) + (let ((s (get-output-string outsp))) + (close-output-port outsp) + s))) + +(define (call-with-input-string s f) + (let* ((insp (open-input-string s)) + (res (f insp))) + (close-input-port insp) + res)) + +;;; CHAR-CODE-LIMIT is one greater than the largest integer which can +;;; be returned by CHAR->INTEGER. +(define char-code-limit 256) + +;;; MOST-POSITIVE-FIXNUM is used in modular.scm +(define most-positive-fixnum 8388608) ; 23 bit integers ? + +;;; Return argument +(define (identity x) x) + +;;; If your implementation provides eval SLIB:EVAL is single argument +;;; eval using the top-level (user) environment. +(define slib:eval eval) + +(define *macros* '()) +(define (defmacro? m) (and (assq m *macros*) #t)) + +(define-macro (defmacro key pattern . body) + `(begin + (define-macro ,(cons key pattern) ,@body) + (set! *macros* (cons (cons ',key (lambda ,pattern ,@body)) *macros*)))) + +(define (macroexpand-1 e) + (if (pair? e) (let ((a (car e))) + (cond ((symbol? a) (set! a (assq a *macros*)) + (if a (apply (cdr a) (cdr e)) e)) + (else e))) + e)) + +(define (macroexpand e) + (if (pair? e) (let ((a (car e))) + (cond ((symbol? a) + (set! a (assq a *macros*)) + (if a (macroexpand (apply (cdr a) (cdr e))) e)) + (else e))) + e)) + +(define gentemp + (let ((*gensym-counter* -1)) + (lambda () + (set! *gensym-counter* (+ *gensym-counter* 1)) + (string->symbol + (string-append "slib:G" (number->string *gensym-counter*)))))) + +(define defmacro:eval slib:eval) +(define defmacro:load load) +;;; If your implementation provides R4RS macros: +;(define macro:eval slib:eval) +;(define macro:load load) + +(define (slib:eval-load <pathname> evl) + (if (not (file-exists? <pathname>)) + (set! <pathname> (string-append <pathname> (scheme-file-suffix)))) + (call-with-input-file <pathname> + (lambda (port) + (let ((old-load-pathname *load-pathname*)) + (set! *load-pathname* <pathname>) + (do ((o (read port) (read port))) + ((eof-object? o)) + (evl o)) + (set! *load-pathname* old-load-pathname))))) + +;;; define an error procedure for the library +(define slib:error error) + +;;; define these as appropriate for your system. +(define slib:tab #\tab) +(define slib:form-feed #\formfeed) + +;;; Define these if your implementation's syntax can support it and if +;;; they are not already defined. + +;(define (1+ n) (+ n 1)) +;(define (-1+ n) (+ n -1)) +;(define 1- -1+) + +(define in-vicinity string-append) + +;;; Define SLIB:EXIT to be the implementation procedure to exit or +;;; return if exitting not supported. +(define slib:exit + (lambda args + (exit (cond ((null? args) 0) + ((eqv? #t (car args)) 0) + ((and (number? (car args)) (integer? (car args))) (car args)) + (else 1))))) + +;;; Here for backward compatability +(define scheme-file-suffix + (let ((suffix (case (software-type) + ((NOSVE) "_scm") + (else ".scm")))) + (lambda () suffix))) + +;;; (SLIB:LOAD-SOURCE "foo") should load "foo.scm" or with whatever +;;; suffix all the module files in SLIB have. See feature 'SOURCE. + +; Modify the already modified _load_ so that it copes with +; environments correctly. The change involves using +; _(global-environment)_ if none is explicitly specified. +; If this is not done, definitions in files loaded by other files will +; not be loaded in the correct environment. + +(define slib:load-source + (let ((primitive-load load)) + (lambda (<pathname> . rest) + (let ((env (if (null? rest) (list (global-environment)) rest))) + (apply primitive-load <pathname> env))))) + +;;; (SLIB:LOAD-COMPILED "foo") should load the file that was produced +;;; by compiling "foo.scm" if this implementation can compile files. +;;; See feature 'COMPILED. + +(define slib:load-compiled + (let ((primitive-load load)) + (lambda (<pathname> . rest) + (apply primitive-load (string->symbol (string-append name ".o")) rest)))) + +;;; At this point SLIB:LOAD must be able to load SLIB files. + +(define slib:load slib:load-source) ;WARNING: redefining LOAD + +(slib:load (in-vicinity (library-vicinity) "require")) diff --git a/factor.scm b/factor.scm new file mode 100644 index 0000000..a5d3e8c --- /dev/null +++ b/factor.scm @@ -0,0 +1,149 @@ +;;;; "factor.scm", prime test and factorization for Scheme +;;; Copyright (C) 1991, 1992, 1993 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'random) +(require 'modular) + +;;; (modulo p 16) is because we care only about the low order bits. +;;; The odd? tests are inline of (expt -1 ...) + +(define (prime:jacobi-symbol p q) + (cond ((zero? p) 0) + ((= 1 p) 1) + ((odd? p) + (if (odd? (quotient (* (- (modulo p 16) 1) (- q 1)) 4)) + (- (prime:jacobi-symbol (modulo q p) p)) + (prime:jacobi-symbol (modulo q p) p))) + (else + (let ((qq (modulo q 16))) + (if (odd? (quotient (- (* qq qq) 1) 8)) + (- (prime:jacobi-symbol (quotient p 2) q)) + (prime:jacobi-symbol (quotient p 2) q)))))) + +;;;; Solovay-Strassen Prime Test +;;; if n is prime, then J(a,n) is congruent mod n to a**((n-1)/2) + +;;; See: +;;; Robert Solovay and Volker Strassen, +;;; "A Fast Monte-Carlo Test for Primality," +;;; SIAM Journal on Computing, 1977, pp 84-85. + +;;; checks if n is prime. Returns #f if not prime. #t if (probably) prime. +;;; probability of a mistake = (expt 2 (- prime:trials)) +;;; choosing prime:trials=30 should be enough +(define prime:trials 30) +;;; prime:product is a product of small primes. +(define prime:product + (let ((p 210)) + (for-each (lambda (s) (set! p (or (string->number s) p))) + '("2310" "30030" "510510" "9699690" "223092870" + "6469693230" "200560490130")) + p)) + +(define (prime:prime? n) + (set! n (abs n)) + (cond ((<= n 36) (and (memv n '(2 3 5 7 11 13 17 19 23 29 31)) #t)) + ((= 1 (gcd n prime:product)) + (do ((i prime:trials (- i 1)) + (a (+ 1 (random (- n 1))) (+ 1 (random (- n 1))))) + ((not (and (positive? i) + (= (gcd a n) 1) + (= (modulo (prime:jacobi-symbol a n) n) + (modular:expt n a (quotient (- n 1) 2))))) + (if (positive? i) #f #t)))) + (else #f))) + +;;;;Lankinen's recursive factoring algorithm: +;From: ld231782@longs.LANCE.ColoState.EDU (L. Detweiler) + +; | undefined if n<0, +; | (u,v) if n=0, +;Let f(u,v,b,n) := | [otherwise] +; | f(u+b,v,2b,(n-v)/2) or f(u,v+b,2b,(n-u)/2) if n odd +; | f(u,v,2b,n/2) or f(u+b,v+b,2b,(n-u-v-b)/2) if n even + +;Thm: f(1,1,2,(m-1)/2) = (p,q) iff pq=m for odd m. + +;It may be illuminating to consider the relation of the Lankinen function in +;a `computational hierarchy' of other factoring functions.* Assumptions are +;made herein on the basis of conventional digital (binary) computers. Also, +;complexity orders are given for the worst case scenarios (when the number to +;be factored is prime). However, all algorithms would probably perform to +;the same constant multiple of the given orders for complete composite +;factorizations. + +;Thm: Eratosthenes' Sieve is very roughtly O(ln(n)/n) in time and +; O(n*log2(n)) in space. +;Pf: It works with all prime factors less than n (about ln(n)/n by the prime +; number thm), requiring an array of size proportional to n with log2(n) +; space for each entry. + +;Thm: `Odd factors' is O((sqrt(n)/2)*log2(n)) in time and O(log2(n)) in +; space. +;Pf: It tests all odd factors less than the square root of n (about +; sqrt(n)/2), with log2(n) time for each division. It requires only +; log2(n) space for the number and divisors. + +;Thm: Lankinen's algorithm is O(sqrt(n)/2) in time and O((sqrt(n)/2)*log2(n)) +; in space. +;Pf: The algorithm is easily modified to seach only for factors p<q for all +; pq=m. Then the recursive call tree forms a geometric progression +; starting at one, and doubling until reaching sqrt(n)/2, or a length of +; log2(sqrt(n)/2). From the formula for a geometric progression, there is +; a total of about 2^log2(sqrt(n)/2) = sqrt(n)/2 calls. Assuming that +; addition, subtraction, comparison, and multiplication/division by two +; occur in constant time, this implies O(sqrt(n)/2) time and a +; O((sqrt(n)/2)*log2(n)) requirement of stack space. + +(define (prime:f u v b n) + (if (<= n 0) + (cond ((negative? n) #f) + ((= u 1) #f) + ((= v 1) #f) + ; Do both of these factors need to be factored? + (else (append (or (prime:f 1 1 2 (quotient (- u 1) 2)) + (list u)) + (or (prime:f 1 1 2 (quotient (- v 1) 2)) + (list v))))) + (if (even? n) + (or (prime:f u v (+ b b) (quotient n 2)) + (prime:f (+ u b) (+ v b) (+ b b) (quotient (- n (+ u v b)) 2))) + (or (prime:f (+ u b) v (+ b b) (quotient (- n v) 2)) + (prime:f u (+ v b) (+ b b) (quotient (- n u) 2)))))) + +(define (prime:factor m) + (if + (negative? m) (cons -1 (prime:factor (- m))) + (let* ((s (gcd m prime:product)) + (r (quotient m s))) + (if (even? s) + (append + (if (= 1 r) '() (prime:factor r)) + (cons 2 (let ((s/2 (quotient s 2))) + (if (= s/2 1) '() + (or (prime:f 1 1 2 (quotient (- s/2 1) 2)) + (list s/2)))))) + (if (= 1 s) (or (prime:f 1 1 2 (quotient (- m 1) 2)) (list m)) + (append (if (= 1 r) '() + (or (prime:f 1 1 2 (quotient (- r 1) 2)) (list r))) + (or (prime:f 1 1 2 (quotient (- s 1) 2)) (list s)))))))) + +(define jacobi-symbol prime:jacobi-symbol) +(define prime? prime:prime?) +(define factor prime:factor) diff --git a/fluidlet.scm b/fluidlet.scm new file mode 100644 index 0000000..c93b288 --- /dev/null +++ b/fluidlet.scm @@ -0,0 +1,45 @@ +; "fluidlet.scm", FLUID-LET for Scheme +; Copyright (c) 1992, Dorai Sitaram (dorai@cs.rice.edu) +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'rev4-optional-procedures) +(require 'common-list-functions) +(require 'dynamic-wind) +(require 'macro) + +(define list-set! (lambda (s i v) (set-car! (list-tail s i) v))) + +(define-syntax fluid-let + (syntax-rules () + ((fluid-let ((x v) ...) . body) + (let ((%x-names (list 'x ...)) + (%x-values (list x ...)) + (%fluid-x-values (list v ...))) + (dynamic-wind + (lambda () + (set! x (list-ref %fluid-x-values + (comlist:position 'x %x-names))) + ...) + (lambda () . body) + (lambda () + (let ((%x-position (comlist:position 'x %x-names))) + (list-set! %fluid-x-values %x-position x) + (set! x (list-ref %x-values %x-position))) + ...)))))) + +;--- end of file diff --git a/format.scm b/format.scm new file mode 100644 index 0000000..1650e72 --- /dev/null +++ b/format.scm @@ -0,0 +1,1678 @@ +;;; "format.scm" Common LISP text output formatter for SLIB +; Written 1992-1994 by Dirk Lutzebaeck (lutzeb@cs.tu-berlin.de) +; +; This code is in the public domain. + +; Authors of the original version (< 1.4) were Ken Dickey and Aubrey Jaffer. +; Please send error reports to the email address above. +; For documentation see slib.texi and format.doc. +; For testing load formatst.scm. +; +; Version 3.0 + +(provide 'format) +(require 'string-case) +(require 'string-port) +(require 'rev4-optional-procedures) + +;;; Configuration ------------------------------------------------------------ + +(define format:symbol-case-conv #f) +;; Symbols are converted by symbol->string so the case of the printed +;; symbols is implementation dependent. format:symbol-case-conv is a +;; one arg closure which is either #f (no conversion), string-upcase!, +;; string-downcase! or string-capitalize!. + +(define format:iobj-case-conv #f) +;; As format:symbol-case-conv but applies for the representation of +;; implementation internal objects. + +(define format:expch #\E) +;; The character prefixing the exponent value in ~e printing. + +(define format:floats (provided? 'inexact)) +;; Detects if the scheme system implements flonums (see at eof). + +(define format:complex-numbers (provided? 'complex)) +;; Detects if the scheme system implements complex numbers. + +(define format:radix-pref (char=? #\# (string-ref (number->string 8 8) 0))) +;; Detects if number->string adds a radix prefix. + +(define format:ascii-non-printable-charnames + '#("nul" "soh" "stx" "etx" "eot" "enq" "ack" "bel" + "bs" "ht" "nl" "vt" "np" "cr" "so" "si" + "dle" "dc1" "dc2" "dc3" "dc4" "nak" "syn" "etb" + "can" "em" "sub" "esc" "fs" "gs" "rs" "us" "space")) + +;;; End of configuration ---------------------------------------------------- + +(define format:version "3.0") +(define format:port #f) ; curr. format output port +(define format:output-col 0) ; curr. format output tty column +(define format:flush-output #f) ; flush output at end of formatting +(define format:case-conversion #f) +(define format:error-continuation #f) +(define format:args #f) +(define format:pos 0) ; curr. format string parsing position +(define format:arg-pos 0) ; curr. format argument position + ; this is global for error presentation + +; format string and char output routines on format:port + +(define (format:out-str str) + (if format:case-conversion + (display (format:case-conversion str) format:port) + (display str format:port)) + (set! format:output-col + (+ format:output-col (string-length str)))) + +(define (format:out-char ch) + (if format:case-conversion + (display (format:case-conversion (string ch)) format:port) + (write-char ch format:port)) + (set! format:output-col + (if (char=? ch #\newline) + 0 + (+ format:output-col 1)))) + +;(define (format:out-substr str i n) ; this allocates a new string +; (display (substring str i n) format:port) +; (set! format:output-col (+ format:output-col n))) + +(define (format:out-substr str i n) + (do ((k i (+ k 1))) + ((= k n)) + (write-char (string-ref str k) format:port)) + (set! format:output-col (+ format:output-col n))) + +;(define (format:out-fill n ch) ; this allocates a new string +; (format:out-str (make-string n ch))) + +(define (format:out-fill n ch) + (do ((i 0 (+ i 1))) + ((= i n)) + (write-char ch format:port)) + (set! format:output-col (+ format:output-col n))) + +; format's user error handler + +(define (format:error . args) ; never returns! + (let ((error-continuation format:error-continuation) + (format-args format:args) + (port (current-error-port))) + (set! format:error format:intern-error) + (if (and (>= (length format:args) 2) + (string? (cadr format:args))) + (let ((format-string (cadr format-args))) + (if (not (zero? format:arg-pos)) + (set! format:arg-pos (- format:arg-pos 1))) + (format port "~%FORMAT: error with call: (format ~a \"~a<===~a\" ~ + ~{~a ~}===>~{~a ~})~% " + (car format:args) + (substring format-string 0 format:pos) + (substring format-string format:pos + (string-length format-string)) + (list-head (cddr format:args) format:arg-pos) + (list-tail (cddr format:args) format:arg-pos))) + (format port + "~%FORMAT: error with call: (format~{ ~a~})~% " + format:args)) + (apply format port args) + (newline port) + (set! format:error format:error-save) + (set! format:error-continuation error-continuation) + (format:abort) + (format:intern-error "format:abort does not jump to toplevel!"))) + +(define format:error-save format:error) + +(define (format:intern-error . args) ;if something goes wrong in format:error + (display "FORMAT: INTERNAL ERROR IN FORMAT:ERROR!") (newline) + (display " format args: ") (write format:args) (newline) + (display " error args: ") (write args) (newline) + (set! format:error format:error-save) + (format:abort)) + +(define (format:format . args) ; the formatter entry + (set! format:args args) + (set! format:arg-pos 0) + (set! format:pos 0) + (if (< (length args) 1) + (format:error "not enough arguments")) + (let ((destination (car args)) + (arglist (cdr args))) + (cond + ((or (and (boolean? destination) ; port output + destination) + (output-port? destination) + (number? destination)) + (format:out (cond + ((boolean? destination) (current-output-port)) + ((output-port? destination) destination) + ((number? destination) (current-error-port))) + (car arglist) (cdr arglist))) + ((and (boolean? destination) ; string output + (not destination)) + (call-with-output-string + (lambda (port) (format:out port (car arglist) (cdr arglist))))) + ((string? destination) ; dest. is format string (Scheme->C) + (call-with-output-string + (lambda (port) + (format:out port destination arglist)))) + (else + (format:error "illegal destination `~a'" destination))))) + +(define (format:out port fmt args) ; the output handler for a port + (set! format:port port) ; global port for output routines + (set! format:case-conversion #f) ; modifier case conversion procedure + (set! format:flush-output #f) ; ~! reset + (let ((arg-pos (format:format-work fmt args)) + (arg-len (length args))) + (cond + ((< arg-pos arg-len) + (set! format:arg-pos (+ arg-pos 1)) + (set! format:pos (string-length fmt)) + (format:error "~a superfluous argument~:p" (- arg-len arg-pos))) + ((> arg-pos arg-len) + (set! format:arg-pos (+ arg-len 1)) + (display format:arg-pos) + (format:error "~a missing argument~:p" (- arg-pos arg-len))) + (else + (if format:flush-output (force-output port)) + #t)))) + +(define format:parameter-characters + '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\- #\+ #\v #\# #\')) + +(define (format:format-work format-string arglist) ; does the formatting work + (letrec + ((format-string-len (string-length format-string)) + (arg-pos 0) ; argument position in arglist + (arg-len (length arglist)) ; number of arguments + (modifier #f) ; 'colon | 'at | 'colon-at | #f + (params '()) ; directive parameter list + (param-value-found #f) ; a directive parameter value found + (conditional-nest 0) ; conditional nesting level + (clause-pos 0) ; last cond. clause beginning char pos + (clause-default #f) ; conditional default clause string + (clauses '()) ; conditional clause string list + (conditional-type #f) ; reflects the contional modifiers + (conditional-arg #f) ; argument to apply the conditional + (iteration-nest 0) ; iteration nesting level + (iteration-pos 0) ; iteration string beginning char pos + (iteration-type #f) ; reflects the iteration modifiers + (max-iterations #f) ; maximum number of iterations + (recursive-pos-save format:pos) + + (next-char ; gets the next char from format-string + (lambda () + (let ((ch (peek-next-char))) + (set! format:pos (+ 1 format:pos)) + ch))) + + (peek-next-char + (lambda () + (if (>= format:pos format-string-len) + (format:error "illegal format string") + (string-ref format-string format:pos)))) + + (one-positive-integer? + (lambda (params) + (cond + ((null? params) #f) + ((and (integer? (car params)) + (>= (car params) 0) + (= (length params) 1)) #t) + (else (format:error "one positive integer parameter expected"))))) + + (next-arg + (lambda () + (if (>= arg-pos arg-len) + (begin + (set! format:arg-pos (+ arg-len 1)) + (format:error "missing argument(s)"))) + (add-arg-pos 1) + (list-ref arglist (- arg-pos 1)))) + + (prev-arg + (lambda () + (add-arg-pos -1) + (if (negative? arg-pos) + (format:error "missing backward argument(s)")) + (list-ref arglist arg-pos))) + + (rest-args + (lambda () + (let loop ((l arglist) (k arg-pos)) ; list-tail definition + (if (= k 0) l (loop (cdr l) (- k 1)))))) + + (add-arg-pos + (lambda (n) + (set! arg-pos (+ n arg-pos)) + (set! format:arg-pos arg-pos))) + + (anychar-dispatch ; dispatches the format-string + (lambda () + (if (>= format:pos format-string-len) + arg-pos ; used for ~? continuance + (let ((char (next-char))) + (cond + ((char=? char #\~) + (set! modifier #f) + (set! params '()) + (set! param-value-found #f) + (tilde-dispatch)) + (else + (if (and (zero? conditional-nest) + (zero? iteration-nest)) + (format:out-char char)) + (anychar-dispatch))))))) + + (tilde-dispatch + (lambda () + (cond + ((>= format:pos format-string-len) + (format:out-str "~") ; tilde at end of string is just output + arg-pos) ; used for ~? continuance + ((and (or (zero? conditional-nest) + (memv (peek-next-char) ; find conditional directives + (append '(#\[ #\] #\; #\: #\@ #\^) + format:parameter-characters))) + (or (zero? iteration-nest) + (memv (peek-next-char) ; find iteration directives + (append '(#\{ #\} #\: #\@ #\^) + format:parameter-characters)))) + (case (char-upcase (next-char)) + + ;; format directives + + ((#\A) ; Any -- for humans + (set! format:read-proof (memq modifier '(colon colon-at))) + (format:out-obj-padded (memq modifier '(at colon-at)) + (next-arg) #f params) + (anychar-dispatch)) + ((#\S) ; Slashified -- for parsers + (set! format:read-proof (memq modifier '(colon colon-at))) + (format:out-obj-padded (memq modifier '(at colon-at)) + (next-arg) #t params) + (anychar-dispatch)) + ((#\D) ; Decimal + (format:out-num-padded modifier (next-arg) params 10) + (anychar-dispatch)) + ((#\X) ; Hexadecimal + (format:out-num-padded modifier (next-arg) params 16) + (anychar-dispatch)) + ((#\O) ; Octal + (format:out-num-padded modifier (next-arg) params 8) + (anychar-dispatch)) + ((#\B) ; Binary + (format:out-num-padded modifier (next-arg) params 2) + (anychar-dispatch)) + ((#\R) + (if (null? params) + (format:out-obj-padded ; Roman, cardinal, ordinal numerals + #f + ((case modifier + ((at) format:num->roman) + ((colon-at) format:num->old-roman) + ((colon) format:num->ordinal) + (else format:num->cardinal)) + (next-arg)) + #f params) + (format:out-num-padded ; any Radix + modifier (next-arg) (cdr params) (car params))) + (anychar-dispatch)) + ((#\F) ; Fixed-format floating-point + (if format:floats + (format:out-fixed modifier (next-arg) params) + (format:out-str (number->string (next-arg)))) + (anychar-dispatch)) + ((#\E) ; Exponential floating-point + (if format:floats + (format:out-expon modifier (next-arg) params) + (format:out-str (number->string (next-arg)))) + (anychar-dispatch)) + ((#\G) ; General floating-point + (if format:floats + (format:out-general modifier (next-arg) params) + (format:out-str (number->string (next-arg)))) + (anychar-dispatch)) + ((#\$) ; Dollars floating-point + (if format:floats + (format:out-dollar modifier (next-arg) params) + (format:out-str (number->string (next-arg)))) + (anychar-dispatch)) + ((#\I) ; Complex numbers + (if (not format:complex-numbers) + (format:error + "complex numbers not supported by this scheme system")) + (let ((z (next-arg))) + (if (not (complex? z)) + (format:error "argument not a complex number")) + (format:out-fixed modifier (real-part z) params) + (format:out-fixed 'at (imag-part z) params) + (format:out-char #\i)) + (anychar-dispatch)) + ((#\C) ; Character + (let ((ch (if (one-positive-integer? params) + (integer->char (car params)) + (next-arg)))) + (if (not (char? ch)) (format:error "~~c expects a character")) + (case modifier + ((at) + (format:out-str (format:char->str ch))) + ((colon) + (let ((c (char->integer ch))) + (if (< c 0) + (set! c (+ c 256))) ; compensate complement impl. + (cond + ((< c #x20) ; assumes that control chars are < #x20 + (format:out-char #\^) + (format:out-char + (integer->char (+ c #x40)))) + ((>= c #x7f) + (format:out-str "#\\") + (format:out-str + (if format:radix-pref + (let ((s (number->string c 8))) + (substring s 2 (string-length s))) + (number->string c 8)))) + (else + (format:out-char ch))))) + (else (format:out-char ch)))) + (anychar-dispatch)) + ((#\P) ; Plural + (if (memq modifier '(colon colon-at)) + (prev-arg)) + (let ((arg (next-arg))) + (if (not (number? arg)) + (format:error "~~p expects a number argument")) + (if (= arg 1) + (if (memq modifier '(at colon-at)) + (format:out-char #\y)) + (if (memq modifier '(at colon-at)) + (format:out-str "ies") + (format:out-char #\s)))) + (anychar-dispatch)) + ((#\~) ; Tilde + (if (one-positive-integer? params) + (format:out-fill (car params) #\~) + (format:out-char #\~)) + (anychar-dispatch)) + ((#\%) ; Newline + (if (one-positive-integer? params) + (format:out-fill (car params) #\newline) + (format:out-char #\newline)) + (set! format:output-col 0) + (anychar-dispatch)) + ((#\&) ; Fresh line + (if (one-positive-integer? params) + (begin + (if (> (car params) 0) + (format:out-fill (- (car params) + (if (> format:output-col 0) 0 1)) + #\newline)) + (set! format:output-col 0)) + (if (> format:output-col 0) + (format:out-char #\newline))) + (anychar-dispatch)) + ((#\_) ; Space character + (if (one-positive-integer? params) + (format:out-fill (car params) #\space) + (format:out-char #\space)) + (anychar-dispatch)) + ((#\/) ; Tabulator character + (if (one-positive-integer? params) + (format:out-fill (car params) slib:tab) + (format:out-char slib:tab)) + (anychar-dispatch)) + ((#\|) ; Page seperator + (if (one-positive-integer? params) + (format:out-str (car params) slib:form-feed) + (format:out-char slib:form-feed)) + (set! format:output-col 0) + (anychar-dispatch)) + ((#\T) ; Tabulate + (format:tabulate modifier params) + (anychar-dispatch)) + ((#\Y) ; Pretty-print + (require 'pretty-print) + (pretty-print (next-arg) format:port) + (set! format:output-col 0) + (anychar-dispatch)) + ((#\? #\K) ; Indirection (is "~K" in T-Scheme) + (cond + ((memq modifier '(colon colon-at)) + (format:error "illegal modifier in ~~?")) + ((eq? modifier 'at) + (let* ((frmt (next-arg)) + (args (rest-args))) + (add-arg-pos (format:format-work frmt args)))) + (else + (let* ((frmt (next-arg)) + (args (next-arg))) + (format:format-work frmt args)))) + (anychar-dispatch)) + ((#\!) ; Flush output + (set! format:flush-output #t) + (anychar-dispatch)) + ((#\newline) ; Continuation lines + (if (eq? modifier 'at) + (format:out-char #\newline)) + (if (< format:pos format-string-len) + (do ((ch (peek-next-char) (peek-next-char))) + ((or (not (char-whitespace? ch)) + (= format:pos (- format-string-len 1)))) + (if (eq? modifier 'colon) + (format:out-char (next-char)) + (next-char)))) + (anychar-dispatch)) + ((#\*) ; Argument jumping + (case modifier + ((colon) ; jump backwards + (if (one-positive-integer? params) + (do ((i 0 (+ i 1))) + ((= i (car params))) + (prev-arg)) + (prev-arg))) + ((at) ; jump absolute + (set! arg-pos (if (one-positive-integer? params) + (car params) 0))) + ((colon-at) + (format:error "illegal modifier `:@' in ~~* directive")) + (else ; jump forward + (if (one-positive-integer? params) + (do ((i 0 (+ i 1))) + ((= i (car params))) + (next-arg)) + (next-arg)))) + (anychar-dispatch)) + ((#\() ; Case conversion begin + (set! format:case-conversion + (case modifier + ((at) string-capitalize-first) + ((colon) string-capitalize) + ((colon-at) string-upcase) + (else string-downcase))) + (anychar-dispatch)) + ((#\)) ; Case conversion end + (if (not format:case-conversion) + (format:error "missing ~~(")) + (set! format:case-conversion #f) + (anychar-dispatch)) + ((#\[) ; Conditional begin + (set! conditional-nest (+ conditional-nest 1)) + (cond + ((= conditional-nest 1) + (set! clause-pos format:pos) + (set! clause-default #f) + (set! clauses '()) + (set! conditional-type + (case modifier + ((at) 'if-then) + ((colon) 'if-else-then) + ((colon-at) (format:error "illegal modifier in ~~[")) + (else 'num-case))) + (set! conditional-arg + (if (one-positive-integer? params) + (car params) + (next-arg))))) + (anychar-dispatch)) + ((#\;) ; Conditional separator + (if (zero? conditional-nest) + (format:error "~~; not in ~~[~~] conditional")) + (if (not (null? params)) + (format:error "no parameter allowed in ~~;")) + (if (= conditional-nest 1) + (let ((clause-str + (cond + ((eq? modifier 'colon) + (set! clause-default #t) + (substring format-string clause-pos + (- format:pos 3))) + ((memq modifier '(at colon-at)) + (format:error "illegal modifier in ~~;")) + (else + (substring format-string clause-pos + (- format:pos 2)))))) + (set! clauses (append clauses (list clause-str))) + (set! clause-pos format:pos))) + (anychar-dispatch)) + ((#\]) ; Conditional end + (if (zero? conditional-nest) (format:error "missing ~~[")) + (set! conditional-nest (- conditional-nest 1)) + (if modifier + (format:error "no modifier allowed in ~~]")) + (if (not (null? params)) + (format:error "no parameter allowed in ~~]")) + (cond + ((zero? conditional-nest) + (let ((clause-str (substring format-string clause-pos + (- format:pos 2)))) + (if clause-default + (set! clause-default clause-str) + (set! clauses (append clauses (list clause-str))))) + (case conditional-type + ((if-then) + (if conditional-arg + (format:format-work (car clauses) + (list conditional-arg)))) + ((if-else-then) + (add-arg-pos + (format:format-work (if conditional-arg + (cadr clauses) + (car clauses)) + (rest-args)))) + ((num-case) + (if (or (not (integer? conditional-arg)) + (< conditional-arg 0)) + (format:error "argument not a positive integer")) + (if (not (and (>= conditional-arg (length clauses)) + (not clause-default))) + (add-arg-pos + (format:format-work + (if (>= conditional-arg (length clauses)) + clause-default + (list-ref clauses conditional-arg)) + (rest-args)))))))) + (anychar-dispatch)) + ((#\{) ; Iteration begin + (set! iteration-nest (+ iteration-nest 1)) + (cond + ((= iteration-nest 1) + (set! iteration-pos format:pos) + (set! iteration-type + (case modifier + ((at) 'rest-args) + ((colon) 'sublists) + ((colon-at) 'rest-sublists) + (else 'list))) + (set! max-iterations (if (one-positive-integer? params) + (car params) #f)))) + (anychar-dispatch)) + ((#\}) ; Iteration end + (if (zero? iteration-nest) (format:error "missing ~~{")) + (set! iteration-nest (- iteration-nest 1)) + (case modifier + ((colon) + (if (not max-iterations) (set! max-iterations 1))) + ((colon-at at) (format:error "illegal modifier")) + (else (if (not max-iterations) (set! max-iterations 100)))) + (if (not (null? params)) + (format:error "no parameters allowed in ~~}")) + (if (zero? iteration-nest) + (let ((iteration-str + (substring format-string iteration-pos + (- format:pos (if modifier 3 2))))) + (if (string=? iteration-str "") + (set! iteration-str (next-arg))) + (case iteration-type + ((list) + (let ((args (next-arg)) + (args-len 0)) + (if (not (list? args)) + (format:error "expected a list argument")) + (set! args-len (length args)) + (do ((arg-pos 0 (+ arg-pos + (format:format-work + iteration-str + (list-tail args arg-pos)))) + (i 0 (+ i 1))) + ((or (>= arg-pos args-len) + (>= i max-iterations)))))) + ((sublists) + (let ((args (next-arg)) + (args-len 0)) + (if (not (list? args)) + (format:error "expected a list argument")) + (set! args-len (length args)) + (do ((arg-pos 0 (+ arg-pos 1))) + ((or (>= arg-pos args-len) + (>= arg-pos max-iterations))) + (let ((sublist (list-ref args arg-pos))) + (if (not (list? sublist)) + (format:error + "expected a list of lists argument")) + (format:format-work iteration-str sublist))))) + ((rest-args) + (let* ((args (rest-args)) + (args-len (length args)) + (usedup-args + (do ((arg-pos 0 (+ arg-pos + (format:format-work + iteration-str + (list-tail + args arg-pos)))) + (i 0 (+ i 1))) + ((or (>= arg-pos args-len) + (>= i max-iterations)) + arg-pos)))) + (add-arg-pos usedup-args))) + ((rest-sublists) + (let* ((args (rest-args)) + (args-len (length args)) + (usedup-args + (do ((arg-pos 0 (+ arg-pos 1))) + ((or (>= arg-pos args-len) + (>= arg-pos max-iterations)) + arg-pos) + (let ((sublist (list-ref args arg-pos))) + (if (not (list? sublist)) + (format:error "expected list arguments")) + (format:format-work iteration-str sublist))))) + (add-arg-pos usedup-args))) + (else (format:error "internal error in ~~}"))))) + (anychar-dispatch)) + ((#\^) ; Up and out + (let* ((continue + (cond + ((not (null? params)) + (not + (case (length params) + ((1) (zero? (car params))) + ((2) (= (list-ref params 0) (list-ref params 1))) + ((3) (<= (list-ref params 0) + (list-ref params 1) + (list-ref params 2))) + (else (format:error "too much parameters"))))) + (format:case-conversion ; if conversion stop conversion + (set! format:case-conversion string-copy) #t) + ((= iteration-nest 1) #t) + ((= conditional-nest 1) #t) + ((>= arg-pos arg-len) + (set! format:pos format-string-len) #f) + (else #t)))) + (if continue + (anychar-dispatch)))) + + ;; format directive modifiers and parameters + + ((#\@) ; `@' modifier + (if (eq? modifier 'colon-at) + (format:error "double `@' modifier")) + (set! modifier (if (eq? modifier 'colon) 'colon-at 'at)) + (tilde-dispatch)) + ((#\:) ; `:' modifier + (if modifier (format:error "illegal `:' modifier position")) + (set! modifier 'colon) + (tilde-dispatch)) + ((#\') ; Character parameter + (if modifier (format:error "misplaced modifier")) + (set! params (append params (list (char->integer (next-char))))) + (set! param-value-found #t) + (tilde-dispatch)) + ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\- #\+) ; num. paramtr + (if modifier (format:error "misplaced modifier")) + (let ((num-str-beg (- format:pos 1)) + (num-str-end format:pos)) + (do ((ch (peek-next-char) (peek-next-char))) + ((not (char-numeric? ch))) + (next-char) + (set! num-str-end (+ 1 num-str-end))) + (set! params + (append params + (list (string->number + (substring format-string + num-str-beg + num-str-end)))))) + (set! param-value-found #t) + (tilde-dispatch)) + ((#\V) ; Variable parameter from next argum. + (if modifier (format:error "misplaced modifier")) + (set! params (append params (list (next-arg)))) + (set! param-value-found #t) + (tilde-dispatch)) + ((#\#) ; Parameter is number of remaining args + (if modifier (format:error "misplaced modifier")) + (set! params (append params (list (length (rest-args))))) + (set! param-value-found #t) + (tilde-dispatch)) + ((#\,) ; Parameter separators + (if modifier (format:error "misplaced modifier")) + (if (not param-value-found) + (set! params (append params '(#f)))) ; append empty paramtr + (set! param-value-found #f) + (tilde-dispatch)) + ((#\Q) ; Inquiry messages + (if (eq? modifier 'colon) + (format:out-str format:version) + (let ((nl (string #\newline))) + (format:out-str + (string-append + "SLIB Common LISP format version " format:version nl + " (C) copyright 1992-1994 by Dirk Lutzebaeck" nl + " please send bug reports to `lutzeb@cs.tu-berlin.de'" + nl)))) + (anychar-dispatch)) + (else ; Unknown tilde directive + (format:error "unknown control character `~c'" + (string-ref format-string (- format:pos 1)))))) + (else (anychar-dispatch)))))) ; in case of conditional + + (set! format:pos 0) + (set! format:arg-pos 0) + (anychar-dispatch) ; start the formatting + (set! format:pos recursive-pos-save) + arg-pos)) ; return the position in the arg. list + +;; format:obj->str returns a R4RS representation as a string of an arbitrary +;; scheme object. +;; First parameter is the object, second parameter is a boolean if the +;; representation should be slashified as `write' does. +;; It uses format:char->str which converts a character into +;; a slashified string as `write' does and which is implementation dependent. +;; It uses format:iobj->str to print out internal objects as +;; quoted strings so that the output can always be processed by (read) + +(define (format:obj->str obj slashify) + (cond + ((string? obj) + (if slashify + (let ((obj-len (string-length obj))) + (string-append + "\"" + (let loop ((i 0) (j 0)) ; taken from Marc Feeley's pp.scm + (if (= j obj-len) + (string-append (substring obj i j) "\"") + (let ((c (string-ref obj j))) + (if (or (char=? c #\\) + (char=? c #\")) + (string-append (substring obj i j) "\\" + (loop j (+ j 1))) + (loop i (+ j 1)))))))) + obj)) + + ((boolean? obj) (if obj "#t" "#f")) + + ((number? obj) (number->string obj)) + + ((symbol? obj) + (if format:symbol-case-conv + (format:symbol-case-conv (symbol->string obj)) + (symbol->string obj))) + + ((char? obj) + (if slashify + (format:char->str obj) + (string obj))) + + ((null? obj) "()") + + ((input-port? obj) + (format:iobj->str obj)) + + ((output-port? obj) + (format:iobj->str obj)) + + ((list? obj) + (string-append "(" + (let loop ((obj-list obj)) + (if (null? (cdr obj-list)) + (format:obj->str (car obj-list) #t) + (string-append + (format:obj->str (car obj-list) #t) + " " + (loop (cdr obj-list))))) + ")")) + + ((pair? obj) + (string-append "(" + (format:obj->str (car obj) #t) + " . " + (format:obj->str (cdr obj) #t) + ")")) + + ((vector? obj) + (string-append "#" (format:obj->str (vector->list obj) #t))) + + (else ; only objects with an #<...> + (format:iobj->str obj)))) ; representation should fall in here + +;; format:iobj->str reveals the implementation dependent representation of +;; #<...> objects with the use of display and call-with-output-string. +;; If format:read-proof is set to #t the resulting string is additionally +;; set into string quotes. + +(define format:read-proof #f) + +(define (format:iobj->str iobj) + (if (or format:read-proof + format:iobj-case-conv) + (string-append + (if format:read-proof "\"" "") + (if format:iobj-case-conv + (format:iobj-case-conv + (call-with-output-string (lambda (p) (display iobj p)))) + (call-with-output-string (lambda (p) (display iobj p)))) + (if format:read-proof "\"" "")) + (call-with-output-string (lambda (p) (display iobj p))))) + + +;; format:char->str converts a character into a slashified string as +;; done by `write'. The procedure is dependent on the integer +;; representation of characters and assumes a character number according to +;; the ASCII character set. + +(define (format:char->str ch) + (let ((int-rep (char->integer ch))) + (if (< int-rep 0) ; if chars are [-128...+127] + (set! int-rep (+ int-rep 256))) + (string-append + "#\\" + (cond + ((char=? ch #\newline) "newline") + ((and (>= int-rep 0) (<= int-rep 32)) + (vector-ref format:ascii-non-printable-charnames int-rep)) + ((= int-rep 127) "del") + ((>= int-rep 128) ; octal representation + (if format:radix-pref + (let ((s (number->string int-rep 8))) + (substring s 2 (string-length s))) + (number->string int-rep 8))) + (else (string ch)))))) + +(define format:space-ch (char->integer #\space)) +(define format:zero-ch (char->integer #\0)) + +(define (format:par pars length index default name) + (if (> length index) + (let ((par (list-ref pars index))) + (if par + (if name + (if (< par 0) + (format:error + "~s parameter must be a positive integer" name) + par) + par) + default)) + default)) + +(define (format:out-obj-padded pad-left obj slashify pars) + (if (null? pars) + (format:out-str (format:obj->str obj slashify)) + (let ((l (length pars))) + (let ((mincol (format:par pars l 0 0 "mincol")) + (colinc (format:par pars l 1 1 "colinc")) + (minpad (format:par pars l 2 0 "minpad")) + (padchar (integer->char + (format:par pars l 3 format:space-ch #f))) + (objstr (format:obj->str obj slashify))) + (if (not pad-left) + (format:out-str objstr)) + (do ((objstr-len (string-length objstr)) + (i minpad (+ i colinc))) + ((>= (+ objstr-len i) mincol) + (format:out-fill i padchar))) + (if pad-left + (format:out-str objstr)))))) + +(define (format:out-num-padded modifier number pars radix) + (if (not (integer? number)) (format:error "argument not an integer")) + (let ((numstr (number->string number radix))) + (if (and format:radix-pref (not (= radix 10))) + (set! numstr (substring numstr 2 (string-length numstr)))) + (if (and (null? pars) (not modifier)) + (format:out-str numstr) + (let ((l (length pars)) + (numstr-len (string-length numstr))) + (let ((mincol (format:par pars l 0 #f "mincol")) + (padchar (integer->char + (format:par pars l 1 format:space-ch #f))) + (commachar (integer->char + (format:par pars l 2 (char->integer #\,) #f))) + (commawidth (format:par pars l 3 3 "commawidth"))) + (if mincol + (let ((numlen numstr-len)) ; calc. the output len of number + (if (and (memq modifier '(at colon-at)) (> number 0)) + (set! numlen (+ numlen 1))) + (if (memq modifier '(colon colon-at)) + (set! numlen (+ (quotient (- numstr-len + (if (< number 0) 2 1)) + commawidth) + numlen))) + (if (> mincol numlen) + (format:out-fill (- mincol numlen) padchar)))) + (if (and (memq modifier '(at colon-at)) + (> number 0)) + (format:out-char #\+)) + (if (memq modifier '(colon colon-at)) ; insert comma character + (let ((start (remainder numstr-len commawidth)) + (ns (if (< number 0) 1 0))) + (format:out-substr numstr 0 start) + (do ((i start (+ i commawidth))) + ((>= i numstr-len)) + (if (> i ns) + (format:out-char commachar)) + (format:out-substr numstr i (+ i commawidth)))) + (format:out-str numstr))))))) + +(define (format:tabulate modifier pars) + (let ((l (length pars))) + (let ((colnum (format:par pars l 0 1 "colnum")) + (colinc (format:par pars l 1 1 "colinc")) + (padch (integer->char (format:par pars l 2 format:space-ch #f)))) + (case modifier + ((colon colon-at) + (format:error "unsupported modifier for ~~t")) + ((at) ; relative tabulation + (format:out-fill + (if (= colinc 0) + colnum ; colnum = colrel + (do ((c 0 (+ c colinc)) + (col (+ format:output-col colnum))) + ((>= c col) + (- c format:output-col)))) + padch)) + (else ; absolute tabulation + (format:out-fill + (cond + ((< format:output-col colnum) + (- colnum format:output-col)) + ((= colinc 0) + 0) + (else + (do ((c colnum (+ c colinc))) + ((>= c format:output-col) + (- c format:output-col))))) + padch)))))) + + +;; roman numerals (from dorai@cs.rice.edu). + +(define format:roman-alist + '((1000 #\M) (500 #\D) (100 #\C) (50 #\L) + (10 #\X) (5 #\V) (1 #\I))) + +(define format:roman-boundary-values + '(100 100 10 10 1 1 #f)) + +(define format:num->old-roman + (lambda (n) + (if (and (integer? n) (>= n 1)) + (let loop ((n n) + (romans format:roman-alist) + (s '())) + (if (null? romans) (list->string (reverse s)) + (let ((roman-val (caar romans)) + (roman-dgt (cadar romans))) + (do ((q (quotient n roman-val) (- q 1)) + (s s (cons roman-dgt s))) + ((= q 0) + (loop (remainder n roman-val) + (cdr romans) s)))))) + (format:error "only positive integers can be romanized")))) + +(define format:num->roman + (lambda (n) + (if (and (integer? n) (> n 0)) + (let loop ((n n) + (romans format:roman-alist) + (boundaries format:roman-boundary-values) + (s '())) + (if (null? romans) + (list->string (reverse s)) + (let ((roman-val (caar romans)) + (roman-dgt (cadar romans)) + (bdry (car boundaries))) + (let loop2 ((q (quotient n roman-val)) + (r (remainder n roman-val)) + (s s)) + (if (= q 0) + (if (and bdry (>= r (- roman-val bdry))) + (loop (remainder r bdry) (cdr romans) + (cdr boundaries) + (cons roman-dgt + (append + (cdr (assv bdry romans)) + s))) + (loop r (cdr romans) (cdr boundaries) s)) + (loop2 (- q 1) r (cons roman-dgt s))))))) + (format:error "only positive integers can be romanized")))) + +;; cardinals & ordinals (from dorai@cs.rice.edu) + +(define format:cardinal-ones-list + '(#f "one" "two" "three" "four" "five" + "six" "seven" "eight" "nine" "ten" "eleven" "twelve" "thirteen" + "fourteen" "fifteen" "sixteen" "seventeen" "eighteen" + "nineteen")) + +(define format:cardinal-tens-list + '(#f #f "twenty" "thirty" "forty" "fifty" "sixty" "seventy" "eighty" + "ninety")) + +(define format:num->cardinal999 + (lambda (n) + ;this procedure is inspired by the Bruno Haible's CLisp + ;function format-small-cardinal, which converts numbers + ;in the range 1 to 999, and is used for converting each + ;thousand-block in a larger number + (let* ((hundreds (quotient n 100)) + (tens+ones (remainder n 100)) + (tens (quotient tens+ones 10)) + (ones (remainder tens+ones 10))) + (append + (if (> hundreds 0) + (append + (string->list + (list-ref format:cardinal-ones-list hundreds)) + (string->list" hundred") + (if (> tens+ones 0) '(#\space) '())) + '()) + (if (< tens+ones 20) + (if (> tens+ones 0) + (string->list + (list-ref format:cardinal-ones-list tens+ones)) + '()) + (append + (string->list + (list-ref format:cardinal-tens-list tens)) + (if (> ones 0) + (cons #\- + (string->list + (list-ref format:cardinal-ones-list ones)))))))))) + +(define format:cardinal-thousand-block-list + '("" " thousand" " million" " billion" " trillion" " quadrillion" + " quintillion" " sextillion" " septillion" " octillion" " nonillion" + " decillion" " undecillion" " duodecillion" " tredecillion" + " quattuordecillion" " quindecillion" " sexdecillion" " septendecillion" + " octodecillion" " novemdecillion" " vigintillion")) + +(define format:num->cardinal + (lambda (n) + (cond ((not (integer? n)) + (format:error + "only integers can be converted to English cardinals")) + ((= n 0) "zero") + ((< n 0) (string-append "minus " (format:num->cardinal (- n)))) + (else + (let ((power3-word-limit + (length format:cardinal-thousand-block-list))) + (let loop ((n n) + (power3 0) + (s '())) + (if (= n 0) + (list->string s) + (let ((n-before-block (quotient n 1000)) + (n-after-block (remainder n 1000))) + (loop n-before-block + (+ power3 1) + (if (> n-after-block 0) + (append + (if (> n-before-block 0) + (string->list ", ") '()) + (format:num->cardinal999 n-after-block) + (if (< power3 power3-word-limit) + (string->list + (list-ref + format:cardinal-thousand-block-list + power3)) + (append + (string->list " times ten to the ") + (string->list + (format:num->ordinal + (* power3 3))) + (string->list " power"))) + s) + s)))))))))) + +(define format:ordinal-ones-list + '(#f "first" "second" "third" "fourth" "fifth" + "sixth" "seventh" "eighth" "ninth" "tenth" "eleventh" "twelfth" + "thirteenth" "fourteenth" "fifteenth" "sixteenth" "seventeenth" + "eighteenth" "nineteenth")) + +(define format:ordinal-tens-list + '(#f #f "twentieth" "thirtieth" "fortieth" "fiftieth" "sixtieth" + "seventieth" "eightieth" "ninetieth")) + +(define format:num->ordinal + (lambda (n) + (cond ((not (integer? n)) + (format:error + "only integers can be converted to English ordinals")) + ((= n 0) "zeroth") + ((< n 0) (string-append "minus " (format:num->ordinal (- n)))) + (else + (let ((hundreds (quotient n 100)) + (tens+ones (remainder n 100))) + (string-append + (if (> hundreds 0) + (string-append + (format:num->cardinal (* hundreds 100)) + (if (= tens+ones 0) "th" " ")) + "") + (if (= tens+ones 0) "" + (if (< tens+ones 20) + (list-ref format:ordinal-ones-list tens+ones) + (let ((tens (quotient tens+ones 10)) + (ones (remainder tens+ones 10))) + (if (= ones 0) + (list-ref format:ordinal-tens-list tens) + (string-append + (list-ref format:cardinal-tens-list tens) + "-" + (list-ref format:ordinal-ones-list ones)))) + )))))))) + +;; format fixed flonums (~F) + +(define (format:out-fixed modifier number pars) + (if (not (or (number? number) (string? number))) + (format:error "argument is not a number or a number string")) + + (let ((l (length pars))) + (let ((width (format:par pars l 0 #f "width")) + (digits (format:par pars l 1 #f "digits")) + (scale (format:par pars l 2 0 #f)) + (overch (format:par pars l 3 #f #f)) + (padch (format:par pars l 4 format:space-ch #f))) + + (if digits + + (begin ; fixed precision + (format:parse-float + (if (string? number) number (number->string number)) #t scale) + (if (<= (- format:fn-len format:fn-dot) digits) + (format:fn-zfill #f (- digits (- format:fn-len format:fn-dot))) + (format:fn-round digits)) + (if width + (let ((numlen (+ format:fn-len 1))) + (if (or (not format:fn-pos?) (eq? modifier 'at)) + (set! numlen (+ numlen 1))) + (if (and (= format:fn-dot 0) (> width (+ digits 1))) + (set! numlen (+ numlen 1))) + (if (< numlen width) + (format:out-fill (- width numlen) (integer->char padch))) + (if (and overch (> numlen width)) + (format:out-fill width (integer->char overch)) + (format:fn-out modifier (> width (+ digits 1))))) + (format:fn-out modifier #t))) + + (begin ; free precision + (format:parse-float + (if (string? number) number (number->string number)) #t scale) + (format:fn-strip) + (if width + (let ((numlen (+ format:fn-len 1))) + (if (or (not format:fn-pos?) (eq? modifier 'at)) + (set! numlen (+ numlen 1))) + (if (= format:fn-dot 0) + (set! numlen (+ numlen 1))) + (if (< numlen width) + (format:out-fill (- width numlen) (integer->char padch))) + (if (> numlen width) ; adjust precision if possible + (let ((dot-index (- numlen + (- format:fn-len format:fn-dot)))) + (if (> dot-index width) + (if overch ; numstr too big for required width + (format:out-fill width (integer->char overch)) + (format:fn-out modifier #t)) + (begin + (format:fn-round (- width dot-index)) + (format:fn-out modifier #t)))) + (format:fn-out modifier #t))) + (format:fn-out modifier #t))))))) + +;; format exponential flonums (~E) + +(define (format:out-expon modifier number pars) + (if (not (or (number? number) (string? number))) + (format:error "argument is not a number")) + + (let ((l (length pars))) + (let ((width (format:par pars l 0 #f "width")) + (digits (format:par pars l 1 #f "digits")) + (edigits (format:par pars l 2 #f "exponent digits")) + (scale (format:par pars l 3 1 #f)) + (overch (format:par pars l 4 #f #f)) + (padch (format:par pars l 5 format:space-ch #f)) + (expch (format:par pars l 6 #f #f))) + + (if digits ; fixed precision + + (let ((digits (if (> scale 0) + (if (< scale (+ digits 2)) + (+ (- digits scale) 1) + 0) + digits))) + (format:parse-float + (if (string? number) number (number->string number)) #f scale) + (if (<= (- format:fn-len format:fn-dot) digits) + (format:fn-zfill #f (- digits (- format:fn-len format:fn-dot))) + (format:fn-round digits)) + (if width + (if (and edigits overch (> format:en-len edigits)) + (format:out-fill width (integer->char overch)) + (let ((numlen (+ format:fn-len 3))) ; .E+ + (if (or (not format:fn-pos?) (eq? modifier 'at)) + (set! numlen (+ numlen 1))) + (if (and (= format:fn-dot 0) (> width (+ digits 1))) + (set! numlen (+ numlen 1))) + (set! numlen + (+ numlen + (if (and edigits (>= edigits format:en-len)) + edigits + format:en-len))) + (if (< numlen width) + (format:out-fill (- width numlen) + (integer->char padch))) + (if (and overch (> numlen width)) + (format:out-fill width (integer->char overch)) + (begin + (format:fn-out modifier (> width (- numlen 1))) + (format:en-out edigits expch))))) + (begin + (format:fn-out modifier #t) + (format:en-out edigits expch)))) + + (begin ; free precision + (format:parse-float + (if (string? number) number (number->string number)) #f scale) + (format:fn-strip) + (if width + (if (and edigits overch (> format:en-len edigits)) + (format:out-fill width (integer->char overch)) + (let ((numlen (+ format:fn-len 3))) ; .E+ + (if (or (not format:fn-pos?) (eq? modifier 'at)) + (set! numlen (+ numlen 1))) + (if (= format:fn-dot 0) + (set! numlen (+ numlen 1))) + (set! numlen + (+ numlen + (if (and edigits (>= edigits format:en-len)) + edigits + format:en-len))) + (if (< numlen width) + (format:out-fill (- width numlen) + (integer->char padch))) + (if (> numlen width) ; adjust precision if possible + (let ((f (- format:fn-len format:fn-dot))) ; fract len + (if (> (- numlen f) width) + (if overch ; numstr too big for required width + (format:out-fill width + (integer->char overch)) + (begin + (format:fn-out modifier #t) + (format:en-out edigits expch))) + (begin + (format:fn-round (+ (- f numlen) width)) + (format:fn-out modifier #t) + (format:en-out edigits expch)))) + (begin + (format:fn-out modifier #t) + (format:en-out edigits expch))))) + (begin + (format:fn-out modifier #t) + (format:en-out edigits expch)))))))) + +;; format general flonums (~G) + +(define (format:out-general modifier number pars) + (if (not (or (number? number) (string? number))) + (format:error "argument is not a number or a number string")) + + (let ((l (length pars))) + (let ((width (if (> l 0) (list-ref pars 0) #f)) + (digits (if (> l 1) (list-ref pars 1) #f)) + (edigits (if (> l 2) (list-ref pars 2) #f)) + (overch (if (> l 4) (list-ref pars 4) #f)) + (padch (if (> l 5) (list-ref pars 5) #f))) + (format:parse-float + (if (string? number) number (number->string number)) #t 0) + (format:fn-strip) + (let* ((ee (if edigits (+ edigits 2) 4)) ; for the following algorithm + (ww (if width (- width ee) #f)) ; see Steele's CL book p.395 + (n (if (= format:fn-dot 0) ; number less than (abs 1.0) ? + (- (format:fn-zlead)) + format:fn-dot)) + (d (if digits + digits + (max format:fn-len (min n 7)))) ; q = format:fn-len + (dd (- d n))) + (if (<= 0 dd d) + (begin + (format:out-fixed modifier number (list ww dd #f overch padch)) + (format:out-fill ee #\space)) ;~@T not implemented yet + (format:out-expon modifier number pars)))))) + +;; format dollar flonums (~$) + +(define (format:out-dollar modifier number pars) + (if (not (or (number? number) (string? number))) + (format:error "argument is not a number or a number string")) + + (let ((l (length pars))) + (let ((digits (format:par pars l 0 2 "digits")) + (mindig (format:par pars l 1 1 "mindig")) + (width (format:par pars l 2 0 "width")) + (padch (format:par pars l 3 format:space-ch #f))) + + (format:parse-float + (if (string? number) number (number->string number)) #t 0) + (if (<= (- format:fn-len format:fn-dot) digits) + (format:fn-zfill #f (- digits (- format:fn-len format:fn-dot))) + (format:fn-round digits)) + (let ((numlen (+ format:fn-len 1))) + (if (or (not format:fn-pos?) (memq modifier '(at colon-at))) + (set! numlen (+ numlen 1))) + (if (and mindig (> mindig format:fn-dot)) + (set! numlen (+ numlen (- mindig format:fn-dot)))) + (if (and (= format:fn-dot 0) (not mindig)) + (set! numlen (+ numlen 1))) + (if (< numlen width) + (case modifier + ((colon) + (if (not format:fn-pos?) + (format:out-char #\-)) + (format:out-fill (- width numlen) (integer->char padch))) + ((at) + (format:out-fill (- width numlen) (integer->char padch)) + (format:out-char (if format:fn-pos? #\+ #\-))) + ((colon-at) + (format:out-char (if format:fn-pos? #\+ #\-)) + (format:out-fill (- width numlen) (integer->char padch))) + (else + (format:out-fill (- width numlen) (integer->char padch)) + (if (not format:fn-pos?) + (format:out-char #\-)))) + (if format:fn-pos? + (if (memq modifier '(at colon-at)) (format:out-char #\+)) + (format:out-char #\-)))) + (if (and mindig (> mindig format:fn-dot)) + (format:out-fill (- mindig format:fn-dot) #\0)) + (if (and (= format:fn-dot 0) (not mindig)) + (format:out-char #\0)) + (format:out-substr format:fn-str 0 format:fn-dot) + (format:out-char #\.) + (format:out-substr format:fn-str format:fn-dot format:fn-len)))) + +; the flonum buffers + +(define format:fn-max 200) ; max. number of number digits +(define format:fn-str (make-string format:fn-max)) ; number buffer +(define format:fn-len 0) ; digit length of number +(define format:fn-dot #f) ; dot position of number +(define format:fn-pos? #t) ; number positive? +(define format:en-max 10) ; max. number of exponent digits +(define format:en-str (make-string format:en-max)) ; exponent buffer +(define format:en-len 0) ; digit length of exponent +(define format:en-pos? #t) ; exponent positive? + +(define (format:parse-float num-str fixed? scale) + (set! format:fn-pos? #t) + (set! format:fn-len 0) + (set! format:fn-dot #f) + (set! format:en-pos? #t) + (set! format:en-len 0) + (do ((i 0 (+ i 1)) + (left-zeros 0) + (mantissa? #t) + (all-zeros? #t) + (num-len (string-length num-str)) + (c #f)) ; current exam. character in num-str + ((= i num-len) + (if (not format:fn-dot) + (set! format:fn-dot format:fn-len)) + + (if all-zeros? + (begin + (set! left-zeros 0) + (set! format:fn-dot 0) + (set! format:fn-len 1))) + + ;; now format the parsed values according to format's need + + (if fixed? + + (begin ; fixed format m.nnn or .nnn + (if (and (> left-zeros 0) (> format:fn-dot 0)) + (if (> format:fn-dot left-zeros) + (begin ; norm 0{0}nn.mm to nn.mm + (format:fn-shiftleft left-zeros) + (set! left-zeros 0) + (set! format:fn-dot (- format:fn-dot left-zeros))) + (begin ; normalize 0{0}.nnn to .nnn + (format:fn-shiftleft format:fn-dot) + (set! left-zeros (- left-zeros format:fn-dot)) + (set! format:fn-dot 0)))) + (if (or (not (= scale 0)) (> format:en-len 0)) + (let ((shift (+ scale (format:en-int)))) + (cond + (all-zeros? #t) + ((> (+ format:fn-dot shift) format:fn-len) + (format:fn-zfill + #f (- shift (- format:fn-len format:fn-dot))) + (set! format:fn-dot format:fn-len)) + ((< (+ format:fn-dot shift) 0) + (format:fn-zfill #t (- (- shift) format:fn-dot)) + (set! format:fn-dot 0)) + (else + (if (> left-zeros 0) + (if (<= left-zeros shift) ; shift always > 0 here + (format:fn-shiftleft shift) ; shift out 0s + (begin + (format:fn-shiftleft left-zeros) + (set! format:fn-dot (- shift left-zeros)))) + (set! format:fn-dot (+ format:fn-dot shift)))))))) + + (let ((negexp ; expon format m.nnnEee + (if (> left-zeros 0) + (- left-zeros format:fn-dot -1) + (if (= format:fn-dot 0) 1 0)))) + (if (> left-zeros 0) + (begin ; normalize 0{0}.nnn to n.nn + (format:fn-shiftleft left-zeros) + (set! format:fn-dot 1)) + (if (= format:fn-dot 0) + (set! format:fn-dot 1))) + (format:en-set (- (+ (- format:fn-dot scale) (format:en-int)) + negexp)) + (cond + (all-zeros? + (format:en-set 0) + (set! format:fn-dot 1)) + ((< scale 0) ; leading zero + (format:fn-zfill #t (- scale)) + (set! format:fn-dot 0)) + ((> scale format:fn-dot) + (format:fn-zfill #f (- scale format:fn-dot)) + (set! format:fn-dot scale)) + (else + (set! format:fn-dot scale))))) + #t) + + ;; do body + (set! c (string-ref num-str i)) ; parse the output of number->string + (cond ; which can be any valid number + ((char-numeric? c) ; representation of R4RS except + (if mantissa? ; complex numbers + (begin + (if (char=? c #\0) + (if all-zeros? + (set! left-zeros (+ left-zeros 1))) + (begin + (set! all-zeros? #f))) + (string-set! format:fn-str format:fn-len c) + (set! format:fn-len (+ format:fn-len 1))) + (begin + (string-set! format:en-str format:en-len c) + (set! format:en-len (+ format:en-len 1))))) + ((or (char=? c #\-) (char=? c #\+)) + (if mantissa? + (set! format:fn-pos? (char=? c #\+)) + (set! format:en-pos? (char=? c #\+)))) + ((char=? c #\.) + (set! format:fn-dot format:fn-len)) + ((char=? c #\e) + (set! mantissa? #f)) + ((char=? c #\E) + (set! mantissa? #f)) + ((char-whitespace? c) #t) + ((char=? c #\d) #t) ; decimal radix prefix + ((char=? c #\#) #t) + (else + (format:error "illegal character `~c' in number->string" c))))) + +(define (format:en-int) ; convert exponent string to integer + (if (= format:en-len 0) + 0 + (do ((i 0 (+ i 1)) + (n 0)) + ((= i format:en-len) + (if format:en-pos? + n + (- n))) + (set! n (+ (* n 10) (- (char->integer (string-ref format:en-str i)) + format:zero-ch)))))) + +(define (format:en-set en) ; set exponent string number + (set! format:en-len 0) + (set! format:en-pos? (>= en 0)) + (let ((en-str (number->string en))) + (do ((i 0 (+ i 1)) + (en-len (string-length en-str)) + (c #f)) + ((= i en-len)) + (set! c (string-ref en-str i)) + (if (char-numeric? c) + (begin + (string-set! format:en-str format:en-len c) + (set! format:en-len (+ format:en-len 1))))))) + +(define (format:fn-zfill left? n) ; fill current number string with 0s + (if (> (+ n format:fn-len) format:fn-max) ; from the left or right + (format:error "number is too long to format (enlarge format:fn-max)")) + (set! format:fn-len (+ format:fn-len n)) + (if left? + (do ((i format:fn-len (- i 1))) ; fill n 0s to left + ((< i 0)) + (string-set! format:fn-str i + (if (< i n) + #\0 + (string-ref format:fn-str (- i n))))) + (do ((i (- format:fn-len n) (+ i 1))) ; fill n 0s to the right + ((= i format:fn-len)) + (string-set! format:fn-str i #\0)))) + +(define (format:fn-shiftleft n) ; shift left current number n positions + (if (> n format:fn-len) + (format:error "internal error in format:fn-shiftleft (~d,~d)" + n format:fn-len)) + (do ((i n (+ i 1))) + ((= i format:fn-len) + (set! format:fn-len (- format:fn-len n))) + (string-set! format:fn-str (- i n) (string-ref format:fn-str i)))) + +(define (format:fn-round digits) ; round format:fn-str + (set! digits (+ digits format:fn-dot)) + (do ((i digits (- i 1)) ; "099",2 -> "10" + (c 5)) ; "023",2 -> "02" + ((or (= c 0) (< i 0)) ; "999",2 -> "100" + (if (= c 1) ; "005",2 -> "01" + (begin ; carry overflow + (set! format:fn-len digits) + (format:fn-zfill #t 1) ; add a 1 before fn-str + (string-set! format:fn-str 0 #\1) + (set! format:fn-dot (+ format:fn-dot 1))) + (set! format:fn-len digits))) + (set! c (+ (- (char->integer (string-ref format:fn-str i)) + format:zero-ch) c)) + (string-set! format:fn-str i (integer->char + (if (< c 10) + (+ c format:zero-ch) + (+ (- c 10) format:zero-ch)))) + (set! c (if (< c 10) 0 1)))) + +(define (format:fn-out modifier add-leading-zero?) + (if format:fn-pos? + (if (eq? modifier 'at) + (format:out-char #\+)) + (format:out-char #\-)) + (if (= format:fn-dot 0) + (if add-leading-zero? + (format:out-char #\0)) + (format:out-substr format:fn-str 0 format:fn-dot)) + (format:out-char #\.) + (format:out-substr format:fn-str format:fn-dot format:fn-len)) + +(define (format:en-out edigits expch) + (format:out-char (if expch (integer->char expch) format:expch)) + (format:out-char (if format:en-pos? #\+ #\-)) + (if edigits + (if (< format:en-len edigits) + (format:out-fill (- edigits format:en-len) #\0))) + (format:out-substr format:en-str 0 format:en-len)) + +(define (format:fn-strip) ; strip trailing zeros but one + (string-set! format:fn-str format:fn-len #\0) + (do ((i format:fn-len (- i 1))) + ((or (not (char=? (string-ref format:fn-str i) #\0)) + (<= i format:fn-dot)) + (set! format:fn-len (+ i 1))))) + +(define (format:fn-zlead) ; count leading zeros + (do ((i 0 (+ i 1))) + ((or (= i format:fn-len) + (not (char=? (string-ref format:fn-str i) #\0))) + (if (= i format:fn-len) ; found a real zero + 0 + i)))) + + +;;; some global functions not found in SLIB + +;; string-index finds the index of the first occurence of the character `c' +;; in the string `s'; it returns #f if there is no such character in `s'. + +(define (string-index s c) + (let ((slen-1 (- (string-length s) 1))) + (let loop ((i 0)) + (cond + ((char=? c (string-ref s i)) i) + ((= i slen-1) #f) + (else (loop (+ i 1))))))) + +(define (string-capitalize-first str) ; "hello" -> "Hello" + (let ((cap-str (string-copy str)) ; "hELLO" -> "Hello" + (non-first-alpha #f) ; "*hello" -> "*Hello" + (str-len (string-length str))) ; "hello you" -> "Hello you" + (do ((i 0 (+ i 1))) + ((= i str-len) cap-str) + (let ((c (string-ref str i))) + (if (char-alphabetic? c) + (if non-first-alpha + (string-set! cap-str i (char-downcase c)) + (begin + (set! non-first-alpha #t) + (string-set! cap-str i (char-upcase c))))))))) + +(define (list-head l k) + (if (= k 0) + '() + (cons (car l) (list-head (cdr l) (- k 1))))) + + +;; Aborts the program when a formatting error occures. This is a null +;; argument closure to jump to the interpreters toplevel continuation. + +(define format:abort (lambda () (slib:error "error in format"))) + +(define format format:format) + +;; If this is not possible then a continuation is used to recover +;; properly from a format error. In this case format returns #f. + +;(define format:abort +; (lambda () (format:error-continuation #f))) + +;(define format +; (lambda args ; wraps format:format with an error +; (call-with-current-continuation ; continuation +; (lambda (cont) +; (set! format:error-continuation cont) +; (apply format:format args))))) + +;eof diff --git a/formatst.scm b/formatst.scm new file mode 100644 index 0000000..7a2173e --- /dev/null +++ b/formatst.scm @@ -0,0 +1,647 @@ +;; "formatst.scm" SLIB FORMAT Version 3.0 conformance test +; Written by Dirk Lutzebaeck (lutzeb@cs.tu-berlin.de) +; +; This code is in the public domain. + +;; Test run: (slib:load "formatst") + +; Failure reports for various scheme interpreters: +; +; SCM4d +; None. +; Elk 2.2: +; None. +; MIT C-Scheme 7.1: +; The empty list is always evaluated as a boolean and consequently +; represented as `#f'. +; Scheme->C 01nov91: +; None, if format:symbol-case-conv and format:iobj-case-conv are set +; to string-downcase. + +(require 'format) +(if (not (string=? format:version "3.0")) + (begin + (display "You have format version ") + (display format:version) + (display ". This test is for format version 3.0!") + (newline) + (format:abort))) + +(define fails 0) +(define total 0) +(define test-verbose #f) ; shows each test performed + +(define (test format-args out-str) + (set! total (+ total 1)) + (if (not test-verbose) + (if (zero? (modulo total 10)) + (begin + (display total) + (display ",") + (force-output (current-output-port))))) + (let ((format-out (apply format `(#f ,@format-args)))) + (if (string=? out-str format-out) + (if test-verbose + (begin + (display "Verified ") + (write format-args) + (display " returns ") + (write out-str) + (newline))) + (begin + (set! fails (+ fails 1)) + (if (not test-verbose) (newline)) + (display "*Failed* ") + (write format-args) + (newline) + (display " returns ") + (write format-out) + (newline) + (display " expected ") + (write out-str) + (newline))))) + +; ensure format default configuration + +(set! format:symbol-case-conv #f) +(set! format:iobj-case-conv #f) +(set! format:read-proof #f) + +(format #t "~q") + +(format #t "This implementation has~@[ no~] flonums ~ + ~:[but no~;and~] complex numbers~%" + (not format:floats) format:complex-numbers) + +; any object test + +(test '("abc") "abc") +(test '("~a" 10) "10") +(test '("~a" -1.2) "-1.2") +(test '("~a" a) "a") +(test '("~a" #t) "#t") +(test '("~a" #f) "#f") +(test '("~a" "abc") "abc") +(test '("~a" #(1 2 3)) "#(1 2 3)") +(test '("~a" ()) "()") +(test '("~a" (a)) "(a)") +(test '("~a" (a b)) "(a b)") +(test '("~a" (a (b c) d)) "(a (b c) d)") +(test '("~a" (a . b)) "(a . b)") +(test '("~a" (a (b c . d))) "(a (b . (c . d)))") ; this is ugly +(test `("~a" ,display) (format:iobj->str display)) +(test `("~a" ,(current-input-port)) (format:iobj->str (current-input-port))) +(test `("~a" ,(current-output-port)) (format:iobj->str (current-output-port))) + +; # argument test + +(test '("~a ~a" 10 20) "10 20") +(test '("~a abc ~a def" 10 20) "10 abc 20 def") + +; numerical test + +(test '("~d" 100) "100") +(test '("~x" 100) "64") +(test '("~o" 100) "144") +(test '("~b" 100) "1100100") +(test '("~@d" 100) "+100") +(test '("~@d" -100) "-100") +(test '("~@x" 100) "+64") +(test '("~@o" 100) "+144") +(test '("~@b" 100) "+1100100") +(test '("~10d" 100) " 100") +(test '("~:d" 123) "123") +(test '("~:d" 1234) "1,234") +(test '("~:d" 12345) "12,345") +(test '("~:d" 123456) "123,456") +(test '("~:d" 12345678) "12,345,678") +(test '("~:d" -123) "-123") +(test '("~:d" -1234) "-1,234") +(test '("~:d" -12345) "-12,345") +(test '("~:d" -123456) "-123,456") +(test '("~:d" -12345678) "-12,345,678") +(test '("~10:d" 1234) " 1,234") +(test '("~10:d" -1234) " -1,234") +(test '("~10,'*d" 100) "*******100") +(test '("~10,,'|:d" 12345678) "12|345|678") +(test '("~10,,,2:d" 12345678) "12,34,56,78") +(test '("~14,'*,'|,4:@d" 12345678) "****+1234|5678") +(test '("~10r" 100) "100") +(test '("~2r" 100) "1100100") +(test '("~8r" 100) "144") +(test '("~16r" 100) "64") +(test '("~16,10,'*r" 100) "********64") + +; roman numeral test + +(test '("~@r" 4) "IV") +(test '("~@r" 19) "XIX") +(test '("~@r" 50) "L") +(test '("~@r" 100) "C") +(test '("~@r" 1000) "M") +(test '("~@r" 99) "XCIX") +(test '("~@r" 1994) "MCMXCIV") + +; old roman numeral test + +(test '("~:@r" 4) "IIII") +(test '("~:@r" 5) "V") +(test '("~:@r" 10) "X") +(test '("~:@r" 9) "VIIII") + +; cardinal/ordinal English number test + +(test '("~r" 4) "four") +(test '("~r" 10) "ten") +(test '("~r" 19) "nineteen") +(test '("~r" 1984) "one thousand, nine hundred eighty-four") +(test '("~:r" -1984) "minus one thousand, nine hundred eighty-fourth") + +; character test + +(test '("~c" #\a) "a") +(test '("~@c" #\a) "#\\a") +(test `("~@c" ,(integer->char 32)) "#\\space") +(test `("~@c" ,(integer->char 0)) "#\\nul") +(test `("~@c" ,(integer->char 27)) "#\\esc") +(test `("~@c" ,(integer->char 127)) "#\\del") +(test `("~@c" ,(integer->char 128)) "#\\200") +(test `("~@c" ,(integer->char 255)) "#\\377") +(test '("~65c") "A") +(test '("~7@c") "#\\bel") +(test '("~:c" #\a) "a") +(test `("~:c" ,(integer->char 1)) "^A") +(test `("~:c" ,(integer->char 27)) "^[") +(test '("~7:c") "^G") +(test `("~:c" ,(integer->char 128)) "#\\200") +(test `("~:c" ,(integer->char 127)) "#\\177") +(test `("~:c" ,(integer->char 255)) "#\\377") + + +; plural test + +(test '("test~p" 1) "test") +(test '("test~p" 2) "tests") +(test '("test~p" 0) "tests") +(test '("tr~@p" 1) "try") +(test '("tr~@p" 2) "tries") +(test '("tr~@p" 0) "tries") +(test '("~a test~:p" 10) "10 tests") +(test '("~a test~:p" 1) "1 test") + +; tilde test + +(test '("~~~~") "~~") +(test '("~3~") "~~~") + +; whitespace character test + +(test '("~%") " +") +(test '("~3%") " + + +") +(test '("~&") "") +(test '("abc~&") "abc +") +(test '("abc~&def") "abc +def") +(test '("~&") " +") +(test '("~3&") " + +") +(test '("abc~3&") "abc + + +") +(test '("~|") (string slib:form-feed)) +(test '("~_~_~_") " ") +(test '("~3_") " ") +(test '("~/") (string slib:tab)) +(test '("~3/") (make-string 3 slib:tab)) + +; tabulate test + +(test '("~0&~3t") " ") +(test '("~0&~10t") " ") +(test '("~10t") "") +(test '("~0&1234567890~,8tABC") "1234567890 ABC") +(test '("~0&1234567890~0,8tABC") "1234567890 ABC") +(test '("~0&1234567890~1,8tABC") "1234567890 ABC") +(test '("~0&1234567890~2,8tABC") "1234567890ABC") +(test '("~0&1234567890~3,8tABC") "1234567890 ABC") +(test '("~0&1234567890~4,8tABC") "1234567890 ABC") +(test '("~0&1234567890~5,8tABC") "1234567890 ABC") +(test '("~0&1234567890~6,8tABC") "1234567890 ABC") +(test '("~0&1234567890~7,8tABC") "1234567890 ABC") +(test '("~0&1234567890~8,8tABC") "1234567890 ABC") +(test '("~0&1234567890~9,8tABC") "1234567890 ABC") +(test '("~0&1234567890~10,8tABC") "1234567890ABC") +(test '("~0&1234567890~11,8tABC") "1234567890 ABC") +(test '("~0&12345~,8tABCDE~,8tXYZ") "12345 ABCDE XYZ") +(test '("~,8t+++~,8t===") " +++ ===") +(test '("~0&ABC~,8,'.tDEF") "ABC......DEF") +(test '("~0&~3,8@tABC") " ABC") +(test '("~0&1234~3,8@tABC") "1234 ABC") +(test '("~0&12~3,8@tABC~3,8@tDEF") "12 ABC DEF") + +; indirection test + +(test '("~a ~? ~a" 10 "~a ~a" (20 30) 40) "10 20 30 40") +(test '("~a ~@? ~a" 10 "~a ~a" 20 30 40) "10 20 30 40") + +; field test + +(test '("~10a" "abc") "abc ") +(test '("~10@a" "abc") " abc") +(test '("~10a" "0123456789abc") "0123456789abc") +(test '("~10@a" "0123456789abc") "0123456789abc") + +; pad character test + +(test '("~10,,,'*a" "abc") "abc*******") +(test '("~10,,,'Xa" "abc") "abcXXXXXXX") +(test '("~10,,,42a" "abc") "abc*******") +(test '("~10,,,'*@a" "abc") "*******abc") +(test '("~10,,3,'*a" "abc") "abc*******") +(test '("~10,,3,'*a" "0123456789abc") "0123456789abc***") ; min. padchar length +(test '("~10,,3,'*@a" "0123456789abc") "***0123456789abc") + +; colinc, minpad padding test + +(test '("~10,8,0,'*a" 123) "123********") +(test '("~10,9,0,'*a" 123) "123*********") +(test '("~10,10,0,'*a" 123) "123**********") +(test '("~10,11,0,'*a" 123) "123***********") +(test '("~8,1,0,'*a" 123) "123*****") +(test '("~8,2,0,'*a" 123) "123******") +(test '("~8,3,0,'*a" 123) "123******") +(test '("~8,4,0,'*a" 123) "123********") +(test '("~8,5,0,'*a" 123) "123*****") +(test '("~8,1,3,'*a" 123) "123*****") +(test '("~8,1,5,'*a" 123) "123*****") +(test '("~8,1,6,'*a" 123) "123******") +(test '("~8,1,9,'*a" 123) "123*********") + +; slashify test + +(test '("~s" "abc") "\"abc\"") +(test '("~s" "abc \\ abc") "\"abc \\\\ abc\"") +(test '("~a" "abc \\ abc") "abc \\ abc") +(test '("~s" "abc \" abc") "\"abc \\\" abc\"") +(test '("~a" "abc \" abc") "abc \" abc") +(test '("~s" #\space) "#\\space") +(test '("~s" #\newline) "#\\newline") +(test '("~s" #\tab) "#\\ht") +(test '("~s" #\a) "#\\a") +(test '("~a" (a "b" c)) "(a \"b\" c)") + +; symbol case force test + +(define format:old-scc format:symbol-case-conv) +(set! format:symbol-case-conv string-upcase) +(test '("~a" abc) "ABC") +(set! format:symbol-case-conv string-downcase) +(test '("~s" abc) "abc") +(set! format:symbol-case-conv string-capitalize) +(test '("~s" abc) "Abc") +(set! format:symbol-case-conv format:old-scc) + +; read proof test + +(test `("~:s" ,display) + (begin + (set! format:read-proof #t) + (format:iobj->str display))) +(test `("~:a" ,display) + (begin + (set! format:read-proof #t) + (format:iobj->str display))) +(test `("~:a" (1 2 ,display)) + (begin + (set! format:read-proof #t) + (string-append "(1 2 " (format:iobj->str display) ")"))) +(test '("~:a" "abc") "abc") +(set! format:read-proof #f) + +; internal object case type force test + +(set! format:iobj-case-conv string-upcase) +(test `("~a" ,display) (string-upcase (format:iobj->str display))) +(set! format:iobj-case-conv string-downcase) +(test `("~s" ,display) (string-downcase (format:iobj->str display))) +(set! format:iobj-case-conv string-capitalize) +(test `("~s" ,display) (string-capitalize (format:iobj->str display))) +(set! format:iobj-case-conv #f) + +; continuation line test + +(test '("abc~ + 123") "abc123") +(test '("abc~ +123") "abc123") +(test '("abc~ +") "abc") +(test '("abc~: + def") "abc def") +(test '("abc~@ + def") +"abc +def") + +; flush output (can't test it here really) + +(test '("abc ~! xyz") "abc xyz") + +; string case conversion + +(test '("~a ~(~a~) ~a" "abc" "HELLO WORLD" "xyz") "abc hello world xyz") +(test '("~a ~:(~a~) ~a" "abc" "HELLO WORLD" "xyz") "abc Hello World xyz") +(test '("~a ~@(~a~) ~a" "abc" "HELLO WORLD" "xyz") "abc Hello world xyz") +(test '("~a ~:@(~a~) ~a" "abc" "hello world" "xyz") "abc HELLO WORLD xyz") +(test '("~:@(~a~)" (a b c)) "(A B C)") +(test '("~:@(~x~)" 255) "FF") +(test '("~:@(~p~)" 2) "S") +(test `("~:@(~a~)" ,display) (string-upcase (format:iobj->str display))) +(test '("~:(~a ~a ~a~) ~a" "abc" "xyz" "123" "world") "Abc Xyz 123 world") + +; variable parameter + +(test '("~va" 10 "abc") "abc ") +(test '("~v,,,va" 10 42 "abc") "abc*******") + +; number of remaining arguments as parameter + +(test '("~#,,,'*@a ~a ~a ~a" 1 1 1 1) "***1 1 1 1") + +; argument jumping + +(test '("~a ~* ~a" 10 20 30) "10 30") +(test '("~a ~2* ~a" 10 20 30 40) "10 40") +(test '("~a ~:* ~a" 10) "10 10") +(test '("~a ~a ~2:* ~a ~a" 10 20) "10 20 10 20") +(test '("~a ~a ~@* ~a ~a" 10 20) "10 20 10 20") +(test '("~a ~a ~4@* ~a ~a" 10 20 30 40 50 60) "10 20 50 60") + +; conditionals + +(test '("~[abc~;xyz~]" 0) "abc") +(test '("~[abc~;xyz~]" 1) "xyz") +(test '("~[abc~;xyz~:;456~]" 99) "456") +(test '("~0[abc~;xyz~:;456~]") "abc") +(test '("~1[abc~;xyz~:;456~] ~a" 100) "xyz 100") +(test '("~#[no arg~;~a~;~a and ~a~;~a, ~a and ~a~]") "no arg") +(test '("~#[no arg~;~a~;~a and ~a~;~a, ~a and ~a~]" 10) "10") +(test '("~#[no arg~;~a~;~a and ~a~;~a, ~a and ~a~]" 10 20) "10 and 20") +(test '("~#[no arg~;~a~;~a and ~a~;~a, ~a and ~a~]" 10 20 30) "10, 20 and 30") +(test '("~:[hello~;world~] ~a" #t 10) "world 10") +(test '("~:[hello~;world~] ~a" #f 10) "hello 10") +(test '("~@[~a tests~]" #f) "") +(test '("~@[~a tests~]" 10) "10 tests") +(test '("~@[~a test~:p~] ~a" 10 done) "10 tests done") +(test '("~@[~a test~:p~] ~a" 1 done) "1 test done") +(test '("~@[~a test~:p~] ~a" 0 done) "0 tests done") +(test '("~@[~a test~:p~] ~a" #f done) " done") +(test '("~@[ level = ~d~]~@[ length = ~d~]" #f 5) " length = 5") +(test '("~[abc~;~[4~;5~;6~]~;xyz~]" 0) "abc") ; nested conditionals (irrghh) +(test '("~[abc~;~[4~;5~;6~]~;xyz~]" 2) "xyz") +(test '("~[abc~;~[4~;5~;6~]~;xyz~]" 1 2) "6") + +; iteration + +(test '("~{ ~a ~}" (a b c)) " a b c ") +(test '("~{ ~a ~}" ()) "") +(test '("~{ ~a ~5,,,'*a~}" (a b c d)) " a b**** c d****") +(test '("~{ ~a,~a ~}" (a 1 b 2 c 3)) " a,1 b,2 c,3 ") +(test '("~2{ ~a,~a ~}" (a 1 b 2 c 3)) " a,1 b,2 ") +(test '("~3{~a ~} ~a" (a b c d e) 100) "a b c 100") +(test '("~0{~a ~} ~a" (a b c d e) 100) " 100") +(test '("~:{ ~a,~a ~}" ((a b) (c d e f) (g h))) " a,b c,d g,h ") +(test '("~2:{ ~a,~a ~}" ((a b) (c d e f) (g h))) " a,b c,d ") +(test '("~@{ ~a,~a ~}" a 1 b 2 c 3) " a,1 b,2 c,3 ") +(test '("~2@{ ~a,~a ~} <~a|~a>" a 1 b 2 c 3) " a,1 b,2 <c|3>") +(test '("~:@{ ~a,~a ~}" (a 1) (b 2) (c 3)) " a,1 b,2 c,3 ") +(test '("~2:@{ ~a,~a ~} ~a" (a 1) (b 2) (c 3)) " a,1 b,2 (c 3)") +(test '("~{~}" "<~a,~a>" (a 1 b 2 c 3)) "<a,1><b,2><c,3>") +(test '("~{ ~a ~{<~a>~}~} ~a" (a (1 2) b (3 4)) 10) " a <1><2> b <3><4> 10") + +; up and out + +(test '("abc ~^ xyz") "abc ") +(test '("~@(abc ~^ xyz~) ~a" 10) "ABC xyz 10") +(test '("done. ~^ ~d warning~:p. ~^ ~d error~:p.") "done. ") +(test '("done. ~^ ~d warning~:p. ~^ ~d error~:p." 10) "done. 10 warnings. ") +(test '("done. ~^ ~d warning~:p. ~^ ~d error~:p." 10 1) + "done. 10 warnings. 1 error.") +(test '("~{ ~a ~^<~a>~} ~a" (a b c d e f) 10) " a <b> c <d> e <f> 10") +(test '("~{ ~a ~^<~a>~} ~a" (a b c d e) 10) " a <b> c <d> e 10") +(test '("abc~0^ xyz") "abc") +(test '("abc~9^ xyz") "abc xyz") +(test '("abc~7,4^ xyz") "abc xyz") +(test '("abc~7,7^ xyz") "abc") +(test '("abc~3,7,9^ xyz") "abc") +(test '("abc~8,7,9^ xyz") "abc xyz") +(test '("abc~3,7,5^ xyz") "abc xyz") + +; complexity tests (oh my god, I hardly understand them myself (see CL std)) + +(define fmt "Items:~#[ none~; ~a~; ~a and ~a~:;~@{~#[~; and~] ~a~^,~}~].") + +(test `(,fmt ) "Items: none.") +(test `(,fmt foo) "Items: foo.") +(test `(,fmt foo bar) "Items: foo and bar.") +(test `(,fmt foo bar baz) "Items: foo, bar, and baz.") +(test `(,fmt foo bar baz zok) "Items: foo, bar, baz, and zok.") + +; fixed floating points + +(cond + (format:floats + (test '("~6,2f" 3.14159) " 3.14") + (test '("~6,1f" 3.14159) " 3.1") + (test '("~6,0f" 3.14159) " 3.") + (test '("~5,1f" 0) " 0.0") + (test '("~10,7f" 3.14159) " 3.1415900") + (test '("~10,7f" -3.14159) "-3.1415900") + (test '("~10,7@f" 3.14159) "+3.1415900") + (test '("~6,3f" 0.0) " 0.000") + (test '("~6,4f" 0.007) "0.0070") + (test '("~6,3f" 0.007) " 0.007") + (test '("~6,2f" 0.007) " 0.01") + (test '("~3,2f" 0.007) ".01") + (test '("~3,2f" -0.007) "-.01") + (test '("~6,2,,,'*f" 3.14159) "**3.14") + (test '("~6,3,,'?f" 12345.56789) "??????") + (test '("~6,3f" 12345.6789) "12345.679") + (test '("~,3f" 12345.6789) "12345.679") + (test '("~,3f" 9.9999) "10.000") + (test '("~6f" 23.4) " 23.4") + (test '("~6f" 1234.5) "1234.5") + (test '("~6f" 12345678) "12345678.0") + (test '("~6,,,'?f" 12345678) "??????") + (test '("~6f" 123.56789) "123.57") + (test '("~6f" 123.0) " 123.0") + (test '("~6f" -123.0) "-123.0") + (test '("~6f" 0.0) " 0.0") + (test '("~3f" 3.141) "3.1") + (test '("~2f" 3.141) "3.") + (test '("~1f" 3.141) "3.141") + (test '("~f" 123.56789) "123.56789") + (test '("~f" -314.0) "-314.0") + (test '("~f" 1e4) "10000.0") + (test '("~f" -1.23e10) "-12300000000.0") + (test '("~f" 1e-4) "0.0001") + (test '("~f" -1.23e-10) "-0.000000000123") + (test '("~@f" 314.0) "+314.0") + (test '("~,,3f" 0.123456) "123.456") + (test '("~,,-3f" -123.456) "-0.123456") + (test '("~5,,3f" 0.123456) "123.5") +)) + +; exponent floating points + +(cond + (format:floats + (test '("~e" 3.14159) "3.14159E+0") + (test '("~e" 0.00001234) "1.234E-5") + (test '("~,,,0e" 0.00001234) "0.1234E-4") + (test '("~,3e" 3.14159) "3.142E+0") + (test '("~,3@e" 3.14159) "+3.142E+0") + (test '("~,3@e" 0.0) "+0.000E+0") + (test '("~,0e" 3.141) "3.E+0") + (test '("~,3,,0e" 3.14159) "0.314E+1") + (test '("~,5,3,-2e" 3.14159) "0.00314E+003") + (test '("~,5,3,-5e" -3.14159) "-0.00000E+006") + (test '("~,5,2,2e" 3.14159) "31.4159E-01") + (test '("~,5,2,,,,'ee" 0.0) "0.00000e+00") + (test '("~12,3e" -3.141) " -3.141E+0") + (test '("~12,3,,,,'#e" -3.141) "###-3.141E+0") + (test '("~10,2e" -1.236e-4) " -1.24E-4") + (test '("~5,3e" -3.141) "-3.141E+0") + (test '("~5,3,,,'*e" -3.141) "*****") + (test '("~3e" 3.14159) "3.14159E+0") + (test '("~4e" 3.14159) "3.14159E+0") + (test '("~5e" 3.14159) "3.E+0") + (test '("~5,,,,'*e" 3.14159) "3.E+0") + (test '("~6e" 3.14159) "3.1E+0") + (test '("~7e" 3.14159) "3.14E+0") + (test '("~7e" -3.14159) "-3.1E+0") + (test '("~8e" 3.14159) "3.142E+0") + (test '("~9e" 3.14159) "3.1416E+0") + (test '("~9,,,,,,'ee" 3.14159) "3.1416e+0") + (test '("~10e" 3.14159) "3.14159E+0") + (test '("~11e" 3.14159) " 3.14159E+0") + (test '("~12e" 3.14159) " 3.14159E+0") + (test '("~13,6,2,-5e" 3.14159) " 0.000003E+06") + (test '("~13,6,2,-4e" 3.14159) " 0.000031E+05") + (test '("~13,6,2,-3e" 3.14159) " 0.000314E+04") + (test '("~13,6,2,-2e" 3.14159) " 0.003142E+03") + (test '("~13,6,2,-1e" 3.14159) " 0.031416E+02") + (test '("~13,6,2,0e" 3.14159) " 0.314159E+01") + (test '("~13,6,2,1e" 3.14159) " 3.141590E+00") + (test '("~13,6,2,2e" 3.14159) " 31.41590E-01") + (test '("~13,6,2,3e" 3.14159) " 314.1590E-02") + (test '("~13,6,2,4e" 3.14159) " 3141.590E-03") + (test '("~13,6,2,5e" 3.14159) " 31415.90E-04") + (test '("~13,6,2,6e" 3.14159) " 314159.0E-05") + (test '("~13,6,2,7e" 3.14159) " 3141590.E-06") + (test '("~13,6,2,8e" 3.14159) "31415900.E-07") + (test '("~7,3,,-2e" 0.001) ".001E+0") + (test '("~8,3,,-2@e" 0.001) "+.001E+0") + (test '("~8,3,,-2@e" -0.001) "-.001E+0") + (test '("~8,3,,-2e" 0.001) "0.001E+0") + (test '("~7,,,-2e" 0.001) "0.00E+0") + (test '("~12,3,1e" 3.14159e12) " 3.142E+12") + (test '("~12,3,1,,'*e" 3.14159e12) "************") + (test '("~5,3,1e" 3.14159e12) "3.142E+12") +)) + +; general floating point (this test is from Steele's CL book) + +(cond + (format:floats + (test '("~9,2,1,,'*g|~9,3,2,3,'?,,'$g|~9,3,2,0,'%g|~9,2g" + 0.0314159 0.0314159 0.0314159 0.0314159) + " 3.14E-2|314.2$-04|0.314E-01| 3.14E-2") + (test '("~9,2,1,,'*g|~9,3,2,3,'?,,'$g|~9,3,2,0,'%g|~9,2g" + 0.314159 0.314159 0.314159 0.314159) + " 0.31 |0.314 |0.314 | 0.31 ") + (test '("~9,2,1,,'*g|~9,3,2,3,'?,,'$g|~9,3,2,0,'%g|~9,2g" + 3.14159 3.14159 3.14159 3.14159) + " 3.1 | 3.14 | 3.14 | 3.1 ") + (test '("~9,2,1,,'*g|~9,3,2,3,'?,,'$g|~9,3,2,0,'%g|~9,2g" + 31.4159 31.4159 31.4159 31.4159) + " 31. | 31.4 | 31.4 | 31. ") + (test '("~9,2,1,,'*g|~9,3,2,3,'?,,'$g|~9,3,2,0,'%g|~9,2g" + 314.159 314.159 314.159 314.159) + " 3.14E+2| 314. | 314. | 3.14E+2") + (test '("~9,2,1,,'*g|~9,3,2,3,'?,,'$g|~9,3,2,0,'%g|~9,2g" + 3141.59 3141.59 3141.59 3141.59) + " 3.14E+3|314.2$+01|0.314E+04| 3.14E+3") + (test '("~9,2,1,,'*g|~9,3,2,3,'?,,'$g|~9,3,2,0,'%g|~9,2g" + 3.14E12 3.14E12 3.14E12 3.14E12) + "*********|314.0$+10|0.314E+13| 3.14E+12") + (test '("~9,2,1,,'*g|~9,3,2,3,'?,,'$g|~9,3,2,0,'%g|~9,2g" + 3.14E120 3.14E120 3.14E120 3.14E120) + "*********|?????????|%%%%%%%%%|3.14E+120") + + (test '("~g" 0.0) "0.0 ") ; further ~g tests + (test '("~g" 0.1) "0.1 ") + (test '("~g" 0.01) "1.0E-2") + (test '("~g" 123.456) "123.456 ") + (test '("~g" 123456.7) "123456.7 ") + (test '("~g" 123456.78) "123456.78 ") + (test '("~g" 0.9282) "0.9282 ") + (test '("~g" 0.09282) "9.282E-2") + (test '("~g" 1) "1.0 ") + (test '("~g" 12) "12.0 ") + )) + +; dollar floating point + +(cond + (format:floats + (test '("~$" 1.23) "1.23") + (test '("~$" 1.2) "1.20") + (test '("~$" 0.0) "0.00") + (test '("~$" 9.999) "10.00") + (test '("~3$" 9.9999) "10.000") + (test '("~,4$" 3.2) "0003.20") + (test '("~,4$" 10000.2) "10000.20") + (test '("~,4,10$" 3.2) " 0003.20") + (test '("~,4,10@$" 3.2) " +0003.20") + (test '("~,4,10:@$" 3.2) "+ 0003.20") + (test '("~,4,10:$" -3.2) "- 0003.20") + (test '("~,4,10$" -3.2) " -0003.20") + (test '("~,,10@$" 3.2) " +3.20") + (test '("~,,10:@$" 3.2) "+ 3.20") + (test '("~,,10:@$" -3.2) "- 3.20") + (test '("~,,10,'_@$" 3.2) "_____+3.20") + (test '("~,,4$" 1234.4) "1234.40") +)) + +; complex numbers + +(cond + (format:complex-numbers + (test '("~i" 3.0) "3.0+0.0i") + (test '("~,3i" 3.0) "3.000+0.000i") + (test `("~7,2i" ,(string->number "3.0+5.0i")) " 3.00 +5.00i") + (test `("~7,2,1i" ,(string->number "3.0+5.0i")) " 30.00 +50.00i") + (test `("~7,2@i" ,(string->number "3.0+5.0i")) " +3.00 +5.00i") + (test `("~7,2,,,'*@i" ,(string->number "3.0+5.0i")) "**+3.00**+5.00i") + )) ; note: some parsers choke syntactically on reading a complex + ; number though format:complex is #f; this is why we put them in + ; strings + +; inquiry test + +(test '("~:q") format:version) + +(if (not test-verbose) (display "done.")) + +(format #t "~%~a Test~:p completed. (~a failure~:p)~2%" total fails) + +; eof diff --git a/gambit.init b/gambit.init new file mode 100644 index 0000000..47717dc --- /dev/null +++ b/gambit.init @@ -0,0 +1,219 @@ +;;;"gambit.init" Initialisation for SLIB for Gambit -*-scheme-*- +;;; Copyright (C) 1991, 1992, 1993 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;; Updated 1992 February 1 for Gambit v1.71 -- by Ken Dickey +;;; Date: Wed, 12 Jan 1994 15:03:12 -0500 +;;; From: barnett@armadillo.urich.edu (Lewis Barnett) +;;; Relative pathnames for Slib in MacGambit + +(define (SOFTWARE-TYPE) 'UNIX) ; 'MACOS for MacGambit. + +(define (scheme-implementation-type) 'gambit) + +(define (scheme-implementation-version) "?") + +(define SYSTEM ##unix-system) ; Comment out for 'MACOS + +;;; (implementation-vicinity) should be defined to be the pathname of +;;; the directory where any auxillary files to your Scheme +;;; implementation reside. + +(define implementation-vicinity + (let ((arg0 (vector-ref ##argv 0))) + (let loop ((i (- (string-length arg0) 1))) + (cond ((negative? i) "") + ((char=? #\: (string-ref arg0 i)) + (lambda () + (substring arg0 0 (+ i 1)))) + (else (loop (- i 1))))))) + +;;; (library-vicinity) should be defined to be the pathname of the +;;; directory where files of Scheme library functions reside. + +;;; This assumes that the slib files are in a folder +;;; called slib in the same directory as the MacGambit Interpreter. + +(define library-vicinity + (let ((library-path + (case (software-type) + ((UNIX) "/usr/local/lib/slib/") + ((MACOS) (string-append (implementation-vicinity) ":slib:")) + ((AMIGA) "dh0:scm/Library/") + ((VMS) "lib$scheme:") + ((MS-DOS) "C:\\SLIB\\") + (else "")))) + (lambda () library-path))) + +;;; *features* should be set to a list of symbols describing features +;;; of this implementation. See Template.scm for the list of feature +;;; names. + +(define *features* + ((lambda (l) + (if (eq? (SOFTWARE-TYPE) 'MACOS) l (cons 'system l))) + '( + source ;can load scheme source files + ;(slib:load-source "filename") + compiled ;can load compiled files + ;(slib:load-compiled "filename") + rev4-report + ieee-p1178 + sicp + rev4-optional-procedures + rev3-procedures + rev2-procedures + multiarg/and- + multiarg-apply + object-hash + rationalize + delay + with-file + transcript + char-ready? + ieee-floating-point + full-continuation + ))) + +;;; (OUTPUT-PORT-WIDTH <port>) +(define (output-port-width . arg) 79) + +;;; (OUTPUT-PORT-HEIGHT <port>) +(define (output-port-height . arg) 24) + +;;; (CURRENT-ERROR-PORT) +(define current-error-port + (let ((port (current-output-port))) + (lambda () port))) + +;;; (TMPNAM) makes a temporary file name. +(define tmpnam + (let ((cntr 100)) + (lambda () (set! cntr (+ 1 cntr)) + (let ((tmp (string-append "slib_" (number->string cntr)))) + (if (file-exists? tmp) (tmpnam) tmp))))) + +;;; FORCE-OUTPUT flushes any pending output on optional arg output port +;;; use this definition if your system doesn't have such a procedure. +(define (force-output . arg) #t) + +;;; CHAR-CODE-LIMIT is one greater than the largest integer which can +;;; be returned by CHAR->INTEGER. +(define char-code-limit 256) + +;; MOST-POSITIVE-FIXNUM is used in modular.scm +(define most-positive-fixnum #x1FFFFFFF) ;; 3-bit tag for 68K + +;;; Return argument +(define (identity x) x) + +;;; If your implementation provides eval, SLIB:EVAL is single argument +;;; eval using the top-level (user) environment. +(define SLIB:EVAL ##eval-global);; Gambit v1.71 + +;;; If your implementation provides R4RS macros: +;(define macro:eval slib:eval) +;(define macro:load load) + +(define *defmacros* + (list (cons 'defmacro + (lambda (name parms . body) + `(set! *defmacros* (cons (cons ',name (lambda ,parms ,@body)) + *defmacros*)))))) +(define (defmacro? m) (and (assq m *defmacros*) #t)) + +(define (macroexpand-1 e) + (if (pair? e) (let ((a (car e))) + (cond ((symbol? a) (set! a (assq a *defmacros*)) + (if a (apply (cdr a) (cdr e)) e)) + (else e))) + e)) + +(define (macroexpand e) + (if (pair? e) (let ((a (car e))) + (cond ((symbol? a) + (set! a (assq a *defmacros*)) + (if a (macroexpand (apply (cdr a) (cdr e))) e)) + (else e))) + e)) + +(define gentemp + (let ((*gensym-counter* -1)) + (lambda () + (set! *gensym-counter* (+ *gensym-counter* 1)) + (string->symbol + (string-append "slib:G" (number->string *gensym-counter*)))))) + +(define base:eval slib:eval) +(define (defmacro:eval x) (base:eval (defmacro:expand* x))) +(define (defmacro:expand* x) + (require 'defmacroexpand) (apply defmacro:expand* x '())) + +(define (defmacro:load <pathname>) + (slib:eval-load <pathname> defmacro:eval)) + +(define (slib:eval-load <pathname> evl) + (if (not (file-exists? <pathname>)) + (set! <pathname> (string-append <pathname> (scheme-file-suffix)))) + (call-with-input-file <pathname> + (lambda (port) + (let ((old-load-pathname *load-pathname*)) + (set! *load-pathname* <pathname>) + (do ((o (read port) (read port))) + ((eof-object? o)) + (evl o)) + (set! *load-pathname* old-load-pathname))))) + +;; define an error procedure for the library +(define SLIB:ERROR error) + +;; define these as appropriate for your system. +(define slib:tab (integer->char 9)) +(define slib:form-feed (integer->char 12)) + +(define (1+ n) (+ n 1)) +(define (-1+ n) (- n 1)) +(define 1- -1+) + +(define in-vicinity string-append) + +;;; Define SLIB:EXIT to be the implementation procedure to exit or +;;; return if exitting not supported. +(define slib:exit (lambda args (exit))) + +;;; Here for backward compatability + +(define (scheme-file-suffix) ".scm") + +;;; (SLIB:LOAD-SOURCE "foo") should load "foo.scm" or with whatever +;;; suffix all the module files in SLIB have. See feature 'SOURCE. + +(define slib:load-source load) + +;;; (SLIB:LOAD-COMPILED "foo") should load the file that was produced +;;; by compiling "foo.scm" if this implementation can compile files. +;;; See feature 'COMPILED. + +(define slib:load-compiled load) + +;;; At this point SLIB:LOAD must be able to load SLIB files. + +(define slib:load slib:load-source) + +(slib:load (in-vicinity (library-vicinity) "require")) +;;; --- E O F --- diff --git a/genwrite.scm b/genwrite.scm new file mode 100644 index 0000000..0bb4e56 --- /dev/null +++ b/genwrite.scm @@ -0,0 +1,264 @@ +;;"genwrite.scm" generic write used by pretty-print and truncated-print. +;; Copyright (c) 1991, Marc Feeley +;; Author: Marc Feeley (feeley@iro.umontreal.ca) +;; Distribution restrictions: none + +(define (generic-write obj display? width output) + + (define (read-macro? l) + (define (length1? l) (and (pair? l) (null? (cdr l)))) + (let ((head (car l)) (tail (cdr l))) + (case head + ((QUOTE QUASIQUOTE UNQUOTE UNQUOTE-SPLICING) (length1? tail)) + (else #f)))) + + (define (read-macro-body l) + (cadr l)) + + (define (read-macro-prefix l) + (let ((head (car l)) (tail (cdr l))) + (case head + ((QUOTE) "'") + ((QUASIQUOTE) "`") + ((UNQUOTE) ",") + ((UNQUOTE-SPLICING) ",@")))) + + (define (out str col) + (and col (output str) (+ col (string-length str)))) + + (define (wr obj col) + + (define (wr-expr expr col) + (if (read-macro? expr) + (wr (read-macro-body expr) (out (read-macro-prefix expr) col)) + (wr-lst expr col))) + + (define (wr-lst l col) + (if (pair? l) + (let loop ((l (cdr l)) + (col (and col (wr (car l) (out "(" col))))) + (cond ((not col) col) + ((pair? l) + (loop (cdr l) (wr (car l) (out " " col)))) + ((null? l) (out ")" col)) + (else (out ")" (wr l (out " . " col)))))) + (out "()" col))) + + (cond ((pair? obj) (wr-expr obj col)) + ((null? obj) (wr-lst obj col)) + ((vector? obj) (wr-lst (vector->list obj) (out "#" col))) + ((boolean? obj) (out (if obj "#t" "#f") col)) + ((number? obj) (out (number->string obj) col)) + ((symbol? obj) (out (symbol->string obj) col)) + ((procedure? obj) (out "#[procedure]" col)) + ((string? obj) (if display? + (out obj col) + (let loop ((i 0) (j 0) (col (out "\"" col))) + (if (and col (< j (string-length obj))) + (let ((c (string-ref obj j))) + (if (or (char=? c #\\) + (char=? c #\")) + (loop j + (+ j 1) + (out "\\" + (out (substring obj i j) + col))) + (loop i (+ j 1) col))) + (out "\"" + (out (substring obj i j) col)))))) + ((char? obj) (if display? + (out (make-string 1 obj) col) + (out (case obj + ((#\space) "space") + ((#\newline) "newline") + (else (make-string 1 obj))) + (out "#\\" col)))) + ((input-port? obj) (out "#[input-port]" col)) + ((output-port? obj) (out "#[output-port]" col)) + ((eof-object? obj) (out "#[eof-object]" col)) + (else (out "#[unknown]" col)))) + + (define (pp obj col) + + (define (spaces n col) + (if (> n 0) + (if (> n 7) + (spaces (- n 8) (out " " col)) + (out (substring " " 0 n) col)) + col)) + + (define (indent to col) + (and col + (if (< to col) + (and (out (make-string 1 #\newline) col) (spaces to 0)) + (spaces (- to col) col)))) + + (define (pr obj col extra pp-pair) + (if (or (pair? obj) (vector? obj)) ; may have to split on multiple lines + (let ((result '()) + (left (min (+ (- (- width col) extra) 1) max-expr-width))) + (generic-write obj display? #f + (lambda (str) + (set! result (cons str result)) + (set! left (- left (string-length str))) + (> left 0))) + (if (> left 0) ; all can be printed on one line + (out (reverse-string-append result) col) + (if (pair? obj) + (pp-pair obj col extra) + (pp-list (vector->list obj) (out "#" col) extra pp-expr)))) + (wr obj col))) + + (define (pp-expr expr col extra) + (if (read-macro? expr) + (pr (read-macro-body expr) + (out (read-macro-prefix expr) col) + extra + pp-expr) + (let ((head (car expr))) + (if (symbol? head) + (let ((proc (style head))) + (if proc + (proc expr col extra) + (if (> (string-length (symbol->string head)) + max-call-head-width) + (pp-general expr col extra #f #f #f pp-expr) + (pp-call expr col extra pp-expr)))) + (pp-list expr col extra pp-expr))))) + + ; (head item1 + ; item2 + ; item3) + (define (pp-call expr col extra pp-item) + (let ((col* (wr (car expr) (out "(" col)))) + (and col + (pp-down (cdr expr) col* (+ col* 1) extra pp-item)))) + + ; (item1 + ; item2 + ; item3) + (define (pp-list l col extra pp-item) + (let ((col (out "(" col))) + (pp-down l col col extra pp-item))) + + (define (pp-down l col1 col2 extra pp-item) + (let loop ((l l) (col col1)) + (and col + (cond ((pair? l) + (let ((rest (cdr l))) + (let ((extra (if (null? rest) (+ extra 1) 0))) + (loop rest + (pr (car l) (indent col2 col) extra pp-item))))) + ((null? l) + (out ")" col)) + (else + (out ")" + (pr l + (indent col2 (out "." (indent col2 col))) + (+ extra 1) + pp-item))))))) + + (define (pp-general expr col extra named? pp-1 pp-2 pp-3) + + (define (tail1 rest col1 col2 col3) + (if (and pp-1 (pair? rest)) + (let* ((val1 (car rest)) + (rest (cdr rest)) + (extra (if (null? rest) (+ extra 1) 0))) + (tail2 rest col1 (pr val1 (indent col3 col2) extra pp-1) col3)) + (tail2 rest col1 col2 col3))) + + (define (tail2 rest col1 col2 col3) + (if (and pp-2 (pair? rest)) + (let* ((val1 (car rest)) + (rest (cdr rest)) + (extra (if (null? rest) (+ extra 1) 0))) + (tail3 rest col1 (pr val1 (indent col3 col2) extra pp-2))) + (tail3 rest col1 col2))) + + (define (tail3 rest col1 col2) + (pp-down rest col2 col1 extra pp-3)) + + (let* ((head (car expr)) + (rest (cdr expr)) + (col* (wr head (out "(" col)))) + (if (and named? (pair? rest)) + (let* ((name (car rest)) + (rest (cdr rest)) + (col** (wr name (out " " col*)))) + (tail1 rest (+ col indent-general) col** (+ col** 1))) + (tail1 rest (+ col indent-general) col* (+ col* 1))))) + + (define (pp-expr-list l col extra) + (pp-list l col extra pp-expr)) + + (define (pp-LAMBDA expr col extra) + (pp-general expr col extra #f pp-expr-list #f pp-expr)) + + (define (pp-IF expr col extra) + (pp-general expr col extra #f pp-expr #f pp-expr)) + + (define (pp-COND expr col extra) + (pp-call expr col extra pp-expr-list)) + + (define (pp-CASE expr col extra) + (pp-general expr col extra #f pp-expr #f pp-expr-list)) + + (define (pp-AND expr col extra) + (pp-call expr col extra pp-expr)) + + (define (pp-LET expr col extra) + (let* ((rest (cdr expr)) + (named? (and (pair? rest) (symbol? (car rest))))) + (pp-general expr col extra named? pp-expr-list #f pp-expr))) + + (define (pp-BEGIN expr col extra) + (pp-general expr col extra #f #f #f pp-expr)) + + (define (pp-DO expr col extra) + (pp-general expr col extra #f pp-expr-list pp-expr-list pp-expr)) + + ; define formatting style (change these to suit your style) + + (define indent-general 2) + + (define max-call-head-width 5) + + (define max-expr-width 50) + + (define (style head) + (case head + ((LAMBDA LET* LETREC DEFINE) pp-LAMBDA) + ((IF SET!) pp-IF) + ((COND) pp-COND) + ((CASE) pp-CASE) + ((AND OR) pp-AND) + ((LET) pp-LET) + ((BEGIN) pp-BEGIN) + ((DO) pp-DO) + (else #f))) + + (pr obj col 0 pp-expr)) + + (if width + (out (make-string 1 #\newline) (pp obj 0)) + (wr obj 0))) + +; (reverse-string-append l) = (apply string-append (reverse l)) + +(define (reverse-string-append l) + + (define (rev-string-append l i) + (if (pair? l) + (let* ((str (car l)) + (len (string-length str)) + (result (rev-string-append (cdr l) (+ i len)))) + (let loop ((j 0) (k (- (- (string-length result) i) len))) + (if (< j len) + (begin + (string-set! result k (string-ref str j)) + (loop (+ j 1) (+ k 1))) + result))) + (make-string i))) + + (rev-string-append l 0)) diff --git a/getopt.scm b/getopt.scm new file mode 100644 index 0000000..c2962db --- /dev/null +++ b/getopt.scm @@ -0,0 +1,80 @@ +;;; "getopt.scm" POSIX command argument processing +;Copyright (C) 1993, 1994 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(define getopt:scan #f) +(define getopt:char #\-) +(define getopt:opt #f) +(define *optind* 1) +(define *optarg* 0) + +(define (getopt argc argv optstring) + (let ((opts (string->list optstring)) + (place #f) + (arg #f) + (argref (lambda () ((if (vector? argv) vector-ref list-ref) + argv *optind*)))) + (and + (cond ((and getopt:scan (not (string=? "" getopt:scan))) #t) + ((>= *optind* argc) #f) + (else + (set! arg (argref)) + (cond ((or (<= (string-length arg) 1) + (not (char=? (string-ref arg 0) getopt:char))) + #f) + ((and (= (string-length arg) 2) + (char=? (string-ref arg 1) getopt:char)) + (set! *optind* (+ *optind* 1)) + #f) + (else + (set! getopt:scan + (substring arg 1 (string-length arg))) + #t)))) + (begin + (set! getopt:opt (string-ref getopt:scan 0)) + (set! getopt:scan + (substring getopt:scan 1 (string-length getopt:scan))) + (if (string=? "" getopt:scan) (set! *optind* (+ *optind* 1))) + (set! place (member getopt:opt opts)) + (cond ((not place) #\?) + ((or (null? (cdr place)) (not (char=? #\: (cadr place)))) + getopt:opt) + ((not (string=? "" getopt:scan)) + (set! *optarg* getopt:scan) + (set! *optind* (+ *optind* 1)) + (set! getopt:scan #f) + getopt:opt) + ((< *optind* argc) + (set! *optarg* (argref)) + (set! *optind* (+ *optind* 1)) + getopt:opt) + ((and (not (null? opts)) (char=? #\: (car opts))) #\:) + (else #\?)))))) + +(define (getopt-- argc argv optstring) + (let* ((opt (getopt argc argv (string-append optstring "-:"))) + (optarg *optarg*)) + (cond ((eqv? #\- opt) ;long option + (do ((l (string-length *optarg*)) + (i 0 (+ 1 i))) + ((or (>= i l) (char=? #\= (string-ref optarg i))) + (cond + ((>= i l) (set! *optarg* #f) optarg) + (else (set! *optarg* (substring optarg (+ 1 i) l)) + (substring optarg 0 i)))))) + (else opt)))) diff --git a/hash.scm b/hash.scm new file mode 100644 index 0000000..ab02138 --- /dev/null +++ b/hash.scm @@ -0,0 +1,153 @@ +; "hash.scm", hashing functions for Scheme. +; Copyright (c) 1992, 1993, 1995 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(define (hash:hash-char-ci char n) + (modulo (char->integer (char-downcase char)) n)) + +(define hash:hash-char hash:hash-char-ci) + +(define (hash:hash-symbol sym n) + (hash:hash-string (symbol->string sym) n)) + +;;; This can overflow on implemenatations where inexacts have a larger +;;; range than exact integers. +(define hash:hash-number + (if (provided? 'inexact) + (lambda (num n) + (if (integer? num) + (modulo (if (exact? num) num (inexact->exact num)) n) + (hash:hash-string-ci + (number->string (if (exact? num) (exact->inexact num) num)) + n))) + (lambda (num n) + (if (integer? num) + (modulo num n) + (hash:hash-string-ci (number->string num) n))))) + +(define (hash:hash-string-ci str n) + (let ((len (string-length str))) + (if (> len 5) + (let loop ((h (modulo 264 n)) (i 5)) + (if (positive? i) + (loop (modulo (+ (* h 256) + (char->integer + (char-downcase + (string-ref str (modulo h len))))) + n) + (- i 1)) + h)) + (let loop ((h 0) (i (- len 1))) + (if (>= i 0) + (loop (modulo (+ (* h 256) + (char->integer + (char-downcase (string-ref str i)))) + n) + (- i 1)) + h))))) + +(define hash:hash-string hash:hash-string-ci) + +(define (hash:hash obj n) + (let hs ((d 10) (obj obj)) + (cond + ((number? obj) (hash:hash-number obj n)) + ((char? obj) (modulo (char->integer (char-downcase obj)) n)) + ((symbol? obj) (hash:hash-symbol obj n)) + ((string? obj) (hash:hash-string obj n)) + ((vector? obj) + (let ((len (vector-length obj))) + (if (> len 5) + (let lp ((h 1) (i (quotient d 2))) + (if (positive? i) + (lp (modulo (+ (* h 256) + (hs 2 (vector-ref obj (modulo h len)))) + n) + (- i 1)) + h)) + (let loop ((h (- n 1)) (i (- len 1))) + (if (>= i 0) + (loop (modulo (+ (* h 256) (hs (quotient d len) + (vector-ref obj i))) + n) + (- i 1)) + h))))) + ((pair? obj) + (if (positive? d) (modulo (+ (hs (quotient d 2) (car obj)) + (hs (quotient d 2) (cdr obj))) + n) + 1)) + (else + (modulo + (cond + ((null? obj) 256) + ((boolean? obj) (if obj 257 258)) + ((eof-object? obj) 259) + ((input-port? obj) 260) + ((output-port? obj) 261) + ((procedure? obj) 262) + ((and (provided? 'RECORD) (record? obj)) + (let* ((rtd (record-type-descriptor obj)) + (fns (record-type-field-names rtd)) + (len (length fns))) + (if (> len 5) + (let lp ((h (modulo 266 n)) (i (quotient d 2))) + (if (positive? i) + (lp (modulo + (+ (* h 256) + (hs 2 ((record-accessor + rtd (list-ref fns (modulo h len))) + obj))) + n) + (- i 1)) + h)) + (let loop ((h (- n 1)) (i (- len 1))) + (if (>= i 0) + (loop (modulo + (+ (* h 256) + (hs (quotient d len) + ((record-accessor + rtd (list-ref fns (modulo h len))) + obj))) + n) + (- i 1)) + h))))) + (else 263)) + n))))) + +(define hash hash:hash) +(define hashv hash:hash) + +;;; Object-hash is somewhat expensive on copying GC systems (like +;;; PC-Scheme and MITScheme). We use it only on strings, pairs, +;;; vectors, and records. This also allows us to use it for both +;;; hashq and hashv. + +(if (provided? 'object-hash) + (set! hashv + (if (provided? 'record) + (lambda (obj k) + (if (or (string? obj) (pair? obj) (vector? obj) (record? obj)) + (modulo (object-hash obj) k) + (hash:hash obj k))) + (lambda (obj k) + (if (or (string? obj) (pair? obj) (vector? obj)) + (modulo (object-hash obj) k) + (hash:hash obj k)))))) + +(define hashq hashv) diff --git a/hashtab.scm b/hashtab.scm new file mode 100644 index 0000000..317efe2 --- /dev/null +++ b/hashtab.scm @@ -0,0 +1,79 @@ +; "hashtab.scm", hash tables for Scheme. +; Copyright (c) 1992, 1993 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'hash) +(require 'alist) + +(define (predicate->hash pred) + (cond ((eq? pred eq?) hashq) + ((eq? pred eqv?) hashv) + ((eq? pred equal?) hash) + ((eq? pred =) hashv) + ((eq? pred char=?) hashv) + ((eq? pred char-ci=?) hashv) + ((eq? pred string=?) hash) + ((eq? pred string-ci=?) hash) + (else (slib:error "unknown predicate for hash" pred)))) + +(define (make-hash-table k) (make-vector k '())) + +(define (predicate->hash-asso pred) + (let ((hashfun (predicate->hash pred)) + (asso (predicate->asso pred))) + (lambda (key hashtab) + (asso key + (vector-ref hashtab (hashfun key (vector-length hashtab))))))) + +(define (hash-inquirer pred) + (let ((hashfun (predicate->hash pred)) + (ainq (alist-inquirer pred))) + (lambda (hashtab key) + (ainq (vector-ref hashtab (hashfun key (vector-length hashtab))) + key)))) + +(define (hash-associator pred) + (let ((hashfun (predicate->hash pred)) + (asso (alist-associator pred))) + (lambda (hashtab key val) + (let* ((num (hashfun key (vector-length hashtab)))) + (vector-set! hashtab num + (asso (vector-ref hashtab num) key val))) + hashtab))) + +(define (hash-remover pred) + (let ((hashfun (predicate->hash pred)) + (arem (alist-remover pred))) + (lambda (hashtab key) + (let* ((num (hashfun key (vector-length hashtab)))) + (vector-set! hashtab num + (arem (vector-ref hashtab num) key))) + hashtab))) + +(define (hash-map proc ht) + (define nht (make-vector (vector-length ht))) + (do ((i (+ -1 (vector-length ht)) (+ -1 i))) + ((negative? i) nht) + (vector-set! + nht i + (alist-map proc (vector-ref ht i))))) + +(define (hash-for-each proc ht) + (do ((i (+ -1 (vector-length ht)) (+ -1 i))) + ((negative? i)) + (alist-for-each proc (vector-ref ht i)))) diff --git a/lineio.scm b/lineio.scm new file mode 100644 index 0000000..ad8320b --- /dev/null +++ b/lineio.scm @@ -0,0 +1,50 @@ +; "lineio.scm", line oriented input/output functions for Scheme. +; Copyright (c) 1992, 1993 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(define (read-line . arg) + (let* ((char (apply read-char arg))) + (if (eof-object? char) + char + (do ((char char (apply read-char arg)) + (clist '() (cons char clist))) + ((or (eof-object? char) (char=? #\newline char)) + (list->string (reverse clist))))))) + +(define (read-line! str . arg) + (let* ((char (apply read-char arg)) + (len (+ -1 (string-length str)))) + (if (eof-object? char) + char + (do ((char char (apply read-char arg)) + (i 0 (+ 1 i))) + ((or (eof-object? char) + (char=? #\newline char) + (>= i len)) + (cond ((or (eof-object? char) (char=? #\newline char)) + i) + (else + (string-set! str i char) + (set! char (apply peek-char arg)) + (if (or (eof-object? char) (char=? #\newline char)) + (+ 1 i) #f)))) + (string-set! str i char))))) + +(define (write-line str . arg) + (apply display str arg) + (apply newline arg)) diff --git a/logical.scm b/logical.scm new file mode 100644 index 0000000..1cc0726 --- /dev/null +++ b/logical.scm @@ -0,0 +1,150 @@ +;;;; "logical.scm", bit access and operations for integers for Scheme +;;; Copyright (C) 1991, 1993 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(define logical:integer-expt + (if (provided? 'inexact) + expt + (lambda (n k) + (logical:ipow-by-squaring n k 1 *)))) + +(define (logical:ipow-by-squaring x k acc proc) + (cond ((zero? k) acc) + ((= 1 k) (proc acc x)) + (else (logical:ipow-by-squaring (proc x x) + (quotient k 2) + (if (even? k) acc (proc acc x)) + proc)))) + +(define (logical:logand n1 n2) + (cond ((= n1 n2) n1) + ((zero? n1) 0) + ((zero? n2) 0) + (else + (+ (* (logical:logand (logical:ash-4 n1) (logical:ash-4 n2)) 16) + (vector-ref (vector-ref logical:boole-and (modulo n1 16)) + (modulo n2 16)))))) + +(define (logical:logior n1 n2) + (cond ((= n1 n2) n1) + ((zero? n1) n2) + ((zero? n2) n1) + (else + (+ (* (logical:logior (logical:ash-4 n1) (logical:ash-4 n2)) 16) + (- 15 (vector-ref (vector-ref logical:boole-and + (- 15 (modulo n1 16))) + (- 15 (modulo n2 16)))))))) + +(define (logical:logxor n1 n2) + (cond ((= n1 n2) 0) + ((zero? n1) n2) + ((zero? n2) n1) + (else + (+ (* (logical:logxor (logical:ash-4 n1) (logical:ash-4 n2)) 16) + (vector-ref (vector-ref logical:boole-xor (modulo n1 16)) + (modulo n2 16)))))) + +(define (logical:lognot n) (- -1 n)) + +(define (logical:logtest int1 int2) + (not (zero? (logical:logand int1 int2)))) + +(define (logical:logbit? index int) + (logical:logtest (logical:integer-expt 2 index) int)) + +(define (logical:bit-extract n start end) + (logical:logand (- (logical:integer-expt 2 (- end start)) 1) + (logical:ash n (- start)))) + +(define (logical:ash int cnt) + (if (negative? cnt) + (let ((n (logical:integer-expt 2 (- cnt)))) + (if (negative? int) + (+ -1 (quotient (+ 1 int) n)) + (quotient int n))) + (* (logical:integer-expt 2 cnt) int))) + +(define (logical:ash-4 x) + (if (negative? x) + (+ -1 (quotient (+ 1 x) 16)) + (quotient x 16))) + +(define (logical:logcount n) + (cond ((zero? n) 0) + ((negative? n) (logical:logcount (logical:lognot n))) + (else + (+ (logical:logcount (logical:ash-4 n)) + (vector-ref '#(0 1 1 2 1 2 2 3 1 2 2 3 2 3 3 4) + (modulo n 16)))))) + +(define (logical:integer-length n) + (case n + ((0 -1) 0) + ((1 -2) 1) + ((2 3 -3 -4) 2) + ((4 5 6 7 -5 -6 -7 -8) 3) + (else (+ 4 (logical:integer-length (logical:ash-4 n)))))) + +(define logical:boole-xor + '#(#(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + #(1 0 3 2 5 4 7 6 9 8 11 10 13 12 15 14) + #(2 3 0 1 6 7 4 5 10 11 8 9 14 15 12 13) + #(3 2 1 0 7 6 5 4 11 10 9 8 15 14 13 12) + #(4 5 6 7 0 1 2 3 12 13 14 15 8 9 10 11) + #(5 4 7 6 1 0 3 2 13 12 15 14 9 8 11 10) + #(6 7 4 5 2 3 0 1 14 15 12 13 10 11 8 9) + #(7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8) + #(8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7) + #(9 8 11 10 13 12 15 14 1 0 3 2 5 4 7 6) + #(10 11 8 9 14 15 12 13 2 3 0 1 6 7 4 5) + #(11 10 9 8 15 14 13 12 3 2 1 0 7 6 5 4) + #(12 13 14 15 8 9 10 11 4 5 6 7 0 1 2 3) + #(13 12 15 14 9 8 11 10 5 4 7 6 1 0 3 2) + #(14 15 12 13 10 11 8 9 6 7 4 5 2 3 0 1) + #(15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0))) + +(define logical:boole-and + '#(#(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + #(0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1) + #(0 0 2 2 0 0 2 2 0 0 2 2 0 0 2 2) + #(0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3) + #(0 0 0 0 4 4 4 4 0 0 0 0 4 4 4 4) + #(0 1 0 1 4 5 4 5 0 1 0 1 4 5 4 5) + #(0 0 2 2 4 4 6 6 0 0 2 2 4 4 6 6) + #(0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7) + #(0 0 0 0 0 0 0 0 8 8 8 8 8 8 8 8) + #(0 1 0 1 0 1 0 1 8 9 8 9 8 9 8 9) + #(0 0 2 2 0 0 2 2 8 8 10 10 8 8 10 10) + #(0 1 2 3 0 1 2 3 8 9 10 11 8 9 10 11) + #(0 0 0 0 4 4 4 4 8 8 8 8 12 12 12 12) + #(0 1 0 1 4 5 4 5 8 9 8 9 12 13 12 13) + #(0 0 2 2 4 4 6 6 8 8 10 10 12 12 14 14) + #(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15))) + +(define logand logical:logand) +(define logior logical:logior) +(define logxor logical:logxor) +(define lognot logical:lognot) +(define logtest logical:logtest) +(define logbit? logical:logbit?) +(define ash logical:ash) +(define logcount logical:logcount) +(define integer-length logical:integer-length) +(define bit-extract logical:bit-extract) +(define ipow-by-squaring logical:ipow-by-squaring) +(define integer-expt logical:integer-expt) diff --git a/macrotst.scm b/macrotst.scm new file mode 100644 index 0000000..b5b5046 --- /dev/null +++ b/macrotst.scm @@ -0,0 +1,54 @@ +;;;"macrotst.scm" Test for R4RS Macros +;;; From Revised^4 Report on the Algorithmic Language Scheme +;;; Editors: William Clinger and Jonathon Rees +; +; We intend this report to belong to the entire Scheme community, and so +; we grant permission to copy it in whole or in part without fee. In +; particular, we encourage implementors of Scheme to use this report as +; a starting point for manuals and other documentation, modifying it as +; necessary. + +;;; To run this code type +;;; (require 'macro) +;;; (macro:load "macrotst.scm") + +(write "this code should print now, outer, and 7") (newline) + +(write + (let-syntax ((when (syntax-rules () + ((when test stmt1 stmt2 ...) + (if test + (begin stmt1 + stmt2 ...)))))) + (let ((if #t)) + (when if (set! if 'now)) + if))) +(newline) +;;; ==> now + +(write + (let ((x 'outer)) + (let-syntax ((m (syntax-rules () ((m) x)))) + (let ((x 'inner)) + (m))))) +(newline) +;;; ==> outer +(write + (letrec-syntax + ((or (syntax-rules () + ((or) #f) + ((or e) e) + ((or e1 e2 ...) + (let ((temp e1)) + (if temp temp (or e2 ...))))))) + (let ((x #f) + (y 7) + (temp 8) + (let odd?) + (if even?)) + (or x + (let temp) + (if y) + y)))) +(newline) +;;; ==> 7 diff --git a/macscheme.init b/macscheme.init new file mode 100644 index 0000000..56c53a2 --- /dev/null +++ b/macscheme.init @@ -0,0 +1,265 @@ +;;;"macscheme.init" Configuration of *features* for MacScheme -*-scheme-*- +;Copyright (C) 1994 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;; From: jjb@isye.gatech.edu (John Bartholdi) + +;;; (software-type) should be set to the generic operating system type. +;;; UNIX, VMS, MACOS, AMIGA and MS-DOS are supported. + +(define (software-type) 'MACOS) + +;;; (scheme-implementation-type) should return the name of the scheme +;;; implementation loading this file. + +(define (scheme-implementation-type) 'MacScheme) + +;;; (scheme-implementation-version) should return a string describing +;;; the version the scheme implementation loading this file. + +(define (scheme-implementation-version) "4.2") + +;;; (implementation-vicinity) should be defined to be the pathname of +;;; the directory where any auxillary files to your Scheme +;;; implementation reside. + +(define (implementation-vicinity) "Macintosh.HD:MacScheme 4.2:") + +;;; (library-vicinity) should be defined to be the pathname of the +;;; directory where files of Scheme library functions reside. + +(define (library-vicinity) "Macintosh.HD:MacScheme 4.2:slib:") + +;;; *FEATURES* should be set to a list of symbols describing features +;;; of this implementation. Suggestions for features are: + +(define *features* + '( + source ;can load scheme source files + ;(slib:load-source "filename") +; compiled ;can load compiled files + ;(slib:load-compiled "filename") + rev4-report ;conforms to + rev3-report ;conforms to + ieee-p1178 ;conforms to +; sicp ;runs code from Structure and + ;Interpretation of Computer + ;Programs by Abelson and Sussman. + rev4-optional-procedures ;LIST-TAIL, STRING->LIST, + ;LIST->STRING, STRING-COPY, + ;STRING-FILL!, LIST->VECTOR, + ;VECTOR->LIST, and VECTOR-FILL! + rev3-procedures ;LAST-PAIR, T, and NIL +; rev2-procedures ;SUBSTRING-MOVE-LEFT!, + ;SUBSTRING-MOVE-RIGHT!, + ;SUBSTRING-FILL!, + ;STRING-NULL?, APPEND!, 1+, + ;-1+, <?, <=?, =?, >?, >=? + multiarg/and- ;/ and - can take more than 2 args. + multiarg-apply ;APPLY can take more than 2 args. + rationalize + delay ;has DELAY and FORCE + with-file ;has WITH-INPUT-FROM-FILE and + ;WITH-OUTPUT-FROM-FILE + string-port ;has CALL-WITH-INPUT-STRING and + ;CALL-WITH-OUTPUT-STRING +; transcript ;TRANSCRIPT-ON and TRANSCRIPT-OFF +; char-ready? +; macro ;has R4RS high level macros +; defmacro ;has Common Lisp DEFMACRO + eval ;SLIB:EVAL is single argument eval +; record ;has user defined data structures +; values ;proposed multiple values +; dynamic-wind ;proposed dynamic-wind + ieee-floating-point ;conforms to + full-continuation ;can return multiple times +; object-hash ;has OBJECT-HASH + +; sort +; queue ;queues + pretty-print +; object->string +; format +; trace ;has macros: TRACE and UNTRACE + compiler ;has (COMPILER) +; ed ;(ED) is editor +; system ;posix (system <string>) +; getenv ;posix (getenv <string>) +; program-arguments ;returns list of strings (argv) +; Xwindows ;X support +; curses ;screen management package +; termcap ;terminal description package +; terminfo ;sysV terminal description + )) + +;;; (OUTPUT-PORT-WIDTH <port>) +(define (output-port-width . arg) 79) + +;;; (OUTPUT-PORT-HEIGHT <port>) +(define (output-port-height . arg) 24) + +;;; (CURRENT-ERROR-PORT) +(define current-error-port + (let ((port (current-output-port))) + (lambda () port))) + +;;; (TMPNAM) makes a temporary file name. +(define tmpnam (let ((cntr 100)) + (lambda () (set! cntr (+ 1 cntr)) + (string-append "slib_" (number->string cntr))))) + +;;; (FILE-EXISTS? <string>) +(define (file-exists? f) #f) + +;;; (DELETE-FILE <string>) +(define (delete-file f) #f) + +;;; FORCE-OUTPUT flushes any pending output on optional arg output port +;;; use this definition if your system doesn't have such a procedure. +(define (force-output . arg) #t) + +;;; CALL-WITH-INPUT-STRING and CALL-WITH-OUTPUT-STRING are the string +;;; port versions of CALL-WITH-*PUT-FILE. +(define (call-with-output-string f) + (let ((outsp (open-output-string))) + (f outsp) + (let ((s (get-output-string outsp))) + (close-output-port outsp) + s))) + +(define (call-with-input-string s f) + (let* ((insp (open-input-string s)) + (res (f insp))) + (close-input-port insp) + res)) + +;;; CHAR-CODE-LIMIT is one greater than the largest integer which can +;;; be returned by CHAR->INTEGER. +(define char-code-limit 256) + +;;; MOST-POSITIVE-FIXNUM is used in modular.scm +(define most-positive-fixnum 536870911) + +;;; Return argument +(define (identity x) x) + +;;; If your implementation provides eval SLIB:EVAL is single argument +;;; eval using the top-level (user) environment. +(define slib:eval eval) + +;;; If your implementation provides R4RS macros: +;(define macro:eval slib:eval) +;(define macro:load load) + +(define *defmacros* + (list (cons 'defmacro + (lambda (name parms . body) + `(set! *defmacros* (cons (cons ',name (lambda ,parms ,@body)) + *defmacros*)))))) +(define (defmacro? m) (and (assq m *defmacros*) #t)) + +(define (macroexpand-1 e) + (if (pair? e) (let ((a (car e))) + (cond ((symbol? a) (set! a (assq a *defmacros*)) + (if a (apply (cdr a) (cdr e)) e)) + (else e))) + e)) + +(define (macroexpand e) + (if (pair? e) (let ((a (car e))) + (cond ((symbol? a) + (set! a (assq a *defmacros*)) + (if a (macroexpand (apply (cdr a) (cdr e))) e)) + (else e))) + e)) + +(define gentemp + (let ((*gensym-counter* -1)) + (lambda () + (set! *gensym-counter* (+ *gensym-counter* 1)) + (string->symbol + (string-append "slib:G" (number->string *gensym-counter*)))))) + +(define base:eval slib:eval) +(define (defmacro:eval x) (base:eval (defmacro:expand* x))) +(define (defmacro:expand* x) + (require 'defmacroexpand) (apply defmacro:expand* x '())) + +(define (defmacro:load <pathname>) + (slib:eval-load <pathname> defmacro:eval)) + +(define (slib:eval-load <pathname> evl) + (if (not (file-exists? <pathname>)) + (set! <pathname> (string-append <pathname> (scheme-file-suffix)))) + (call-with-input-file <pathname> + (lambda (port) + (let ((old-load-pathname *load-pathname*)) + (set! *load-pathname* <pathname>) + (do ((o (read port) (read port))) + ((eof-object? o)) + (evl o)) + (set! *load-pathname* old-load-pathname))))) + +;;; define an error procedure for the library +(define slib:error + (lambda args + (cerror "Error: " args))) + +;;; define these as appropriate for your system. +(define slib:tab #\tab) +(define slib:form-feed #\page) + +;;; Define these if your implementation's syntax can support it and if +;;; they are not already defined. + +;(define (1+ n) (+ n 1)) +;(define (-1+ n) (+ n -1)) +;(define 1- -1+) + +(define in-vicinity string-append) + +;;; Define SLIB:EXIT to be the implementation procedure to exit or +;;; return if exitting not supported. +; MacScheme does not return a value when it exits, +; so simply invoke system procedure exit with 0 args. +(define slib:exit (lambda args (exit))) + +;;; Here for backward compatability +(define scheme-file-suffix + (let ((suffix (case (software-type) + ((NOSVE) "_scm") + (else ".scm")))) + (lambda () suffix))) + +;;; (SLIB:LOAD-SOURCE "foo") should load "foo.scm" or with whatever +;;; suffix all the module files in SLIB have. See feature 'SOURCE. + +;(define slib:load-source load) +(define (slib:load-source f) (load (string-append f (scheme-file-suffix)))) + +;;; (SLIB:LOAD-COMPILED "foo") should load the file that was produced +;;; by compiling "foo.scm" if this implementation can compile files. +;;; See feature 'COMPILED. + +(define slib:load-compiled load) + +;;; At this point SLIB:LOAD must be able to load SLIB files. + +(define slib:load slib:load-source) + +(slib:load (in-vicinity (library-vicinity) "require")) diff --git a/macwork.scm b/macwork.scm new file mode 100644 index 0000000..6336ae5 --- /dev/null +++ b/macwork.scm @@ -0,0 +1,126 @@ +;;;; "macwork.scm": Will Clinger's macros that work. -*- Scheme -*- +;Copyright 1992 William Clinger +; +; Permission to copy this software, in whole or in part, to use this +; software for any lawful purpose, and to redistribute this software +; is granted subject to the restriction that all copies made of this +; software must include this copyright notice in full. +; +; I also request that you send me a copy of any improvements that you +; make to this software so that they may be incorporated within it to +; the benefit of the Scheme community. + +(slib:load (in-vicinity (program-vicinity) "mwexpand")) + +;;;; Miscellaneous routines. + +(define (mw:warn msg . more) + (display "WARNING from macro expander:") + (newline) + (display msg) + (newline) + (for-each (lambda (x) (write x) (newline)) + more)) + +(define (mw:error msg . more) + (display "ERROR detected during macro expansion:") + (newline) + (display msg) + (newline) + (for-each (lambda (x) (write x) (newline)) + more) + (mw:quit #f)) + +(define (mw:bug msg . more) + (display "BUG in macro expander: ") + (newline) + (display msg) + (newline) + (for-each (lambda (x) (write x) (newline)) + more) + (mw:quit #f)) + +; Given a <formals>, returns a list of bound variables. + +(define (mw:make-null-terminated x) + (cond ((null? x) '()) + ((pair? x) + (cons (car x) (mw:make-null-terminated (cdr x)))) + (else (list x)))) + +; Returns the length of the given list, or -1 if the argument +; is not a list. Does not check for circular lists. + +(define (mw:safe-length x) + (define (loop x n) + (cond ((null? x) n) + ((pair? x) (loop (cdr x) (+ n 1))) + (else -1))) + (loop x 0)) + +(require 'common-list-functions) + +; Given an association list, copies the association pairs. + +(define (mw:syntax-copy alist) + (map (lambda (x) (cons (car x) (cdr x))) + alist)) + +;;;; Implementation-dependent parameters and preferences that determine +; how identifiers are represented in the output of the macro expander. +; +; The basic problem is that there are no reserved words, so the +; syntactic keywords of core Scheme that are used to express the +; output need to be represented by data that cannot appear in the +; input. This file defines those data. + +; The following definitions assume that identifiers of mixed case +; cannot appear in the input. + +;(define mw:begin1 (string->symbol "Begin")) +;(define mw:define1 (string->symbol "Define")) +;(define mw:quote1 (string->symbol "Quote")) +;(define mw:lambda1 (string->symbol "Lambda")) +;(define mw:if1 (string->symbol "If")) +;(define mw:set!1 (string->symbol "Set!")) + +(define mw:begin1 'begin) +(define mw:define1 'define) +(define mw:quote1 'quote) +(define mw:lambda1 'lambda) +(define mw:if1 'if) +(define mw:set!1 'set!) + +; The following defines an implementation-dependent expression +; that evaluates to an undefined (not unspecified!) value, for +; use in expanding the (define x) syntax. + +(define mw:undefined (list (string->symbol "Undefined"))) + +; A variable is renamed by suffixing a vertical bar followed by a unique +; integer. In IEEE and R4RS Scheme, a vertical bar cannot appear as part +; of an identifier, but presumably this is enforced by the reader and not +; by the compiler. Any other character that cannot appear as part of an +; identifier may be used instead of the vertical bar. + +(define mw:suffix-character #\|) + +(slib:load (in-vicinity (program-vicinity) "mwdenote")) +(slib:load (in-vicinity (program-vicinity) "mwsynrul")) + +(define macro:expand macwork:expand) + +;;; Here are EVAL, EVAL! and LOAD which expand macros. You can replace the +;;; implementation's eval and load with them if you like. +(define base:eval slib:eval) +(define base:load load) + +(define (macwork:eval x) (base:eval (macwork:expand x))) +(define macro:eval macwork:eval) + +(define (macwork:load <pathname>) + (slib:eval-load <pathname> macwork:eval)) +(define macro:load macwork:load) + +(provide 'macros-that-work) +(provide 'macro) diff --git a/makcrc.scm b/makcrc.scm new file mode 100644 index 0000000..b11f80e --- /dev/null +++ b/makcrc.scm @@ -0,0 +1,86 @@ +;;;; "makcrc.scm" Compute Cyclic Checksums +;;; Copyright (C) 1995, 1996 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;;(define crc (eval (make-port-crc 16 #o010013))) +;;;(define crc (eval (make-port-crc 08 #o053))) + +(define (file-check-sum file) (call-with-input-file file crc32)) + +(define (make-port-crc . margs) + (define (make-mask hibit) + (+ (ash (+ -1 (ash 1 (+ 1 (- hibit 2)))) 1) 1)) + (define accum-bits 32) + (define chunk-bits (integer-length (+ -1 char-code-limit))) + (define generator #f) + (cond ((pair? margs) + (set! accum-bits (car margs)) + (cond ((pair? (cdr margs)) + (set! generator (cadr margs)))))) + (cond ((not generator) + (case accum-bits + ((32) (set! generator #b00000100110000010001110110110111)) + (else (slib:error 'make-port-crc "no default polynomial for" + accum-bits "bits"))))) + (let* ((chunk-mask (make-mask chunk-bits)) + (crctab (make-vector (+ 1 chunk-mask)))) + (define (accum src) + `(set! + crc + (logxor (ash (logand ,(make-mask (- accum-bits chunk-bits)) crc) + ,chunk-bits) + (vector-ref crctab + (logand ,chunk-mask + (logxor + (ash crc ,(- chunk-bits accum-bits)) + ,src)))))) + (define (make-crc-table) + (letrec ((r (make-vector chunk-bits)) + (remd (lambda (m) + (define rem 0) + (do ((i 0 (+ 1 i))) + ((>= i chunk-bits) rem) + (if (logbit? i m) + (set! rem (logxor rem (vector-ref r i)))))))) + (vector-set! r 0 generator) + (do ((i 1 (+ 1 i))) + ((>= i chunk-bits)) + (let ((r-1 (vector-ref r (+ -1 i))) + (m-1 (make-mask (+ -1 accum-bits)))) + (vector-set! r i (if (logbit? (+ -1 accum-bits) r-1) + (logxor (ash (logand m-1 r-1) 1) generator) + (ash (logand m-1 r-1) 1))))) + (do ((i 0 (+ 1 i))) + ((> i chunk-mask)) + (vector-set! crctab i (remd i))))) + (cond ((>= (integer-length generator) accum-bits) + (slib:error 'make-port-crc + "generator longer than" accum-bits "bits"))) + (make-crc-table) + `(lambda (port) + (define crc 0) + (define byte-count 0) + (define crctab ,crctab) + (do ((ci (read-char port) (read-char port))) + ((eof-object? ci)) + ,(accum '(char->integer ci)) + (set! byte-count (+ 1 byte-count))) + (do ((byte-count byte-count (ash byte-count ,(- chunk-bits)))) + ((zero? byte-count)) + ,(accum 'byte-count)) + (logxor ,(make-mask accum-bits) crc)))) @@ -0,0 +1,362 @@ +;;;; "mbe.scm" "Macro by Example" (Eugene Kohlbecker, r4rs) +;;; From: Dorai Sitaram, dorai@cs.rice.edu, 1991, revised Sept. 3, 1992, +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;; revised Dec. 6, 1993 to r4rs syntax (if not semantics). +;;; revised Mar. 2 1994 for SLIB (jaffer@ai.mit.edu). + +;;; A vanilla implementation of Macro-by-Example (Eugene +;;; Kohlbecker, r4rs). This file requires defmacro. + +(require 'common-list-functions) ;nconc, some, every +;(require 'rev2-procedures) ;append! alternate for nconc +(require 'rev4-optional-procedures) ;list-tail +(require 'defmacroexpand) + +;;; A vanilla implementation of a hygiene filter for define-syntax + +;(define hyg:tag-generic +; (lambda (e kk tmps) e)) + +;;; if you don't want the hygiene filter, comment out the following +;;; s-exp and uncomment the previous one. + +(define hyg:tag-generic + (lambda (e kk tmps) + (if (pair? e) + (let ((a (car e))) + (case a + ((quote) `(quote ,(hyg:tag-vanilla (cadr e) kk tmps))) + ((if begin) + `(,a ,@(map (lambda (e1) (hyg:tag-generic e1 kk tmps)) + (cdr e)))) + ((set! define) + `(,a ,(hyg:tag-vanilla (cadr e) kk tmps) + ,@(map (lambda (e1) (hyg:tag-generic e1 kk tmps)) + (cddr e)))) + ((lambda) (hyg:tag-lambda (cdr e) kk tmps)) + ((letrec) (hyg:tag-letrec (cdr e) kk tmps)) + ((let) (hyg:tag-let (cdr e) kk tmps)) + ((let*) (hyg:tag-let-star (cdr e) kk tmps)) + ((do) (hyg:tag-do (cdr e) kk tmps)) + ((case) + `(case ,(hyg:tag-generic (cadr e) kk tmps) + ,@(map + (lambda (cl) + `(,(hyg:tag-vanilla (car cl) kk tmps) + ,@(map + (lambda (e1) + (hyg:tag-generic e1 kk tmps)) + (cdr cl)))) + (cddr e)))) + ((cond) + `(cond ,@(map + (lambda (cl) + (map (lambda (e1) + (hyg:tag-generic e1 kk tmps)) + cl)) + (cdr e)))) + (else (map (lambda (e1) + (hyg:tag-generic e1 kk tmps)) + e)))) + (hyg:tag-vanilla e kk tmps)))) + +(define hyg:tag-vanilla + (lambda (e kk tmps) + (cond ((symbol? e) + (cond ((memq e kk) e) + ((assq e tmps) => cdr) + (else e))) + ((pair? e) + (cons (hyg:tag-vanilla (car e) kk tmps) + (hyg:tag-vanilla (cdr e) kk tmps))) + (else e)))) + +(define hyg:tag-lambda + (lambda (e kk tmps) + (let* ((bvv (car e)) + (tmps2 (append + (map (lambda (v) (cons v (gentemp))) + (hyg:flatten bvv)) + tmps))) + `(lambda + ,(hyg:tag-vanilla bvv kk tmps2) + ,@(map + (lambda (e1) + (hyg:tag-generic e1 kk tmps2)) + (cdr e)))))) + +(define hyg:flatten + (lambda (e) + (let loop ((e e) (r '())) + (cond ((pair? e) (loop (car e) + (loop (cdr e) r))) + ((null? e) r) + (else (cons e r)))))) + +(define hyg:tag-letrec + (lambda (e kk tmps) + (let* ((varvals (car e)) + (tmps2 (append + (map (lambda (v) (cons v (gentemp))) + (map car varvals)) + tmps))) + `(letrec ,(map + (lambda (varval) + `(,(hyg:tag-vanilla (car varval) + kk tmps2) + ,(hyg:tag-generic (cadr varval) + kk tmps2))) + varvals) + ,@(map (lambda (e1) + (hyg:tag-generic e1 kk tmps2)) + (cdr e)))))) + +(define hyg:tag-let + (lambda (e kk tmps) + (let* ((tt (if (symbol? (car e)) (cons (car e) (gentemp)) '())) + (e (if (null? tt) e (cdr e))) + (tmps (if (null? tt) tmps (append (list tt) tmps)))) + (let* ((varvals (car e)) + (tmps2 (append (map (lambda (v) (cons v (gentemp))) + (map car varvals)) + tmps))) + `(let + ,@(if (null? tt) '() `(,(hyg:tag-vanilla (car tt) + kk + tmps))) + ,(let loop ((varvals varvals) + (i (length varvals))) + (if (null? varvals) '() + (let ((varval (car varvals)) + (tmps3 (list-tail tmps2 i))) + (cons `(,(hyg:tag-vanilla (car varval) + kk tmps2) + ,(hyg:tag-generic (cadr varval) + kk tmps3)) + (loop (cdr varvals) (- i 1)))))) + ,@(map + (lambda (e1) + (hyg:tag-generic e1 kk tmps2)) + (cdr e))))))) + +(define hyg:tag-do + (lambda (e kk tmps) + (let* ((varinistps (car e)) + (tmps2 (append (map (lambda (v) (cons v (gentemp))) + (map car varinistps)) + tmps))) + `(do + ,(let loop ((varinistps varinistps) + (i (length varinistps))) + (if (null? varinistps) '() + (let ((varinistp (car varinistps)) + (tmps3 (list-tail tmps2 i))) + (cons `(,(hyg:tag-vanilla (car varinistp) + kk tmps2) + ,(hyg:tag-generic (cadr varinistp) + kk tmps3) + ,@(hyg:tag-generic (cddr varinistp) + kk tmps2)) + (loop (cdr varinistps) (- i 1)))))) + ,(map (lambda (e1) + (hyg:tag-generic e1 kk tmps2)) (cadr e)) + ,@(map + (lambda (e1) + (hyg:tag-generic e1 kk tmps2)) + (cddr e)))))) + +(define hyg:tag-let-star + (lambda (e kk tmps) + (let* ((varvals (car e)) + (tmps2 (append (reverse (map (lambda (v) (cons v (gentemp))) + (map car varvals))) + tmps))) + `(let* + ,(let loop ((varvals varvals) + (i (- (length varvals) 1))) + (if (null? varvals) '() + (let ((varval (car varvals)) + (tmps3 (list-tail tmps2 i))) + (cons `(,(hyg:tag-vanilla (car varval) + kk tmps3) + ,(hyg:tag-generic (cadr varval) + kk (cdr tmps3))) + (loop (cdr varvals) (- i 1)))))) + ,@(map + (lambda (e1) + (hyg:tag-generic e1 kk tmps2)) + (cdr e)))))) + +;;;; End of hygiene filter. + +;;; finds the leftmost index of list l where something equal to x +;;; occurs +(define mbe:position + (lambda (x l) + (let loop ((l l) (i 0)) + (cond ((not (pair? l)) #f) + ((equal? (car l) x) i) + (else (loop (cdr l) (+ i 1))))))) + +;;; tests if expression e matches pattern p where k is the list of +;;; keywords +(define mbe:matches-pattern? + (lambda (p e k) + (cond ((mbe:ellipsis? p) + (and (or (null? e) (pair? e)) + (let* ((p-head (car p)) + (p-tail (cddr p)) + (e-head=e-tail (mbe:split-at-ellipsis e p-tail))) + (and e-head=e-tail + (let ((e-head (car e-head=e-tail)) + (e-tail (cdr e-head=e-tail))) + (and (comlist:every + (lambda (x) (mbe:matches-pattern? p-head x k)) + e-head) + (mbe:matches-pattern? p-tail e-tail k))))))) + ((pair? p) + (and (pair? e) + (mbe:matches-pattern? (car p) (car e) k) + (mbe:matches-pattern? (cdr p) (cdr e) k))) + ((symbol? p) (if (memq p k) (eq? p e) #t)) + (else (equal? p e))))) + +;;; gets the bindings of pattern variables of pattern p for +;;; expression e; +;;; k is the list of keywords +(define mbe:get-bindings + (lambda (p e k) + (cond ((mbe:ellipsis? p) + (let* ((p-head (car p)) + (p-tail (cddr p)) + (e-head=e-tail (mbe:split-at-ellipsis e p-tail)) + (e-head (car e-head=e-tail)) + (e-tail (cdr e-head=e-tail))) + (cons (cons (mbe:get-ellipsis-nestings p-head k) + (map (lambda (x) (mbe:get-bindings p-head x k)) + e-head)) + (mbe:get-bindings p-tail e-tail k)))) + ((pair? p) + (append (mbe:get-bindings (car p) (car e) k) + (mbe:get-bindings (cdr p) (cdr e) k))) + ((symbol? p) + (if (memq p k) '() (list (cons p e)))) + (else '())))) + +;;; expands pattern p using environment r; +;;; k is the list of keywords +(define mbe:expand-pattern + (lambda (p r k) + (cond ((mbe:ellipsis? p) + (append (let* ((p-head (car p)) + (nestings (mbe:get-ellipsis-nestings p-head k)) + (rr (mbe:ellipsis-sub-envs nestings r))) + (map (lambda (r1) + (mbe:expand-pattern p-head (append r1 r) k)) + rr)) + (mbe:expand-pattern (cddr p) r k))) + ((pair? p) + (cons (mbe:expand-pattern (car p) r k) + (mbe:expand-pattern (cdr p) r k))) + ((symbol? p) + (if (memq p k) p + (let ((x (assq p r))) + (if x (cdr x) p)))) + (else p)))) + +;;; returns a list that nests a pattern variable as deeply as it +;;; is ellipsed +(define mbe:get-ellipsis-nestings + (lambda (p k) + (let sub ((p p)) + (cond ((mbe:ellipsis? p) (cons (sub (car p)) (sub (cddr p)))) + ((pair? p) (append (sub (car p)) (sub (cdr p)))) + ((symbol? p) (if (memq p k) '() (list p))) + (else '()))))) + +;;; finds the subenvironments in r corresponding to the ellipsed +;;; variables in nestings +(define mbe:ellipsis-sub-envs + (lambda (nestings r) + (comlist:some (lambda (c) + (if (mbe:contained-in? nestings (car c)) (cdr c) #f)) + r))) + +;;; checks if nestings v and y have an intersection +(define mbe:contained-in? + (lambda (v y) + (if (or (symbol? v) (symbol? y)) (eq? v y) + (comlist:some (lambda (v_i) + (comlist:some (lambda (y_j) + (mbe:contained-in? v_i y_j)) + y)) + v)))) + +;;; split expression e so that its second half matches with +;;; pattern p-tail +(define mbe:split-at-ellipsis + (lambda (e p-tail) + (if (null? p-tail) (cons e '()) + (let ((i (mbe:position (car p-tail) e))) + (if i (cons (butlast e (- (length e) i)) + (list-tail e i)) + (slib:error 'mbe:split-at-ellipsis 'bad-arg)))))) + +;;; tests if x is an ellipsing pattern, i.e., of the form +;;; (blah ... . blah2) +(define mbe:ellipsis? + (lambda (x) + (and (pair? x) (pair? (cdr x)) (eq? (cadr x) '...)))) + +;define-syntax + +(defmacro define-syntax (macro-name syn-rules) + (if (or (not (pair? syn-rules)) + (not (eq? (car syn-rules) 'syntax-rules))) + (slib:error 'define-syntax 'not-an-r4rs-high-level-macro + macro-name syn-rules) + (let ((keywords (cons macro-name (cadr syn-rules))) + (clauses (cddr syn-rules))) + `(defmacro ,macro-name macro-arg + (let ((macro-arg (cons ',macro-name macro-arg)) + (keywords ',keywords)) + (cond ,@(map + (lambda (clause) + (let ((in-pattern (car clause)) + (out-pattern (cadr clause))) + `((mbe:matches-pattern? ',in-pattern macro-arg + keywords) + (hyg:tag-generic + (mbe:expand-pattern + ',out-pattern + (mbe:get-bindings ',in-pattern macro-arg + keywords) + keywords) + (nconc + (hyg:flatten ',in-pattern) + keywords) + '())))) + clauses) + (else (slib:error ',macro-name 'no-matching-clause + ',clauses)))))))) + +(define macro:eval slib:eval) +(define macro:load slib:load) +(provide 'macro) +;eof diff --git a/mitcomp.pat b/mitcomp.pat new file mode 100644 index 0000000..78cb9b9 --- /dev/null +++ b/mitcomp.pat @@ -0,0 +1,1466 @@ +;"mitcomp.pat", patch file of definitions for compiling SLIB with MitScheme. +;;; Copyright (C) 1993 Matthew McDonald. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +From: mafm@cs.uwa.edu.au (Matthew MCDONALD) + + Added declarations to files providing these: +dynamic alist hash hash-table logical random random-inexact modular +prime charplot common-list-functions format generic-write pprint-file +pretty-print-to-string object->string string-case printf line-i/o +synchk priority-queue process red-black-tree sort + +(for-each cf + '("dynamic.scm" "alist.scm" "hash.scm" "hashtab.scm" "logical.scm" + "random.scm" "randinex.scm" "modular.scm" "prime.scm" "charplot.scm" + "comlist.scm" "format.scm" "genwrite.scm" "ppfile.scm" "pp2str.scm" + "obj2str.scm" "strcase.scm" "printf.scm" "lineio.scm" "synchk.scm" + "priorque.scm" "process.scm" "rbtree.scm" "sort.scm)) + +while in the SLIB directory will compile all of these. + + They all appear to still be working... They should be +everything CScheme currently uses (except [1] below.) + +NOTES: + +[1] Not altered: + debug Not worth optimising + test " " " + fluid-let compiler chokes over + (lambda () . body) + scmacro Fails when compiled, not immediately obvious why + synclo " " " + r4rsyn " " " + yasos requires the macros + collect " " " + +[2] removed 'sort from list of MIT features. The library version is +more complete (and needed for charplot.) + +[3] Remember that mitscheme.init gets the .bin put in the wrong place +by the compiler and thus doesn't get recognised by LOAD. +====================================================================== +diff -c slib/alist.scm nlib/alist.scm +*** slib/alist.scm Thu Jan 21 00:01:34 1993 +--- nlib/alist.scm Tue Feb 9 00:21:07 1993 +*************** +*** 44,50 **** + ;(define rem (alist-remover string-ci=?)) + ;(set! alist (rem alist "fOO")) + +! (define (predicate->asso pred) + (cond ((eq? eq? pred) assq) + ((eq? = pred) assv) + ((eq? eqv? pred) assv) +--- 44,53 ---- + ;(define rem (alist-remover string-ci=?)) + ;(set! alist (rem alist "fOO")) + +! ;;; Declarations for CScheme +! (declare (usual-integrations)) +! +! (define-integrable (predicate->asso pred) + (cond ((eq? eq? pred) assq) + ((eq? = pred) assv) + ((eq? eqv? pred) assv) +*************** +*** 57,69 **** + ((pred key (caar al)) (car al)) + (else (l (cdr al))))))))) + +! (define (alist-inquirer pred) + (let ((assofun (predicate->asso pred))) + (lambda (alist key) + (let ((pair (assofun key alist))) + (and pair (cdr pair)))))) + +! (define (alist-associator pred) + (let ((assofun (predicate->asso pred))) + (lambda (alist key val) + (let* ((pair (assofun key alist))) +--- 60,72 ---- + ((pred key (caar al)) (car al)) + (else (l (cdr al))))))))) + +! (define-integrable (alist-inquirer pred) + (let ((assofun (predicate->asso pred))) + (lambda (alist key) + (let ((pair (assofun key alist))) + (and pair (cdr pair)))))) + +! (define-integrable (alist-associator pred) + (let ((assofun (predicate->asso pred))) + (lambda (alist key val) + (let* ((pair (assofun key alist))) +*************** +*** 71,77 **** + alist) + (else (cons (cons key val) alist))))))) + +! (define (alist-remover pred) + (lambda (alist key) + (cond ((null? alist) alist) + ((pred key (caar alist)) (cdr alist)) +--- 74,80 ---- + alist) + (else (cons (cons key val) alist))))))) + +! (define-integrable (alist-remover pred) + (lambda (alist key) + (cond ((null? alist) alist) + ((pred key (caar alist)) (cdr alist)) +diff -c slib/charplot.scm nlib/charplot.scm +*** slib/charplot.scm Sat Nov 14 21:50:54 1992 +--- nlib/charplot.scm Tue Feb 9 00:21:07 1993 +*************** +*** 7,12 **** +--- 7,24 ---- + ;are strings with names to label the x and y axii with. + + ;;;;--------------------------------------------------------------- ++ ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ++ (declare (integrate-external "sort")) ++ (declare (integrate ++ rows ++ columns ++ charplot:height ++ charplot:width ++ charplot:plot ++ plot!)) ++ + (require 'sort) + + (define rows 24) +*************** +*** 27,39 **** + (write-char char) + (charplot:printn! (+ n -1) char)))) + +! (define (charplot:center-print! str width) + (let ((lpad (quotient (- width (string-length str)) 2))) + (charplot:printn! lpad #\ ) + (display str) + (charplot:printn! (- width (+ (string-length str) lpad)) #\ ))) + +! (define (scale-it z scale) + (if (and (exact? z) (integer? z)) + (quotient (* z (car scale)) (cadr scale)) + (inexact->exact (round (/ (* z (car scale)) (cadr scale)))))) +--- 39,51 ---- + (write-char char) + (charplot:printn! (+ n -1) char)))) + +! (define-integrable (charplot:center-print! str width) + (let ((lpad (quotient (- width (string-length str)) 2))) + (charplot:printn! lpad #\ ) + (display str) + (charplot:printn! (- width (+ (string-length str) lpad)) #\ ))) + +! (define-integrable (scale-it z scale) + (if (and (exact? z) (integer? z)) + (quotient (* z (car scale)) (cadr scale)) + (inexact->exact (round (/ (* z (car scale)) (cadr scale)))))) +diff -c slib/comlist.scm nlib/comlist.scm +*** slib/comlist.scm Wed Jan 27 11:08:44 1993 +--- nlib/comlist.scm Tue Feb 9 00:21:08 1993 +*************** +*** 6,11 **** +--- 6,14 ---- + + ;;;; LIST FUNCTIONS FROM COMMON LISP + ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ++ + ;;;From: hugh@ear.mit.edu (Hugh Secker-Walker) + (define (make-list k . init) + (set! init (if (pair? init) (car init))) +*************** +*** 13,21 **** + (result '() (cons init result))) + ((<= k 0) result))) + +! (define (copy-list lst) (append lst '())) + +! (define (adjoin e l) (if (memq e l) l (cons e l))) + + (define (union l1 l2) + (cond ((null? l1) l2) +--- 16,24 ---- + (result '() (cons init result))) + ((<= k 0) result))) + +! (define-integrable (copy-list lst) (append lst '())) + +! (define-integrable (adjoin e l) (if (memq e l) l (cons e l))) + + (define (union l1 l2) + (cond ((null? l1) l2) +*************** +*** 33,39 **** + ((memv (car l1) l2) (set-difference (cdr l1) l2)) + (else (cons (car l1) (set-difference (cdr l1) l2))))) + +! (define (position obj lst) + (letrec ((pos (lambda (n lst) + (cond ((null? lst) #f) + ((eqv? obj (car lst)) n) +--- 36,42 ---- + ((memv (car l1) l2) (set-difference (cdr l1) l2)) + (else (cons (car l1) (set-difference (cdr l1) l2))))) + +! (define-integrable (position obj lst) + (letrec ((pos (lambda (n lst) + (cond ((null? lst) #f) + ((eqv? obj (car lst)) n) +*************** +*** 45,51 **** + init + (reduce-init p (p init (car l)) (cdr l)))) + +! (define (reduce p l) + (cond ((null? l) l) + ((null? (cdr l)) (car l)) + (else (reduce-init p (car l) (cdr l))))) +--- 48,54 ---- + init + (reduce-init p (p init (car l)) (cdr l)))) + +! (define-integrable (reduce p l) + (cond ((null? l) l) + ((null? (cdr l)) (car l)) + (else (reduce-init p (car l) (cdr l))))) +*************** +*** 58,64 **** + (or (null? l) + (and (pred (car l)) (every pred (cdr l))))) + +! (define (notevery pred l) (not (every pred l))) + + (define (find-if t l) + (cond ((null? l) #f) +--- 61,67 ---- + (or (null? l) + (and (pred (car l)) (every pred (cdr l))))) + +! (define-integrable (notevery pred l) (not (every pred l))) + + (define (find-if t l) + (cond ((null? l) #f) +*************** +*** 121,141 **** + (define (nthcdr n lst) + (if (zero? n) lst (nthcdr (+ -1 n) (cdr lst)))) + +! (define (last lst n) + (nthcdr (- (length lst) n) lst)) + + ;;;; CONDITIONALS + +! (define (and? . args) + (cond ((null? args) #t) + ((car args) (apply and? (cdr args))) + (else #f))) + +! (define (or? . args) + (cond ((null? args) #f) + ((car args) #t) + (else (apply or? (cdr args))))) + +! (define (identity x) x) + + (require 'rev3-procedures) +--- 124,144 ---- + (define (nthcdr n lst) + (if (zero? n) lst (nthcdr (+ -1 n) (cdr lst)))) + +! (define-integrable (last lst n) + (nthcdr (- (length lst) n) lst)) + + ;;;; CONDITIONALS + +! (define-integrable (and? . args) + (cond ((null? args) #t) + ((car args) (apply and? (cdr args))) + (else #f))) + +! (define-integrable (or? . args) + (cond ((null? args) #f) + ((car args) #t) + (else (apply or? (cdr args))))) + +! (define-integrable (identity x) x) + + (require 'rev3-procedures) +diff -c slib/dynamic.scm nlib/dynamic.scm +*** slib/dynamic.scm Thu Sep 17 23:35:46 1992 +--- nlib/dynamic.scm Tue Feb 9 00:21:08 1993 +*************** +*** 31,36 **** +--- 31,43 ---- + ; + ;There was also a DYNAMIC-BIND macro which I haven't implemented. + ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ++ ++ (declare (integrate-external "record")) ++ (declare (integrate-external "dynwind")) ++ (declare (integrate dynamic:errmsg)) ++ + (require 'record) + (require 'dynamic-wind) + +*************** +*** 48,60 **** + (record-accessor dynamic-environment-rtd 'parent)) + + (define *current-dynamic-environment* #f) +! (define (extend-current-dynamic-environment dynamic obj) + (set! *current-dynamic-environment* + (make-dynamic-environment dynamic obj + *current-dynamic-environment*))) + + (define dynamic-rtd (make-record-type "dynamic" '())) +! (define make-dynamic + (let ((dynamic-constructor (record-constructor dynamic-rtd))) + (lambda (obj) + (let ((dynamic (dynamic-constructor))) +--- 55,69 ---- + (record-accessor dynamic-environment-rtd 'parent)) + + (define *current-dynamic-environment* #f) +! +! (define-integrable (extend-current-dynamic-environment dynamic obj) + (set! *current-dynamic-environment* + (make-dynamic-environment dynamic obj + *current-dynamic-environment*))) + + (define dynamic-rtd (make-record-type "dynamic" '())) +! +! (define-integrable make-dynamic + (let ((dynamic-constructor (record-constructor dynamic-rtd))) + (lambda (obj) + (let ((dynamic (dynamic-constructor))) +*************** +*** 61,68 **** + (extend-current-dynamic-environment dynamic obj) + dynamic)))) + +! (define dynamic? (record-predicate dynamic-rtd)) +! (define (guarantee-dynamic dynamic) + (or (dynamic? dynamic) + (slib:error "Not a dynamic" dynamic))) + +--- 70,78 ---- + (extend-current-dynamic-environment dynamic obj) + dynamic)))) + +! (define-integrable dynamic? (record-predicate dynamic-rtd)) +! +! (define-integrable (guarantee-dynamic dynamic) + (or (dynamic? dynamic) + (slib:error "Not a dynamic" dynamic))) + +*************** +*** 69,75 **** + (define dynamic:errmsg + "No value defined for this dynamic in the current dynamic environment") + +! (define (dynamic-ref dynamic) + (guarantee-dynamic dynamic) + (let loop ((env *current-dynamic-environment*)) + (cond ((not env) +--- 79,85 ---- + (define dynamic:errmsg + "No value defined for this dynamic in the current dynamic environment") + +! (define-integrable (dynamic-ref dynamic) + (guarantee-dynamic dynamic) + (let loop ((env *current-dynamic-environment*)) + (cond ((not env) +*************** +*** 79,85 **** + (else + (loop (dynamic-environment:parent env)))))) + +! (define (dynamic-set! dynamic obj) + (guarantee-dynamic dynamic) + (let loop ((env *current-dynamic-environment*)) + (cond ((not env) +--- 89,95 ---- + (else + (loop (dynamic-environment:parent env)))))) + +! (define-integrable (dynamic-set! dynamic obj) + (guarantee-dynamic dynamic) + (let loop ((env *current-dynamic-environment*)) + (cond ((not env) +diff -c slib/format.scm nlib/format.scm +*** slib/format.scm Tue Jan 5 14:56:48 1993 +--- nlib/format.scm Tue Feb 9 00:21:09 1993 +*************** +*** 78,84 **** + ; * removed C-style padding support + ; + +! ;;; SCHEME IMPLEMENTATION DEPENDENCIES --------------------------------------- + + ;; To configure the format module for your scheme system, set the variable + ;; format:scheme-system to one of the symbols of (slib elk any). You may add +--- 78,88 ---- + ; * removed C-style padding support + ; + +! ;;; SCHEME IMPLEMENTATION DEPENDENCIES +! ;;; --------------------------------------- +! +! ;;; (minimal) Declarations for CScheme +! (declare (usual-integrations)) + + ;; To configure the format module for your scheme system, set the variable + ;; format:scheme-system to one of the symbols of (slib elk any). You may add +diff -c slib/genwrite.scm nlib/genwrite.scm +*** slib/genwrite.scm Mon Oct 19 14:49:06 1992 +--- nlib/genwrite.scm Tue Feb 9 00:21:10 1993 +*************** +*** 26,31 **** +--- 26,34 ---- + ; + ; where display-string = (lambda (s) (for-each write-char (string->list s)) #t) + ++ ;;; (minimal) Declarations for CScheme ++ (declare (usual-integrations)) ++ + (define (generic-write obj display? width output) + + (define (read-macro? l) +diff -c slib/hash.scm nlib/hash.scm +*** slib/hash.scm Thu Sep 10 00:05:52 1992 +--- nlib/hash.scm Tue Feb 9 00:21:10 1993 +*************** +*** 23,35 **** + ;the equality predicate pred. Pred should be EQ?, EQV?, EQUAL?, =, + ;CHAR=?, CHAR-CI=?, STRING=?, or STRING-CI=?. + +! (define (hash:hash-char char n) + (modulo (char->integer char) n)) + +! (define (hash:hash-char-ci char n) + (modulo (char->integer (char-downcase char)) n)) + +! (define (hash:hash-symbol sym n) + (hash:hash-string (symbol->string sym) n)) + + ;;; I am trying to be careful about overflow and underflow here. +--- 23,40 ---- + ;the equality predicate pred. Pred should be EQ?, EQV?, EQUAL?, =, + ;CHAR=?, CHAR-CI=?, STRING=?, or STRING-CI=?. + +! +! ;;; Declarations for CScheme +! (declare (usual-integrations)) +! (declare (integrate hash)) +! +! (define-integrable (hash:hash-char char n) + (modulo (char->integer char) n)) + +! (define-integrable (hash:hash-char-ci char n) + (modulo (char->integer (char-downcase char)) n)) + +! (define-integrable (hash:hash-symbol sym n) + (hash:hash-string (symbol->string sym) n)) + + ;;; I am trying to be careful about overflow and underflow here. +*************** +*** 173,179 **** + + (define hashq hashv) + +! (define (predicate->hash pred) + (cond ((eq? pred eq?) hashq) + ((eq? pred eqv?) hashv) + ((eq? pred equal?) hash) +--- 178,184 ---- + + (define hashq hashv) + +! (define-integrable (predicate->hash pred) + (cond ((eq? pred eq?) hashq) + ((eq? pred eqv?) hashv) + ((eq? pred equal?) hash) +diff -c slib/hashtab.scm nlib/hashtab.scm +*** slib/hashtab.scm Mon Oct 19 14:49:44 1992 +--- nlib/hashtab.scm Tue Feb 9 00:21:11 1993 +*************** +*** 36,47 **** + ;Returns a procedure of 2 arguments, hashtab and key, which modifies + ;hashtab so that the association whose key is key removed. + + (require 'hash) + (require 'alist) + +! (define (make-hash-table k) (make-vector k '())) + +! (define (predicate->hash-asso pred) + (let ((hashfun (predicate->hash pred)) + (asso (predicate->asso pred))) + (lambda (key hashtab) +--- 36,53 ---- + ;Returns a procedure of 2 arguments, hashtab and key, which modifies + ;hashtab so that the association whose key is key removed. + ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ++ ++ (declare (integrate-external "hash")) ++ (declare (integrate-external "alist")) ++ + (require 'hash) + (require 'alist) + +! (define-integrable (make-hash-table k) (make-vector k '())) + +! (define-integrable (predicate->hash-asso pred) + (let ((hashfun (predicate->hash pred)) + (asso (predicate->asso pred))) + (lambda (key hashtab) +*************** +*** 48,54 **** + (asso key + (vector-ref hashtab (hashfun key (vector-length hashtab))))))) + +! (define (hash-inquirer pred) + (let ((hashfun (predicate->hash pred)) + (ainq (alist-inquirer pred))) + (lambda (hashtab key) +--- 54,60 ---- + (asso key + (vector-ref hashtab (hashfun key (vector-length hashtab))))))) + +! (define-integrable (hash-inquirer pred) + (let ((hashfun (predicate->hash pred)) + (ainq (alist-inquirer pred))) + (lambda (hashtab key) +*************** +*** 55,61 **** + (ainq (vector-ref hashtab (hashfun key (vector-length hashtab))) + key)))) + +! (define (hash-associator pred) + (let ((hashfun (predicate->hash pred)) + (asso (alist-associator pred))) + (lambda (hashtab key val) +--- 61,67 ---- + (ainq (vector-ref hashtab (hashfun key (vector-length hashtab))) + key)))) + +! (define-integrable (hash-associator pred) + (let ((hashfun (predicate->hash pred)) + (asso (alist-associator pred))) + (lambda (hashtab key val) +*************** +*** 64,70 **** + (asso (vector-ref hashtab num) key val))) + hashtab))) + +! (define (hash-remover pred) + (let ((hashfun (predicate->hash pred)) + (arem (alist-remover pred))) + (lambda (hashtab key) +--- 70,76 ---- + (asso (vector-ref hashtab num) key val))) + hashtab))) + +! (define-integrable (hash-remover pred) + (let ((hashfun (predicate->hash pred)) + (arem (alist-remover pred))) + (lambda (hashtab key) +diff -c slib/lineio.scm nlib/lineio.scm +*** slib/lineio.scm Sun Oct 25 01:40:38 1992 +--- nlib/lineio.scm Tue Feb 9 00:21:11 1993 +*************** +*** 28,33 **** +--- 28,36 ---- + ;unspecified value. Port may be ommited, in which case it defaults to + ;the value returned by current-input-port. + ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ++ + (define (read-line . arg) + (let* ((char (apply read-char arg))) + (if (eof-object? char) +*************** +*** 56,61 **** + (+ 1 i) #f)))) + (string-set! str i char))))) + +! (define (write-line str . arg) + (apply display str arg) + (apply newline arg)) +--- 59,64 ---- + (+ 1 i) #f)))) + (string-set! str i char))))) + +! (define-integrable (write-line str . arg) + (apply display str arg) + (apply newline arg)) +diff -c slib/logical.scm nlib/logical.scm +*** slib/logical.scm Mon Feb 1 22:22:04 1993 +--- nlib/logical.scm Tue Feb 9 00:21:11 1993 +*************** +*** 48,53 **** +--- 48,66 ---- + ; + ;;;;------------------------------------------------------------------ + ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ++ (declare (integrate logand ; Exported functions ++ logor ++ logxor ++ lognot ++ ash ++ logcount ++ integer-length ++ bit-extract ++ ipow-by-squaring ++ integer-expt)) ++ + (define logical:integer-expt + (if (provided? 'inexact) + expt +*************** +*** 61,67 **** + (quotient k 2) + (if (even? k) acc (proc acc x)) + proc)))) +- + (define (logical:logand n1 n2) + (cond ((= n1 n2) n1) + ((zero? n1) 0) +--- 74,79 ---- +*************** +*** 90,102 **** + (vector-ref (vector-ref logical:boole-xor (modulo n1 16)) + (modulo n2 16)))))) + +! (define (logical:lognot n) (- -1 n)) + +! (define (logical:bit-extract n start end) + (logical:logand (- (logical:integer-expt 2 (- end start)) 1) + (logical:ash n (- start)))) + +! (define (logical:ash int cnt) + (if (negative? cnt) + (let ((n (logical:integer-expt 2 (- cnt)))) + (if (negative? int) +--- 102,114 ---- + (vector-ref (vector-ref logical:boole-xor (modulo n1 16)) + (modulo n2 16)))))) + +! (define-integrable (logical:lognot n) (- -1 n)) + +! (define-integrable (logical:bit-extract n start end) + (logical:logand (- (logical:integer-expt 2 (- end start)) 1) + (logical:ash n (- start)))) + +! (define-integrable (logical:ash int cnt) + (if (negative? cnt) + (let ((n (logical:integer-expt 2 (- cnt)))) + (if (negative? int) +*************** +*** 104,110 **** + (quotient int n))) + (* (logical:integer-expt 2 cnt) int))) + +! (define (logical:ash-4 x) + (if (negative? x) + (+ -1 (quotient (+ 1 x) 16)) + (quotient x 16))) +--- 116,122 ---- + (quotient int n))) + (* (logical:integer-expt 2 cnt) int))) + +! (define-integrable (logical:ash-4 x) + (if (negative? x) + (+ -1 (quotient (+ 1 x) 16)) + (quotient x 16))) +diff -c slib/mitscheme.init nlib/mitscheme.init +*** slib/mitscheme.init Fri Jan 22 00:52:04 1993 +--- nlib/mitscheme.init Tue Feb 9 00:21:12 1993 +*************** +*** 48,55 **** + + ;;; FORCE-OUTPUT flushes any pending output on optional arg output port + ;;; use this definition if your system doesn't have such a procedure. +! ;(define (force-output . arg) #t) +! (define force-output flush-output) + + ;;; CHAR-CODE-LIMIT is one greater than the largest integer which can + ;;; be returned by CHAR->INTEGER. It is defined by MITScheme. +--- 47,54 ---- + + ;;; FORCE-OUTPUT flushes any pending output on optional arg output port + ;;; use this definition if your system doesn't have such a procedure. +! (define (force-output . arg) #t) +! ;(define force-output flush-output) + + ;;; CHAR-CODE-LIMIT is one greater than the largest integer which can + ;;; be returned by CHAR->INTEGER. It is defined by MITScheme. +diff -c slib/modular.scm nlib/modular.scm +*** slib/modular.scm Sun Feb 2 12:53:26 1992 +--- nlib/modular.scm Tue Feb 9 00:21:13 1993 +*************** +*** 36,41 **** +--- 36,48 ---- + ;Returns (k2 ^ k3) mod k1. + ; + ;;;;-------------------------------------------------------------- ++ ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ++ ++ (declare (integrate-external "logical")) ++ (declare (integrate modular:negate extended-euclid)) ++ + (require 'logical) + + ;;; from: +*************** +*** 51,57 **** + (caddr res) + (- (cadr res) (* (quotient a b) (caddr res))))))) + +! (define (modular:invert m a) + (let ((d (modular:extended-euclid a m))) + (if (= 1 (car d)) + (modulo (cadr d) m) +--- 58,64 ---- + (caddr res) + (- (cadr res) (* (quotient a b) (caddr res))))))) + +! (define-integrable (modular:invert m a) + (let ((d (modular:extended-euclid a m))) + (if (= 1 (car d)) + (modulo (cadr d) m) +*************** +*** 59,67 **** + + (define modular:negate -) + +! (define (modular:+ m a b) (modulo (+ (- a m) b) m)) + +! (define (modular:- m a b) (modulo (- a b) m)) + + ;;; See: L'Ecuyer, P. and Cote, S. "Implementing a Random Number Package + ;;; with Splitting Facilities." ACM Transactions on Mathematical +--- 66,74 ---- + + (define modular:negate -) + +! (define-integrable (modular:+ m a b) (modulo (+ (- a m) b) m)) + +! (define-integrable (modular:- m a b) (modulo (- a b) m)) + + ;;; See: L'Ecuyer, P. and Cote, S. "Implementing a Random Number Package + ;;; with Splitting Facilities." ACM Transactions on Mathematical +*************** +*** 98,104 **** + (modulo (+ (if (positive? p) (- p m) p) + (* a0 (modulo b q))) m))))) + +! (define (modular:expt m a b) + (cond ((= a 1) 1) + ((= a (- m 1)) (if (odd? b) a 1)) + ((zero? a) 0) +--- 105,111 ---- + (modulo (+ (if (positive? p) (- p m) p) + (* a0 (modulo b q))) m))))) + +! (define-integrable (modular:expt m a b) + (cond ((= a 1) 1) + ((= a (- m 1)) (if (odd? b) a 1)) + ((zero? a) 0) +diff -c slib/obj2str.scm nlib/obj2str.scm +*** slib/obj2str.scm Mon Oct 19 14:49:08 1992 +--- nlib/obj2str.scm Tue Feb 9 00:21:13 1993 +*************** +*** 2,13 **** + + (require 'generic-write) + + ; (object->string obj) returns the textual representation of 'obj' as a + ; string. + ; + ; Note: (write obj) = (display (object->string obj)) + +! (define (object->string obj) + (let ((result '())) + (generic-write obj #f #f (lambda (str) (set! result (cons str result)) #t)) + (reverse-string-append result))) +--- 2,17 ---- + + (require 'generic-write) + ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ++ (declare (integrate-external "genwrite")) ++ + ; (object->string obj) returns the textual representation of 'obj' as a + ; string. + ; + ; Note: (write obj) = (display (object->string obj)) + +! (define-integrable (object->string obj) + (let ((result '())) + (generic-write obj #f #f (lambda (str) (set! result (cons str result)) #t)) + (reverse-string-append result))) +diff -c slib/pp2str.scm nlib/pp2str.scm +*** slib/pp2str.scm Mon Oct 19 14:49:08 1992 +--- nlib/pp2str.scm Tue Feb 9 00:21:13 1993 +*************** +*** 2,11 **** + + (require 'generic-write) + + ; (pretty-print-to-string obj) returns a string with the pretty-printed + ; textual representation of 'obj'. + +! (define (pp:pretty-print-to-string obj) + (let ((result '())) + (generic-write obj #f 79 (lambda (str) (set! result (cons str result)) #t)) + (reverse-string-append result))) +--- 2,16 ---- + + (require 'generic-write) + ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ++ (declare (integrate-external "genwrite")) ++ (declare (integrate pretty-print-to-string)) ++ + ; (pretty-print-to-string obj) returns a string with the pretty-printed + ; textual representation of 'obj'. + +! (define-integrable (pp:pretty-print-to-string obj) + (let ((result '())) + (generic-write obj #f 79 (lambda (str) (set! result (cons str result)) #t)) + (reverse-string-append result))) +diff -c slib/ppfile.scm nlib/ppfile.scm +*** slib/ppfile.scm Mon Oct 19 14:49:08 1992 +--- nlib/ppfile.scm Tue Feb 9 00:21:14 1993 +*************** +*** 10,15 **** +--- 10,19 ---- + ; + (require 'pretty-print) + ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ++ (declare (integrate-external "pp")) ++ + (define (pprint-file ifile . optarg) + (let ((lst (call-with-input-file ifile + (lambda (iport) +diff -c slib/prime.scm nlib/prime.scm +*** slib/prime.scm Mon Feb 8 20:49:46 1993 +--- nlib/prime.scm Tue Feb 9 00:24:16 1993 +*************** +*** 24,29 **** +--- 24,39 ---- + ;(sort! (factor k) <) + + ;;;;-------------------------------------------------------------- ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ++ (declare (integrate-external "random")) ++ (declare (integrate-external "modular")) ++ (declare (integrate ++ jacobi-symbol ++ prime? ++ factor)) ++ ++ + (require 'random) + (require 'modular) + +*************** +*** 56,62 **** + ;;; choosing prime:trials=30 should be enough + (define prime:trials 30) + ;;; prime:product is a product of small primes. +! (define prime:product + (let ((p 210)) + (for-each (lambda (s) (set! p (or (string->number s) p))) + '("2310" "30030" "510510" "9699690" "223092870" +--- 66,72 ---- + ;;; choosing prime:trials=30 should be enough + (define prime:trials 30) + ;;; prime:product is a product of small primes. +! (define-integrable prime:product + (let ((p 210)) + (for-each (lambda (s) (set! p (or (string->number s) p))) + '("2310" "30030" "510510" "9699690" "223092870" +*************** +*** 86,92 **** + ; | f(u,v,2b,n/2) or f(u+b,v+b,2b,(n-u-v-b)/2) if n even + + ;Thm: f(1,1,2,(m-1)/2) = (p,q) iff pq=m for odd m. +! + ;It may be illuminating to consider the relation of the Lankinen function in + ;a `computational hierarchy' of other factoring functions.* Assumptions are + ;made herein on the basis of conventional digital (binary) computers. Also, +--- 96,102 ---- + ; | f(u,v,2b,n/2) or f(u+b,v+b,2b,(n-u-v-b)/2) if n even + + ;Thm: f(1,1,2,(m-1)/2) = (p,q) iff pq=m for odd m. +! + ;It may be illuminating to consider the relation of the Lankinen function in + ;a `computational hierarchy' of other factoring functions.* Assumptions are + ;made herein on the basis of conventional digital (binary) computers. Also, +*************** +*** 94,100 **** + ;be factored is prime). However, all algorithms would probably perform to + ;the same constant multiple of the given orders for complete composite + ;factorizations. +! + ;Thm: Eratosthenes' Sieve is very roughtly O(ln(n)/n) in time and + ; O(n*log2(n)) in space. + ;Pf: It works with all prime factors less than n (about ln(n)/n by the prime +--- 104,110 ---- + ;be factored is prime). However, all algorithms would probably perform to + ;the same constant multiple of the given orders for complete composite + ;factorizations. +! + ;Thm: Eratosthenes' Sieve is very roughtly O(ln(n)/n) in time and + ; O(n*log2(n)) in space. + ;Pf: It works with all prime factors less than n (about ln(n)/n by the prime +diff -c slib/priorque.scm nlib/priorque.scm +*** slib/priorque.scm Mon Oct 19 14:49:42 1992 +--- nlib/priorque.scm Tue Feb 9 00:21:15 1993 +*************** +*** 22,41 **** + ;;; 1989 MIT Press. + + (require 'record) + (define heap-rtd (make-record-type "heap" '(array size heap<?))) +! (define make-heap + (let ((cstr (record-constructor heap-rtd))) + (lambda (pred<?) + (cstr (make-vector 4) 0 pred<?)))) +! (define heap-ref + (let ((ra (record-accessor heap-rtd 'array))) + (lambda (a i) + (vector-ref (ra a) (+ -1 i))))) +! (define heap-set! + (let ((ra (record-accessor heap-rtd 'array))) + (lambda (a i v) + (vector-set! (ra a) (+ -1 i) v)))) +! (define heap-exchange + (let ((aa (record-accessor heap-rtd 'array))) + (lambda (a i j) + (set! i (+ -1 i)) +--- 22,53 ---- + ;;; 1989 MIT Press. + + (require 'record) ++ ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ++ ++ (declare (integrate ++ heap-size ++ heap<?)) ++ + (define heap-rtd (make-record-type "heap" '(array size heap<?))) +! +! (define-integrable make-heap + (let ((cstr (record-constructor heap-rtd))) + (lambda (pred<?) + (cstr (make-vector 4) 0 pred<?)))) +! +! (define-integrable heap-ref + (let ((ra (record-accessor heap-rtd 'array))) + (lambda (a i) + (vector-ref (ra a) (+ -1 i))))) +! +! (define-integrable heap-set! + (let ((ra (record-accessor heap-rtd 'array))) + (lambda (a i v) + (vector-set! (ra a) (+ -1 i) v)))) +! +! (define-integrable heap-exchange + (let ((aa (record-accessor heap-rtd 'array))) + (lambda (a i j) + (set! i (+ -1 i)) +*************** +*** 44,51 **** +--- 56,66 ---- + (tmp (vector-ref ra i))) + (vector-set! ra i (vector-ref ra j)) + (vector-set! ra j tmp))))) ++ + (define heap-size (record-accessor heap-rtd 'size)) ++ + (define heap<? (record-accessor heap-rtd 'heap<?)) ++ + (define heap-set-size + (let ((aa (record-accessor heap-rtd 'array)) + (am (record-modifier heap-rtd 'array)) +*************** +*** 59,68 **** + (vector-set! nra i (vector-ref ra i))))) + (sm a s))))) + +! (define (heap-parent i) (quotient i 2)) +! (define (heap-left i) (* 2 i)) +! (define (heap-right i) (+ 1 (* 2 i))) + + (define (heapify a i) + (define l (heap-left i)) + (define r (heap-right i)) +--- 74,85 ---- + (vector-set! nra i (vector-ref ra i))))) + (sm a s))))) + +! (define-integrable (heap-parent i) (quotient i 2)) + ++ (define-integrable (heap-left i) (* 2 i)) ++ ++ (define-integrable (heap-right i) (+ 1 (* 2 i))) ++ + (define (heapify a i) + (define l (heap-left i)) + (define r (heap-right i)) +*************** +*** 99,104 **** +--- 116,122 ---- + max)) + + (define heap #f) ++ + (define (heap-test) + (set! heap (make-heap char>?)) + (heap-insert! heap #\A) +diff -c slib/process.scm nlib/process.scm +*** slib/process.scm Wed Nov 4 12:26:50 1992 +--- nlib/process.scm Tue Feb 9 00:21:15 1993 +*************** +*** 21,30 **** + ; + ;;;;---------------------------------------------------------------------- + + (require 'full-continuation) + (require 'queue) + +! (define (add-process! thunk1) + (cond ((procedure? thunk1) + (defer-ints) + (enqueue! process:queue thunk1) +--- 21,33 ---- + ; + ;;;;---------------------------------------------------------------------- + ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ++ + (require 'full-continuation) + (require 'queue) + +! (define-integrable (add-process! thunk1) + (cond ((procedure? thunk1) + (defer-ints) + (enqueue! process:queue thunk1) +*************** +*** 55,63 **** + (define ints-disabled #f) + (define alarm-deferred #f) + +! (define (defer-ints) (set! ints-disabled #t)) + +! (define (allow-ints) + (set! ints-disabled #f) + (cond (alarm-deferred + (set! alarm-deferred #f) +--- 58,66 ---- + (define ints-disabled #f) + (define alarm-deferred #f) + +! (define-integrable (defer-ints) (set! ints-disabled #t)) + +! (define-integrable (allow-ints) + (set! ints-disabled #f) + (cond (alarm-deferred + (set! alarm-deferred #f) +*************** +*** 66,72 **** + ;;; Make THE process queue. + (define process:queue (make-queue)) + +! (define (alarm-interrupt) + (alarm 1) + (if ints-disabled (set! alarm-deferred #t) + (process:schedule!))) +--- 69,75 ---- + ;;; Make THE process queue. + (define process:queue (make-queue)) + +! (define-integrable (alarm-interrupt) + (alarm 1) + (if ints-disabled (set! alarm-deferred #t) + (process:schedule!))) +diff -c slib/randinex.scm nlib/randinex.scm +*** slib/randinex.scm Wed Nov 18 22:59:20 1992 +--- nlib/randinex.scm Tue Feb 9 00:21:16 1993 +*************** +*** 47,52 **** +--- 47,59 ---- + ;For an exponential distribution with mean U use (* U (random:exp)). + ;;;;----------------------------------------------------------------- + ++ ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ++ (declare (integrate-external "random")) ++ (declare (integrate ++ random:float-radix)) ++ + (define random:float-radix + (+ 1 (exact->inexact random:MASK))) + +*************** +*** 56,61 **** +--- 63,69 ---- + (if (= 1.0 (+ 1 x)) + l + (random:size-float (+ l 1) (/ x random:float-radix)))) ++ + (define random:chunks/float (random:size-float 1 1.0)) + + (define (random:uniform-chunk n state) +*************** +*** 67,73 **** + random:float-radix))) + + ;;; Generate an inexact real between 0 and 1. +! (define (random:uniform state) + (random:uniform-chunk random:chunks/float state)) + + ;;; If x and y are independent standard normal variables, then with +--- 75,81 ---- + random:float-radix))) + + ;;; Generate an inexact real between 0 and 1. +! (define-integrable (random:uniform state) + (random:uniform-chunk random:chunks/float state)) + + ;;; If x and y are independent standard normal variables, then with +*************** +*** 89,95 **** + (do! n (* r (cos t))) + (if (positive? n) (do! (- n 1) (* r (sin t))))))))) + +! (define random:normal + (let ((vect (make-vector 1))) + (lambda args + (apply random:normal-vector! vect args) +--- 97,103 ---- + (do! n (* r (cos t))) + (if (positive? n) (do! (- n 1) (* r (sin t))))))))) + +! (define-integrable random:normal + (let ((vect (make-vector 1))) + (lambda args + (apply random:normal-vector! vect args) +*************** +*** 98,104 **** + ;;; For the uniform distibution on the hollow sphere, pick a normal + ;;; family and scale. + +! (define (random:hollow-sphere! vect . args) + (let ((ms (sqrt (apply random:normal-vector! vect args)))) + (do ((n (- (vector-length vect) 1) (- n 1))) + ((negative? n)) +--- 106,112 ---- + ;;; For the uniform distibution on the hollow sphere, pick a normal + ;;; family and scale. + +! (define-integrable (random:hollow-sphere! vect . args) + (let ((ms (sqrt (apply random:normal-vector! vect args)))) + (do ((n (- (vector-length vect) 1) (- n 1))) + ((negative? n)) +*************** +*** 117,123 **** + ((negative? n)) + (vector-set! vect n (* r (vector-ref vect n)))))) + +! (define (random:exp . args) + (let ((state (if (null? args) *random-state* (car args)))) + (- (log (random:uniform state))))) + +--- 125,131 ---- + ((negative? n)) + (vector-set! vect n (* r (vector-ref vect n)))))) + +! (define-integrable (random:exp . args) + (let ((state (if (null? args) *random-state* (car args)))) + (- (log (random:uniform state))))) + +diff -c slib/random.scm nlib/random.scm +*** slib/random.scm Tue Feb 2 00:02:58 1993 +--- nlib/random.scm Tue Feb 9 00:21:18 1993 +*************** +*** 35,40 **** +--- 35,50 ---- + ;procedures for generating inexact distributions. + ;;;;------------------------------------------------------------------ + ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ++ (declare (integrate-external "logical")) ++ (declare (integrateb ++ random:tap-1 ++ random:size ++ random:chunk-size ++ random:MASK ++ random)) ++ + (require 'logical) + + (define random:tap 24) +*************** +*** 45,50 **** +--- 55,61 ---- + (if (and (exact? trial) (>= most-positive-fixnum trial)) + l + (random:size-int (- l 1))))) ++ + (define random:chunk-size (* 4 (random:size-int 8))) + + (define random:MASK +*************** +*** 107,113 **** + ;;;random:uniform is in randinex.scm. It is needed only if inexact is + ;;;supported. + +! (define (random:make-random-state . args) + (let ((state (if (null? args) *random-state* (car args)))) + (list->vector (vector->list state)))) + +--- 118,124 ---- + ;;;random:uniform is in randinex.scm. It is needed only if inexact is + ;;;supported. + +! (define-integrable (random:make-random-state . args) + (let ((state (if (null? args) *random-state* (car args)))) + (list->vector (vector->list state)))) + +diff -c slib/rbtree.scm nlib/rbtree.scm +*** slib/rbtree.scm Sat Jan 9 13:40:56 1993 +--- nlib/rbtree.scm Tue Feb 9 00:21:18 1993 +*************** +*** 5,11 **** +--- 5,24 ---- + ;;;; PGS, 6 Jul 1990 + ;;; jaffer@ai.mit.edu Ported to SLIB, 1/6/93 + ++ ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ++ (declare (integrate ++ rb-tree-root ++ set-rb-tree-root! ++ rb-tree-left-rotation-field-maintainer ++ rb-tree-right-rotation-field-maintainer ++ rb-tree-insertion-field-maintainer ++ rb-tree-deletion-field-maintainer ++ rb-tree-prior?)) ++ + (require 'record) ++ + (define rb-tree + (make-record-type + "rb-tree" +*************** +*** 227,233 **** + y) + (set! x y) + (set! y (rb-node-parent y))))) +- + + ;;;; Deletion. We do not entirely follow Cormen, Leiserson and Rivest's lead + ;;;; here, because their use of sentinels is in rather obscenely poor taste. +--- 240,245 ---- +diff -c slib/sort.scm nlib/sort.scm +*** slib/sort.scm Wed Nov 6 00:50:38 1991 +--- nlib/sort.scm Tue Feb 9 00:22:03 1993 +*************** +*** 118,123 **** +--- 118,125 ---- + ; in Scheme. + ;;; -------------------------------------------------------------------- + ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ; Honestly, nothing defined here clashes! + + ;;; (sorted? sequence less?) + ;;; is true when sequence is a list (x0 x1 ... xm) or a vector #(x0 ... xm) +diff -c slib/printf.scm nlib/printf.scm +*** slib/printf.scm Mon Oct 19 14:48:58 1992 +--- nlib/printf.scm Tue Feb 9 00:22:03 1993 +*************** +*** 3,8 **** +--- 3,19 ---- + + ;;; Floating point is not handled yet. It should not be hard to do. + ++ ;;; Declarations for CScheme ++ (declare (usual-integrations)) ++ ++ (declare (integrate ++ printf ++ fprintf ++ sprintf ++ stdin ++ stdout ++ stderr)) ++ + (define (stdio:iprintf out format . args) + (let loop ((pos 0) (args args)) + (if (< pos (string-length format)) +*************** +*** 96,105 **** + (else (out (string-ref format pos)) + (loop (+ pos 1) args)))))) + +! (define (stdio:printf format . args) + (apply stdio:iprintf display format args)) + +! (define (stdio:fprintf port format . args) + (if (equal? port (current-output-port)) + (apply stdio:iprintf display format args) + (apply stdio:iprintf (lambda (x) (display x port)) format args))) +--- 107,116 ---- + (else (out (string-ref format pos)) + (loop (+ pos 1) args)))))) + +! (define-integrable (stdio:printf format . args) + (apply stdio:iprintf display format args)) + +! (define-integrable (stdio:fprintf port format . args) + (if (equal? port (current-output-port)) + (apply stdio:iprintf display format args) + (apply stdio:iprintf (lambda (x) (display x port)) format args))) +diff -c slib/strcase.scm nlib/strcase.scm +*** slib/strcase.scm Wed Nov 18 14:15:18 1992 +--- nlib/strcase.scm Tue Feb 9 00:22:03 1993 +*************** +*** 8,27 **** + ;string-upcase!, string-downcase!, string-capitalize! + ; are destructive versions. + +! (define (string-upcase! str) + (do ((i (- (string-length str) 1) (- i 1))) + ((< i 0) str) + (string-set! str i (char-upcase (string-ref str i))))) + +! (define (string-upcase str) + (string-upcase! (string-copy str))) + +! (define (string-downcase! str) + (do ((i (- (string-length str) 1) (- i 1))) + ((< i 0) str) + (string-set! str i (char-downcase (string-ref str i))))) + +! (define (string-downcase str) + (string-downcase! (string-copy str))) + + (define (string-capitalize! str) ; "hello" -> "Hello" +--- 8,30 ---- + ;string-upcase!, string-downcase!, string-capitalize! + ; are destructive versions. + +! ;;; Declarations for CScheme +! (declare (usual-integrations)) +! +! (define-integrable (string-upcase! str) + (do ((i (- (string-length str) 1) (- i 1))) + ((< i 0) str) + (string-set! str i (char-upcase (string-ref str i))))) + +! (define-integrable (string-upcase str) + (string-upcase! (string-copy str))) + +! (define-integrable (string-downcase! str) + (do ((i (- (string-length str) 1) (- i 1))) + ((< i 0) str) + (string-set! str i (char-downcase (string-ref str i))))) + +! (define-integrable (string-downcase str) + (string-downcase! (string-copy str))) + + (define (string-capitalize! str) ; "hello" -> "Hello" +*************** +*** 38,42 **** + (string-set! str i (char-upcase c)))) + (set! non-first-alpha #f)))))) + +! (define (string-capitalize str) + (string-capitalize! (string-copy str))) +--- 41,45 ---- + (string-set! str i (char-upcase c)))) + (set! non-first-alpha #f)))))) + +! (define-integrable (string-capitalize str) + (string-capitalize! (string-copy str))) +diff -c slib/synchk.scm nlib/synchk.scm +*** slib/synchk.scm Mon Jan 27 09:28:48 1992 +--- nlib/synchk.scm Tue Feb 9 00:22:03 1993 +*************** +*** 35,45 **** + ;;; written by Alan Bawden + ;;; modified by Chris Hanson + +! (define (syntax-check pattern form) + (if (not (syntax-match? (cdr pattern) (cdr form))) + (syntax-error "ill-formed special form" form))) + +! (define (ill-formed-syntax form) + (syntax-error "ill-formed special form" form)) + + (define (syntax-match? pattern object) +--- 35,48 ---- + ;;; written by Alan Bawden + ;;; modified by Chris Hanson + +! ;;; Declarations for CScheme +! (declare (usual-integrations)) +! +! (define-integrable (syntax-check pattern form) + (if (not (syntax-match? (cdr pattern) (cdr form))) + (syntax-error "ill-formed special form" form))) + +! (define-integrable (ill-formed-syntax form) + (syntax-error "ill-formed special form" form)) + + (define (syntax-match? pattern object) diff --git a/mitscheme.init b/mitscheme.init new file mode 100644 index 0000000..a6f1c0e --- /dev/null +++ b/mitscheme.init @@ -0,0 +1,254 @@ +;;;"mitscheme.init" Initialization for SLIB for MITScheme -*-scheme-*- +;;; Copyright (C) 1991, 1992, 1993 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;; Make this part of your ~/.scheme.init file. + +;;; (software-type) should be set to the generic operating system type. +(define (software-type) 'UNIX) + +;;; (scheme-implementation-type) should return the name of the scheme +;;; implementation loading this file. + +(define (scheme-implementation-type) 'MITScheme) + +;;; (scheme-implementation-version) should return a string describing +;;; the version the scheme implementation loading this file. + +(define (scheme-implementation-version) "7.3.0") + +;;; *features* should be set to a list of symbols describing features +;;; of this implementation. See Template.scm for the list of feature +;;; names. + +;the following may not be the Right Thing for this application, since +;it causes an error (rather than just returning nil) when the environment +;variable is not defined. +(define getenv get-environment-variable) + +;;; (implementation-vicinity) should be defined to be the pathname of +;;; the directory where any auxillary files to your Scheme +;;; implementation reside. + +(define (implementation-vicinity) + (case (software-type) + ((UNIX) "/usr/local/src/scheme/") + ((VMS) "scheme$src:"))) + +;;; (library-vicinity) should be defined to be the pathname of the +;;; directory where files of Scheme library functions reside. + +(define library-vicinity + (let ((library-path + (or (getenv "SCHEME_LIBRARY_PATH") + ;; Use this path if your scheme does not support GETENV. + (case (software-type) + ((UNIX) "/usr/local/lib/slib/") + ((VMS) "lib$scheme:") + ((MS-DOS) "C:\\SLIB\\") + (else ""))))) + (lambda () library-path))) + +(define *features* + '( + source ;can load scheme source files + ;(slib:load-source "filename") + compiled ;can load compiled files + ;(slib:load-compiled "filename") + rev4-report + ieee-p1178 + sicp + rev4-optional-procedures + rev3-procedures + rev2-procedures + multiarg/and- + multiarg-apply + rationalize + object-hash + delay + with-file + string-port + transcript + char-ready? + record + values + dynamic-wind + ieee-floating-point + full-continuation +; sort + queue + pretty-print + object->string + trace ;has macros: TRACE and UNTRACE + compiler + getenv + Xwindows + )) + +;;; (OUTPUT-PORT-WIDTH <port>) +(define output-port-width output-port/x-size) + +;;; (OUTPUT-PORT-HEIGHT <port>) +(define (output-port-height . arg) 24) + +;;; (CURRENT-ERROR-PORT) +(define current-error-port + (let ((port console-output-port)) + (lambda () port))) + +;;; (TMPNAM) makes a temporary file name. +(define tmpnam + (let ((cntr 100)) + (lambda () (set! cntr (+ 1 cntr)) + (let ((tmp (string-append "slib_" (number->string cntr)))) + (if (file-exists? tmp) (tmpnam) tmp))))) + +;;; FORCE-OUTPUT flushes any pending output on optional arg output port. +(define force-output flush-output) +;;; MITScheme 7.2 is missing flush-output. Use this instead +;(define (force-output . arg) #t) + +;;; CALL-WITH-INPUT-STRING and CALL-WITH-OUTPUT-STRING are the string +;;; port versions of CALL-WITH-*PUT-FILE. +(define (call-with-output-string proc) + (let ((co (current-output-port))) + (with-output-to-string + (lambda () + (let ((port (current-output-port))) + (with-output-to-port co + (lambda () (proc port)))))))) + +(define (call-with-input-string string proc) + (let ((ci (current-input-port))) + (with-input-from-string string + (lambda () + (let ((port (current-input-port))) + (with-input-from-port ci + (lambda () (proc port)))))))) + +(define object->string write-to-string) + +;;; CHAR-CODE-LIMIT is one greater than the largest integer which can +;;; be returned by CHAR->INTEGER. It is defined by MITScheme. + +;;; MOST-POSITIVE-FIXNUM is used in modular.scm +(define most-positive-fixnum #x03FFFFFF) + +;;; Return argument +(define (identity x) x) + +;;; If your implementation provides eval, SLIB:EVAL is single argument +;;; eval using the top-level (user) environment. +;(define (slib:eval form) (eval form (repl/environment (nearest-repl)))) +(define (slib:eval form) (eval form user-initial-environment)) + +(define *macros* '(defmacro)) +(define (defmacro? m) (and (memq m *macros*) #t)) + +(syntax-table-define system-global-syntax-table 'defmacro + (macro defmacargs + (let ((macname (car defmacargs)) (macargs (cadr defmacargs)) + (macbdy (cddr defmacargs))) + `(begin + (set! *macros* (cons ',macname *macros*)) + (syntax-table-define system-global-syntax-table ',macname + (macro ,macargs ,@macbdy)))))) + +(define (macroexpand-1 e) + (if (pair? e) (let ((a (car e))) + (if (and (symbol? a) (defmacro? a)) + (apply (syntax-table-ref system-global-syntax-table a) + (cdr e)) + e)) + e)) + +(define (macroexpand e) + (if (pair? e) (let ((a (car e))) + (if (and (symbol? a) (defmacro? a)) + (macroexpand + (apply (syntax-table-ref system-global-syntax-table a) + (cdr e))) + e)) + e)) + +(define gentemp + (let ((*gensym-counter* -1)) + (lambda () + (set! *gensym-counter* (+ *gensym-counter* 1)) + (string->symbol + (string-append "slib:G" (number->string *gensym-counter*)))))) + +(define defmacro:eval slib:eval) +(define defmacro:load load) +;;; If your implementation provides R4RS macros: +;(define macro:eval slib:eval) +;(define macro:load load) + +(define (slib:eval-load <pathname> evl) + (if (not (file-exists? <pathname>)) + (set! <pathname> (string-append <pathname> (scheme-file-suffix)))) + (call-with-input-file <pathname> + (lambda (port) + (let ((old-load-pathname *load-pathname*)) + (set! *load-pathname* <pathname>) + (do ((o (read port) (read port))) + ((eof-object? o)) + (evl o)) + (set! *load-pathname* old-load-pathname))))) + +(define record-modifier record-updater) ;some versions need this? + +;; define an error procedure for the library +(define (slib:error . args) + (apply error-procedure (append args (list (the-environment))))) + +;; define these as appropriate for your system. +(define slib:tab (integer->char 9)) +(define slib:form-feed (integer->char 12)) + +(define in-vicinity string-append) + +;;; Define SLIB:EXIT to be the implementation procedure to exit or +;;; return if exitting not supported. +(define slib:exit + (lambda args + (cond ((null? args) (exit)) + ((eqv? #t (car args)) (exit)) + ((and (number? (car args)) (integer? (car args))) (exit (car args))) + (else (exit 1))))) + +;;; Here for backward compatability + +(define (scheme-file-suffix) "") + +;;; (SLIB:LOAD-SOURCE "foo") should load "foo.scm" or with whatever +;;; suffix all the module files in SLIB have. See feature 'SOURCE. + +(define slib:load-source load) + +;;; (SLIB:LOAD-COMPILED "foo") should load the file that was produced +;;; by compiling "foo.scm" if this implementation can compile files. +;;; See feature 'COMPILED. + +(define slib:load-compiled load) + +;;; At this point SLIB:LOAD must be able to load SLIB files. + +(define slib:load slib:load-source) + +(slib:load (in-vicinity (library-vicinity) "require")) diff --git a/modular.scm b/modular.scm new file mode 100644 index 0000000..357ce77 --- /dev/null +++ b/modular.scm @@ -0,0 +1,158 @@ +;;;; "modular.scm", modular fixnum arithmetic for Scheme +;;; Copyright (C) 1991, 1993, 1995 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(define (symmetric:modulus n) + (cond ((or (not (number? n)) (not (positive? n)) (even? n)) + (slib:error 'symmetric:modulus n)) + (else (quotient (+ -1 n) -2)))) + +(define (modulus->integer m) + (cond ((negative? m) (- 1 m m)) + ((zero? m) #f) + (else m))) + +(define (modular:normalize m k) + (cond ((positive? m) (modulo k m)) + ((zero? m) k) + ((<= m k (- m)) k) + ((or (provided? 'bignum) + (<= m (quotient (+ -1 most-positive-fixnum) 2))) + (let* ((pm (+ 1 (* -2 m))) + (s (modulo k pm))) + (if (<= s (- m)) s (- s pm)))) + ((positive? k) (+ (+ (+ k -1) m) m)) + (else (- (- (+ k 1) m) m)))) + +;;;; NOTE: The rest of these functions assume normalized arguments! + +(require 'logical) + +(define (modular:extended-euclid x y) + (define q 0) + (do ((r0 x r1) (r1 y (remainder r0 r1)) + (u0 1 u1) (u1 0 (- u0 (* q u1))) + (v0 0 v1) (v1 1 (- v0 (* q v1)))) + ;; (assert (= r0 (+ (* u0 x) (* v0 y)))) + ;; (assert (= r1 (+ (* u1 x) (* v1 y)))) + ((zero? r1) (list r0 u0 v0)) + (set! q (quotient r0 r1)))) + +(define (modular:invertable? m a) + (eqv? 1 (gcd (or (modulus->integer m) 0) a))) + +(define (modular:invert m a) + (cond ((eqv? 1 (abs a)) a) ; unit + (else + (let ((pm (modulus->integer m))) + (cond + (pm + (let ((d (modular:extended-euclid (modular:normalize pm a) pm))) + (if (= 1 (car d)) + (modular:normalize m (cadr d)) + (slib:error 'modular:invert "can't invert" m a)))) + (else (slib:error 'modular:invert "can't invert" m a))))))) + +(define (modular:negate m a) + (if (zero? a) 0 + (if (negative? m) (- a) + (- m a)))) + +;;; Being careful about overflow here +(define (modular:+ m a b) + (cond ((positive? m) + (modulo (+ (- a m) b) m)) + ((zero? m) (+ a b)) + ((negative? a) + (if (negative? b) + (let ((s (+ (- a m) b))) + (if (negative? s) + (- s -1 m) + (+ s m))) + (+ a b))) + ((negative? b) (+ a b)) + (else (let ((s (+ (+ a m) b))) + (if (positive? s) + (+ s -1 m) + (- s m)))))) + +(define (modular:- m a b) + (cond ((positive? m) (modulo (- a b) m)) + ((zero? m) (- a b)) + (else (modular:+ m a (- b))))) + +;;; See: L'Ecuyer, P. and Cote, S. "Implementing a Random Number Package +;;; with Splitting Facilities." ACM Transactions on Mathematical +;;; Software, 17:98-111 (1991) + +;;; modular:r = 2**((nb-2)/2) where nb = number of bits in a word. +(define modular:r + (ash 1 (quotient (integer-length most-positive-fixnum) 2))) +(define modular:* + (if (provided? 'bignum) + (lambda (m a b) + (cond ((zero? m) (* a b)) + ((positive? m) (modulo (* a b) m)) + (else (modular:normalize m (* a b))))) + (lambda (m a b) + (let ((a0 a) + (p 0)) + (cond + ((zero? m) (* a b)) + ((negative? m) + "This doesn't work for the full range of modulus M;" + "Someone please create or convert the following" + "algorighm to work with symmetric representation" + (modular:normalize m (* a b))) + (else + (cond + ((< a modular:r)) + ((< b modular:r) (set! a b) (set! b a0) (set! a0 a)) + (else + (set! a0 (modulo a modular:r)) + (let ((a1 (quotient a modular:r)) + (qh (quotient m modular:r)) + (rh (modulo m modular:r))) + (cond ((>= a1 modular:r) + (set! a1 (- a1 modular:r)) + (set! p (modulo (- (* modular:r (modulo b qh)) + (* (quotient b qh) rh)) m)))) + (cond ((not (zero? a1)) + (let ((q (quotient m a1))) + (set! p (- p (* (quotient b q) (modulo m a1)))) + (set! p (modulo (+ (if (positive? p) (- p m) p) + (* a1 (modulo b q))) m))))) + (set! p (modulo (- (* modular:r (modulo p qh)) + (* (quotient p qh) rh)) m))))) + (if (zero? a0) + p + (let ((q (quotient m a0))) + (set! p (- p (* (quotient b q) (modulo m a0)))) + (modulo (+ (if (positive? p) (- p m) p) + (* a0 (modulo b q))) m))))))))) + +(define (modular:expt m a b) + (cond ((= a 1) 1) + ((= a (- m 1)) (if (odd? b) a 1)) + ((zero? a) 0) + ((zero? m) (integer-expt a b)) + (else + (logical:ipow-by-squaring a b 1 + (lambda (c d) (modular:* m c d)))))) + +(define extended-euclid modular:extended-euclid) diff --git a/mulapply.scm b/mulapply.scm new file mode 100644 index 0000000..d696ee2 --- /dev/null +++ b/mulapply.scm @@ -0,0 +1,28 @@ +; "mulapply.scm" Redefine APPLY take more than 2 arguments. +;Copyright (C) 1991 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(define two-arg:apply apply) +(define apply + (lambda args + (two-arg:apply (car args) (apply:append-to-last (cdr args))))) + +(define (apply:append-to-last lst) + (if (null? (cdr lst)) + (car lst) + (cons (car lst) (apply:append-to-last (cdr lst))))) diff --git a/mularg.scm b/mularg.scm new file mode 100644 index 0000000..3d62cf4 --- /dev/null +++ b/mularg.scm @@ -0,0 +1,10 @@ +;;; "mularg.scm" Redefine - and / to take more than 2 arguments. + +(let ((maker + (lambda (op) + (lambda (d1 . ds) + (cond ((null? ds) (op d1)) + ((null? (cdr ds)) (op d1 (car ds))) + (else (for-each (lambda (d) (set! d1 (op d1 d))) ds) d1)))))) + (set! / (maker /)) + (set! - (maker -))) diff --git a/mwdenote.scm b/mwdenote.scm new file mode 100644 index 0000000..c3fe5f3 --- /dev/null +++ b/mwdenote.scm @@ -0,0 +1,273 @@ +;"mwdenote.scm" Syntactic Environments +; Copyright 1992 William Clinger +; +; Permission to copy this software, in whole or in part, to use this +; software for any lawful purpose, and to redistribute this software +; is granted subject to the restriction that all copies made of this +; software must include this copyright notice in full. +; +; I also request that you send me a copy of any improvements that you +; make to this software so that they may be incorporated within it to +; the benefit of the Scheme community. + +;;;; Syntactic environments. + +; A syntactic environment maps identifiers to denotations, +; where a denotation is one of +; +; (special <special>) +; (macro <rules> <env>) +; (identifier <id>) +; +; and where <special> is one of +; +; quote +; lambda +; if +; set! +; begin +; define +; define-syntax +; let-syntax +; letrec-syntax +; syntax-rules +; +; and where <rules> is a compiled <transformer spec> (see R4RS), +; <env> is a syntactic environment, and <id> is an identifier. + +(define mw:standard-syntax-environment + '((quote . (special quote)) + (lambda . (special lambda)) + (if . (special if)) + (set! . (special set!)) + (begin . (special begin)) + (define . (special define)) + (let . (special let)) ;; @@ added KAD + (let* . (special let*)) ;; @@ " + (letrec . (special letrec)) ;; @@ " + (quasiquote . (special quasiquote)) ;; @@ " + (unquote . (special unquote)) ;; @@ " + (unquote-splicing . (special unquote-splicing)) ; @@ " + (do . (special do)) ;; @@ " + (define-syntax . (special define-syntax)) + (let-syntax . (special let-syntax)) + (letrec-syntax . (special letrec-syntax)) + (syntax-rules . (special syntax-rules)) + (... . (identifier ...)) + (::: . (identifier :::)))) + +; An unforgeable synonym for lambda, used to expand definitions. + +(define mw:lambda0 (string->symbol " lambda ")) + +; The mw:global-syntax-environment will always be a nonempty +; association list since there is no way to remove the entry +; for mw:lambda0. That entry is used as a header by destructive +; operations. + +(define mw:global-syntax-environment + (cons (cons mw:lambda0 + (cdr (assq 'lambda mw:standard-syntax-environment))) + (mw:syntax-copy mw:standard-syntax-environment))) + +(define (mw:global-syntax-environment-set! env) + (set-cdr! mw:global-syntax-environment env)) + +(define (mw:syntax-bind-globally! id denotation) + (if (and (mw:identifier? denotation) + (eq? id (mw:identifier-name denotation))) + (letrec ((remove-bindings-for-id + (lambda (bindings) + (cond ((null? bindings) '()) + ((eq? (caar bindings) id) + (remove-bindings-for-id (cdr bindings))) + (else (cons (car bindings) + (remove-bindings-for-id (cdr bindings)))))))) + (mw:global-syntax-environment-set! + (remove-bindings-for-id (cdr mw:global-syntax-environment)))) + (let ((x (assq id mw:global-syntax-environment))) + (if x + (set-cdr! x denotation) + (mw:global-syntax-environment-set! + (cons (cons id denotation) + (cdr mw:global-syntax-environment))))))) + +(define (mw:syntax-divert env1 env2) + (append env2 env1)) + +(define (mw:syntax-extend env ids denotations) + (mw:syntax-divert env (map cons ids denotations))) + +(define (mw:syntax-lookup-raw env id) + (let ((entry (assq id env))) + (if entry + (cdr entry) + #f))) + +(define (mw:syntax-lookup env id) + (or (mw:syntax-lookup-raw env id) + (mw:make-identifier-denotation id))) + +(define (mw:syntax-assign! env id denotation) + (let ((entry (assq id env))) + (if entry + (set-cdr! entry denotation) + (mw:bug "Bug detected in mw:syntax-assign!" env id denotation)))) + +(define mw:denote-of-quote + (mw:syntax-lookup mw:standard-syntax-environment 'quote)) + +(define mw:denote-of-lambda + (mw:syntax-lookup mw:standard-syntax-environment 'lambda)) + +(define mw:denote-of-if + (mw:syntax-lookup mw:standard-syntax-environment 'if)) + +(define mw:denote-of-set! + (mw:syntax-lookup mw:standard-syntax-environment 'set!)) + +(define mw:denote-of-begin + (mw:syntax-lookup mw:standard-syntax-environment 'begin)) + +(define mw:denote-of-define + (mw:syntax-lookup mw:standard-syntax-environment 'define)) + +(define mw:denote-of-define-syntax + (mw:syntax-lookup mw:standard-syntax-environment 'define-syntax)) + +(define mw:denote-of-let-syntax + (mw:syntax-lookup mw:standard-syntax-environment 'let-syntax)) + +(define mw:denote-of-letrec-syntax + (mw:syntax-lookup mw:standard-syntax-environment 'letrec-syntax)) + +(define mw:denote-of-syntax-rules + (mw:syntax-lookup mw:standard-syntax-environment 'syntax-rules)) + +(define mw:denote-of-... + (mw:syntax-lookup mw:standard-syntax-environment '...)) + +(define mw:denote-of-::: + (mw:syntax-lookup mw:standard-syntax-environment ':::)) + +(define mw:denote-of-let + (mw:syntax-lookup mw:standard-syntax-environment 'let)) ;; @@ KenD + +(define mw:denote-of-let* + (mw:syntax-lookup mw:standard-syntax-environment 'let*)) ;; @@ KenD + +(define mw:denote-of-letrec + (mw:syntax-lookup mw:standard-syntax-environment 'letrec)) ;; @@ KenD + +(define mw:denote-of-quasiquote + (mw:syntax-lookup mw:standard-syntax-environment 'quasiquote)) ;; @@ KenD + +(define mw:denote-of-unquote + (mw:syntax-lookup mw:standard-syntax-environment 'unquote)) ;; @@ KenD + +(define mw:denote-of-unquote-splicing + (mw:syntax-lookup mw:standard-syntax-environment 'unquote-splicing)) ;@@ KenD + +(define mw:denote-of-do + (mw:syntax-lookup mw:standard-syntax-environment 'do)) ;; @@ KenD + +(define mw:denote-class car) + +;(define (mw:special? denotation) +; (eq? (mw:denote-class denotation) 'special)) + +;(define (mw:macro? denotation) +; (eq? (mw:denote-class denotation) 'macro)) + +(define (mw:identifier? denotation) + (eq? (mw:denote-class denotation) 'identifier)) + +(define (mw:make-identifier-denotation id) + (list 'identifier id)) + +(define macwork:rules cadr) +(define macwork:env caddr) +(define mw:identifier-name cadr) + +(define (mw:same-denotation? d1 d2) + (or (eq? d1 d2) + (and (mw:identifier? d1) + (mw:identifier? d2) + (eq? (mw:identifier-name d1) + (mw:identifier-name d2))))) + +; Renaming of variables. + +; Given a datum, strips the suffixes from any symbols that appear within +; the datum, trying not to copy any more of the datum than necessary. +; Well, right now I'm just copying the datum, but I need to fix that! + +(define (mw:strip x) + (cond ((symbol? x) + (let ((chars (memv mw:suffix-character + (reverse (string->list + (symbol->string x)))))) + (if chars + (string->symbol + (list->string (reverse (cdr chars)))) + x))) + ((pair? x) + (cons (mw:strip (car x)) + (mw:strip (cdr x)))) + ((vector? x) + (list->vector (map mw:strip (vector->list x)))) + (else x))) + +; Given a list of identifiers, returns an alist that associates each +; identifier with a fresh identifier. + +(define (mw:rename-vars vars) + (set! mw:renaming-counter (+ mw:renaming-counter 1)) + (let ((suffix (string-append (string mw:suffix-character) + (number->string mw:renaming-counter)))) + (map (lambda (var) + (if (symbol? var) + (cons var + (string->symbol + (string-append (symbol->string var) suffix))) + (slib:error "Illegal variable" var))) + vars))) + +; Given a syntactic environment env to be extended, an alist returned +; by mw:rename-vars, and a syntactic environment env2, extends env by +; binding the fresh identifiers to the denotations of the original +; identifiers in env2. + +(define (mw:syntax-alias env alist env2) + (mw:syntax-divert + env + (map (lambda (name-pair) + (let ((old-name (car name-pair)) + (new-name (cdr name-pair))) + (cons new-name + (mw:syntax-lookup env2 old-name)))) + alist))) + +; Given a syntactic environment and an alist returned by mw:rename-vars, +; extends the environment by binding the old identifiers to the fresh +; identifiers. + +(define (mw:syntax-rename env alist) + (mw:syntax-divert env + (map (lambda (old new) + (cons old (mw:make-identifier-denotation new))) + (map car alist) + (map cdr alist)))) + +; Given a <formals> and an alist returned by mw:rename-vars that contains +; a new name for each formal identifier in <formals>, renames the +; formal identifiers. + +(define (mw:rename-formals formals alist) + (cond ((null? formals) '()) + ((pair? formals) + (cons (cdr (assq (car formals) alist)) + (mw:rename-formals (cdr formals) alist))) + (else (cdr (assq formals alist))))) + +(define mw:renaming-counter 0) diff --git a/mwexpand.scm b/mwexpand.scm new file mode 100644 index 0000000..10083a3 --- /dev/null +++ b/mwexpand.scm @@ -0,0 +1,548 @@ +;"mwexpand.scm" macro expander +; Copyright 1992 William Clinger +; +; Permission to copy this software, in whole or in part, to use this +; software for any lawful purpose, and to redistribute this software +; is granted subject to the restriction that all copies made of this +; software must include this copyright notice in full. +; +; I also request that you send me a copy of any improvements that you +; make to this software so that they may be incorporated within it to +; the benefit of the Scheme community. + +; The external entry points and kernel of the macro expander. +; +; Part of this code is snarfed from the Twobit macro expander. + +(define mw:define-syntax-scope + (let ((flag 'letrec)) + (lambda args + (cond ((null? args) flag) + ((not (null? (cdr args))) + (apply mw:warn + "Too many arguments passed to define-syntax-scope" + args)) + ((memq (car args) '(letrec letrec* let*)) + (set! flag (car args))) + (else (mw:warn "Unrecognized argument to define-syntax-scope" + (car args))))))) + +(define mw:quit ; assigned by macwork:expand + (lambda (v) v)) + +(define (macwork:expand def-or-exp) + (call-with-current-continuation + (lambda (k) + (set! mw:quit k) + (set! mw:renaming-counter 0) + (mw:desugar-definitions def-or-exp mw:global-syntax-environment)))) + +(define (mw:desugar-definitions exp env) + (letrec + ((define-loop + (lambda (exp rest first) + (cond ((and (pair? exp) + (eq? (mw:syntax-lookup env (car exp)) + mw:denote-of-begin) + (pair? (cdr exp))) + (define-loop (cadr exp) (append (cddr exp) rest) first)) + ((and (pair? exp) + (eq? (mw:syntax-lookup env (car exp)) + mw:denote-of-define)) + (let ((exp (desugar-define exp env))) + (cond ((and (null? first) (null? rest)) + exp) + ((null? rest) + (cons mw:begin1 (reverse (cons exp first)))) + (else (define-loop (car rest) + (cdr rest) + (cons exp first)))))) + ((and (pair? exp) + (eq? (mw:syntax-lookup env (car exp)) + mw:denote-of-define-syntax) + (null? first)) + (define-syntax-loop exp rest)) + ((and (null? first) (null? rest)) + (mw:expand exp env)) + ((null? rest) + (cons mw:begin1 (reverse (cons (mw:expand exp env) first)))) + (else (cons mw:begin1 + (append (reverse first) + (map (lambda (exp) (mw:expand exp env)) + (cons exp rest)))))))) + + (desugar-define + (lambda (exp env) + (cond + ((null? (cdr exp)) (mw:error "Malformed definition" exp)) + ; (define foo) syntax is transformed into (define foo (undefined)). + ((null? (cddr exp)) + (let ((id (cadr exp))) + (redefinition id) + (mw:syntax-bind-globally! id (mw:make-identifier-denotation id)) + (list mw:define1 id mw:undefined))) + ((pair? (cadr exp)) + ; mw:lambda0 is an unforgeable lambda, needed here because the + ; lambda expression will undergo further expansion. + (desugar-define `(,mw:define1 ,(car (cadr exp)) + (,mw:lambda0 ,(cdr (cadr exp)) + ,@(cddr exp))) + env)) + ((> (length exp) 3) (mw:error "Malformed definition" exp)) + (else (let ((id (cadr exp))) + (redefinition id) + (mw:syntax-bind-globally! id (mw:make-identifier-denotation id)) + `(,mw:define1 ,id ,(mw:expand (caddr exp) env))))))) + + (define-syntax-loop + (lambda (exp rest) + (cond ((and (pair? exp) + (eq? (mw:syntax-lookup env (car exp)) + mw:denote-of-begin) + (pair? (cdr exp))) + (define-syntax-loop (cadr exp) (append (cddr exp) rest))) + ((and (pair? exp) + (eq? (mw:syntax-lookup env (car exp)) + mw:denote-of-define-syntax)) + (if (pair? (cdr exp)) + (redefinition (cadr exp))) + (if (null? rest) + (mw:define-syntax exp env) + (begin (mw:define-syntax exp env) + (define-syntax-loop (car rest) (cdr rest))))) + ((null? rest) + (mw:expand exp env)) + (else (cons mw:begin1 + (map (lambda (exp) (mw:expand exp env)) + (cons exp rest))))))) + + (redefinition + (lambda (id) + (if (symbol? id) + (if (not (mw:identifier? + (mw:syntax-lookup mw:global-syntax-environment id))) + (mw:warn "Redefining keyword" id)) + (mw:error "Malformed variable or keyword" id))))) + + ; body of letrec + + (define-loop exp '() '()))) + +; Given an expression and a syntactic environment, +; returns an expression in core Scheme. + +(define (mw:expand exp env) + (if (not (pair? exp)) + (mw:atom exp env) + (let ((keyword (mw:syntax-lookup env (car exp)))) + (case (mw:denote-class keyword) + ((special) + (cond + ((eq? keyword mw:denote-of-quote) (mw:quote exp)) + ((eq? keyword mw:denote-of-lambda) (mw:lambda exp env)) + ((eq? keyword mw:denote-of-if) (mw:if exp env)) + ((eq? keyword mw:denote-of-set!) (mw:set exp env)) + ((eq? keyword mw:denote-of-begin) (mw:begin exp env)) + ((eq? keyword mw:denote-of-let-syntax) (mw:let-syntax exp env)) + ((eq? keyword mw:denote-of-letrec-syntax) + (mw:letrec-syntax exp env)) + ; @@ let, let*, letrec, paint within quasiquotation -- kend + ((eq? keyword mw:denote-of-let) (mw:let exp env)) + ((eq? keyword mw:denote-of-let*) (mw:let* exp env)) + ((eq? keyword mw:denote-of-letrec) (mw:letrec exp env)) + ((eq? keyword mw:denote-of-quasiquote) (mw:quasiquote exp env)) + ((eq? keyword mw:denote-of-do) (mw:do exp env)) + ((or (eq? keyword mw:denote-of-define) + (eq? keyword mw:denote-of-define-syntax)) + ;; slight hack to allow expansion into defines -KenD + (if mw:in-define? + (mw:error "Definition out of context" exp) + (begin + (set! mw:in-define? #t) + (let ( (result (mw:desugar-definitions exp env)) ) + (set! mw:in-define? #f) + result)) + )) + (else (mw:bug "Bug detected in mw:expand" exp env)))) + ((macro) (mw:macro exp env)) + ((identifier) (mw:application exp env)) + (else (mw:bug "Bug detected in mw:expand" exp env)) + ) ) +) ) + +(define mw:in-define? #f) ; should be fluid + +(define (mw:atom exp env) + (cond ((not (symbol? exp)) + ; Here exp ought to be a boolean, number, character, or string, + ; but I'll allow for non-standard extensions by passing exp + ; to the underlying Scheme system without further checking. + exp) + (else (let ((denotation (mw:syntax-lookup env exp))) + (case (mw:denote-class denotation) + ((special macro) + (mw:error "Syntactic keyword used as a variable" exp env)) + ((identifier) (mw:identifier-name denotation)) + (else (mw:bug "Bug detected by mw:atom" exp env))))))) + +(define (mw:quote exp) + (if (= (mw:safe-length exp) 2) + (list mw:quote1 (mw:strip (cadr exp))) + (mw:error "Malformed quoted constant" exp))) + +(define (mw:lambda exp env) + (if (> (mw:safe-length exp) 2) + (let* ((formals (cadr exp)) + (alist (mw:rename-vars (mw:make-null-terminated formals))) + (env (mw:syntax-rename env alist)) + (body (cddr exp))) + (list mw:lambda1 + (mw:rename-formals formals alist) + (mw:body body env))) + (mw:error "Malformed lambda expression" exp))) + +(define (mw:body body env) + (define (loop body env defs) + (if (null? body) + (mw:error "Empty body")) + (let ((exp (car body))) + (if (and (pair? exp) + (symbol? (car exp))) + (let ((denotation (mw:syntax-lookup env (car exp)))) + (case (mw:denote-class denotation) + ((special) + (cond ((eq? denotation mw:denote-of-begin) + (loop (append (cdr exp) (cdr body)) env defs)) + ((eq? denotation mw:denote-of-define) + (loop (cdr body) env (cons exp defs))) + (else (mw:finalize-body body env defs)))) + ((macro) + (mw:transcribe exp + env + (lambda (exp env) + (loop (cons exp (cdr body)) + env + defs)))) + ((identifier) + (mw:finalize-body body env defs)) + (else (mw:bug "Bug detected in mw:body" body env)))) + (mw:finalize-body body env defs)))) + (loop body env '())) + +(define (mw:finalize-body body env defs) + (if (null? defs) + (let ((body (map (lambda (exp) (mw:expand exp env)) + body))) + (if (null? (cdr body)) + (car body) + (cons mw:begin1 body))) + (let* ((alist (mw:rename-vars '(quote lambda set!))) + (env (mw:syntax-alias env alist mw:standard-syntax-environment)) + (new-quote (cdr (assq 'quote alist))) + (new-lambda (cdr (assq 'lambda alist))) + (new-set! (cdr (assq 'set! alist)))) + (define (desugar-definition def) + (if (> (mw:safe-length def) 2) + (cond ((pair? (cadr def)) + (desugar-definition + `(,(car def) + ,(car (cadr def)) + (,new-lambda + ,(cdr (cadr def)) + ,@(cddr def))))) + ((= (length def) 3) + (cdr def)) + (else (mw:error "Malformed definition" def env))) + (mw:error "Malformed definition" def env))) + (mw:letrec + `(letrec ,(map desugar-definition (reverse defs)) ,@body) + env))) + ) + +(define (mw:if exp env) + (let ((n (mw:safe-length exp))) + (if (or (= n 3) (= n 4)) + (cons mw:if1 (map (lambda (exp) (mw:expand exp env)) (cdr exp))) + (mw:error "Malformed if expression" exp env)))) + +(define (mw:set exp env) + (if (= (mw:safe-length exp) 3) + `(,mw:set!1 ,(mw:expand (cadr exp) env) ,(mw:expand (caddr exp) env)) + (mw:error "Malformed assignment" exp env))) + +(define (mw:begin exp env) + (if (positive? (mw:safe-length exp)) + `(,mw:begin1 ,@(map (lambda (exp) (mw:expand exp env)) (cdr exp))) + (mw:error "Malformed begin expression" exp env))) + +(define (mw:application exp env) + (if (> (mw:safe-length exp) 0) + (map (lambda (exp) (mw:expand exp env)) + exp) + (mw:error "Malformed application"))) + +; I think the environment argument should always be global here. + +(define (mw:define-syntax exp env) + (cond ((and (= (mw:safe-length exp) 3) + (symbol? (cadr exp))) + (mw:define-syntax1 (cadr exp) + (caddr exp) + env + (mw:define-syntax-scope))) + ((and (= (mw:safe-length exp) 4) + (symbol? (cadr exp)) + (memq (caddr exp) '(letrec letrec* let*))) + (mw:define-syntax1 (cadr exp) + (cadddr exp) + env + (caddr exp))) + (else (mw:error "Malformed define-syntax" exp env)))) + +(define (mw:define-syntax1 keyword spec env scope) + (case scope + ((letrec) (mw:define-syntax-letrec keyword spec env)) + ((letrec*) (mw:define-syntax-letrec* keyword spec env)) + ((let*) (mw:define-syntax-let* keyword spec env)) + (else (mw:bug "Weird scope" scope))) + (list mw:quote1 keyword)) + +(define (mw:define-syntax-letrec keyword spec env) + (mw:syntax-bind-globally! + keyword + (mw:compile-transformer-spec spec env))) + +(define (mw:define-syntax-letrec* keyword spec env) + (let* ((env (mw:syntax-extend (mw:syntax-copy env) + (list keyword) + '((fake denotation)))) + (transformer (mw:compile-transformer-spec spec env))) + (mw:syntax-assign! env keyword transformer) + (mw:syntax-bind-globally! keyword transformer))) + +(define (mw:define-syntax-let* keyword spec env) + (mw:syntax-bind-globally! + keyword + (mw:compile-transformer-spec spec (mw:syntax-copy env)))) + +(define (mw:let-syntax exp env) + (if (and (> (mw:safe-length exp) 2) + (comlist:every (lambda (binding) + (and (pair? binding) + (symbol? (car binding)) + (pair? (cdr binding)) + (null? (cddr binding)))) + (cadr exp))) + (mw:body (cddr exp) + (mw:syntax-extend env + (map car (cadr exp)) + (map (lambda (spec) + (mw:compile-transformer-spec + spec + env)) + (map cadr (cadr exp))))) + (mw:error "Malformed let-syntax" exp env))) + +(define (mw:letrec-syntax exp env) + (if (and (> (mw:safe-length exp) 2) + (comlist:every (lambda (binding) + (and (pair? binding) + (symbol? (car binding)) + (pair? (cdr binding)) + (null? (cddr binding)))) + (cadr exp))) + (let ((env (mw:syntax-extend env + (map car (cadr exp)) + (map (lambda (id) + '(fake denotation)) + (cadr exp))))) + (for-each (lambda (id spec) + (mw:syntax-assign! + env + id + (mw:compile-transformer-spec spec env))) + (map car (cadr exp)) + (map cadr (cadr exp))) + (mw:body (cddr exp) env)) + (mw:error "Malformed let-syntax" exp env))) + +(define (mw:macro exp env) + (mw:transcribe exp + env + (lambda (exp env) + (mw:expand exp env)))) + +; To do: +; Clean up alist hacking et cetera. + +;;----------------------------------------------------------------- +;; The following was added to allow expansion without flattening +;; LETs to LAMBDAs so that the origianl structure of the program +;; is preserved by macro expansion. I.e. so that usual.scm is not +;; required. -- added KenD + +(define (mw:process-let-bindings alist binding-list env) ;; helper proc + (map (lambda (bind) + (list (cdr (assq (car bind) alist)) ; renamed name + (mw:body (cdr bind) env))) ; alpha renamed value expression + binding-list) +) + +(define (mw:strip-begin exp) ;; helper proc: mw:body sometimes puts one in + (if (and (pair? exp) (eq? (car exp) 'begin)) + (cdr exp) + exp) +) + +; LET +(define (mw:let exp env) + (let* ( (name (if (or (pair? (cadr exp)) (null? (cadr exp))) + #f + (cadr exp))) ; named let? + (binds (if name (caddr exp) (cadr exp))) + (body (if name (cdddr exp) (cddr exp))) + (vars (if (null? binds) #f (map car binds))) + (alist (if vars (mw:rename-vars vars) #f)) + (newenv (if alist (mw:syntax-rename env alist) env)) + ) + (if name ;; extend env with new name + (let ( (rename (mw:rename-vars (list name))) ) + (set! alist (append rename alist)) + (set! newenv (mw:syntax-rename newenv rename)) + ) ) + `(let + ,@(if name (list (cdr (assq name alist))) '()) + ,(mw:process-let-bindings alist binds env) + ,(mw:body body newenv)) +) ) + + +; LETREC differs from LET in that the binding values are processed in the +; new rather than the original environment. + +(define (mw:letrec exp env) + (let* ( (binds (cadr exp)) + (body (cddr exp)) + (vars (if (null? binds) #f (map car binds))) + (alist (if vars (mw:rename-vars vars) #f)) + (newenv (if alist (mw:syntax-rename env alist) env)) + ) + `(letrec + ,(mw:process-let-bindings alist binds newenv) + ,(mw:body body newenv)) +) ) + + +; LET* adds to ENV for each new binding. + +(define (mw:let* exp env) + (let ( (binds (cadr exp)) + (body (cddr exp)) + ) + (let bind-loop ( (bindings binds) (newbinds '()) (newenv env) ) + (if (null? bindings) + `(let* ,(reverse newbinds) ,(mw:body body newenv)) + (let* ( (bind (car bindings)) + (var (car bind)) + (valexp (cdr bind)) + (rename (mw:rename-vars (list var))) + (next-newenv (mw:syntax-rename newenv rename)) + ) + (bind-loop (cdr bindings) + (cons (list (cdr (assq var rename)) + (mw:body valexp newenv)) + newbinds) + next-newenv)) +) ) ) ) + + +; DO + +(define (mw:process-do-bindings var-init-steps alist oldenv newenv) ;; helper proc + (map (lambda (vis) + (let ( (v (car vis)) + (i (cadr vis)) + (s (if (null? (cddr vis)) (car vis) (caddr vis)))) + `( ,(cdr (assq v alist)) ; renamed name + ,(mw:body (list i) oldenv) ; init in outer/old env + ,(mw:body (list s) newenv) ))) ; step in letrec/inner/new env + var-init-steps) +) + +(define (mw:do exp env) + (let* ( (vis (cadr exp)) ; (Var Init Step ...) + (ts (caddr exp)) ; (Test Sequence ...) + (com (cdddr exp)) ; (COMmand ...) + (vars (if (null? vis) #f (map car vis))) + (rename (if vars (mw:rename-vars vars) #f)) + (newenv (if vars (mw:syntax-rename env rename) env)) + ) + `(do ,(if vars (mw:process-do-bindings vis rename env newenv) '()) + ,(if (null? ts) '() (mw:strip-begin (mw:body (list ts) newenv))) + ,@(if (null? com) '() (list (mw:body com newenv)))) +) ) + +; +; Quasiquotation (backquote) +; +; At level 0, unquoted forms are left painted (not mw:strip'ed). +; At higher levels, forms which are unquoted to level 0 are painted. +; This includes forms within quotes. E.g.: +; (lambda (a) +; (quasiquote +; (a (unquote a) b (quasiquote (a (unquote (unquote a)) b))))) +;or equivalently: +; (lambda (a) `(a ,a b `(a ,,a b))) +;=> +; (lambda (a|1) `(a ,a|1 b `(a ,,a|1 b))) + +(define (mw:quasiquote exp env) + + (define (mw:atom exp env) + (if (not (symbol? exp)) + exp + (let ((denotation (mw:syntax-lookup env exp))) + (case (mw:denote-class denotation) + ((special macro identifier) (mw:identifier-name denotation)) + (else (mw:bug "Bug detected by mw:atom" exp env)))) + ) ) + + (define (quasi subexp level) + (cond + ((null? subexp) subexp) + ((not (or (pair? subexp) (vector? subexp))) + (if (zero? level) (mw:atom subexp env) subexp) ; the work is here + ) + ((vector? subexp) + (let* ((l (vector-length subexp)) + (v (make-vector l))) + (do ((i 0 (+ i 1))) + ((= i l) v) + (vector-set! v i (quasi (vector-ref subexp i) level)) + ) + ) + ) + (else + (let ( (keyword (mw:syntax-lookup env (car subexp))) ) + (cond + ((eq? keyword mw:denote-of-unquote) + (cons 'unquote (quasi (cdr subexp) (- level 1))) + ) + ((eq? keyword mw:denote-of-unquote-splicing) + (cons 'unquote-splicing (quasi (cdr subexp) (- level 1))) + ) + ((eq? keyword mw:denote-of-quasiquote) + (cons 'quasiquote (quasi (cdr subexp) (+ level 1))) + ) + (else + (cons (quasi (car subexp) level) (quasi (cdr subexp) level)) + ) + ) + ) ) ; end else, let + ) ; end cond + ) + + (quasi exp 0) ; need to unquote to level 0 to paint +) + +;; --- E O F --- diff --git a/mwsynrul.scm b/mwsynrul.scm new file mode 100644 index 0000000..1784441 --- /dev/null +++ b/mwsynrul.scm @@ -0,0 +1,343 @@ +; "mwsynrul.scm" Compiler for a <transformer spec>. +; Copyright 1992 William Clinger +; +; Permission to copy this software, in whole or in part, to use this +; software for any lawful purpose, and to redistribute this software +; is granted subject to the restriction that all copies made of this +; software must include this copyright notice in full. +; +; I also request that you send me a copy of any improvements that you +; make to this software so that they may be incorporated within it to +; the benefit of the Scheme community. + +;;;; Compiler for a <transformer spec>. + +;;; The input is a <transformer spec> and a syntactic environment. +;;; Syntactic environments are described in another file. + +;;; Transormer specs are in slib.texi. + +(define mw:pattern-variable-flag (list 'v)) +(define mw:ellipsis-pattern-flag (list 'e)) +(define mw:ellipsis-template-flag mw:ellipsis-pattern-flag) + +(define (mw:make-patternvar v rank) + (vector mw:pattern-variable-flag v rank)) +(define (mw:make-ellipsis-pattern P vars) + (vector mw:ellipsis-pattern-flag P vars)) +(define (mw:make-ellipsis-template T vars) + (vector mw:ellipsis-template-flag T vars)) + +(define (mw:patternvar? x) + (and (vector? x) + (= (vector-length x) 3) + (eq? (vector-ref x 0) mw:pattern-variable-flag))) + +(define (mw:ellipsis-pattern? x) + (and (vector? x) + (= (vector-length x) 3) + (eq? (vector-ref x 0) mw:ellipsis-pattern-flag))) + +(define (mw:ellipsis-template? x) + (and (vector? x) + (= (vector-length x) 3) + (eq? (vector-ref x 0) mw:ellipsis-template-flag))) + +(define (mw:patternvar-name V) (vector-ref V 1)) +(define (mw:patternvar-rank V) (vector-ref V 2)) +(define (mw:ellipsis-pattern P) (vector-ref P 1)) +(define (mw:ellipsis-pattern-vars P) (vector-ref P 2)) +(define (mw:ellipsis-template T) (vector-ref T 1)) +(define (mw:ellipsis-template-vars T) (vector-ref T 2)) + +(define (mw:pattern-variable v vars) + (cond ((null? vars) #f) + ((eq? v (mw:patternvar-name (car vars))) + (car vars)) + (else (mw:pattern-variable v (cdr vars))))) + +; Given a <transformer spec> and a syntactic environment, +; returns a macro denotation. +; +; A macro denotation is of the form +; +; (macro (<rule> ...) env) +; +; where each <rule> has been compiled as described above. + +(define (mw:compile-transformer-spec spec env) + (if (and (> (mw:safe-length spec) 1) + (eq? (mw:syntax-lookup env (car spec)) + mw:denote-of-syntax-rules)) + (let ((literals (cadr spec)) + (rules (cddr spec))) + (if (or (not (list? literals)) + (not (comlist:every (lambda (rule) + (and (= (mw:safe-length rule) 2) + (pair? (car rule)))) + rules))) + (mw:error "Malformed syntax-rules" spec)) + (list 'macro + (map (lambda (rule) + (mw:compile-rule rule literals env)) + rules) + env)) + (mw:error "Malformed syntax-rules" spec))) + +(define (mw:compile-rule rule literals env) + (mw:compile-pattern (cdr (car rule)) + literals + env + (lambda (compiled-rule patternvars) + ; should check uniqueness of pattern variables here!!!!! + (cons compiled-rule + (mw:compile-template + (cadr rule) + patternvars + env))))) + +(define (mw:compile-pattern P literals env k) + (define (loop P vars rank k) + (cond ((symbol? P) + (if (memq P literals) + (k P vars) + (let ((var (mw:make-patternvar P rank))) + (k var (cons var vars))))) + ((null? P) (k '() vars)) + ((pair? P) + (if (and (pair? (cdr P)) + (symbol? (cadr P)) + (eq? (mw:syntax-lookup env (cadr P)) + mw:denote-of-...)) + (if (null? (cddr P)) + (loop (car P) + '() + (+ rank 1) + (lambda (P vars1) + (k (mw:make-ellipsis-pattern P vars1) + (comlist:union vars1 vars)))) + (mw:error "Malformed pattern" P)) + (loop (car P) + vars + rank + (lambda (P1 vars) + (loop (cdr P) + vars + rank + (lambda (P2 vars) + (k (cons P1 P2) vars))))))) + ((vector? P) + (loop (vector->list P) + vars + rank + (lambda (P vars) + (k (vector P) vars)))) + (else (k P vars)))) + (loop P '() 0 k)) + +(define (mw:compile-template T vars env) + + (define (loop T inserted referenced rank escaped? k) + (cond ((symbol? T) + (let ((x (mw:pattern-variable T vars))) + (if x + (if (>= rank (mw:patternvar-rank x)) + (k x inserted (cons x referenced)) + (mw:error + "Too few ellipses follow pattern variable in template" + (mw:patternvar-name x))) + (k T (cons T inserted) referenced)))) + ((null? T) (k '() inserted referenced)) + ((pair? T) + (cond ((and (not escaped?) + (symbol? (car T)) + (eq? (mw:syntax-lookup env (car T)) + mw:denote-of-:::) + (pair? (cdr T)) + (null? (cddr T))) + (loop (cadr T) inserted referenced rank #t k)) + ((and (not escaped?) + (pair? (cdr T)) + (symbol? (cadr T)) + (eq? (mw:syntax-lookup env (cadr T)) + mw:denote-of-...)) + (loop1 T inserted referenced rank escaped? k)) + (else + (loop (car T) + inserted + referenced + rank + escaped? + (lambda (T1 inserted referenced) + (loop (cdr T) + inserted + referenced + rank + escaped? + (lambda (T2 inserted referenced) + (k (cons T1 T2) inserted referenced)))))))) + ((vector? T) + (loop (vector->list T) + inserted + referenced + rank + escaped? + (lambda (T inserted referenced) + (k (vector T) inserted referenced)))) + (else (k T inserted referenced)))) + + (define (loop1 T inserted referenced rank escaped? k) + (loop (car T) + inserted + '() + (+ rank 1) + escaped? + (lambda (T1 inserted referenced1) + (loop (cddr T) + inserted + (append referenced1 referenced) + rank + escaped? + (lambda (T2 inserted referenced) + (k (cons (mw:make-ellipsis-template + T1 + (comlist:remove-if-not + (lambda (var) (> (mw:patternvar-rank var) + rank)) + referenced1)) + T2) + inserted + referenced)))))) + + (loop T + '() + '() + 0 + #f + (lambda (T inserted referenced) + (list T inserted)))) + +; The pattern matcher. +; +; Given an input, a pattern, and two syntactic environments, +; returns a pattern variable environment (represented as an alist) +; if the input matches the pattern, otherwise returns #f. + +(define mw:empty-pattern-variable-environment + (list (mw:make-patternvar (string->symbol "") 0))) + +(define (mw:match F P env-def env-use) + + (define (match F P answer rank) + (cond ((null? P) + (and (null? F) answer)) + ((pair? P) + (and (pair? F) + (let ((answer (match (car F) (car P) answer rank))) + (and answer (match (cdr F) (cdr P) answer rank))))) + ((symbol? P) + (and (symbol? F) + (mw:same-denotation? (mw:syntax-lookup env-def P) + (mw:syntax-lookup env-use F)) + answer)) + ((mw:patternvar? P) + (cons (cons P F) answer)) + ((mw:ellipsis-pattern? P) + (match1 F P answer (+ rank 1))) + ((vector? P) + (and (vector? F) + (match (vector->list F) (vector-ref P 0) answer rank))) + (else (and (equal? F P) answer)))) + + (define (match1 F P answer rank) + (cond ((not (list? F)) #f) + ((null? F) + (append (map (lambda (var) (cons var '())) + (mw:ellipsis-pattern-vars P)) + answer)) + (else + (let* ((P1 (mw:ellipsis-pattern P)) + (answers (map (lambda (F) (match F P1 answer rank)) + F))) + (if (comlist:every identity answers) + (append (map (lambda (var) + (cons var + (map (lambda (answer) + (cdr (assq var answer))) + answers))) + (mw:ellipsis-pattern-vars P)) + answer) + #f))))) + + (match F P mw:empty-pattern-variable-environment 0)) + +(define (mw:rewrite T alist) + + (define (rewrite T alist rank) + (cond ((null? T) '()) + ((pair? T) + ((if (mw:ellipsis-pattern? (car T)) + append + cons) + (rewrite (car T) alist rank) + (rewrite (cdr T) alist rank))) + ((symbol? T) (cdr (assq T alist))) + ((mw:patternvar? T) (cdr (assq T alist))) + ((mw:ellipsis-template? T) + (rewrite1 T alist (+ rank 1))) + ((vector? T) + (list->vector (rewrite (vector-ref T 0) alist rank))) + (else T))) + + (define (rewrite1 T alist rank) + (let* ((T1 (mw:ellipsis-template T)) + (vars (mw:ellipsis-template-vars T)) + (rows (map (lambda (var) (cdr (assq var alist))) + vars))) + (map (lambda (alist) (rewrite T1 alist rank)) + (make-columns vars rows alist)))) + + (define (make-columns vars rows alist) + (define (loop rows) + (if (null? (car rows)) + '() + (cons (append (map (lambda (var row) + (cons var (car row))) + vars + rows) + alist) + (loop (map cdr rows))))) + (if (or (null? (cdr rows)) + (apply = (map length rows))) + (loop rows) + (mw:error "Use of macro is not consistent with definition" + vars + rows))) + + (rewrite T alist 0)) + +; Given a use of a macro, the syntactic environment of the use, +; and a continuation that expects a transcribed expression and +; a new environment in which to continue expansion, +; does the right thing. + +(define (mw:transcribe exp env-use k) + (let* ((m (mw:syntax-lookup env-use (car exp))) + (rules (macwork:rules m)) + (env-def (macwork:env m)) + (F (cdr exp))) + (define (loop rules) + (if (null? rules) + (mw:error "Use of macro does not match definition" exp) + (let* ((rule (car rules)) + (pattern (car rule)) + (alist (mw:match F pattern env-def env-use))) + (if alist + (let* ((template (cadr rule)) + (inserted (caddr rule)) + (alist2 (mw:rename-vars inserted)) + (newexp (mw:rewrite template (append alist2 alist)))) + (k newexp + (mw:syntax-alias env-use alist2 env-def))) + (loop (cdr rules)))))) + (loop rules))) diff --git a/obj2str.scm b/obj2str.scm new file mode 100644 index 0000000..19d8464 --- /dev/null +++ b/obj2str.scm @@ -0,0 +1,61 @@ +;;; "obj2str.scm", write objects to a string. +;Copyright (C) 1993, 1994 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'string-port) + +(define (object->string obj) + (cond ((symbol? obj) (symbol->string obj)) + ((number? obj) (number->string obj)) + (else + (call-with-output-string + (lambda (port) (write obj port)))))) + +; File: "obj2str.scm" (c) 1991, Marc Feeley + +;(require 'generic-write) + +; (object->string obj) returns the textual representation of 'obj' as a +; string. +; +; Note: (write obj) = (display (object->string obj)) + +;(define (object->string obj) +; (let ((result '())) +; (generic-write obj #f #f (lambda (str) (set! result (cons str result)) #t)) +; (reverse-string-append result))) + +; (object->limited-string obj limit) returns a string containing the first +; 'limit' characters of the textual representation of 'obj'. + +(define (object->limited-string obj limit) + (require 'generic-write) + (let ((result '()) (left limit)) + (generic-write obj #f #f + (lambda (str) + (let ((len (string-length str))) + (if (> len left) + (begin + (set! result (cons (substring str 0 left) result)) + (set! left 0) + #f) + (begin + (set! result (cons str result)) + (set! left (- left len)) + #t))))) + (reverse-string-append result))) diff --git a/object.scm b/object.scm new file mode 100644 index 0000000..4ba28fb --- /dev/null +++ b/object.scm @@ -0,0 +1,97 @@ +;;; "object.scm" Macroless Object System +;;;From: whumeniu@datap.ca (Wade Humeniuk) + +;;;Date: February 15, 1994 + +;; Object Construction: +;; 0 1 2 3 4 +;; #(object-tag get-method make-method! unmake-method! get-all-methods) + +(define object:tag "object") + +;;; This might be better done using COMLIST:DELETE-IF. +(define (object:removeq obj alist) + (if (null? alist) + alist + (if (eq? (caar alist) obj) + (cdr alist) + (cons (car alist) (object:removeq obj (cdr alist)))))) + +(define (get-all-methods obj) + (if (object? obj) + ((vector-ref obj 4)) + (slib:error "Cannot get methods on non-object: " obj))) + +(define (object? obj) + (and (vector? obj) + (eq? object:tag (vector-ref obj 0)))) + +(define (make-method! obj generic-method method) + (if (object? obj) + (if (procedure? method) + (begin + ((vector-ref obj 2) generic-method method) + method) + (slib:error "Method must be a procedure: " method)) + (slib:error "Cannot make method on non-object: " obj))) + +(define (get-method obj generic-method) + (if (object? obj) + ((vector-ref obj 1) generic-method) + (slib:error "Cannot get method on non-object: " obj))) + +(define (unmake-method! obj generic-method) + (if (object? obj) + ((vector-ref obj 3) generic-method) + (slib:error "Cannot unmake method on non-object: " obj))) + +(define (make-predicate! obj generic-predicate) + (if (object? obj) + ((vector-ref obj 2) generic-predicate (lambda (self) #t)) + (slib:error "Cannot make predicate on non-object: " obj))) + +(define (make-generic-method . exception-procedure) + (define generic-method + (lambda (obj . operands) + (if (object? obj) + (let ((object-method ((vector-ref obj 1) generic-method))) + (if object-method + (apply object-method (cons obj operands)) + (slib:error "Method not supported: " obj))) + (apply exception-procedure (cons obj operands))))) + + (if (not (null? exception-procedure)) + (if (procedure? (car exception-procedure)) + (set! exception-procedure (car exception-procedure)) + (slib:error "Exception Handler Not Procedure:")) + (set! exception-procedure + (lambda (obj . params) + (slib:error "Operation not supported: " obj)))) + generic-method) + +(define (make-generic-predicate) + (define generic-predicate + (lambda (obj) + (if (object? obj) + (if ((vector-ref obj 1) generic-predicate) + #t + #f) + #f))) + generic-predicate) + +(define (make-object . ancestors) + (define method-list + (apply append (map (lambda (obj) (get-all-methods obj)) ancestors))) + (define (make-method! generic-method method) + (set! method-list (cons (cons generic-method method) method-list)) + method) + (define (unmake-method! generic-method) + (set! method-list (object:removeq generic-method method-list)) + #t) + (define (all-methods) method-list) + (define (get-method generic-method) + (let ((method-def (assq generic-method method-list))) + (if method-def (cdr method-def) #f))) + (vector object:tag get-method make-method! unmake-method! all-methods)) + + diff --git a/paramlst.scm b/paramlst.scm new file mode 100644 index 0000000..f01788b --- /dev/null +++ b/paramlst.scm @@ -0,0 +1,215 @@ +;;; "paramlst.scm" passing parameters by name. +; Copyright 1995 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;; Format of arity-spec: (name predicate conversion) + +(require 'common-list-functions) + +(define arity->arity-spec + (let ((table + `((nary + ,(lambda (a) #t) + ,identity) + (nary1 + ,(lambda (a) (not (null? a))) + ,identity) + (single + ,(lambda (a) (and (pair? a) (null? (cdr a)))) + ,car) + (optional + ,(lambda (a) (or (null? a) (and (pair? a) (null? (cdr a))))) + ,identity) + (boolean + ,(lambda (a) + (or (null? a) + (and (pair? a) (null? (cdr a)) (boolean? (car a))))) + ,(lambda (a) (if (null? a) #f (car a))))))) + (lambda (arity) + (assq arity table)))) + +(define (fill-empty-parameters defaults parameter-list) + (map (lambda (default parameter) + (cond ((null? (cdr parameter)) + (cons (car parameter) + (if default (default parameter-list) '()))) + (else parameter))) + defaults parameter-list)) + +(define (check-parameters checks parameter-list) + (for-each (lambda (check parameter) + (for-each + (lambda (p) + (cond ((and check (not (check p))) + (slib:error (car parameter) + "parameter is wrong type: " p)))) + (cdr parameter))) + checks parameter-list) + parameter-list) + +(define (check-arities arity-specs parameter-list) + (and (every identity arity-specs) + (every + (lambda (arity-spec param) + ((cadr arity-spec) (cdr param))) + arity-specs parameter-list))) + +(define (parameter-list->arglist positions arities parameter-list) + (and (= (length arities) (length positions) (length parameter-list)) + (let ((arity-specs (map arity->arity-spec arities)) + (ans (make-vector (length positions) #f))) + (and (check-arities arity-specs parameter-list) + (for-each + (lambda (pos arity-spec param) + (vector-set! ans (+ -1 pos) + ((caddr arity-spec) (cdr param)))) + positions arity-specs parameter-list) + (vector->list ans))))) + +(define (make-parameter-list parameter-names) + (map list parameter-names)) + +(define (parameter-list-ref parameter-list i) + (let ((ans (assoc i parameter-list))) + (and ans (cdr ans)))) + +(define (parameter-list-expand expanders parms) + (do ((lens (map length parms) (map length parms)) + (olens '() lens)) + ((equal? lens olens)) + (for-each (lambda (expander parm) + (cond + (expander + (for-each + (lambda (news) + (cond ((adjoin-parameters! parms news)) + (else (slib:error + "expanded feature unknown: " news)))) + (apply append + (map (lambda (p) + (cond ((expander p)) + ((not '()) '()) + (else (slib:error + "couldn't expand feature: " p)))) + (cdr parm))))))) + expanders + parms))) + +(define (adjoin-parameters! parameter-list . parameters) + (let ((apairs (map (lambda (param) + (cond ((pair? param) + (assoc (car param) parameter-list)) + (else (assoc param parameter-list)))) + parameters))) + (and (every identity apairs) ;same as APPLY AND? + (for-each + (lambda (apair param) + (cond ((pair? param) + (for-each (lambda (o) + (if (not (member o (cdr apair))) + (set-cdr! apair (cons o (cdr apair))))) + (cdr param))) + (else (if (not (memv #t (cdr apair))) + (set-cdr! apair (cons #t (cdr apair))))))) + apairs parameters) + parameter-list))) + +(define (getopt->parameter-list argc argv optnames arities types aliases) + (define (can-take-arg? opt) + (not (eq? (list-ref arities (position opt optnames)) + 'boolean))) + (define (coerce-val val curopt) + (define ntyp (list-ref types (position curopt optnames))) + (case ntyp + ((expression) val) + (else (coerce val ntyp)))) + (require 'getopt) + (let ((optlist '()) + (long-opt-list '()) + (optstring #f) + (parameter-list (make-parameter-list optnames)) + (curopt '*unclaimed-argument*)) + (set! aliases (map (lambda (alias) + (define str (string-copy (car alias))) + (do ((i (+ -1 (string-length str)) (+ -1 i))) + ((negative? i) (cons str (cdr alias))) + (cond ((char=? #\ (string-ref str i)) + (string-set! str i #\-))))) + aliases)) + (for-each + (lambda (alias) + (define opt (car alias)) + (cond ((not (string? opt))) + ((< 1 (string-length opt)) + (set! long-opt-list (cons opt long-opt-list))) + ((not (= 1 (string-length opt)))) + ((can-take-arg? (cadr alias)) + (set! optlist (cons (string-ref opt 0) + (cons #\: optlist)))) + (else (set! optlist (cons (string-ref opt 0) optlist))))) + aliases) + (set! optstring (list->string (cons #\: optlist))) + (let loop () + (let ((opt (getopt-- argc argv optstring))) + (case opt + ((#\: #\?) + (slib:error + 'getopt->parameter-list "unrecognized option" + getopt:opt)) + ((#f) + (cond ((and (< *optind* argc) + (string=? "-" (list-ref argv *optind*))) + (set! *optind* (+ 1 *optind*))) + ((< *optind* argc) + (cond ((and (member curopt optnames) + (adjoin-parameters! + parameter-list + (list curopt + (coerce-val (list-ref argv *optind*) + curopt)))) + (set! *optind* (+ 1 *optind*)) + (loop)) + (else (slib:error 'getopt->parameter-list curopt + (list-ref argv *optind*) + "not supported")))))) + (else + (cond ((char? opt) (set! opt (string opt)))) + (let ((topt (assoc opt aliases))) + (cond (topt (set! topt (cadr topt))) + (else (slib:error "Option not recognized -" opt))) + (cond + ((not (can-take-arg? topt)) + (adjoin-parameters! parameter-list (list topt #t))) + (*optarg* + (set! curopt topt) + (adjoin-parameters! parameter-list + (list topt (coerce-val *optarg* curopt)))) + (else + (set! curopt topt) + (rdms:warn + 'getopt->parameter-list "argument missing for option--" opt)))) + (loop))))) + parameter-list)) + +(define (getopt->arglist argc argv optnames positions + arities types defaults checks aliases) + (let* ((params (getopt->parameter-list + argc argv optnames arities types aliases)) + (fparams (fill-empty-parameters defaults params))) + (and (list? params) (check-parameters checks fparams)) + (and (list? params) (parameter-list->arglist positions arities fparams)))) diff --git a/plottest.scm b/plottest.scm new file mode 100644 index 0000000..20734f4 --- /dev/null +++ b/plottest.scm @@ -0,0 +1,47 @@ +;"plottest.scm" test charplot.scm +;Copyright (C) 1992 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'charplot) +(require 'random) + +(define strophoid + (let ((l '())) + (do ((x -1.0 (+ x 0.05))) + ((> x 4.0)) + (let* ((a (/ (- 2 x) (+ 2 x)))) + (if (>= a 0.0) + (let* ((y (* x (sqrt a)))) + (set! l (cons (cons x y) l)) + (set! l (cons (cons x (- y)) l)))))) + l)) + +(plot! strophoid "x" "y") (newline) + +(define unif + (let* ((l 6) + (v (make-vector l))) + (do ((i (- l 1) (- i 1))) + ((negative? i)) + (vector-set! v i (cons i 0))) + (do ((i 24 (- i 1)) + (r (random l) (random l))) + ((zero? i) (vector->list v)) + (set-cdr! (vector-ref v r) (+ 1 (cdr (vector-ref v r))))))) + +(plot! unif "n" "occur") @@ -0,0 +1,12 @@ +;"pp.scm" Pretty-print + +(require 'generic-write) + +; (pretty-print obj port) pretty prints 'obj' on 'port'. The current +; output port is used if 'port' is not specified. + +(define (pp:pretty-print obj . opt) + (let ((port (if (pair? opt) (car opt) (current-output-port)))) + (generic-write obj #f 79 (lambda (s) (display s port) #t)))) + +(define pretty-print pp:pretty-print) diff --git a/ppfile.scm b/ppfile.scm new file mode 100644 index 0000000..4b21b6e --- /dev/null +++ b/ppfile.scm @@ -0,0 +1,70 @@ +;;;; "ppfile.scm". Pretty print a Scheme file. +;Copyright (C) 1993, 1994 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'pretty-print) + +(define (pprint-filter-file inport filter . optarg) + ((lambda (fun) + (if (input-port? inport) + (fun inport) + (call-with-input-file inport fun))) + (lambda (port) + ((lambda (fun) + (let ((outport + (if (null? optarg) (current-output-port) (car optarg)))) + (if (output-port? outport) + (fun outport) + (call-with-output-file outport fun)))) + (lambda (export) + (let ((old-load-pathname *load-pathname*)) + (set! *load-pathname* inport) + (letrec ((lp (lambda (c) + (cond ((eof-object? c)) + ((char-whitespace? c) + (display (read-char port) export) + (lp (peek-char port))) + ((char=? #\; c) + (cmt c)) + (else (sx))))) + (cmt (lambda (c) + (cond ((eof-object? c)) + ((char=? #\newline c) + (display (read-char port) export) + (lp (peek-char port))) + (else + (display (read-char port) export) + (cmt (peek-char port)))))) + (sx (lambda () + (let ((o (read port))) + (cond ((eof-object? o)) + (else + (pretty-print (filter o) export) + ;; pretty-print seems to have extra newline + (let ((c (peek-char port))) + (cond ((eqv? #\newline c) + (read-char port) + (set! c (peek-char port)))) + (lp c)))))))) + (lp (peek-char port))) + (set! *load-pathname* old-load-pathname))))))) + +(define (pprint-file ifile . optarg) + (pprint-filter-file ifile + (lambda (x) x) + (if (null? optarg) (current-output-port) (car optarg)))) diff --git a/primes.scm b/primes.scm new file mode 100644 index 0000000..a27b240 --- /dev/null +++ b/primes.scm @@ -0,0 +1,181 @@ +;; "primes.scm", test and generate prime numbers. +; Written by Michael H Coffin (mhc@edsdrd.eds.com) +; +; This code is in the public domain. + +;Date: Thu, 23 Feb 1995 07:47:49 +0500 +;From: mhc@edsdrd.eds.com (Michael H Coffin) +;; +;; Test numbers for primality using Rabin-Miller Monte-Carlo +;; primality test. +;; +;; Public functions: +;; +;; (primes start count . iter) +;; +;; (probably-prime? p . iter) +;; +;; +;; Please contact the author if you have problems or suggestions: +;; +;; Mike Coffin +;; 1196 Whispering Knoll +;; Rochester Hills, Mi. 48306 +;; +;; mhc@edsdrd.eds.com +;; + +(require 'random) + +;; The default number of times to perform the Rabin-Miller test. The +;; probability of a composite number passing the Rabin-Miller test for +;; primality with this many random numbers is at most +;; 1/(4^primes:iterations). The default yields about 1e-9. +;; +(define primes:iter 15) + +;; Is n probably prime? +;; +(define (primes:probably-prime? n . iter) + (let ((iter (if (null? iter) primes:iter (car iter)))) + (primes:prob-pr? n iter))) + + +;; Return a list of the first `number' odd probable primes less +;; than `start'. + +(define (primes:primes< start number . iter) + (let ((iter (if (null? iter) primes:iter (car iter)))) + (do ((candidate (if (odd? start) start (- start 1)) + (- candidate 2)) + (count 0) + (result '()) + ) + ((or (< candidate 3) (>= count number)) result) + (if (primes:prob-pr? candidate iter) + (begin + (set! count (1+ count)) + (set! result (cons candidate result))) + )))) + +(define (primes:primes> start number . iter) + (let ((iter (if (null? iter) primes:iter (car iter)))) + (do ((candidate (if (odd? start) start (+ 1 start)) + (+ 2 candidate)) + (count 0) + (result '()) + ) + ((= count number) (reverse result)) + (if (primes:prob-pr? candidate iter) + (begin + (set! count (1+ count)) + (set! result (cons candidate result))) + )))) + + +;; Is n probably prime? First we check for divisibility by small +;; primes; if it passes that, and it's less than the maximum small +;; prime squared, we try Rabin-Miller. +;; +(define (primes:prob-pr? n count) + (and (not (primes:dbsp? n)) + (or (< n (* primes:max-small-prime primes:max-small-prime)) + (primes:rm-prime? n count)))) + + +;; Is `n' Divisible By a Small Prime? +;; +(define (primes:dbsp? n) + (let ((limit (min (sqrt n) primes:max-small-prime)) + (divisible #f) + ) + (do ((i 0 (1+ i))) + ((let* ((divisor (array-ref primes:small-primes i))) + (set! divisible (= (modulo n divisor) 0)) + (or divisible (>= divisor limit))) + divisible) + ))) + + +;; Does `n' pass the R.-M. primality test for `m' random numbers? +;; +(define (primes:rm-prime? n m) + (do ((i 0 (1+ i)) + (x (+ 2 (random (- n 2))))) + ((or (= i m) (primes:rm-composite? n x)) + (= i m)))) + + +;; Does `x' prove `n' composite using Rabin-Miller? +;; +(define (primes:rm-composite? n x) + (let ((f (primes:extract2s (- n 1)))) + (primes:rm-comp? n (cdr f) (car f) x))) + + +;; Is `n' (where n-1 = 2^k * q) proven composite by `x'? +;; +(define (primes:rm-comp? n q k x) + (let ((y (primes:expt-mod x q n))) + (if (= y 1) + #f + (let loop ((j 0) (y y)) + (cond ((= j k) #t) + ((= y (- n 1)) #f) + ((= y 1) #t) + (else (loop (1+ j) (primes:expt-mod y 2 n))) + ))))) + + +;; Extract factors of 2; that is, factor x as 2^k * q +;; and return (k . q) +;; +(define (primes:extract2s x) + (do ((k 0 (1+ k)) + (q x (quotient q 2))) + ((odd? q) (cons k q)) + )) + + +;; Raise `base' to the power `exp' modulo `modulus' Could use the +;; modulo package, but we only need this function (and besides, this +;; implementation is quite a bit faster). +;; +(define (primes:expt-mod base exp modulus) + (do ((y 1) + (k exp (quotient k 2)) + (z base (modulo (* z z) modulus))) + ((= k 0) y) + (if (odd? k) + (set! y (modulo (* y z) modulus))) + )) + +;; This table seems big enough so that making it larger really +;; doesn't have much effect. +;; +(define primes:max-small-prime 997) + +(define primes:small-primes + #( 2 3 5 7 11 13 17 19 23 29 + 31 37 41 43 47 53 59 61 67 71 + 73 79 83 89 97 101 103 107 109 113 + 127 131 137 139 149 151 157 163 167 173 + 179 181 191 193 197 199 211 223 227 229 + 233 239 241 251 257 263 269 271 277 281 + 283 293 307 311 313 317 331 337 347 349 + 353 359 367 373 379 383 389 397 401 409 + 419 421 431 433 439 443 449 457 461 463 + 467 479 487 491 499 503 509 521 523 541 + 547 557 563 569 571 577 587 593 599 601 + 607 613 617 619 631 641 643 647 653 659 + 661 673 677 683 691 701 709 719 727 733 + 739 743 751 757 761 769 773 787 797 809 + 811 821 823 827 829 839 853 857 859 863 + 877 881 883 887 907 911 919 929 937 941 + 947 953 967 971 977 983 991 997 )) + +(define primes< primes:primes<) +(define primes> primes:primes>) +(define probably-prime? primes:probably-prime?) + +(provide 'primes) diff --git a/printf.scm b/printf.scm new file mode 100644 index 0000000..dffe90d --- /dev/null +++ b/printf.scm @@ -0,0 +1,278 @@ +;;;; "printf.scm" Implementation of standard C functions for Scheme +;;; Copyright (C) 1991-1993, 1996 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'string-case) + +;;; Floating point is not handled yet. + +(define (stdio:iprintf out-proc format-string . args) + (define char-count 0) + (define (out c) + (cond ((char? c) (set! char-count (+ 1 char-count))) + (else (set! char-count (+ (string-length c) char-count)))) + (out-proc c) #t) + (cond + ((not (equal? "" format-string)) + (let ((pos -1) + (fl (string-length format-string)) + (fc (string-ref format-string 0))) + + (define (advance) + (set! pos (+ 1 pos)) + (cond ((>= pos fl) (set! fc #f)) + (else (set! fc (string-ref format-string pos))))) + (define (must-advance) + (set! pos (+ 1 pos)) + (cond ((>= pos fl) (incomplete)) + (else (set! fc (string-ref format-string pos))))) + (define (end-of-format?) + (>= pos fl)) + (define (incomplete) + (slib:error 'printf "conversion specification incomplete" + format-string)) + + (let loop ((args args)) + (advance) + (cond + ((end-of-format?)) + ((eqv? #\\ fc);;Emulating C strings may not be a good idea. + (must-advance) + (case fc + ((#\n #\N) (out #\newline)) + ((#\t #\T) (out slib:tab)) + ((#\r #\R) (out #\return)) + ((#\f #\F) (out slib:form-feed)) + ((#\newline) #f) + (else (out fc))) + (loop args)) + ((eqv? #\% fc) + (must-advance) + (let ((left-adjust #f) ;- + (signed #f) ;+ + (blank #f) + (alternate-form #f) ;# + (leading-0s #f) ;0 + (width 0) + (precision -1) + (type-modifier #f) + (read-format-number + (lambda () + (cond + ((eqv? #\* fc) ; GNU extension + (must-advance) + (let ((ans (car args))) + (set! args (cdr args)) + ans)) + (else + (do ((c fc fc) + (accum 0 (+ (* accum 10) + (string->number (string c))))) + ((not (char-numeric? fc)) accum) + (must-advance))))))) + (define integer-pad + (lambda (s radix) + (cond ((not (negative? precision)) + (set! leading-0s #f))) + (let* ((pre + (cond ((equal? "" s) "") + ((eqv? #\- (string-ref s 0)) + (set! s (substring s 1 (string-length s))) + "-") + (signed "+") + (blank " ") + ((equal? "" s) "") + (alternate-form + (case radix + ((8) "0") + ((16) "0x") + (else ""))) + (else ""))) + (length-so-far (+ (string-length pre) + (string-length s)))) + (cond ((<= width length-so-far) + (string-append pre s)) + (left-adjust + (string-append + pre s + (make-string (- width length-so-far) #\ ))) + (leading-0s + (string-append + pre (make-string (- width length-so-far) #\0) + s)) + (else + (string-append + (make-string (- width length-so-far) #\ ) + pre s)))))) + + (do () + ((case fc + ((#\-) (set! left-adjust #t) #f) + ((#\+) (set! signed #t) #f) + ((#\ ) (set! blank #t) #f) + ((#\#) (set! alternate-form #t) #f) + ((#\0) (set! leading-0s #t) #f) + (else #t))) + (must-advance)) + (cond (left-adjust (set! leading-0s #f))) + (cond (signed (set! blank #f))) + + (set! width (read-format-number)) + (cond ((negative? width) + (set! left-adjust #t) + (set! width (- width)))) + (cond ((eqv? #\. fc) + (must-advance) + (set! precision (read-format-number)))) + (case fc ;Ignore these specifiers + ((#\l #\L #\h) + (set! type-modifier fc) + (must-advance))) + + (case fc + ;; only - is allowed between % and c + ((#\c #\C) ; C is enhancement + (out (string (car args))) + (loop (cdr args))) + + ;; only - flag, no type-modifiers + ((#\s #\S) ; S is enhancement + (let ((s (cond + ((symbol? (car args)) (symbol->string (car args))) + ((not (car args)) "(NULL)") + (else (car args))))) + (cond ((not (or (negative? precision) + (>= precision (string-length s)))) + (set! s (substring s 0 precision)))) + (out + (cond + ((<= width (string-length s)) s) + (left-adjust + (string-append + s (make-string (- width (string-length s)) #\ ))) + (else + (string-append (make-string (- width (string-length s)) + (if leading-0s #\0 #\ )) s)))) + (loop (cdr args)))) + + ;; SLIB extension + ((#\a #\A) ;#\y #\Y are pretty-print + (require 'generic-write) + (let ((os "") (pr precision)) + (generic-write + (car args) (not alternate-form) #f + (cond ((and left-adjust (negative? pr)) + out) + (left-adjust + (lambda (s) + (define sl (- pr (string-length s))) + (set! pr (cond ((negative? sl) + (out (substring s 0 pr)) 0) + (else (out s) sl))) + (positive? sl))) + ((negative? pr) + (set! pr width) + (lambda (s) + (set! pr (- pr (string-length s))) + (cond ((not os) (out s)) + ((negative? pr) + (out os) + (set! os #f) + (out s)) + (else (set! os (string-append os s)))) + #t)) + (else + (lambda (s) + (define sl (- pr (string-length s))) + (cond ((negative? sl) + (set! os (string-append + os (substring s 0 pr)))) + (else (set! os (string-append os s)))) + (set! pr sl) + (positive? sl))))) + (cond (left-adjust + (cond + ((> width (- precision pr)) + (out (make-string (- width (- precision pr)) + #\ ))))) + ((not os)) + ((<= width (string-length os)) (out os)) + (else + (out (make-string (- width (string-length os)) #\ )) + (out os)))) + (loop (cdr args))) + + ((#\d #\D #\i #\I #\u #\U) + (out (integer-pad + (cond ((symbol? (car args)) + (symbol->string (car args))) + ((number? (car args)) + (number->string (car args))) + ((not (car args)) "0") + (else "1")) + 10)) + (loop (cdr args))) + ((#\o #\O) + (out (integer-pad (number->string (car args) 8) 8)) + (loop (cdr args))) + ((#\x #\X) + (out + ((if (char-upper-case? fc) string-upcase string-downcase) + (integer-pad (number->string (car args) 16) 16))) + (loop (cdr args))) + ((#\%) (out #\%) + (loop args)) + (else + (cond ((end-of-format?) (incomplete)) + (else (out #\%) (out fc) (out #\?) + (loop args))))))) + (else (out fc) + (loop args))))))) + char-count) ; return number of characters output. + +(define (stdio:printf format . args) + (apply stdio:iprintf display format args)) + +(define (stdio:fprintf port format . args) + (if (equal? port (current-output-port)) + (apply stdio:iprintf display format args) + (apply stdio:iprintf (lambda (x) (display x port)) format args))) + +(define (stdio:sprintf s format . args) + (let ((p 0) (end (string-length s))) + (apply stdio:iprintf + (lambda (x) + (cond ((string? x) + (do ((i 0 (+ i 1))) + ((>= i (min (string-length x) end))) + (string-set! s p (string-ref x i)) + (set! p (+ p 1)))) + ((>= p end)) + ((char? x) + (string-set! s p x) + (set! p (+ p 1))) + (else + (string-set! s p #\?) + (set! p (+ p 1))))) + format + args) + p)) + +(define printf stdio:printf) +(define fprintf stdio:fprintf) +(define sprintf stdio:sprintf) diff --git a/priorque.scm b/priorque.scm new file mode 100644 index 0000000..927ffbe --- /dev/null +++ b/priorque.scm @@ -0,0 +1,141 @@ +;;;; "priorque.scm" priority queues for Scheme. +;;; Copyright (C) 1992, 1993 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;; Algorithm from: +;;; Introduction to Algorithms by T. Cormen, C. Leiserson, R. Rivest. +;;; 1989 MIT Press. + +(require 'record) + +;; Record type. +(define heap:rtd (make-record-type "heap" '(array size heap<?))) + +;; Constructor. +(define heap:make-heap + (let ((cstr (record-constructor heap:rtd))) + (lambda (pred<?) + (cstr (make-vector 4) 0 pred<?)))) + +;; Reference an element. +(define heap:ref + (let ((ra (record-accessor heap:rtd 'array))) + (lambda (a i) + (vector-ref (ra a) (+ -1 i))))) + +;; Set an element. +(define heap:set! + (let ((ra (record-accessor heap:rtd 'array))) + (lambda (a i v) + (vector-set! (ra a) (+ -1 i) v)))) + +;; Exchange two elements. +(define heap:exchange + (let ((aa (record-accessor heap:rtd 'array))) + (lambda (a i j) + (set! i (+ -1 i)) + (set! j (+ -1 j)) + (let* ((ra (aa a)) + (tmp (vector-ref ra i))) + (vector-set! ra i (vector-ref ra j)) + (vector-set! ra j tmp))))) + + +;; Get length. +(define heap:length (record-accessor heap:rtd 'size)) + +(define heap:heap<? (record-accessor heap:rtd 'heap<?)) + +(define heap:set-size! + (let ((aa (record-accessor heap:rtd 'array)) + (am (record-modifier heap:rtd 'array)) + (sm (record-modifier heap:rtd 'size))) + (lambda (a s) + (let ((ra (aa a))) + (if (> s (vector-length ra)) + (let ((nra (make-vector (+ s (quotient s 2))))) + (do ((i (+ -1 (vector-length ra)) (+ -1 i))) + ((negative? i) (am a nra)) + (vector-set! nra i (vector-ref ra i))))) + (sm a s))))) + +(define (heap:parent i) (quotient i 2)) +(define (heap:left i) (* 2 i)) +(define (heap:right i) (+ 1 (* 2 i))) + +(define (heap:heapify a i) + (let* ((l (heap:left i)) + (r (heap:right i)) + (largest (if (and (<= l (heap:length a)) + ((heap:heap<? a) (heap:ref a i) (heap:ref a l))) + l + i))) + (cond ((and (<= r (heap:length a)) + ((heap:heap<? a) (heap:ref a largest) (heap:ref a r))) + (set! largest r))) + (cond ((not (= largest i)) + (heap:exchange a i largest) + (heap:heapify a largest))))) + +(define (heap:insert! a key) + (define i (+ 1 (heap:length a))) + (heap:set-size! a i) + (do () + ((not (and (> i 1) + ((heap:heap<? a) (heap:ref a (heap:parent i)) key)))) + (heap:set! a i (heap:ref a (heap:parent i))) + (set! i (heap:parent i))) + (heap:set! a i key)) + +(define (heap:extract-max! a) + (if (< (heap:length a) 1) + (slib:error "heap underflow" a)) + (let ((max (heap:ref a 1))) + (heap:set! a 1 (heap:ref a (heap:length a))) + (heap:set-size! a (+ -1 (heap:length a))) + (heap:heapify a 1) + max)) + +;; +;; Externals. +;; +(define make-heap heap:make-heap) +(define heap-insert! heap:insert!) +(define heap-extract-max! heap:extract-max!) +(define heap-length heap:length) + +(define (heap:test) + (require 'debug) + (let ((heap #f)) + (set! heap (make-heap char>?)) + (heap-insert! heap #\A) + (heap-insert! heap #\Z) + (heap-insert! heap #\G) + (heap-insert! heap #\B) + (heap-insert! heap #\G) + (heap-insert! heap #\Q) + (heap-insert! heap #\S) + (heap-insert! heap #\R) + (print (heap-extract-max! heap)) + (print (heap-extract-max! heap)) + (print (heap-extract-max! heap)) + (print (heap-extract-max! heap)) + (print (heap-extract-max! heap)) + (print (heap-extract-max! heap)) + (print (heap-extract-max! heap)) + (print (heap-extract-max! heap)))) diff --git a/process.scm b/process.scm new file mode 100644 index 0000000..6b0acc3 --- /dev/null +++ b/process.scm @@ -0,0 +1,68 @@ +;;;; "process.scm", Multi-Processing for Scheme +;;; Copyright (C) 1992, 1993 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'full-continuation) +(require 'queue) + +(define (add-process! thunk1) + (cond ((procedure? thunk1) + (defer-ints) + (enqueue! process:queue thunk1) + (allow-ints)) + (else (slib:error "add-process!: wrong type argument " thunk1)))) + +(define (process:schedule!) + (defer-ints) + (cond ((queue-empty? process:queue) (allow-ints) + 'still-running) + (else (call-with-current-continuation + (lambda (cont) + (enqueue! process:queue cont) + (let ((proc (dequeue! process:queue))) + (allow-ints) + (proc 'run)) + (kill-process!)))))) + +(define (kill-process!) + (defer-ints) + (cond ((queue-empty? process:queue) (allow-ints) + (slib:exit)) + (else (let ((proc (dequeue! process:queue))) + (allow-ints) + (proc 'run)) + (kill-process!)))) + +(define ints-disabled #f) +(define alarm-deferred #f) + +(define (defer-ints) (set! ints-disabled #t)) + +(define (allow-ints) + (set! ints-disabled #f) + (cond (alarm-deferred + (set! alarm-deferred #f) + (alarm-interrupt)))) + +;;; Make THE process queue. +(define process:queue (make-queue)) + +(define (alarm-interrupt) + (alarm 1) + (if ints-disabled (set! alarm-deferred #t) + (process:schedule!))) diff --git a/promise.scm b/promise.scm new file mode 100644 index 0000000..f38aebf --- /dev/null +++ b/promise.scm @@ -0,0 +1,29 @@ +;;;"promise.scm" promise for force and delay +;;; From Revised^4 Report on the Algorithmic Language Scheme +;;; Editors: William Clinger and Jonathon Rees +; +; We intend this report to belong to the entire Scheme community, and so +; we grant permission to copy it in whole or in part without fee. In +; particular, we encourage implementors of Scheme to use this report as +; a starting point for manuals and other documentation, modifying it as +; necessary. + +(define promise:force (lambda (object) (object))) + +(define make-promise + (lambda (proc) + (let ((result-ready? #f) + (result #f)) + (lambda () + (if result-ready? + result + (let ((x (proc))) + (if result-ready? + result + (begin (set! result-ready? #t) + (set! result x) + result)))))))) + +;;; change occurences of (DELAY <expression>) to +;;; (MAKE-PROMISE (LAMBDA () <expression>)) +;;; and (define force promise:force) @@ -0,0 +1,149 @@ +;;;; "qp.scm" Print finite length representation for any Scheme object. +;;; Copyright (C) 1991, 1992, 1993, 1995 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(define *qp-width* (output-port-width (current-output-port))) + +(define qp:qpn + (let ((newline newline) (apply apply)) + (lambda objs (apply qp:qp objs) (newline)))) + +(define qp:qpr + (let ((- -) (apply apply) (length length) (list-ref list-ref)) + (lambda objs (apply qp:qpn objs) + (list-ref objs (- (length objs) 1))))) + +(define qp:qp + (let + ((+ +) (- -) (< <) (= =) (>= >=) (apply apply) (boolean? boolean?) + (car car) (cdr cdr) (char? char?) (display display) (eq? eq?) + (for-each for-each) (input-port? input-port?) + (not not) (null? null?) (number->string number->string) + (number? number?) (output-port? output-port?) (eof-object? eof-object?) + (procedure? procedure?) (string-length string-length) + (string? string?) (substring substring) + (symbol->string symbol->string) (symbol? symbol?) + (vector-length vector-length) (vector-ref vector-ref) + (vector? vector?) (write write) (quotient quotient)) + (letrec + ((num-cdrs + (lambda (pairs max-cdrs) + (cond + ((null? pairs) 0) + ((< max-cdrs 1) 1) + ((pair? pairs) (+ 1 (num-cdrs (cdr pairs) (- max-cdrs 1)))) + (else 1)))) + + (l-elt-room + (lambda (room pairs) + (quotient room (num-cdrs pairs (quotient room 8))))) + + (qp-pairs + (lambda (cdrs room) + (cond + ((null? cdrs) 0) + ((not (pair? cdrs)) + (display " . ") + (+ 3 (qp-obj cdrs (l-elt-room (- room 3) cdrs)))) + ((< 11 room) + (display #\ ) + ((lambda (used) + (+ (qp-pairs (cdr cdrs) (- room used)) used)) + (+ 1 (qp-obj (car cdrs) (l-elt-room (- room 1) cdrs))))) + (else + (display " ...") 4)))) + + (v-elt-room + (lambda (room vleft) + (quotient room (min vleft (quotient room 8))))) + + (qp-vect + (lambda (vect i room) + (cond + ((= (vector-length vect) i) 0) + ((< 11 room) + (display #\ ) + ((lambda (used) + (+ (qp-vect vect (+ i 1) (- room used)) used)) + (+ 1 (qp-obj (vector-ref vect i) + (v-elt-room (- room 1) + (- (vector-length vect) i)))))) + (else + (display " ...") 4)))) + + (qp-string + (lambda (str room) + (cond + ((>= (string-length str) room 3) + (display (substring str 0 (- room 3))) + (display "...") + room) + (else + (display str) + (string-length str))))) + + (qp-obj + (lambda (obj room) + (cond + ((null? obj) (write obj) 2) + ((boolean? obj) (write obj) 2) + ((char? obj) (write obj) 8) + ((number? obj) (qp-string (number->string obj) room)) + ((string? obj) + (display #\") + ((lambda (ans) (display #\") ans) + (+ 2 (qp-string obj (- room 2))))) + ((symbol? obj) (qp-string (symbol->string obj) room)) + ((input-port? obj) (display "#[input]") 8) + ((output-port? obj) (display "#[output]") 9) + ((procedure? obj) (display "#[proc]") 7) + ((eof-object? obj) (display "#[eof]") 6) + ((vector? obj) + (set! room (- room 3)) + (display "#(") + ((lambda (used) (display #\)) (+ used 3)) + (cond + ((= 0 (vector-length obj)) 0) + ((< room 8) (display "...") 3) + (else + ((lambda (used) (+ (qp-vect obj 1 (- room used)) used)) + (qp-obj (vector-ref obj 0) + (v-elt-room room (vector-length obj)))))))) + ((pair? obj) + (set! room (- room 2)) + (display #\() + ((lambda (used) (display #\)) (+ 2 used)) + (if (< room 8) (begin (display "...") 3) + ((lambda (used) + (+ (qp-pairs (cdr obj) (- room used)) used)) + (qp-obj (car obj) (l-elt-room room obj)))))) + (else (display "#[unknown]") 10))))) + + (lambda objs + (cond + ((= 0 *qp-width*) + (for-each (lambda (x) (write x) (display #\ )) objs) + (newline)) + (else + (qp-pairs (cdr objs) + (- *qp-width* + (qp-obj (car objs) (l-elt-room *qp-width* objs)))))))))) + +(define qp qp:qp) +(define qpn qp:qpn) +(define qpr qp:qpr) diff --git a/queue.scm b/queue.scm new file mode 100644 index 0000000..4557746 --- /dev/null +++ b/queue.scm @@ -0,0 +1,72 @@ +; "queue.scm" Queues/Stacks for Scheme +; Written by Andrew Wilcox (awilcox@astro.psu.edu) on April 1, 1992. +; +; This code is in the public domain. + +(require 'record) + +; Elements in a queue are stored in a list. The last pair in the list +; is stored in the queue type so that datums can be added in constant +; time. + +(define queue:record-type + (make-record-type "queue" '(first-pair last-pair))) +(define make-queue + (let ((construct-queue (record-constructor queue:record-type))) + (lambda () + (construct-queue '() '())))) + +(define queue? (record-predicate queue:record-type)) + +(define queue:first-pair (record-accessor queue:record-type + 'first-pair)) +(define queue:set-first-pair! (record-modifier queue:record-type + 'first-pair)) +(define queue:last-pair (record-accessor queue:record-type + 'last-pair)) +(define queue:set-last-pair! (record-modifier queue:record-type + 'last-pair)) + +(define (queue-empty? q) + (null? (queue:first-pair q))) + +(define (queue-front q) + (let ((first-pair (queue:first-pair q))) + (if (null? first-pair) + (slib:error "queue is empty" q)) + (car first-pair))) + +(define (queue-rear q) + (let ((last-pair (queue:last-pair q))) + (if (null? last-pair) + (slib:error "queue is empty" q)) + (car last-pair))) + +(define (queue-push! q datum) + (let* ((old-first-pair (queue:first-pair q)) + (new-first-pair (cons datum old-first-pair))) + (queue:set-first-pair! q new-first-pair) + (if (null? old-first-pair) + (queue:set-last-pair! q new-first-pair))) + q) + +(define (enqueue! q datum) + (let ((new-pair (cons datum '()))) + (cond ((null? (queue:first-pair q)) + (queue:set-first-pair! q new-pair)) + (else + (set-cdr! (queue:last-pair q) new-pair))) + (queue:set-last-pair! q new-pair)) + q) + +(define (dequeue! q) + (let ((first-pair (queue:first-pair q))) + (if (null? first-pair) + (slib:error "queue is empty" q)) + (let ((first-cdr (cdr first-pair))) + (queue:set-first-pair! q first-cdr) + (if (null? first-cdr) + (queue:set-last-pair! q '())) + (car first-pair)))) + +(define queue-pop! dequeue!) diff --git a/r4rsyn.scm b/r4rsyn.scm new file mode 100644 index 0000000..500d68c --- /dev/null +++ b/r4rsyn.scm @@ -0,0 +1,542 @@ +;;; "r4rsyn.scm" R4RS syntax -*-Scheme-*- +;;; Copyright (c) 1989-91 Massachusetts Institute of Technology +;;; +;;; This material was developed by the Scheme project at the +;;; Massachusetts Institute of Technology, Department of Electrical +;;; Engineering and Computer Science. Permission to copy this +;;; software, to redistribute it, and to use it for any purpose is +;;; granted, subject to the following restrictions and understandings. +;;; +;;; 1. Any copy made of this software must include this copyright +;;; notice in full. +;;; +;;; 2. Users of this software agree to make their best efforts (a) to +;;; return to the MIT Scheme project any improvements or extensions +;;; that they make, so that these may be included in future releases; +;;; and (b) to inform MIT of noteworthy uses of this software. +;;; +;;; 3. All materials developed as a consequence of the use of this +;;; software shall duly acknowledge such use, in accordance with the +;;; usual standards of acknowledging credit in academic research. +;;; +;;; 4. MIT has made no warrantee or representation that the operation +;;; of this software will be error-free, and MIT is under no +;;; obligation to provide any services, by way of maintenance, update, +;;; or otherwise. +;;; +;;; 5. In conjunction with products arising from the use of this +;;; material, there shall be no use of the name of the Massachusetts +;;; Institute of Technology nor of any adaptation thereof in any +;;; advertising, promotional, or sales literature without prior +;;; written consent from MIT in each case. + +;;;; R4RS Syntax + +(define scheme-syntactic-environment #f) + +(define (initialize-scheme-syntactic-environment!) + (set! scheme-syntactic-environment + ((compose-macrologies + (make-core-primitive-macrology) + (make-binding-macrology syntactic-binding-theory + 'LET-SYNTAX 'LETREC-SYNTAX 'DEFINE-SYNTAX) + (make-binding-macrology variable-binding-theory + 'LET 'LETREC 'DEFINE) + (make-r4rs-primitive-macrology) + (make-core-expander-macrology) + (make-syntax-rules-macrology)) + root-syntactic-environment))) + +;;;; Core Primitives + +(define (make-core-primitive-macrology) + (make-primitive-macrology + (lambda (define-classifier define-compiler) + + (define-classifier 'BEGIN + (lambda (form environment definition-environment) + (syntax-check '(KEYWORD * FORM) form) + (make-body-item (classify/subforms (cdr form) + environment + definition-environment)))) + + (define-compiler 'DELAY + (lambda (form environment) + (syntax-check '(KEYWORD EXPRESSION) form) + (output/delay + (compile/subexpression (cadr form) + environment)))) + + (define-compiler 'IF + (lambda (form environment) + (syntax-check '(KEYWORD EXPRESSION EXPRESSION ? EXPRESSION) form) + (output/conditional + (compile/subexpression (cadr form) environment) + (compile/subexpression (caddr form) environment) + (if (null? (cdddr form)) + (output/unspecific) + (compile/subexpression (cadddr form) + environment))))) + + (define-compiler 'QUOTE + (lambda (form environment) + environment ;ignore + (syntax-check '(KEYWORD DATUM) form) + (output/literal-quoted (strip-syntactic-closures (cadr form)))))))) + +;;;; Bindings + +(define (make-binding-macrology binding-theory + let-keyword letrec-keyword define-keyword) + (make-primitive-macrology + (lambda (define-classifier define-compiler) + + (let ((pattern/let-like + '(KEYWORD (* (IDENTIFIER EXPRESSION)) + FORM)) + (compile/let-like + (lambda (form environment body-environment output/let) + ;; Force evaluation order. + (let ((bindings + (let loop + ((bindings + (map (lambda (binding) + (cons (car binding) + (classify/subexpression + (cadr binding) + environment))) + (cadr form)))) + (if (null? bindings) + '() + (let ((binding + (binding-theory body-environment + (caar bindings) + (cdar bindings)))) + (if binding + (cons binding (loop (cdr bindings))) + (loop (cdr bindings)))))))) + (output/let (map car bindings) + (map (lambda (binding) + (compile-item/expression (cdr binding))) + bindings) + (compile-item/expression + (classify/body (cddr form) + body-environment))))))) + + (define-compiler let-keyword + (lambda (form environment) + (syntax-check pattern/let-like form) + (compile/let-like form + environment + (internal-syntactic-environment environment) + output/let))) + + (define-compiler letrec-keyword + (lambda (form environment) + (syntax-check pattern/let-like form) + (let ((environment (internal-syntactic-environment environment))) + (reserve-names! (map car (cadr form)) environment) + (compile/let-like form + environment + environment + output/letrec))))) + + (define-classifier define-keyword + (lambda (form environment definition-environment) + (syntax-check '(KEYWORD IDENTIFIER EXPRESSION) form) + (syntactic-environment/define! definition-environment + (cadr form) + (make-reserved-name-item)) + (make-definition-item binding-theory + (cadr form) + (make-promise + (lambda () + (classify/subexpression + (caddr form) + environment))))))))) + +;;;; Bodies + +(define (classify/body forms environment) + (let ((environment (internal-syntactic-environment environment))) + (let forms-loop + ((forms forms) + (bindings '())) + (if (null? forms) + (syntax-error "no expressions in body" + "") + (let items-loop + ((items + (item->list + (classify/subform (car forms) + environment + environment))) + (bindings bindings)) + (cond ((null? items) + (forms-loop (cdr forms) + bindings)) + ((definition-item? (car items)) + (items-loop (cdr items) + (let ((binding + (bind-definition-item! environment + (car items)))) + (if binding + (cons binding bindings) + bindings)))) + (else + (let ((body + (make-body-item + (append items + (flatten-body-items + (classify/subforms + (cdr forms) + environment + environment)))))) + (make-expression-item + (lambda () + (output/letrec + (map car bindings) + (map (lambda (binding) + (compile-item/expression (cdr binding))) + bindings) + (compile-item/expression body))) forms))))))))) + +;;;; R4RS Primitives + +(define (make-r4rs-primitive-macrology) + (make-primitive-macrology + (lambda (define-classifier define-compiler) + + (define (transformer-keyword expander->classifier) + (lambda (form environment definition-environment) + definition-environment ;ignore + (syntax-check '(KEYWORD EXPRESSION) form) + (let ((item + (classify/subexpression (cadr form) + scheme-syntactic-environment))) + (let ((transformer (base:eval (compile-item/expression item)))) + (if (procedure? transformer) + (make-keyword-item + (expander->classifier transformer environment) item) + (syntax-error "transformer not a procedure" + transformer)))))) + + (define-classifier 'TRANSFORMER + ;; "Syntactic Closures" transformer + (transformer-keyword sc-expander->classifier)) + + (define-classifier 'ER-TRANSFORMER + ;; "Explicit Renaming" transformer + (transformer-keyword er-expander->classifier)) + + (define-compiler 'LAMBDA + (lambda (form environment) + (syntax-check '(KEYWORD R4RS-BVL + FORM) form) + (let ((environment (internal-syntactic-environment environment))) + ;; Force order -- bind names before classifying body. + (let ((bvl-description + (let ((rename + (lambda (identifier) + (bind-variable! environment identifier)))) + (let loop ((bvl (cadr form))) + (cond ((null? bvl) + '()) + ((pair? bvl) + (cons (rename (car bvl)) (loop (cdr bvl)))) + (else + (rename bvl))))))) + (output/lambda bvl-description + (compile-item/expression + (classify/body (cddr form) + environment))))))) + + (define-compiler 'SET! + (lambda (form environment) + (syntax-check '(KEYWORD FORM EXPRESSION) form) + (output/assignment + (let loop + ((form (cadr form)) + (environment environment)) + (cond ((identifier? form) + (let ((item + (syntactic-environment/lookup environment form))) + (if (variable-item? item) + (variable-item/name item) + (slib:error "target of assignment not a variable" + form)))) + ((syntactic-closure? form) + (let ((form (syntactic-closure/form form)) + (environment + (filter-syntactic-environment + (syntactic-closure/free-names form) + environment + (syntactic-closure/environment form)))) + (loop form + environment))) + (else + (slib:error "target of assignment not an identifier" + form)))) + (compile/subexpression (caddr form) + environment)))) + + ;; end MAKE-R4RS-PRIMITIVE-MACROLOGY + ))) + +;;;; Core Expanders + +(define (make-core-expander-macrology) + (make-er-expander-macrology + (lambda (define-expander base-environment) + + (let ((keyword (make-syntactic-closure base-environment '() 'DEFINE))) + (define-expander 'DEFINE + (lambda (form rename compare) + compare ;ignore + (if (syntax-match? '((IDENTIFIER . R4RS-BVL) + FORM) (cdr form)) + `(,keyword ,(caadr form) + (,(rename 'LAMBDA) ,(cdadr form) ,@(cddr form))) + `(,keyword ,@(cdr form)))))) + + (let ((keyword (make-syntactic-closure base-environment '() 'LET))) + (define-expander 'LET + (lambda (form rename compare) + compare ;ignore + (if (syntax-match? '(IDENTIFIER (* (IDENTIFIER EXPRESSION)) + FORM) + (cdr form)) + (let ((name (cadr form)) + (bindings (caddr form))) + `((,(rename 'LETREC) + ((,name (,(rename 'LAMBDA) ,(map car bindings) ,@(cdddr form)))) + ,name) + ,@(map cadr bindings))) + `(,keyword ,@(cdr form)))))) + + (define-expander 'LET* + (lambda (form rename compare) + compare ;ignore + (if (syntax-match? '((* (IDENTIFIER EXPRESSION)) + FORM) (cdr form)) + (let ((bindings (cadr form)) + (body (cddr form)) + (keyword (rename 'LET))) + (if (null? bindings) + `(,keyword ,bindings ,@body) + (let loop ((bindings bindings)) + (if (null? (cdr bindings)) + `(,keyword ,bindings ,@body) + `(,keyword (,(car bindings)) + ,(loop (cdr bindings))))))) + (ill-formed-syntax form)))) + + (define-expander 'AND + (lambda (form rename compare) + compare ;ignore + (if (syntax-match? '(* EXPRESSION) (cdr form)) + (let ((operands (cdr form))) + (if (null? operands) + `#T + (let ((if-keyword (rename 'IF))) + (let loop ((operands operands)) + (if (null? (cdr operands)) + (car operands) + `(,if-keyword ,(car operands) + ,(loop (cdr operands)) + #F)))))) + (ill-formed-syntax form)))) + + (define-expander 'OR + (lambda (form rename compare) + compare ;ignore + (if (syntax-match? '(* EXPRESSION) (cdr form)) + (let ((operands (cdr form))) + (if (null? operands) + `#F + (let ((let-keyword (rename 'LET)) + (if-keyword (rename 'IF)) + (temp (rename 'TEMP))) + (let loop ((operands operands)) + (if (null? (cdr operands)) + (car operands) + `(,let-keyword ((,temp ,(car operands))) + (,if-keyword ,temp + ,temp + ,(loop (cdr operands))))))))) + (ill-formed-syntax form)))) + + (define-expander 'CASE + (lambda (form rename compare) + (if (syntax-match? '(EXPRESSION + (DATUM + EXPRESSION)) (cdr form)) + (letrec + ((process-clause + (lambda (clause rest) + (cond ((null? (car clause)) + (process-rest rest)) + ((and (identifier? (car clause)) + (compare (rename 'ELSE) (car clause)) + (null? rest)) + `(,(rename 'BEGIN) ,@(cdr clause))) + ((list? (car clause)) + `(,(rename 'IF) (,(rename 'MEMV) ,(rename 'TEMP) + ',(car clause)) + (,(rename 'BEGIN) ,@(cdr clause)) + ,(process-rest rest))) + (else + (syntax-error "ill-formed clause" clause))))) + (process-rest + (lambda (rest) + (if (null? rest) + (unspecific-expression) + (process-clause (car rest) (cdr rest)))))) + `(,(rename 'LET) ((,(rename 'TEMP) ,(cadr form))) + ,(process-clause (caddr form) (cdddr form)))) + (ill-formed-syntax form)))) + + (define-expander 'COND + (lambda (form rename compare) + (letrec + ((process-clause + (lambda (clause rest) + (cond + ((or (not (list? clause)) + (null? clause)) + (syntax-error "ill-formed clause" clause)) + ((and (identifier? (car clause)) + (compare (rename 'ELSE) (car clause))) + (cond + ((or (null? (cdr clause)) + (and (identifier? (cadr clause)) + (compare (rename '=>) (cadr clause)))) + (syntax-error "ill-formed ELSE clause" clause)) + ((not (null? rest)) + (syntax-error "misplaced ELSE clause" clause)) + (else + `(,(rename 'BEGIN) ,@(cdr clause))))) + ((null? (cdr clause)) + `(,(rename 'OR) ,(car clause) ,(process-rest rest))) + ((and (identifier? (cadr clause)) + (compare (rename '=>) (cadr clause))) + (if (and (pair? (cddr clause)) + (null? (cdddr clause))) + `(,(rename 'LET) + ((,(rename 'TEMP) ,(car clause))) + (,(rename 'IF) ,(rename 'TEMP) + (,(caddr clause) ,(rename 'TEMP)) + ,(process-rest rest))) + (syntax-error "ill-formed => clause" clause))) + (else + `(,(rename 'IF) ,(car clause) + (,(rename 'BEGIN) ,@(cdr clause)) + ,(process-rest rest)))))) + (process-rest + (lambda (rest) + (if (null? rest) + (unspecific-expression) + (process-clause (car rest) (cdr rest)))))) + (let ((clauses (cdr form))) + (if (null? clauses) + (syntax-error "no clauses" form) + (process-clause (car clauses) (cdr clauses))))))) + + (define-expander 'DO + (lambda (form rename compare) + compare ;ignore + (if (syntax-match? '((* (IDENTIFIER EXPRESSION ? EXPRESSION)) + (+ EXPRESSION) + * FORM) + (cdr form)) + (let ((bindings (cadr form))) + `(,(rename 'LETREC) + ((,(rename 'DO-LOOP) + (,(rename 'LAMBDA) + ,(map car bindings) + (,(rename 'IF) ,(caaddr form) + ,(if (null? (cdaddr form)) + (unspecific-expression) + `(,(rename 'BEGIN) ,@(cdaddr form))) + (,(rename 'BEGIN) + ,@(cdddr form) + (,(rename 'DO-LOOP) + ,@(map (lambda (binding) + (if (null? (cddr binding)) + (car binding) + (caddr binding))) + bindings))))))) + (,(rename 'DO-LOOP) ,@(map cadr bindings)))) + (ill-formed-syntax form)))) + + (define-expander 'QUASIQUOTE + (lambda (form rename compare) + (define (descend-quasiquote x level return) + (cond ((pair? x) (descend-quasiquote-pair x level return)) + ((vector? x) (descend-quasiquote-vector x level return)) + (else (return 'QUOTE x)))) + (define (descend-quasiquote-pair x level return) + (cond ((not (and (pair? x) + (identifier? (car x)) + (pair? (cdr x)) + (null? (cddr x)))) + (descend-quasiquote-pair* x level return)) + ((compare (rename 'QUASIQUOTE) (car x)) + (descend-quasiquote-pair* x (+ level 1) return)) + ((compare (rename 'UNQUOTE) (car x)) + (if (zero? level) + (return 'UNQUOTE (cadr x)) + (descend-quasiquote-pair* x (- level 1) return))) + ((compare (rename 'UNQUOTE-SPLICING) (car x)) + (if (zero? level) + (return 'UNQUOTE-SPLICING (cadr x)) + (descend-quasiquote-pair* x (- level 1) return))) + (else + (descend-quasiquote-pair* x level return)))) + (define (descend-quasiquote-pair* x level return) + (descend-quasiquote + (car x) level + (lambda (car-mode car-arg) + (descend-quasiquote + (cdr x) level + (lambda (cdr-mode cdr-arg) + (cond ((and (eq? car-mode 'QUOTE) (eq? cdr-mode 'QUOTE)) + (return 'QUOTE x)) + ((eq? car-mode 'UNQUOTE-SPLICING) + (if (and (eq? cdr-mode 'QUOTE) (null? cdr-arg)) + (return 'UNQUOTE car-arg) + (return 'APPEND + (list car-arg + (finalize-quasiquote cdr-mode + cdr-arg))))) + ((and (eq? cdr-mode 'QUOTE) (list? cdr-arg)) + (return 'LIST + (cons (finalize-quasiquote car-mode car-arg) + (map (lambda (element) + (finalize-quasiquote 'QUOTE + element)) + cdr-arg)))) + ((eq? cdr-mode 'LIST) + (return 'LIST + (cons (finalize-quasiquote car-mode car-arg) + cdr-arg))) + (else + (return + 'CONS + (list (finalize-quasiquote car-mode car-arg) + (finalize-quasiquote cdr-mode cdr-arg)))))))))) + (define (descend-quasiquote-vector x level return) + (descend-quasiquote + (vector->list x) level + (lambda (mode arg) + (case mode + ((QUOTE) (return 'QUOTE x)) + ((LIST) (return 'VECTOR arg)) + (else + (return 'LIST->VECTOR + (list (finalize-quasiquote mode arg)))))))) + (define (finalize-quasiquote mode arg) + (case mode + ((QUOTE) `(,(rename 'QUOTE) ,arg)) + ((UNQUOTE) arg) + ((UNQUOTE-SPLICING) (syntax-error ",@ in illegal context" arg)) + (else `(,(rename mode) ,@arg)))) + (if (syntax-match? '(EXPRESSION) (cdr form)) + (descend-quasiquote (cadr form) 0 finalize-quasiquote) + (ill-formed-syntax form)))) + +;;; end MAKE-CORE-EXPANDER-MACROLOGY + ))) diff --git a/randinex.scm b/randinex.scm new file mode 100644 index 0000000..1c2b702 --- /dev/null +++ b/randinex.scm @@ -0,0 +1,99 @@ +;;;"randinex.scm" Pseudo-Random inexact real numbers for scheme. +;;; Copyright (C) 1991, 1993 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;This file is loaded by random.scm if inexact numbers are supported by +;the implementation. + +;;; Fixed sphere and normal functions from: Harald Hanche-Olsen + +(define random:float-radix + (+ 1 (exact->inexact random:MASK))) + +;;; This determines how many chunks will be neccessary to completely +;;; fill up an inexact real. +(define (random:size-float l x) + (cond ((= 1.0 (+ 1 x)) l) + ((= 4 l) l) + (else (random:size-float (+ l 1) (/ x random:float-radix))))) +(define random:chunks/float (random:size-float 0 1.0)) + +(define (random:uniform-chunk n state) + (if (= 1 n) + (/ (exact->inexact (random:chunk state)) + random:float-radix) + (/ (+ (random:uniform-chunk (- n 1) state) + (exact->inexact (random:chunk state))) + random:float-radix))) + +;;; Generate an inexact real between 0 and 1. +(define (random:uniform state) + (random:uniform-chunk random:chunks/float state)) + +;;; If x and y are independent standard normal variables, then with +;;; x=r*cos(t), y=r*sin(t), we find that t is uniformly distributed +;;; over [0,2*pi] and the cumulative distribution of r is +;;; 1-exp(-r^2/2). This latter means that u=exp(-r^2/2) is uniformly +;;; distributed on [0,1], so r=sqrt(-2 log u) can be used to generate r. + +(define (random:normal-vector! vect . args) + (let ((state (if (null? args) *random-state* (car args))) + (sum2 0)) + (let ((do! (lambda (k x) + (vector-set! vect k x) + (set! sum2 (+ sum2 (* x x)))))) + (do ((n (- (vector-length vect) 1) (- n 2))) + ((negative? n) sum2) + (let ((t (* 6.28318530717958 (random:uniform state))) + (r (sqrt (* -2 (log (random:uniform state)))))) + (do! n (* r (cos t))) + (if (positive? n) (do! (- n 1) (* r (sin t))))))))) + +(define random:normal + (let ((vect (make-vector 1))) + (lambda args + (apply random:normal-vector! vect args) + (vector-ref vect 0)))) + +;;; For the uniform distibution on the hollow sphere, pick a normal +;;; family and scale. + +(define (random:hollow-sphere! vect . args) + (let ((ms (sqrt (apply random:normal-vector! vect args)))) + (do ((n (- (vector-length vect) 1) (- n 1))) + ((negative? n)) + (vector-set! vect n (/ (vector-ref vect n) ms))))) + +;;; For the uniform distribution on the solid sphere, note that in +;;; this distribution the length r of the vector has cumulative +;;; distribution r^n; i.e., u=r^n is uniform [0,1], so r kan be +;;; generated as r=u^(1/n). + +(define (random:solid-sphere! vect . args) + (apply random:hollow-sphere! vect args) + (let ((r (expt (random:uniform (if (null? args) *random-state* (car args))) + (/ (vector-length vect))))) + (do ((n (- (vector-length vect) 1) (- n 1))) + ((negative? n)) + (vector-set! vect n (* r (vector-ref vect n)))))) + +(define (random:exp . args) + (let ((state (if (null? args) *random-state* (car args)))) + (- (log (random:uniform state))))) + +(require 'random) diff --git a/random.scm b/random.scm new file mode 100644 index 0000000..4f5a11d --- /dev/null +++ b/random.scm @@ -0,0 +1,101 @@ +;;;; "random.scm" Pseudo-Random number generator for scheme. +;;; Copyright (C) 1991, 1993 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'logical) + +(define random:tap 24) +(define random:size 55) + +(define (random:size-int l) + (let ((trial (string->number (make-string l #\f) 16))) + (if (and (exact? trial) (>= most-positive-fixnum trial)) + l + (random:size-int (- l 1))))) +(define random:chunk-size (* 4 (random:size-int 8))) + +(define random:MASK + (string->number (make-string (quotient random:chunk-size 4) #\f) 16)) + +(define *random-state* + '#( + "d909ef3e" "fd330ab3" "e33f7843" "76783fbd" "f3675fb3" + "b54ef879" "0be45590" "a6794679" "0bcd56d3" "fabcdef8" + "9cbd3efd" "3fd3efcd" "e064ef27" "dddecc08" "34444292" + "85444454" "4c519210" "c0366273" "54734567" "70abcddc" + "1bbdac53" "616c5a86" "a982efa9" "105996a0" "5f0cccba" + "1ea055e1" "fe2acd8d" "1891c1d4" "e6690270" "6912bccc" + "2678e141" "61222224" "907abcbb" "4ad6829b" "9cdd1404" + "57798841" "5b892496" "871c9cd1" "d1e67bda" "8b0a3233" + "578ef23f" "28274ef6" "823ef5ef" "845678c5" "e67890a5" + "5890abcb" "851fa9ab" "13efa13a" "b12278d6" "daf805ab" + "a0befc36" "0068a7b5" "e024fd90" "a7b690e2" "27f3571a" + 0)) + +(let ((random-strings *random-state*)) + (set! *random-state* (make-vector (+ random:size 1) 0)) + (let ((nibbles (quotient random:chunk-size 4))) + (do ((i 0 (+ i 1))) + ((= i random:size)) + (vector-set! + *random-state* i + (string->number (substring (vector-ref random-strings i) + 0 nibbles) + 16))))) + +;;; random:chunk returns an integer in the range of +;;; 0 to (- (expt 2 random:chunk-size) 1) +(define (random:chunk v) + (let* ((p (vector-ref v random:size)) + (ans (logical:logxor + (vector-ref v (modulo (- p random:tap) random:size)) + (vector-ref v p)))) + (vector-set! v p ans) + (vector-set! v random:size (modulo (- p 1) random:size)) + ans)) + +(define (random:random modu . args) + (let ((state (if (null? args) *random-state* (car args)))) + (if (exact? modu) + (do ((ilen 0 (+ 1 ilen)) + (s random:MASK + (+ random:MASK (* (+ 1 random:MASK) s)))) + ((>= s (- modu 1)) + (let ((slop (modulo (+ s (- 1 modu)) modu))) + (let loop ((n ilen) + (r (random:chunk state))) + (cond ((not (zero? n)) + (loop (+ -1 n) + (+ (* r (+ 1 random:MASK)) + (random:chunk state)))) + ((>= r slop) (modulo r modu)) + (else (loop ilen (random:chunk state)))))))) + + (* (random:uniform state) modu)))) +;;;random:uniform is in randinex.scm. It is needed only if inexact is +;;;supported. + +(define (random:make-random-state . args) + (let ((state (if (null? args) *random-state* (car args)))) + (list->vector (vector->list state)))) + +(define random random:random) +(define make-random-state random:make-random-state) + +(provide 'random) ;to prevent loops +(if (provided? 'inexact) (require 'random-inexact)) diff --git a/ratize.scm b/ratize.scm new file mode 100644 index 0000000..d8cad11 --- /dev/null +++ b/ratize.scm @@ -0,0 +1,13 @@ +;;;; "ratize.scm" Convert number to rational number + +(define (rational:simplest x y) + (define (sr x y) (let ((fx (floor x)) (fy (floor y))) + (cond ((not (< fx x)) fx) + ((= fx fy) (+ fx (/ (sr (/ (- y fy)) (/ (- x fx)))))) + (else (+ 1 fx))))) + (cond ((< y x) (rational:simplest y x)) + ((not (< x y)) (if (rational? x) x (slib:error))) + ((positive? x) (sr x y)) + ((negative? y) (- (sr (- y) (- x)))) + (else (if (and (exact? x) (exact? y)) 0 0.0)))) +(define (rationalize x e) (rational:simplest (- x e) (+ x e))) diff --git a/rdms.scm b/rdms.scm new file mode 100644 index 0000000..0fd4a2c --- /dev/null +++ b/rdms.scm @@ -0,0 +1,598 @@ +;;; "rdms.scm" rewrite 6 - the saga continues +; Copyright 1994 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(define rdms:catalog-name '*catalog-data*) +(define rdms:domains-name '*domains-data*) +(define rdms:columns-name '*columns*) + +(define catalog:init-cols + '((1 #t table-name #f atom) + (2 #f column-limit #f uint) + (3 #f coltab-name #f atom) + (4 #f bastab-id #f base-id) + (5 #f user-integrity-rule #f expression) + (6 #f view-procedure #f expression))) + +(define catalog:column-limit-pos 2) +(define catalog:coltab-name-pos 3) +(define catalog:bastab-id-pos 4) +(define catalog:integrity-rule-pos 5) +(define catalog:view-proc-pos 6) + +(define columns:init-cols + '((1 #t column-number #f uint) + (2 #f primary-key? #f boolean) + (3 #f column-name #f symbol) + (4 #f column-integrity-rule #f expression) + (5 #f domain-name #f domain))) + +(define columns:primary?-pos 2) +(define columns:name-pos 3) +(define columns:integrity-rule-pos 4) +(define columns:domain-name-pos 5) + +(define domains:init-cols + '((1 #t domain-name #f atom) + (2 #f foreign-table #f atom) + (3 #f domain-integrity-rule #f expression) + (4 #f type-id #f type) + (5 #f type-param #f expression))) + +(define domains:foreign-pos 2) +(define domains:integrity-rule-pos 3) +(define domains:type-id-pos 4) +(define domains:type-param-pos 5) + +(define domains:init-data + `((atom #f + (lambda (x) (or (not x) (symbol? x) (number? x))) + atom + #f) + (type #f + #f ;type checked when openning + symbol + #f) + (base-id #f + (lambda (x) (or (symbol? x) (number? x))) + base-id + #f) + (uint #f + (lambda (x) + (and (number? x) + (integer? x) + (not (negative? x)))) + integer + #f) + (expression #f #f expression #f) + (boolean #f boolean? boolean #f) + (symbol #f symbol? symbol #f) + (string #f string? string #f) + (domain ,rdms:domains-name #f atom #f))) + +(define (rdms:warn identifier msg obj) + (display identifier) (display #\ ) (display msg) (write obj) (newline)) +(define rdms:error slib:error) + +(define (make-relational-system base) + (define basic + (lambda (name) + (let ((meth (base name))) + (cond ((not meth) (rdms:error 'make-relational-system + "essential method missing for:" name))) + meth))) + + (define (desc-row-type row) + (let ((domain (assq (car (cddddr row)) domains:init-data))) + (and domain (cadddr domain)))) + + (let ((make-base (base 'make-base)) + (open-base (basic 'open-base)) + (write-base (base 'write-base)) + (sync-base (base 'sync-base)) + (close-base (basic 'close-base)) + (base:supported-type? (basic 'supported-type?)) + (base:supported-key-type? (basic 'supported-key-type?)) + (base:make-table (base 'make-table)) + (base:open-table (basic 'open-table)) + (base:kill-table (base 'kill-table)) + (present? (basic 'present?)) + (base:ordered-for-each-key (basic 'ordered-for-each-key)) + (base:for-each-primary-key (basic 'for-each-key)) + (base:map-primary-key (basic 'map-key)) + (base:catalog-id (basic 'catalog-id)) + (cat:keyify-1 ((basic 'make-keyifier-1) + (desc-row-type (assv 1 catalog:init-cols)))) + (itypes + (lambda (rows) + (map (lambda (row) + (let ((domrow (assq (car (cddddr row)) domains:init-data))) + (cond (domrow (cadddr domrow)) + (else (rdms:error 'itypes "type not found for:" + (car (cddddr row))))))) + rows)))) + + (define (init-tab lldb id descriptor rows) + (let ((han (base:open-table lldb id 1 (itypes descriptor))) + (keyify-1 + ((base 'make-keyifier-1) (desc-row-type (assv 1 descriptor)))) + (putter ((basic 'make-putter) 1 (itypes descriptor)))) + (for-each (lambda (row) (putter han (keyify-1 (car row)) (cdr row))) + rows))) + + (define cat:get-row + (let ((cat:getter ((basic 'make-getter) 1 (itypes catalog:init-cols)))) + (lambda (bastab key) + (cat:getter bastab (cat:keyify-1 key))))) + + (define dom:get-row + (let ((dom:getter ((basic 'make-getter) 1 (itypes domains:init-cols))) + (dom:keyify-1 ((basic 'make-keyifier-1) + (desc-row-type (assv 1 domains:init-cols))))) + (lambda (bastab key) + (dom:getter bastab (dom:keyify-1 key))))) + + (define des:get-row + (let ((des:getter ((basic 'make-getter) 1 (itypes columns:init-cols))) + (des:keyify-1 ((basic 'make-keyifier-1) + (desc-row-type (assv 1 columns:init-cols))))) + (lambda (bastab key) + (des:getter bastab (des:keyify-1 key))))) + + (define (create-database filename) + (cond ((and filename (file-exists? filename)) + (rdms:warn 'create-database "file exists:" filename))) + (let* ((lldb (make-base filename 1 (itypes catalog:init-cols))) + (cattab (and lldb (base:open-table lldb base:catalog-id 1 + (itypes catalog:init-cols))))) + (cond + ((not lldb) (rdms:error 'make-base "failed.") #f) + ((not cattab) (rdms:error 'make-base "catalog missing.") + (close-base lldb) + #f) + (else + (let ((desdes-id (base:make-table lldb 1 (itypes columns:init-cols))) + (domdes-id (base:make-table lldb 1 (itypes columns:init-cols))) + (catdes-id (base:make-table lldb 1 (itypes columns:init-cols))) + (domtab-id (base:make-table lldb 1 (itypes domains:init-cols))) + ) + (cond + ((not (and catdes-id domdes-id domtab-id desdes-id)) + (rdms:error 'create-database "make-table failed.") + (close-base lldb) + #f) + (else + (init-tab lldb desdes-id columns:init-cols columns:init-cols) + (init-tab lldb domdes-id columns:init-cols domains:init-cols) + (init-tab lldb catdes-id columns:init-cols catalog:init-cols) + (init-tab lldb domtab-id domains:init-cols domains:init-data) + (init-tab + lldb base:catalog-id catalog:init-cols + `((*catalog-desc* 5 ,rdms:columns-name ,catdes-id #f #f) + (*domains-desc* 5 ,rdms:columns-name ,domdes-id #f #f) + (,rdms:catalog-name 6 *catalog-desc* ,base:catalog-id #f #f) + (,rdms:domains-name 5 *domains-desc* ,domtab-id #f #f) + (,rdms:columns-name 5 ,rdms:columns-name ,desdes-id #f #f))) + (init-database + filename #t lldb cattab + (base:open-table lldb domtab-id 1 (itypes domains:init-cols)) + #f)))))))) + + (define (base:catalog->domains lldb base:catalog) + (let ((cat:row (cat:get-row base:catalog rdms:domains-name))) + (and cat:row + (base:open-table lldb + (list-ref cat:row (+ -2 catalog:bastab-id-pos)) + 1 (itypes domains:init-cols))))) + + (define (open-database filename mutable) + (let* ((lldb (open-base filename mutable)) + (base:catalog + (and lldb (base:open-table lldb base:catalog-id + 1 (itypes catalog:init-cols)))) + (base:domains + (and base:catalog (base:catalog->domains lldb base:catalog)))) + (cond + ((not lldb) #f) + ((not base:domains) (close-base lldb) #f) + (else (init-database + filename mutable lldb base:catalog base:domains #f))))) + + (define (init-database rdms:filename mutable lldb + base:catalog base:domains rdms:catalog) + + (define (write-database filename) + (write-base lldb filename) + (set! rdms:filename filename)) + + (define (close-database) + (close-base lldb) + (set! rdms:filename #f) + (set! base:catalog #f) + (set! base:domains #f) + (set! rdms:catalog #f)) + + (define row-ref (lambda (row pos) (list-ref row (+ -2 pos)))) + (define row-eval (lambda (row pos) + (let ((ans (list-ref row (+ -2 pos)))) + (and ans (slib:eval ans))))) + + (define (open-table table-name writable) + (define cat:row (cat:get-row base:catalog table-name)) + (cond ((and writable (not mutable)) + (rdms:error "can't open-table for writing:" table-name))) + (let ((column-limit (row-ref cat:row catalog:column-limit-pos)) + (desc-table + (base:open-table + lldb + (row-ref (cat:get-row + base:catalog + (row-ref cat:row catalog:coltab-name-pos)) + catalog:bastab-id-pos) + 1 (itypes columns:init-cols))) + (base-table #f) + (base:get #f) + (primary-limit 1) + (column-name-alist '()) + (column-foreign-list '()) + (column-domain-list '()) + (column-type-list '()) + (export-alist '()) + (cirs '()) + (dirs '()) + (list->key #f) + (key->list #f)) + + (if (not desc-table) + (rdms:error "descriptor table doesn't exist for:" table-name)) + (do ((ci column-limit (+ -1 ci))) + ((zero? ci)) + (let* ((des:row (des:get-row desc-table ci)) + (column-name (row-ref des:row columns:name-pos)) + (column-domain (row-ref des:row columns:domain-name-pos))) + (set! cirs + (cons (row-eval des:row columns:integrity-rule-pos) cirs)) + (set! column-name-alist + (cons (cons column-name ci) column-name-alist)) + (cond + (column-domain + (let ((dom:row (dom:get-row base:domains column-domain))) + (set! dirs + (cons (row-eval dom:row domains:integrity-rule-pos) + dirs)) + (set! column-type-list + (cons (row-ref dom:row domains:type-id-pos) + column-type-list)) + (set! column-domain-list + (cons column-domain column-domain-list)) + (set! column-foreign-list + (cons + (let ((foreign-name + (row-ref dom:row domains:foreign-pos))) + (cond + ((or (not foreign-name) + (eq? foreign-name table-name)) #f) + (else + (let* ((tab (open-table foreign-name #f)) + (p? (and tab (tab 'get 1)))) + (cond + ((not tab) + (rdms:error "foreign key table missing for:" + foreign-name)) + ((not (= (tab 'primary-limit) 1)) + (rdms:error "foreign key table wrong type:" + foreign-name)) + (else p?)))))) + column-foreign-list)))) + (else + (rdms:error "missing domain for column:" ci column-name))) + (cond + ((row-ref des:row columns:primary?-pos) + (set! primary-limit (max primary-limit ci)) + (cond + ((base:supported-key-type? (car column-type-list))) + (else (rdms:error "key type not supported by base tables:" + (car column-type-list))))) + ((base:supported-type? (car column-type-list))) + (else (rdms:error "type not supported by base tables:" + (car column-type-list)))))) + (set! base-table + (base:open-table lldb (row-ref cat:row catalog:bastab-id-pos) + primary-limit column-type-list)) + (set! base:get ((basic 'make-getter) primary-limit column-type-list)) + (set! list->key + ((basic 'make-list-keyifier) primary-limit column-type-list)) + (set! key->list + ((basic 'make-key->list) primary-limit column-type-list)) + (let ((export-method + (lambda (name proc) + (set! export-alist + (cons (cons name proc) export-alist)))) + (generalize-to-table + (lambda (operation) + (lambda () + (base:for-each-primary-key base-table operation)))) + (accumulate-over-table + (lambda (operation) + (lambda () (base:map-primary-key base-table operation)))) + (ckey:retrieve ;ckey gets whole row (assumes exists) + (if (= primary-limit column-limit) key->list + (lambda (ckey) (append (key->list ckey) + (base:get base-table ckey)))))) + (export-method + 'row:retrieve + (if (= primary-limit column-limit) + (lambda keys + (let ((ckey (list->key keys))) + (and (present? base-table ckey) keys))) + (lambda keys + (let ((vals (base:get base-table (list->key keys)))) + (and vals (append keys vals)))))) + (export-method 'row:retrieve* + (accumulate-over-table + (if (= primary-limit column-limit) key->list + ckey:retrieve))) + (export-method + 'for-each-row + (let ((r (if (= primary-limit column-limit) key->list + ckey:retrieve))) + (lambda (proc) (base:ordered-for-each-key + base-table (lambda (ckey) (proc (r ckey))))))) + (cond + ((and mutable writable) + (letrec + ((combine-primary-keys + (cond + ((and (= primary-limit column-limit) + (> primary-limit 0)) + list->key) + ((eq? list->key car) list->key) + (else + (case primary-limit + ((1) (let ((keyify-1 ((base 'make-keyifier-1) + (car column-type-list)))) + (lambda (row) (keyify-1 (car row))))) + ((2) (lambda (row) + (list->key (list (car row) (cadr row))))) + ((3) (lambda (row) + (list->key (list (car row) (cadr row) + (caddr row))))) + ((4) (lambda (row) + (list->key + (list (car row) (cadr row) + (caddr row) (cadddr row))))) + (else (rdms:error 'combine-primary-keys + "bad number of primary keys" + primary-limit)))))) + (uir (row-eval cat:row catalog:integrity-rule-pos)) + (check-rules + (lambda (row) + (if (= column-limit (length row)) #t + (rdms:error "bad row length:" row)) + (for-each + (lambda (cir dir value column-name column-domain + foreign) + (cond + ((and dir (not (dir value))) + (rdms:error "violated domain integrity rule:" + table-name column-name + column-domain value)) + ((and cir (not (cir value))) + (rdms:error "violated column integrity rule:" + table-name column-name value)) + ((and foreign (not (foreign value))) + (rdms:error "foreign key missing:" + table-name column-name value)))) + cirs dirs row column-name-alist column-domain-list + column-foreign-list) + (cond ((and uir (not (uir row))) + (rdms:error "violated user integrity rule:" + row))))) + (putter + ((basic 'make-putter) primary-limit column-type-list)) + (row:insert + (lambda (row) + (check-rules row) + (let ((ckey (combine-primary-keys row))) + (if (present? base-table ckey) + (rdms:error 'row:insert "row present:" row)) + (putter base-table ckey + (list-tail row primary-limit))))) + (row:update + (lambda (row) + (check-rules row) + (putter base-table (combine-primary-keys row) + (list-tail row primary-limit))))) + + (export-method 'row:insert row:insert) + (export-method 'row:insert* + (lambda (rows) (for-each row:insert rows))) + (export-method 'row:update row:update) + (export-method 'row:update* + (lambda (rows) (for-each row:update rows)))) + + (letrec ((base:delete (basic 'delete)) + (ckey:remove (lambda (ckey) + (let ((r (ckey:retrieve ckey))) + (and r (base:delete base-table ckey)) + r)))) + (export-method 'row:remove + (lambda keys + (let ((ckey (list->key keys))) + (and (present? base-table ckey) + (ckey:remove ckey))))) + (export-method 'row:delete + (lambda keys + (base:delete base-table (list->key keys)))) + (export-method 'row:remove* + (accumulate-over-table ckey:remove)) + (export-method 'row:delete* + (generalize-to-table + (lambda (ckey) (base:delete base-table ckey)))) + (export-method 'close-table + (lambda () (set! base-table #f) + (set! desc-table #f) + (set! export-alist #f)))))) + + (export-method 'column-names (map car column-name-alist)) + (export-method 'column-foreigns column-foreign-list) + (export-method 'column-domains column-domain-list) + (export-method 'column-types column-type-list) + (export-method 'primary-limit primary-limit) + + (let ((translate-column + (lambda (column) + ;;(print 'translate-column column column-name-alist) + (let ((colp (assq column column-name-alist))) + (cond (colp (cdr colp)) + ((and (number? column) + (integer? column) + (<= 1 column column-limit)) + column) + (else (rdms:error "column not in table:" + column table-name))))))) + (lambda args + (cond + ((null? args) #f) + ((null? (cdr args)) + (let ((pp (assq (car args) export-alist))) + (and pp (cdr pp)))) + ((not (null? (cddr args))) + (rdms:error "too many arguments to methods:" args)) + (else + (let ((ci (translate-column (cadr args)))) + (cond + ((<= ci primary-limit) ;primary-key? + (let ((key-extractor + ((base 'make-key-extractor) + primary-limit column-type-list ci))) + (case (car args) + ((get) (lambda keys + (and (present? base-table (list->key keys)) + (list-ref keys (+ -1 ci))))) + ((get*) (lambda () + (base:map-primary-key + base-table + (lambda (ckey) (key-extractor ckey))))) + (else #f)))) + (else + (let ((index (- ci (+ 1 primary-limit)))) + (case (car args) + ((get) (lambda keys + (let ((row (base:get base-table + (list->key keys)))) + (and row (list-ref row index))))) + ((get*) (lambda () + (base:map-primary-key + base-table + (lambda (ckey) + (list-ref (base:get base-table ckey) + index))))) + (else #f))))))))))))) + + (define create-table + (and + mutable + (lambda (table-name . desc) + (if (not rdms:catalog) + (set! rdms:catalog (open-table rdms:catalog-name #t)) #f) + (cond + ((table-exists? table-name) + (rdms:error "table already exists:" table-name) #f) + ((null? desc) + (let ((colt-id + (base:make-table lldb 1 (itypes columns:init-cols)))) + ((rdms:catalog 'row:insert) + (list table-name + (length columns:init-cols) + ((rdms:catalog 'get 'coltab-name) + rdms:columns-name) + colt-id + #f + #f))) + (open-table table-name #t)) + ((null? (cdr desc)) + (set! desc (car desc)) + (let ((colt-id ((rdms:catalog 'get 'bastab-id) desc))) + (cond + (colt-id + (let ((coltable (open-table desc #f)) + (types '()) + (prilimit 0) + (colimit 0) + (colerr #f)) + (for-each (lambda (n p d) + (if (number? n) (set! colimit (max colimit n)) + (set! colerr #t)) + (if p (set! prilimit (+ 1 prilimit)) #f) + (set! types + (cons (dom:get-row base:domains d) + types))) + ((coltable 'get* 'column-number)) + ((coltable 'get* 'primary-key?)) + ((coltable 'get* 'domain-name))) + (cond (colerr (rdms:error "some column lacks a number.") #f) + ((or (< prilimit 1) + (and (> prilimit 4) + (not (= prilimit colimit)))) + (rdms:error "unreasonable number of primary keys:" + prilimit)) + (else + ((rdms:catalog 'row:insert) + (list table-name colimit desc + (base:make-table lldb prilimit types) #f #f)) + (open-table table-name #t))))) + (else + (rdms:error "table descriptor not found for:" desc) #f)))) + (else (rdms:error 'create-table "too many args:" + (cons table-name desc)) + #f))))) + + (define (table-exists? table-name) + (present? base:catalog (cat:keyify-1 table-name))) + + (define delete-table + (and mutable + (lambda (table-name) + (if (not rdms:catalog) + (set! rdms:catalog (open-table rdms:catalog-name #t)) #f) + (let ((table (open-table table-name #t)) + (row ((rdms:catalog 'row:remove) table-name))) + (and row (base:kill-table + lldb + (list-ref row (+ -1 catalog:bastab-id-pos)) + (table 'primary-limit) + (table 'column-type-list)) + row))))) + + (lambda (operation-name) + (case operation-name + ((close-database) close-database) + ((write-database) write-database) + ((open-table) open-table) + ((delete-table) delete-table) + ((create-table) create-table) + ((table-exists?) table-exists?) + (else #f))) + ) + (lambda (operation-name) + (case operation-name + ((create-database) create-database) + ((open-database) open-database) + (else #f))) + )) diff --git a/recobj.scm b/recobj.scm new file mode 100644 index 0000000..caf55a6 --- /dev/null +++ b/recobj.scm @@ -0,0 +1,54 @@ +;;; "recobj.scm" Records implemented as objects. +;;;From: whumeniu@datap.ca (Wade Humeniuk) + +(require 'object) + +(define record-type-name (make-generic-method)) +(define record-accessor (make-generic-method)) +(define record-modifier (make-generic-method)) +(define record? (make-generic-predicate)) +(define record-constructor (make-generic-method)) + +(define (make-record-type type-name field-names) + (define self (make-object)) + + (make-method! self record-type-name + (lambda (self) + type-name)) + (make-method! self record-accessor + (lambda (self field-name) + (let ((index (comlist:position field-name field-names))) + (if (not index) + (slib:error "record-accessor: invalid field-name argument." + field-name)) + (lambda (obj) + (record-accessor obj index))))) + + (make-method! self record-modifier + (lambda (self field) + (let ((index (comlist:position field field-names))) + (if (not index) + (slib:error "record-accessor: invalid field-name argument." + field-name)) + (lambda (obj newval) + (record-modifier obj index newval))))) + + (make-method! self record? (lambda (self) #t)) + + (make-method! self record-constructor + (lambda (class . field-values) + (let ((values (apply vector field-values))) + (define self (make-object)) + (make-method! self record-accessor + (lambda (self index) + (vector-ref values index))) + (make-method! self record-modifier + (lambda (self index newval) + (vector-set! values index newval))) + (make-method! self record-type-name + (lambda (self) (record-type-name class))) + self))) + self) + +(provide 'record-object) +(provide 'record)
\ No newline at end of file diff --git a/record.scm b/record.scm new file mode 100644 index 0000000..555d3ea --- /dev/null +++ b/record.scm @@ -0,0 +1,211 @@ +; "record.scm" record data types +; Written by David Carlton, carlton@husc.harvard.edu. +; Re-Written by Aubrey Jaffer, jaffer@ai.mit.edu +; +; This code is in the public domain. + +; Implements `record' data structures for Scheme. Using only the +; opacity of procedures, makes record datatypes and +; record-type-descriptors disjoint from R4RS types and each other, and +; prevents forgery and corruption (modification without using +; RECORD-MODIFIER) of records. + +(require 'common-list-functions) + +(define vector? vector?) +(define vector-ref vector-ref) +(define vector-set! vector-set!) +(define vector-fill! vector-fill!) +(define vector->list vector->list) + +(define record-modifier #f) +(define record-accessor #f) +(define record-constructor #f) +(define record-predicate #f) +(define make-record-type #f) + +(let (;; Need to close these to keep magic-cookie hidden. + (make-vector make-vector) + (vector vector) + + ;; We have to wrap these to keep magic-cookie hidden. + (vect? vector?) + (vect-ref vector-ref) + (vect->list vector->list) + + ;; Need to wrap these to protect record data from being corrupted. + (vect-set! vector-set!) + (vect-fill! vector-fill!) + + (nvt "of non-vector type") + ) + (letrec + (;; Tag to identify rtd's. (A record is identified by the rtd + ;; that begins it.) + (magic-cookie (cons 'rtd '())) + (rtd? (lambda (object) + (and (vect? object) + (not (= (vector-length object) 0)) + (eq? (rtd-tag object) magic-cookie)))) + (rec? (lambda (obj) + (and (vect? obj) + (>= (vector-length obj) 1) + (or (eq? magic-cookie (rec-rtd obj)) + (rtd? (rec-rtd obj)))))) + + (vec:error + (lambda (proc-name msg obj) + (slib:error proc-name msg + (cond ((rtd? obj) 'rtd) + ((rec? obj) (rtd-name (rec-rtd obj))) + (else obj))))) + + ;; Internal accessor functions. No error checking. + (rtd-tag (lambda (x) (vect-ref x 0))) + (rtd-name (lambda (rtd) (vect-ref rtd 1))) + (rtd-fields (lambda (rtd) (vect-ref rtd 3))) + ;; rtd-vfields is padded out to the length of the vector, which is 1 + ;; more than the number of fields + (rtd-vfields (lambda (rtd) (cons #f (rtd-fields rtd)))) + ;; rtd-length is the length of the vector. + (rtd-length (lambda (rtd) (vect-ref rtd 4))) + + (rec-rtd (lambda (x) (vect-ref x 0))) + + (make-rec-type + (lambda (type-name field-names) + (if (not (string? type-name)) + (slib:error 'make-record-type "non-string type-name argument." + type-name)) + (if (or (and (list? field-names) (comlist:has-duplicates? field-names)) + (comlist:notevery symbol? field-names)) + (slib:error 'make-record-type "illegal field-names argument." + field-names)) + (let* ((augmented-length (+ 1 (length field-names))) + (rtd (vector magic-cookie + type-name + '() + field-names + augmented-length + #f + #f))) + (vect-set! rtd 5 + (lambda (x) + (and (vect? x) + (= (vector-length x) augmented-length) + (eq? (rec-rtd x) rtd)))) + (vect-set! rtd 6 + (lambda (x) + (and (vect? x) + (>= (vector-length x) augmented-length) + (eq? (rec-rtd x) rtd) + #t))) + rtd))) + + (rec-predicate + (lambda (rtd) + (if (not (rtd? rtd)) + (slib:error 'record-predicate "invalid argument." rtd)) + (vect-ref rtd 5))) + + (rec-constructor + (lambda (rtd . field-names) + (if (not (rtd? rtd)) + (slib:error 'record-constructor "illegal rtd argument." rtd)) + (if (or (null? field-names) + (equal? field-names (rtd-fields rtd))) + (let ((rec-length (- (rtd-length rtd) 1))) + (lambda elts + (if (= (length elts) rec-length) #t + (slib:error 'record-constructor + (rtd-name rtd) + "wrong number of arguments.")) + (apply vector rtd elts))) + (let ((rec-vfields (rtd-vfields rtd)) + (corrected-rec-length (rtd-length rtd)) + (field-names (car field-names))) + (if (or (and (list? field-names) (comlist:has-duplicates? field-names)) + (comlist:notevery (lambda (x) (memq x rec-vfields)) + field-names)) + (slib:error + 'record-constructor "invalid field-names argument." + (cdr rec-vfields))) + (let ((field-length (length field-names)) + (offsets + (map (lambda (field) (comlist:position field rec-vfields)) + field-names))) + (lambda elts + (if (= (length elts) field-length) #t + (slib:error 'record-constructor + (rtd-name rtd) + "wrong number of arguments.")) + (let ((result (make-vector corrected-rec-length))) + (vect-set! result 0 rtd) + (for-each (lambda (offset elt) + (vect-set! result offset elt)) + offsets + elts) + result))))))) + + (rec-accessor + (lambda (rtd field-name) + (if (not (rtd? rtd)) + (slib:error 'record-accessor "invalid rtd argument." rtd)) + (let ((index (comlist:position field-name (rtd-vfields rtd))) + (augmented-length (rtd-length rtd))) + (if (not index) + (slib:error 'record-accessor "invalid field-name argument." + field-name)) + (lambda (x) + (if (and (vect? x) + (>= (vector-length x) augmented-length) + (eq? rtd (rec-rtd x))) + #t + (slib:error 'record-accessor "wrong record type." x "not" rtd)) + (vect-ref x index))))) + + (rec-modifier + (lambda (rtd field-name) + (if (not (rtd? rtd)) + (slib:error 'record-modifier "invalid rtd argument." rtd)) + (let ((index (comlist:position field-name (rtd-vfields rtd))) + (augmented-length (rtd-length rtd))) + (if (not index) + (slib:error 'record-modifier "invalid field-name argument." + field-name)) + (lambda (x y) + (if (and (vect? x) + (>= (vector-length x) augmented-length) + (eq? rtd (rec-rtd x))) + #t + (slib:error 'record-modifier "wrong record type." x "not" rtd)) + (vect-set! x index y))))) + ) + + (set! vector? (lambda (obj) (and (not (rec? obj)) (vector? obj)))) + (set! vector-ref + (lambda (vector k) + (cond ((rec? vector) + (vec:error 'vector-ref nvt vector)) + (else (vect-ref vector k))))) + (set! vector->list + (lambda (vector k) + (cond ((rec? vector) + (vec:error 'vector->list nvt vector)) + (else (vect->list vector k))))) + (set! vector-set! + (lambda (vector k obj) + (cond ((rec? vector) + (vec:error 'vector-set! nvt vector)) + (else (vect-set! vector k obj))))) + (set! vector-fill! + (lambda (vector fill) + (cond ((rec? vector) + (vec:error 'vector-fill! nvt vector)) + (else (vect-fill! vector fill))))) + (set! record-modifier rec-modifier) + (set! record-accessor rec-accessor) + (set! record-constructor rec-constructor) + (set! record-predicate rec-predicate) + (set! make-record-type make-rec-type) + )) diff --git a/repl.scm b/repl.scm new file mode 100644 index 0000000..f51f493 --- /dev/null +++ b/repl.scm @@ -0,0 +1,92 @@ +; "repl.scm", read-eval-print-loop for Scheme +; Copyright (c) 1993, Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'dynamic-wind) +(define (repl:quit) (slib:error "not in repl:repl")) + +(define (repl:top-level repl:eval) + (repl:repl (lambda () (display "> ") + (force-output (current-output-port)) + (read)) + repl:eval + (lambda objs + (cond ((null? objs)) + (else + (write (car objs)) + (for-each (lambda (obj) + (display " ;") (newline) (write obj)) + (cdr objs)))) + (newline)))) + +(define (repl:repl repl:read repl:eval repl:print) + (let* ((old-quit repl:quit) + (old-error slib:error) + (old-eval slib:eval) + (old-load load) + (repl:load (lambda (<pathname>) + (call-with-input-file <pathname> + (lambda (port) + (let ((old-load-pathname *load-pathname*)) + (set! *load-pathname* <pathname>) + (do ((o (read port) (read port))) + ((eof-object? o)) + (repl:eval o)) + (set! *load-pathname* old-load-pathname)))))) + (repl:restart #f) + (values? (provided? 'values)) + (has-char-ready? (provided? 'char-ready?)) + (repl:error (lambda args (require 'debug) (apply qpn args) + (repl:restart #f)))) + (dynamic-wind + (lambda () + (set! load repl:load) + (set! slib:eval repl:eval) + (set! slib:error repl:error) + (set! repl:quit + (lambda () (let ((cont repl:restart)) + (set! repl:restart #f) + (cont #t))))) + (lambda () + (do () ((call-with-current-continuation + (lambda (cont) + (set! repl:restart cont) + (do ((obj (repl:read) (repl:read))) + ((eof-object? obj) (repl:quit)) + (cond + (has-char-ready? + (let loop () + (cond ((char-ready?) + (let ((c (peek-char))) + (cond + ((eof-object? c)) + ((char=? #\newline c) (read-char)) + ((char-whitespace? c) + (read-char) (loop)) + (else (newline))))))))) + (if values? + (call-with-values (lambda () (repl:eval obj)) + repl:print) + (repl:print (repl:eval obj))))))))) + (lambda () (cond (repl:restart + (display ">>ERROR<<") (newline) + (repl:restart #f))) + (set! load old-load) + (set! slib:eval old-eval) + (set! slib:error old-error) + (set! repl:quit old-quit))))) diff --git a/report.scm b/report.scm new file mode 100644 index 0000000..64f4d46 --- /dev/null +++ b/report.scm @@ -0,0 +1,116 @@ +;;; "report.scm" relational-database-utility +; Copyright 1995 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;;; Considerations for report generation: +; * columnar vs. fixed-multi-line vs. variable-multi-line +; * overflow lines within column boundaries. +; * break overflow across page? +; * Page headers and footers (need to know current/previous record-number +; and next record-number). +; * Force page break on general expression (needs next row as arg). +; * Hierachical reports. + +;================================================================ + +(require 'format) +(require 'database-utilities) + +(define (dbutil:database arg) + (cond ((procedure? arg) arg) + ((string? arg) (dbutil:open-database arg)) + ((symbol? arg) (slib:eval arg)) + (else (slib:error "can't coerce to database: " arg)))) + +(define (dbutil:table arg) + (cond ((procedure? arg) arg) + ((and (list? arg) (= 2 (length arg))) + (((dbutil:database (car arg)) 'open-table) (cadr arg) #f)))) + +(define (dbutil:print-report table header reporter footer . args) + (define output-port (and (pair? args) (car args))) + (define page-height (and (pair? args) (pair? (cdr args)) (cadr args))) + (define minimum-break + (and (pair? args) (pair? (cdr args)) (pair? (cddr args)) (caddr args))) + (set! table (dbutil:table table)) + ((lambda (fun) + (cond ((output-port? output-port) + (fun output-port)) + ((string? output-port) + (call-with-output-file output-port fun)) + ((or (boolean? output-port) (null? output-port)) + (fun (current-output-port))) + (else (slib:error "can't coerce to output-port: " arg)))) + (lambda (output-port) + (set! page-height (or page-height (output-port-height output-port))) + (set! minimum-break (or minimum-break 0)) + (let ((output-page 0) + (output-line 0) + (nth-newline-index + (lambda (str n) + (define len (string-length str)) + (do ((i 0 (+ i 1))) + ((or (zero? n) (> i len)) (+ -1 i)) + (cond ((char=? #\newline (string-ref str i)) + (set! n (+ -1 n))))))) + (count-newlines + (lambda (str) + (define cnt 0) + (do ((i (+ -1 (string-length str)) (+ -1 i))) + ((negative? i) cnt) + (cond ((char=? #\newline (string-ref str i)) + (set! cnt (+ 1 cnt))))))) + (format (let ((oformat format)) + (lambda (dest fmt arg) + (cond ((not (procedure? fmt)) (oformat dest fmt arg)) + ((output-port? dest) (fmt dest arg)) + ((eq? #t dest) (fmt (current-output-port) arg)) + ((eq? #f dest) (call-with-output-string + (lambda (port) (fmt port arg)))) + (else (oformat dest fmt arg))))))) + (define column-names (table 'column-names)) + (define (do-header) + (let ((str (format #f header column-names))) + (display str output-port) + (set! output-line (count-newlines str)))) + (define (do-lines str inc) + (cond + ((< (+ output-line inc) page-height) + (display str output-port) + (set! output-line (+ output-line inc))) + (else ;outputting footer + (cond ((and (not (zero? minimum-break)) + (> cnt (* 2 minimum-break)) + (> (- page-height output-line) minimum-break)) + (let ((break (nth-newline-index + str (- page-height output-line)))) + (display (substring str 0 (+ 1 break) output-port)) + (set! str (substring str (+ 1 break) (string-length str))) + (set! inc (- inc (- page-height output-line)))))) + (format output-port footer column-names) + (display slib:form-feed output-port) + (set! output-page (+ 1 output-page)) + (do-header) + (do-lines str inc)))) + + (do-header) + ((table 'for-each-row) + (lambda (row) + (let ((str (format #f reporter row))) + (do-lines str (count-newlines str))))) + output-page)))) diff --git a/require.scm b/require.scm new file mode 100644 index 0000000..d1ebe9a --- /dev/null +++ b/require.scm @@ -0,0 +1,348 @@ +;;;; Implementation of VICINITY and MODULES for Scheme +;Copyright (C) 1991, 1992, 1993, 1994 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(define *SLIB-VERSION* "2a6") + +;;; Standardize msdos -> ms-dos. +(define software-type + (cond ((eq? 'msdos (software-type)) + (lambda () 'ms-dos)) + (else software-type))) + +(define (user-vicinity) + (case (software-type) + ((VMS) "[.]") + (else ""))) + +(define program-vicinity + (let ((*vicinity-suffix* + (case (software-type) + ((AMIGA) '(#\: #\/)) + ((MS-DOS WINDOWS ATARIST OS/2) '(#\\ #\/)) + ((MACOS THINKC) '(#\:)) + ((NOSVE) '(#\: #\.)) + ((UNIX COHERENT) '(#\/)) + ((VMS) '(#\: #\]))))) + (lambda () + (let loop ((i (- (string-length *load-pathname*) 1))) + (cond ((negative? i) "") + ((memv (string-ref *load-pathname* i) *vicinity-suffix*) + (substring *load-pathname* 0 (+ i 1))) + (else (loop (- i 1)))))))) + +(define sub-vicinity + (case (software-type) + ((VMS) + (lambda + (vic name) + (let ((l (string-length vic))) + (if (or (zero? (string-length vic)) + (not (char=? #\] (string-ref vic (- l 1))))) + (string-append vic "[" name "]") + (string-append (substring vic 0 (- l 1)) + "." name "]"))))) + (else + (let ((*vicinity-suffix* + (case (software-type) + ((NOSVE) ".") + ((UNIX COHERENT AMIGA) "/") + ((MACOS THINKC) ":") + ((MS-DOS WINDOWS ATARIST OS/2) "\\")))) + (lambda (vic name) + (string-append vic name *vicinity-suffix*)))))) + +(define (make-vicinity <pathname>) <pathname>) + +(define *catalog* + (map + (lambda (p) + (if (symbol? (cdr p)) p + (cons + (car p) + (if (pair? (cdr p)) + (cons + (cadr p) + (in-vicinity (library-vicinity) (cddr p))) + (in-vicinity (library-vicinity) (cdr p)))))) + '( + (rev4-optional-procedures . "sc4opt") + (rev2-procedures . "sc2") + (multiarg/and- . "mularg") + (multiarg-apply . "mulapply") + (rationalize . "ratize") + (transcript . "trnscrpt") + (with-file . "withfile") + (dynamic-wind . "dynwind") + (dynamic . "dynamic") + (fluid-let macro . "fluidlet") + (alist . "alist") + (hash . "hash") + (sierpinski . "sierpinski") + (soundex . "soundex") + (hash-table . "hashtab") + (logical . "logical") + (random . "random") + (random-inexact . "randinex") + (modular . "modular") + (primes . "primes") + (factor . "factor") + (charplot . "charplot") + (sort . "sort") + (tsort . topological-sort) + (topological-sort . "tsort") + (common-list-functions . "comlist") + (tree . "tree") + (format . "format") + (format-inexact . "formatfl") + (generic-write . "genwrite") + (pretty-print . "pp") + (pprint-file . "ppfile") + (object->string . "obj2str") + (string-case . "strcase") + (stdio . "stdio") + (printf . "printf") + (scanf . "scanf") + (line-i/o . "lineio") + (string-port . "strport") + (getopt . "getopt") + (debug . "debug") + (qp . "qp") + (break defmacro . "break") + (trace defmacro . "trace") +; (eval . "eval") + (record . "record") + (promise . "promise") + (synchk . "synchk") + (defmacroexpand . "defmacex") + (macro-by-example defmacro . "mbe") + (syntax-case . "scainit") + (syntactic-closures . "scmacro") + (macros-that-work . "macwork") + (macro . macros-that-work) + (object . "object") + (record-object . "recobj") + (yasos macro . "yasyn") + (oop . yasos) + (collect macro . "collect") + (struct defmacro . "struct") + (structure syntax-case . "structure") + (values . "values") + (queue . "queue") + (priority-queue . "priorque") + (array . "array") + (array-for-each . "arraymap") + (repl . "repl") + (process . "process") + (chapter-order . "chap") + (posix-time . "time") + (common-lisp-time . "cltime") + (relational-database . "rdms") + (database-utilities . "dbutil") + (database-browse . "dbrowse") + (alist-table . "alistab") + (parameters . "paramlst") + (read-command . "comparse") + (batch . "batch") + (make-crc . "makcrc") + (wt-tree . "wttree") + (string-search . "strsrch") + (root . "root") + ))) + +(set! *catalog* + (append (list + (cons 'schelog + (in-vicinity (sub-vicinity (library-vicinity) "schelog") + "schelog")) + (cons 'portable-scheme-debugger + (in-vicinity (sub-vicinity (library-vicinity) "psd") + "psd-slib"))) + *catalog*)) + +(define *load-pathname* #f) + +(define (slib:pathnameize-load *old-load*) + (lambda (<pathname> . extra) + (let ((old-load-pathname *load-pathname*)) + (set! *load-pathname* <pathname>) + (apply *old-load* (cons <pathname> extra)) + (require:provide <pathname>) + (set! *load-pathname* old-load-pathname)))) + +(set! slib:load-source + (slib:pathnameize-load slib:load-source)) +(set! slib:load + (slib:pathnameize-load slib:load)) + +;;;; MODULES + +(define *modules* '()) + +(define (require:provided? feature) + (if (symbol? feature) + (if (memq feature *features*) #t + (let ((path (cdr (or (assq feature *catalog*) '(#f . #f))))) + (cond ((symbol? path) (provided? path)) + ((member (if (pair? path) (cdr path) path) *modules*) + #t) + (else #f)))) + (and (member feature *modules*) #t))) + +(define (require:feature->path feature) + (if (symbol? feature) + (let ((path (cdr (or (assq feature *catalog*) '(#f . #f))))) + (if (symbol? path) (require:feature->path path) path)) + feature)) + +(define (require:require feature) + (or (require:provided? feature) + (let ((path (require:feature->path feature))) + (cond ((and (not path) (string? feature) (file-exists? feature)) + (set! path feature))) + (cond ((not path) + ;;(newline) (display ";required feature not supported: ") + ;;(display feature) (newline) + (slib:error ";required feature not supported: " feature)) + ((not (pair? path)) ;simple name + (slib:load path) + (require:provide feature)) + (else ;special loads + (require (car path)) + (apply (case (car path) + ((macro) macro:load) + ((syntactic-closures) synclo:load) + ((syntax-case) syncase:load) + ((macros-that-work) macwork:load) + ((macro-by-example) defmacro:load) + ((defmacro) defmacro:load) + ((source) slib:load-source) + ((compiled) slib:load-compiled)) + (if (list? path) (cdr path) (list (cdr path)))) + (require:provide feature)))))) + +(define (require:provide feature) + (if (symbol? feature) + (if (not (memq feature *features*)) + (set! *features* (cons feature *features*))) + (if (not (member feature *modules*)) + (set! *modules* (cons feature *modules*))))) + +(require:provide 'vicinity) + +(define provide require:provide) +(define provided? require:provided?) +(define require require:require) + +;;; Supported by all implementations +(provide 'eval) +(provide 'defmacro) + +(if (and (string->number "0.0") (inexact? (string->number "0.0"))) + (provide 'inexact)) +(if (rational? (string->number "1/19")) (provide 'rational)) +(if (real? (string->number "0.0")) (provide 'real)) +(if (complex? (string->number "1+i")) (provide 'complex)) +(let ((n (string->number "9999999999999999999999999999999"))) + (if (and n (exact? n)) (provide 'bignum))) + +(define current-time + (if (provided? 'current-time) current-time + (let ((c 0)) + (lambda () (set! c (+ c 1)) c)))) +(define difftime (if (provided? 'current-time) difftime -)) +(define offset-time (if (provided? 'current-time) offset-time +)) + +(define report:print + (lambda args + (for-each (lambda (x) (write x) (display #\ )) args) + (newline))) + +(define slib:report + (let ((slib:report (lambda () (slib:report-version) (slib:report-locations)))) + (lambda args + (cond ((null? args) (slib:report)) + ((not (string? (car args))) + (slib:report-version) (slib:report-locations #t)) + ((require:provided? 'transcript) + (transcript-on (car args)) + (slib:report) + (transcript-off)) + ((require:provided? 'with-file) + (with-output-to-file (car args) slib:report)) + (else (slib:report)))))) + +(define slib:report-version + (lambda () + (report:print + 'SLIB *SLIB-VERSION* 'on (scheme-implementation-type) + (scheme-implementation-version) 'on (software-type)))) + +(define slib:report-locations + (let ((features *features*) (catalog *catalog*)) + (lambda args + (report:print '(IMPLEMENTATION-VICINITY) 'is (implementation-vicinity)) + (report:print '(LIBRARY-VICINITY) 'is (library-vicinity)) + (report:print '(SCHEME-FILE-SUFFIX) 'is (scheme-file-suffix)) + (cond (*load-pathname* + (report:print '*LOAD-PATHNAME* 'is *load-pathname*))) + (cond ((not (null? *modules*)) + (report:print 'Loaded '*MODULES* 'are: *modules*))) + (let* ((i (+ -1 5))) + (cond ((eq? (car features) (car *features*))) + (else (report:print 'loaded '*FEATURES* ':) (display slib:tab))) + (for-each + (lambda (x) + (cond ((eq? (car features) x) + (if (not (eq? (car features) (car *features*))) (newline)) + (report:print 'Implementation '*FEATURES* ':) + (display slib:tab) (set! i (+ -1 5))) + ((zero? i) (newline) (display slib:tab) (set! i (+ -1 5))) + ((not (= (+ -1 5) i)) (display #\ ))) + (write x) (set! i (+ -1 i))) + *features*)) + (newline) + (let* ((i #t)) + (cond ((not (eq? (car catalog) (car *catalog*))) + (report:print 'Additional '*CATALOG* ':))) + (cond ((or (pair? args) (not (eq? (car catalog) (car *catalog*)))) + (for-each + (lambda (x) + (cond ((eq? (car catalog) x) + (report:print 'Implementation '*CATALOG* ':) + (set! i (pair? args)) + (cond (i) + (else (display slib:tab) (report:print x) + (display slib:tab) (report:print '...))))) + (cond (i (display slib:tab) (report:print x)))) + *catalog*)) + (else (report:print 'Implementation '*CATALOG* ':) + (display slib:tab) (report:print (car *catalog*)) + (display slib:tab) (report:print '...)))) + (newline)))) + +(let ((sit (scheme-implementation-version))) + (cond ((zero? (string-length sit))) + ((or (not (string? sit)) (char=? #\? (string-ref sit 0))) + (newline) + (slib:report-version) + (report:print 'edit (scheme-implementation-type) ".init" + 'to 'set '(scheme-implementation-version) 'string) + (report:print '(IMPLEMENTATION-VICINITY) 'is (implementation-vicinity)) + (report:print 'type '(slib:report) 'for 'configuration) + (newline)))) diff --git a/root.scm b/root.scm new file mode 100644 index 0000000..5ba78c1 --- /dev/null +++ b/root.scm @@ -0,0 +1,149 @@ +;;;"root.scm" Newton's and Laguerre's methods for finding roots. +;Copyright (C) 1996 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;;; Newton's Method explained in: +;;; D. E. Knuth, "The Art of Computer Programming", Vol 2 / +;;; Seminumerical Algorithms, Reading Massachusetts, Addison-Wesley +;;; Publishing Company, 2nd Edition, p. 510 + +(define (newton:find-integer-root f df/dx x_0) + (let loop ((x x_0) (fx (f x_0))) + (cond + ((zero? fx) x) + (else + (let ((df (df/dx x))) + (cond + ((zero? df) #f) ; stuck at local min/max + (else + (let* ((delta (quotient (+ fx (quotient df 2)) df)) + (next-x (cond ((not (zero? delta)) (- x delta)) + ((positive? fx) (- x 1)) + (else (- x -1)))) + (next-fx (f next-x))) + (cond ((>= (abs next-fx) (abs fx)) x) + (else (loop next-x next-fx))))))))))) + +(define (integer-sqrt y) + (newton:find-integer-root (lambda (x) (- (* x x) y)) + (lambda (x) (* 2 x)) + (ash 1 (quotient (integer-length y) 2)))) + +(define (newton:find-root f df/dx x_0 prec) + (if (and (negative? prec) (integer? prec)) + (let loop ((x x_0) (fx (f x_0)) (count prec)) + (cond ((zero? count) x) + (else (let ((df (df/dx x))) + (cond ((zero? df) #f) ; stuck at local min/max + (else (let* ((next-x (- x (/ fx df))) + (next-fx (f next-x))) + (cond ((= next-x x) x) + ((> (abs next-fx) (abs fx)) #f) + (else (loop next-x next-fx + (+ 1 count))))))))))) + (let loop ((x x_0) (fx (f x_0))) + (cond ((< (abs fx) prec) x) + (else (let ((df (df/dx x))) + (cond ((zero? df) #f) ; stuck at local min/max + (else (let* ((next-x (- x (/ fx df))) + (next-fx (f next-x))) + (cond ((= next-x x) x) + ((> (abs next-fx) (abs fx)) #f) + (else (loop next-x next-fx)))))))))))) + +;;; H. J. Orchard, "The Laguerre Method for Finding the Zeros of +;;; Polynomials", IEEE Transactions on Circuits and Systems, Vol. 36, +;;; No. 11, November 1989, pp 1377-1381. + +(define (laguerre:find-root f df/dz ddf/dz^2 z_0 prec) + (if (and (negative? prec) (integer? prec)) + (let loop ((z z_0) (fz (f z_0)) (count prec)) + (cond ((zero? count) z) + (else + (let* ((df (df/dz z)) + (ddf (ddf/dz^2 z)) + (disc (sqrt (- (* df df) (* fz ddf))))) + (if (zero? disc) + #f + (let* ((next-z + (- z (/ fz (if (negative? (+ (* (real-part df) + (real-part disc)) + (* (imag-part df) + (imag-part disc)))) + (- disc) disc)))) + (next-fz (f next-z))) + (cond ((>= (magnitude next-fz) (magnitude fz)) z) + (else (loop next-z next-fz (+ 1 count)))))))))) + (let loop ((z z_0) (fz (f z_0)) (delta-z #f)) + (cond ((< (magnitude fz) prec) z) + (else + (let* ((df (df/dz z)) + (ddf (ddf/dz^2 z)) + (disc (sqrt (- (* df df) (* fz ddf))))) + (print 'disc disc) + (if (zero? disc) + #f + (let* ((next-z + (- z (/ fz (if (negative? (+ (* (real-part df) + (real-part disc)) + (* (imag-part df) + (imag-part disc)))) + (- disc) disc)))) + (next-delta-z (magnitude (- next-z z)))) + (print 'next-z next-z ) + (print '(f next-z) (f next-z)) + (print 'delta-z delta-z 'next-delta-z next-delta-z) + (cond ((zero? next-delta-z) z) + ((and delta-z (>= next-delta-z delta-z)) z) + (else + (loop next-z (f next-z) next-delta-z))))))))))) + +(define (laguerre:find-polynomial-root deg f df/dz ddf/dz^2 z_0 prec) + (if (and (negative? prec) (integer? prec)) + (let loop ((z z_0) (fz (f z_0)) (count prec)) + (cond ((zero? count) z) + (else + (let* ((df (df/dz z)) + (ddf (ddf/dz^2 z)) + (tmp (* (+ deg -1) df)) + (sqrt-H (sqrt (- (* tmp tmp) (* deg (+ deg -1) fz ddf)))) + (df+sqrt-H (+ df sqrt-H)) + (df-sqrt-H (- df sqrt-H)) + (next-z + (- z (/ (* deg fz) + (if (>= (magnitude df+sqrt-H) + (magnitude df-sqrt-H)) + df+sqrt-H + df-sqrt-H))))) + (loop next-z (f next-z) (+ 1 count)))))) + (let loop ((z z_0) (fz (f z_0))) + (cond ((< (magnitude fz) prec) z) + (else + (let* ((df (df/dz z)) + (ddf (ddf/dz^2 z)) + (tmp (* (+ deg -1) df)) + (sqrt-H (sqrt (- (* tmp tmp) (* deg (+ deg -1) fz ddf)))) + (df+sqrt-H (+ df sqrt-H)) + (df-sqrt-H (- df sqrt-H)) + (next-z + (- z (/ (* deg fz) + (if (>= (magnitude df+sqrt-H) + (magnitude df-sqrt-H)) + df+sqrt-H + df-sqrt-H))))) + (loop next-z (f next-z)))))))) @@ -0,0 +1,66 @@ +;"sc2.scm" Implementation of rev2 procedures eliminated in subsequent versions. +; Copyright (C) 1991, 1993 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(define (substring-move-left! string1 start1 end1 string2 start2) + (do ((i start1 (+ i 1)) + (j start2 (+ j 1)) + (l (- end1 start1) (- l 1))) + ((<= l 0)) + (string-set! string2 j (string-ref string1 i)))) + +(define (substring-move-right! string1 start1 end1 string2 start2) + (do ((i (+ start1 (- end1 start1) -1) (- i 1)) + (j (+ start2 (- end1 start1) -1) (- j 1)) + (l (- end1 start1) (- l 1))) + ((<= l 0)) + (string-set! string2 j (string-ref string1 i)))) + +(define (substring-fill! string start end char) + (do ((i start (+ i 1)) + (l (- end start) (- l 1))) + ((<= l 0)) + (string-set! string i char))) + +(define (string-null? str) + (= 0 (string-length str))) + +(define append! + (lambda args + (cond ((null? args) '()) + ((null? (cdr args)) (car args)) + ((null? (car args)) (apply append! (cdr args))) + (else + (set-cdr! (last-pair (car args)) + (apply append! (cdr args))) + (car args))))) + +;;;; need to add code for OBJECT-HASH and OBJECT-UNHASH + +(define 1+ + (let ((+ +)) + (lambda (n) (+ n 1)))) +(define -1+ + (let ((+ +)) + (lambda (n) (+ n -1)))) + +(define <? <) +(define <=? <=) +(define =? =) +(define >? >) +(define >=? >=) diff --git a/sc4opt.scm b/sc4opt.scm new file mode 100644 index 0000000..176d7f1 --- /dev/null +++ b/sc4opt.scm @@ -0,0 +1,53 @@ +;"sc4opt.scm" Implementation of optional Scheme^4 functions for IEEE Scheme +;Copyright (C) 1991, 1993 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;; Some of these functions may be already defined in your Scheme. +;;; Comment out those definitions for functions which are already defined. + +;;; This code conforms to: William Clinger and Jonathan Rees, editors. +;;; Revised^4 Report on the Algorithmic Language Scheme. + +(define (list-tail l p) + (if (< p 1) l (list-tail (cdr l) (- p 1)))) + +(define (string->list s) + (do ((i (- (string-length s) 1) (- i 1)) + (l '() (cons (string-ref s i) l))) + ((< i 0) l))) + +(define (list->string l) (apply string l)) + +(define string-copy string-append) + +(define (string-fill! s obj) + (do ((i (- (string-length s) 1) (- i 1))) + ((< i 0)) + (string-set! s i obj))) + +(define (list->vector l) (apply vector l)) + +(define (vector->list s) + (do ((i (- (vector-length s) 1) (- i 1)) + (l '() (cons (vector-ref s i) l))) + ((< i 0) l))) + +(define (vector-fill! s obj) + (do ((i (- (vector-length s) 1) (- i 1))) + ((< i 0)) + (vector-set! s i obj))) diff --git a/sc4sc3.scm b/sc4sc3.scm new file mode 100644 index 0000000..a120c5d --- /dev/null +++ b/sc4sc3.scm @@ -0,0 +1,35 @@ +;"sc4sc3.scm" Implementation of rev4 procedures for rev3. +;Copyright (C) 1991 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;;; peek-char, number->string, and string->number need to be written here. + +;;; APPEND, +, *, -, /, =, <, >, <=, >=, MAP, and FOR-EACH need to +;;; accept more general number or arguments. + +(define (list? x) + (let loop ((fast x) (slow x)) + (or (null? fast) + (and (pair? fast) + (let ((fast (cdr fast))) + (or (null? fast) + (and (pair? fast) + (let ((fast (cdr fast)) + (slow (cdr slow))) + (and (not (eq? fast slow)) + (loop fast slow)))))))))) diff --git a/scaexpp.scm b/scaexpp.scm new file mode 100644 index 0000000..aa058a6 --- /dev/null +++ b/scaexpp.scm @@ -0,0 +1,2956 @@ +;;; "scaexpp.scm" syntax-case macros +;;; Copyright (C) 1992 R. Kent Dybvig +;;; +;;; Permission to copy this software, in whole or in part, to use this +;;; software for any lawful purpose, and to redistribute this software +;;; is granted subject to the restriction that all copies made of this +;;; software must include this copyright notice in full. This software +;;; is provided AS IS, with NO WARRANTY, EITHER EXPRESS OR IMPLIED, +;;; INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY +;;; OR FITNESS FOR ANY PARTICULAR PURPOSE. IN NO EVENT SHALL THE +;;; AUTHORS BE LIABLE FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES OF ANY +;;; NATURE WHATSOEVER. + +;;; Written by Robert Hieb & Kent Dybvig + +;;; This file was munged by a simple minded sed script since it left +;;; its original authors' hands. See syncase.sh for the horrid details. + +(begin ((lambda () +(letrec ((lambda-var-list (lambda (vars) +((letrec ((lvl (lambda (vars ls) +(if (pair? vars) +(lvl (cdr vars) +(cons (car vars) +ls)) +(if (id? vars) +(cons vars +ls) +(if (null? +vars) +ls +(if (syntax-object? +vars) +(lvl (unwrap +vars) +ls) +(cons vars +ls)))))))) +lvl) +vars +'()))) +(gen-var (lambda (id) (gen-sym (id-sym-name id)))) +(gen-sym (lambda (sym) +(syncase:new-symbol-hook (symbol->string sym)))) +(strip (lambda (x) +(if (syntax-object? x) +(strip (syntax-object-expression x)) +(if (pair? x) +((lambda (a d) +(if (if (eq? a (car x)) +(eq? d (cdr x)) +#f) +x +(cons a d))) +(strip (car x)) +(strip (cdr x))) +(if (vector? x) +((lambda (old) +((lambda (new) +(if (syncase:andmap eq? old new) +x +(list->vector new))) +(map strip old))) +(vector->list x)) +x))))) +(regen (lambda (x) +((lambda (g000139) +(if (memv g000139 '(ref)) +(syncase:build-lexical-reference (cadr x)) +(if (memv g000139 '(primitive)) +(syncase:build-global-reference (cadr x)) +(if (memv g000139 '(id)) +(syncase:build-identifier (cadr x)) +(if (memv g000139 '(quote)) +(syncase:build-data (cadr x)) +(if (memv +g000139 +'(lambda)) +(syncase:build-lambda +(cadr x) +(regen (caddr x))) +(begin g000139 +(syncase:build-application +(syncase:build-global-reference +(car x)) +(map regen +(cdr x)))))))))) +(car x)))) +(gen-vector (lambda (x) +(if (eq? (car x) 'list) +(syncase:list* 'vector (cdr x)) +(if (eq? (car x) 'quote) +(list +'quote +(list->vector (cadr x))) +(list 'list->vector x))))) +(gen-append (lambda (x y) +(if (equal? y ''()) +x +(list 'append x y)))) +(gen-cons (lambda (x y) +(if (eq? (car y) 'list) +(syncase:list* 'list x (cdr y)) +(if (if (eq? (car x) 'quote) +(eq? (car y) 'quote) +#f) +(list +'quote +(cons (cadr x) (cadr y))) +(if (equal? y ''()) +(list 'list x) +(list 'cons x y)))))) +(gen-map (lambda (e map-env) +((lambda (formals actuals) +(if (eq? (car e) 'ref) +(car actuals) +(if (syncase:andmap +(lambda (x) +(if (eq? (car x) 'ref) +(memq (cadr x) +formals) +#f)) +(cdr e)) +(syncase:list* +'map +(list 'primitive (car e)) +(map ((lambda (r) +(lambda (x) +(cdr (assq (cadr x) +r)))) +(map cons +formals +actuals)) +(cdr e))) +(syncase:list* +'map +(list 'lambda formals e) +actuals)))) +(map cdr map-env) +(map (lambda (x) (list 'ref (car x))) +map-env)))) +(gen-ref (lambda (var level maps k) +(if (= level 0) +(k var maps) +(gen-ref +var +(- level 1) +(cdr maps) +(lambda (outer-var outer-maps) +((lambda (b) +(if b +(k (cdr b) maps) +((lambda (inner-var) +(k inner-var +(cons (cons (cons outer-var +inner-var) +(car maps)) +outer-maps))) +(gen-sym var)))) +(assq outer-var (car maps)))))))) +(chi-syntax (lambda (src exp r w) +((letrec ((gen (lambda (e maps k) +(if (id? e) +((lambda (n) +((lambda (b) +(if (eq? (binding-type +b) +'syntax) +((lambda (level) +(if (< (length +maps) +level) +(syntax-error +src +"missing ellipsis in") +(gen-ref +n +level +maps +(lambda (x +maps) +(k (list +'ref +x) +maps))))) +(binding-value +b)) +(if (ellipsis? +(wrap e +w)) +(syntax-error +src +"invalid context for ... in") +(k (list +'id +(wrap e +w)) +maps)))) +(lookup +n +e +r))) +(id-var-name +e +w)) +((lambda (g000141) +((lambda (g000142) +((lambda (g000140) +(if (not (eq? g000140 +'no)) +((lambda (_dots1 +_dots2) +(if (if (ellipsis? +(wrap _dots1 +w)) +(ellipsis? +(wrap _dots2 +w)) +#f) +(k (list +'id +(wrap _dots1 +w)) +maps) +(g000142))) +(car g000140) +(cadr g000140)) +(g000142))) +(syntax-dispatch +g000141 +'(pair (any) +pair +(any) +atom) +(vector)))) +(lambda () +((lambda (g000144) +((lambda (g000145) +((lambda (g000143) +(if (not (eq? g000143 +'no)) +((lambda (_x +_dots +_y) +(if (ellipsis? +(wrap _dots +w)) +(gen _y +maps +(lambda (y +maps) +(gen _x +(cons '() +maps) +(lambda (x +maps) +(if (null? +(car maps)) +(syntax-error +src +"extra ellipsis in") +(k (gen-append +(gen-map +x +(car maps)) +y) +(cdr maps))))))) +(g000145))) +(car g000143) +(cadr g000143) +(caddr +g000143)) +(g000145))) +(syntax-dispatch +g000144 +'(pair (any) +pair +(any) +any) +(vector)))) +(lambda () +((lambda (g000147) +((lambda (g000146) +(if (not (eq? g000146 +'no)) +((lambda (_x +_y) +(gen _x +maps +(lambda (x +maps) +(gen _y +maps +(lambda (y +maps) +(k (gen-cons +x +y) +maps)))))) +(car g000146) +(cadr g000146)) +((lambda (g000149) +((lambda (g000148) +(if (not (eq? g000148 +'no)) +((lambda (_e1 +_e2) +(gen (cons _e1 +_e2) +maps +(lambda (e +maps) +(k (gen-vector +e) +maps)))) +(car g000148) +(cadr g000148)) +((lambda (g000151) +((lambda (g000150) +(if (not (eq? g000150 +'no)) +((lambda (__) +(k (list +'quote +(wrap e +w)) +maps)) +(car g000150)) +(syntax-error +g000151))) +(syntax-dispatch +g000151 +'(any) +(vector)))) +g000149))) +(syntax-dispatch +g000149 +'(vector +pair +(any) +each +any) +(vector)))) +g000147))) +(syntax-dispatch +g000147 +'(pair (any) +any) +(vector)))) +g000144)))) +g000141)))) +e))))) +gen) +exp +'() +(lambda (e maps) (regen e))))) +(ellipsis? (lambda (x) +;; I dont know what this is supposed to do, and removing it seemed harmless. +;; (if (if (top-level-bound? 'dp) dp #f) +;; (break) +;; (syncase:void)) +(if (identifier? x) +(free-id=? x '...) +#f))) +(chi-syntax-definition (lambda (e w) +((lambda (g000153) +((lambda (g000154) +((lambda (g000152) +(if (not (eq? g000152 +'no)) +((lambda (__ +_name +_val) +(if (id? _name) +(list _name +_val) +(g000154))) +(car g000152) +(cadr g000152) +(caddr +g000152)) +(g000154))) +(syntax-dispatch +g000153 +'(pair (any) +pair +(any) +pair +(any) +atom) +(vector)))) +(lambda () +(syntax-error +g000153)))) +(wrap e w)))) +(chi-definition (lambda (e w) +((lambda (g000156) +((lambda (g000157) +((lambda (g000155) +(if (not (eq? g000155 +'no)) +(apply +(lambda (__ +_name +_args +_e1 +_e2) +(if (if (id? _name) +(valid-bound-ids? +(lambda-var-list +_args)) +#f) +(list _name +(cons '#(syntax-object +lambda +(top)) +(cons _args +(cons _e1 +_e2)))) +(g000157))) +g000155) +(g000157))) +(syntax-dispatch +g000156 +'(pair (any) +pair +(pair (any) any) +pair +(any) +each +any) +(vector)))) +(lambda () +((lambda (g000159) +((lambda (g000158) +(if (not (eq? g000158 +'no)) +((lambda (__ +_name +_val) +(list _name +_val)) +(car g000158) +(cadr g000158) +(caddr +g000158)) +((lambda (g000161) +((lambda (g000162) +((lambda (g000160) +(if (not (eq? g000160 +'no)) +((lambda (__ +_name) +(if (id? _name) +(list _name +(list '#(syntax-object +syncase:void +(top)))) +(g000162))) +(car g000160) +(cadr g000160)) +(g000162))) +(syntax-dispatch +g000161 +'(pair (any) +pair +(any) +atom) +(vector)))) +(lambda () +(syntax-error +g000161)))) +g000159))) +(syntax-dispatch +g000159 +'(pair (any) +pair +(any) +pair +(any) +atom) +(vector)))) +g000156)))) +(wrap e w)))) +(chi-sequence (lambda (e w) +((lambda (g000164) +((lambda (g000163) +(if (not (eq? g000163 'no)) +((lambda (__ _e) _e) +(car g000163) +(cadr g000163)) +(syntax-error g000164))) +(syntax-dispatch +g000164 +'(pair (any) each any) +(vector)))) +(wrap e w)))) +(chi-macro-def (lambda (def r w) +(syncase:eval-hook (chi def null-env w)))) +(chi-local-syntax (lambda (e r w) +((lambda (g000166) +((lambda (g000167) +((lambda (g000165) +(if (not (eq? g000165 +'no)) +(apply +(lambda (_who +_var +_val +_e1 +_e2) +(if (valid-bound-ids? +_var) +((lambda (new-vars) +((lambda (new-w) +(chi-body +(cons _e1 +_e2) +e +(extend-macro-env +new-vars +((lambda (w) +(map (lambda (x) +(chi-macro-def +x +r +w)) +_val)) +(if (free-id=? +_who +'#(syntax-object +letrec-syntax +(top))) +new-w +w)) +r) +new-w)) +(make-binding-wrap +_var +new-vars +w))) +(map gen-var +_var)) +(g000167))) +g000165) +(g000167))) +(syntax-dispatch +g000166 +'(pair (any) +pair +(each pair +(any) +pair +(any) +atom) +pair +(any) +each +any) +(vector)))) +(lambda () +((lambda (g000169) +((lambda (g000168) +(if (not (eq? g000168 +'no)) +((lambda (__) +(syntax-error +(wrap e +w))) +(car g000168)) +(syntax-error +g000169))) +(syntax-dispatch +g000169 +'(any) +(vector)))) +g000166)))) +e))) +(chi-body (lambda (body source r w) +(if (null? (cdr body)) +(chi (car body) r w) +((letrec ((parse1 (lambda (body +var-ids +var-vals +macro-ids +macro-vals) +(if (null? body) +(syntax-error +(wrap source +w) +"no expressions in body") +((letrec ((parse2 (lambda (e) +((lambda (b) +((lambda (g000170) +(if (memv +g000170 +'(macro)) +(parse2 +(chi-macro +(binding-value +b) +e +r +empty-wrap +(lambda (e +r +w) +(wrap e +w)))) +(if (memv +g000170 +'(definition)) +(parse1 +(cdr body) +(cons (cadr b) +var-ids) +(cons (caddr +b) +var-vals) +macro-ids +macro-vals) +(if (memv +g000170 +'(syntax-definition)) +(parse1 +(cdr body) +var-ids +var-vals +(cons (cadr b) +macro-ids) +(cons (caddr +b) +macro-vals)) +(if (memv +g000170 +'(sequence)) +(parse1 +(append +(cdr b) +(cdr body)) +var-ids +var-vals +macro-ids +macro-vals) +(begin g000170 +(if (valid-bound-ids? +(append +var-ids +macro-ids)) +((lambda (new-var-names +new-macro-names) +((lambda (w) +((lambda (r) +(syncase:build-letrec +new-var-names +(map (lambda (x) +(chi x +r +w)) +var-vals) +(syncase:build-sequence +(map (lambda (x) +(chi x +r +w)) +body)))) +(extend-macro-env +new-macro-names +(map (lambda (x) +(chi-macro-def +x +r +w)) +macro-vals) +(extend-var-env +new-var-names +r)))) +(make-binding-wrap +(append +macro-ids +var-ids) +(append +new-macro-names +new-var-names) +empty-wrap))) +(map gen-var +var-ids) +(map gen-var +macro-ids)) +(syntax-error +(wrap source +w) +"invalid identifier")))))))) +(car b))) +(syntax-type +e +r +empty-wrap))))) +parse2) +(car body)))))) +parse1) +(map (lambda (x) (wrap x w)) body) +'() +'() +'() +'())))) +(syntax-type (lambda (e r w) +(if (syntax-object? e) +(syntax-type +(syntax-object-expression e) +r +(join-wraps +(syntax-object-wrap e) +w)) +(if (if (pair? e) +(identifier? (car e)) +#f) +((lambda (n) +((lambda (b) +((lambda (g000171) +(if (memv +g000171 +'(special)) +(if (memv +n +'(define)) +(cons 'definition +(chi-definition +e +w)) +(if (memv +n +'(define-syntax)) +(cons 'syntax-definition +(chi-syntax-definition +e +w)) +(if (memv +n +'(begin)) +(cons 'sequence +(chi-sequence +e +w)) +(begin n +(syncase:void))))) +(begin g000171 +b))) +(binding-type b))) +(lookup n (car e) r))) +(id-var-name (car e) w)) +'(other))))) +(chi-args (lambda (args r w source source-w) +(if (pair? args) +(cons (chi (car args) r w) +(chi-args +(cdr args) +r +w +source +source-w)) +(if (null? args) +'() +(if (syntax-object? args) +(chi-args +(syntax-object-expression +args) +r +(join-wraps +w +(syntax-object-wrap +args)) +source +source-w) +(syntax-error +(wrap source source-w))))))) +(chi-ref (lambda (e name binding w) +((lambda (g000172) +(if (memv g000172 '(lexical)) +(syncase:build-lexical-reference name) +(if (memv +g000172 +'(global global-unbound)) +(syncase:build-global-reference name) +(begin g000172 +(id-error +(wrap e w)))))) +(binding-type binding)))) +(chi-macro (letrec ((check-macro-output (lambda (x) +(if (pair? +x) +(begin (check-macro-output +(car x)) +(check-macro-output +(cdr x))) +((lambda (g000173) +(if g000173 +g000173 +(if (vector? +x) +((lambda (n) +((letrec ((g000174 (lambda (i) +(if (= i +n) +(syncase:void) +(begin (check-macro-output +(vector-ref +x +i)) +(g000174 +(+ i +1))))))) +g000174) +0)) +(vector-length +x)) +(if (symbol? +x) +(syntax-error +x +"encountered raw symbol") +(syncase:void))))) +(syntax-object? +x)))))) +(lambda (p e r w k) +((lambda (mw) +((lambda (x) +(check-macro-output x) +(k x r mw)) +(p (wrap e (join-wraps mw w))))) +(new-mark-wrap))))) +(chi-pair (lambda (e r w k) +((lambda (first rest) +(if (id? first) +((lambda (n) +((lambda (b) +((lambda (g000175) +(if (memv +g000175 +'(core)) +((binding-value b) +e +r +w) +(if (memv +g000175 +'(macro)) +(chi-macro +(binding-value +b) +e +r +w +k) +(if (memv +g000175 +'(special)) +((binding-value +b) +e +r +w +k) +(begin g000175 +(syncase:build-application +(chi-ref +first +n +b +w) +(chi-args +rest +r +w +e +w))))))) +(binding-type b))) +(lookup n first r))) +(id-var-name first w)) +(syncase:build-application +(chi first r w) +(chi-args rest r w e w)))) +(car e) +(cdr e)))) +(chi (lambda (e r w) +(if (symbol? e) +((lambda (n) +(chi-ref e n (lookup n e r) w)) +(id-var-name e w)) +(if (pair? e) +(chi-pair e r w chi) +(if (syntax-object? e) +(chi (syntax-object-expression e) +r +(join-wraps +w +(syntax-object-wrap e))) +(if ((lambda (g000176) +(if g000176 +g000176 +((lambda (g000177) +(if g000177 +g000177 +((lambda (g000178) +(if g000178 +g000178 +(char? +e))) +(string? e)))) +(number? e)))) +(boolean? e)) +(syncase:build-data e) +(syntax-error (wrap e w)))))))) +(chi-top (lambda (e r w) +(if (pair? e) +(chi-pair e r w chi-top) +(if (syntax-object? e) +(chi-top +(syntax-object-expression e) +r +(join-wraps +w +(syntax-object-wrap e))) +(chi e r w))))) +(wrap (lambda (x w) +(if (null? w) +x +(if (syntax-object? x) +(make-syntax-object +(syntax-object-expression x) +(join-wraps +w +(syntax-object-wrap x))) +(if (null? x) +x +(make-syntax-object x w)))))) +(unwrap (lambda (x) +(if (syntax-object? x) +((lambda (e w) +(if (pair? e) +(cons (wrap (car e) w) +(wrap (cdr e) w)) +(if (vector? e) +(list->vector +(map (lambda (x) +(wrap x w)) +(vector->list e))) +e))) +(syntax-object-expression x) +(syntax-object-wrap x)) +x))) +(bound-id-member? (lambda (x list) +(if (not (null? list)) +((lambda (g000179) +(if g000179 +g000179 +(bound-id-member? +x +(cdr list)))) +(bound-id=? x (car list))) +#f))) +(valid-bound-ids? (lambda (ids) +(if ((letrec ((all-ids? (lambda (ids) +((lambda (g000181) +(if g000181 +g000181 +(if (id? (car ids)) +(all-ids? +(cdr ids)) +#f))) +(null? +ids))))) +all-ids?) +ids) +((letrec ((unique? (lambda (ids) +((lambda (g000180) +(if g000180 +g000180 +(if (not (bound-id-member? +(car ids) +(cdr ids))) +(unique? +(cdr ids)) +#f))) +(null? +ids))))) +unique?) +ids) +#f))) +(bound-id=? (lambda (i j) +(if (eq? (id-sym-name i) +(id-sym-name j)) +((lambda (i j) +(if (eq? (car i) (car j)) +(same-marks? +(cdr i) +(cdr j)) +#f)) +(id-var-name&marks i empty-wrap) +(id-var-name&marks j empty-wrap)) +#f))) +(free-id=? (lambda (i j) +(if (eq? (id-sym-name i) (id-sym-name j)) +(eq? (id-var-name i empty-wrap) +(id-var-name j empty-wrap)) +#f))) +(id-var-name&marks (lambda (id w) +(if (null? w) +(if (symbol? id) +(list id) +(id-var-name&marks +(syntax-object-expression +id) +(syntax-object-wrap +id))) +((lambda (n&m first) +(if (pair? first) +((lambda (n) +((letrec ((search (lambda (rib) +(if (null? +rib) +n&m +(if (if (eq? (caar rib) +n) +(same-marks? +(cdr n&m) +(cddar +rib)) +#f) +(cdar rib) +(search +(cdr rib))))))) +search) +first)) +(car n&m)) +(cons (car n&m) +(if ((lambda (g000182) +(if g000182 +g000182 +(not (eqv? first +(cadr n&m))))) +(null? +(cdr n&m))) +(cons first +(cdr n&m)) +(cddr n&m))))) +(id-var-name&marks +id +(cdr w)) +(car w))))) +(id-var-name (lambda (id w) +(if (null? w) +(if (symbol? id) +id +(id-var-name +(syntax-object-expression +id) +(syntax-object-wrap id))) +(if (pair? (car w)) +(car (id-var-name&marks id w)) +(id-var-name id (cdr w)))))) +(same-marks? (lambda (x y) +(if (null? x) +(null? y) +(if (not (null? y)) +(if (eqv? (car x) (car y)) +(same-marks? +(cdr x) +(cdr y)) +#f) +#f)))) +(join-wraps2 (lambda (w1 w2) +((lambda (x w1) +(if (null? w1) +(if (if (not (pair? x)) +(eqv? x (car w2)) +#f) +(cdr w2) +(cons x w2)) +(cons x (join-wraps2 w1 w2)))) +(car w1) +(cdr w1)))) +(join-wraps1 (lambda (w1 w2) +(if (null? w1) +w2 +(cons (car w1) +(join-wraps1 (cdr w1) w2))))) +(join-wraps (lambda (w1 w2) +(if (null? w2) +w1 +(if (null? w1) +w2 +(if (pair? (car w2)) +(join-wraps1 w1 w2) +(join-wraps2 w1 w2)))))) +(make-wrap-rib (lambda (ids new-names w) +(if (null? ids) +'() +(cons ((lambda (n&m) +(cons (car n&m) +(cons (car new-names) +(cdr n&m)))) +(id-var-name&marks +(car ids) +w)) +(make-wrap-rib +(cdr ids) +(cdr new-names) +w))))) +(make-binding-wrap (lambda (ids new-names w) +(if (null? ids) +w +(cons (make-wrap-rib +ids +new-names +w) +w)))) +(new-mark-wrap (lambda () +(set! current-mark +(+ current-mark 1)) +(list current-mark))) +(current-mark 0) +(top-wrap '(top)) +(empty-wrap '()) +(id-sym-name (lambda (x) +(if (symbol? x) +x +(syntax-object-expression x)))) +(id? (lambda (x) +((lambda (g000183) +(if g000183 +g000183 +(if (syntax-object? x) +(symbol? +(syntax-object-expression x)) +#f))) +(symbol? x)))) +(global-extend (lambda (type sym val) +(extend-global-env +sym +(cons type val)))) +(lookup (lambda (name id r) +(if (eq? name (id-sym-name id)) +(global-lookup name) +((letrec ((search (lambda (r name) +(if (null? r) +'(displaced-lexical) +(if (pair? +(car r)) +(if (eq? (caar r) +name) +(cdar r) +(search +(cdr r) +name)) +(if (eq? (car r) +name) +'(lexical) +(search +(cdr r) +name))))))) +search) +r +name)))) +(extend-syntax-env (lambda (vars vals r) +(if (null? vars) +r +(cons (cons (car vars) +(cons 'syntax +(car vals))) +(extend-syntax-env +(cdr vars) +(cdr vals) +r))))) +(extend-var-env append) +(extend-macro-env (lambda (vars vals r) +(if (null? vars) +r +(cons (cons (car vars) +(cons 'macro +(car vals))) +(extend-macro-env +(cdr vars) +(cdr vals) +r))))) +(null-env '()) +(global-lookup (lambda (sym) +((lambda (g000184) +(if g000184 +g000184 +'(global-unbound))) +(syncase:get-global-definition-hook sym)))) +(extend-global-env (lambda (sym binding) +(syncase:put-global-definition-hook +sym +binding))) +(binding-value cdr) +(binding-type car) +(arg-check (lambda (pred? x who) +(if (not (pred? x)) +(syncase:error-hook who "invalid argument" x) +(syncase:void)))) +(id-error (lambda (x) +(syntax-error +x +"invalid context for identifier"))) +(scope-error (lambda (id) +(syntax-error +id +"invalid context for bound identifier"))) +(syntax-object-wrap (lambda (x) (vector-ref x 2))) +(syntax-object-expression (lambda (x) (vector-ref x 1))) +(make-syntax-object (lambda (expression wrap) +(vector +'syntax-object +expression +wrap))) +(syntax-object? (lambda (x) +(if (vector? x) +(if (= (vector-length x) 3) +(eq? (vector-ref x 0) +'syntax-object) +#f) +#f)))) +(global-extend 'core 'letrec-syntax chi-local-syntax) +(global-extend 'core 'let-syntax chi-local-syntax) +(global-extend +'core +'quote +(lambda (e r w) +((lambda (g000136) +((lambda (g000135) +(if (not (eq? g000135 'no)) +((lambda (__ _e) (syncase:build-data (strip _e))) +(car g000135) +(cadr g000135)) +((lambda (g000138) +((lambda (g000137) +(if (not (eq? g000137 'no)) +((lambda (__) +(syntax-error (wrap e w))) +(car g000137)) +(syntax-error g000138))) +(syntax-dispatch +g000138 +'(any) +(vector)))) +g000136))) +(syntax-dispatch +g000136 +'(pair (any) pair (any) atom) +(vector)))) +e))) +(global-extend +'core +'syntax +(lambda (e r w) +((lambda (g000132) +((lambda (g000131) +(if (not (eq? g000131 'no)) +((lambda (__ _x) (chi-syntax e _x r w)) +(car g000131) +(cadr g000131)) +((lambda (g000134) +((lambda (g000133) +(if (not (eq? g000133 'no)) +((lambda (__) +(syntax-error (wrap e w))) +(car g000133)) +(syntax-error g000134))) +(syntax-dispatch +g000134 +'(any) +(vector)))) +g000132))) +(syntax-dispatch +g000132 +'(pair (any) pair (any) atom) +(vector)))) +e))) +(global-extend +'core +'syntax-lambda +(lambda (e r w) +((lambda (g000127) +((lambda (g000128) +((lambda (g000126) +(if (not (eq? g000126 'no)) +((lambda (__ _id _level _exp) +(if (if (valid-bound-ids? _id) +(map (lambda (x) +(if (integer? x) +(if (exact? x) +(not (negative? +x)) +#f) +#f)) +(map unwrap _level)) +#f) +((lambda (new-vars) +(syncase:build-lambda +new-vars +(chi _exp +(extend-syntax-env +new-vars +(map unwrap +_level) +r) +(make-binding-wrap +_id +new-vars +w)))) +(map gen-var _id)) +(g000128))) +(car g000126) +(cadr g000126) +(caddr g000126) +(cadddr g000126)) +(g000128))) +(syntax-dispatch +g000127 +'(pair (any) +pair +(each pair (any) pair (any) atom) +pair +(any) +atom) +(vector)))) +(lambda () +((lambda (g000130) +((lambda (g000129) +(if (not (eq? g000129 'no)) +((lambda (__) +(syntax-error (wrap e w))) +(car g000129)) +(syntax-error g000130))) +(syntax-dispatch +g000130 +'(any) +(vector)))) +g000127)))) +e))) +(global-extend +'core +'lambda +(lambda (e r w) +((lambda (g000121) +((lambda (g000120) +(if (not (eq? g000120 'no)) +((lambda (__ _id _e1 _e2) +(if (not (valid-bound-ids? _id)) +(syntax-error +(wrap e w) +"invalid parameter list") +((lambda (new-vars) +(syncase:build-lambda +new-vars +(chi-body +(cons _e1 _e2) +e +(extend-var-env +new-vars +r) +(make-binding-wrap +_id +new-vars +w)))) +(map gen-var _id)))) +(car g000120) +(cadr g000120) +(caddr g000120) +(cadddr g000120)) +((lambda (g000123) +((lambda (g000122) +(if (not (eq? g000122 'no)) +((lambda (__ _ids _e1 _e2) +((lambda (old-ids) +(if (not (valid-bound-ids? +(lambda-var-list +_ids))) +(syntax-error +(wrap e w) +"invalid parameter list") +((lambda (new-vars) +(syncase:build-improper-lambda +(reverse +(cdr new-vars)) +(car new-vars) +(chi-body +(cons _e1 +_e2) +e +(extend-var-env +new-vars +r) +(make-binding-wrap +old-ids +new-vars +w)))) +(map gen-var +old-ids)))) +(lambda-var-list _ids))) +(car g000122) +(cadr g000122) +(caddr g000122) +(cadddr g000122)) +((lambda (g000125) +((lambda (g000124) +(if (not (eq? g000124 +'no)) +((lambda (__) +(syntax-error +(wrap e w))) +(car g000124)) +(syntax-error +g000125))) +(syntax-dispatch +g000125 +'(any) +(vector)))) +g000123))) +(syntax-dispatch +g000123 +'(pair (any) +pair +(any) +pair +(any) +each +any) +(vector)))) +g000121))) +(syntax-dispatch +g000121 +'(pair (any) +pair +(each any) +pair +(any) +each +any) +(vector)))) +e))) +(global-extend +'core +'letrec +(lambda (e r w) +((lambda (g000116) +((lambda (g000117) +((lambda (g000115) +(if (not (eq? g000115 'no)) +(apply +(lambda (__ _id _val _e1 _e2) +(if (valid-bound-ids? _id) +((lambda (new-vars) +((lambda (w r) +(syncase:build-letrec +new-vars +(map (lambda (x) +(chi x +r +w)) +_val) +(chi-body +(cons _e1 _e2) +e +r +w))) +(make-binding-wrap +_id +new-vars +w) +(extend-var-env +new-vars +r))) +(map gen-var _id)) +(g000117))) +g000115) +(g000117))) +(syntax-dispatch +g000116 +'(pair (any) +pair +(each pair (any) pair (any) atom) +pair +(any) +each +any) +(vector)))) +(lambda () +((lambda (g000119) +((lambda (g000118) +(if (not (eq? g000118 'no)) +((lambda (__) +(syntax-error (wrap e w))) +(car g000118)) +(syntax-error g000119))) +(syntax-dispatch +g000119 +'(any) +(vector)))) +g000116)))) +e))) +(global-extend +'core +'if +(lambda (e r w) +((lambda (g000110) +((lambda (g000109) +(if (not (eq? g000109 'no)) +((lambda (__ _test _then) +(syncase:build-conditional +(chi _test r w) +(chi _then r w) +(chi (list '#(syntax-object +syncase:void +(top))) +r +empty-wrap))) +(car g000109) +(cadr g000109) +(caddr g000109)) +((lambda (g000112) +((lambda (g000111) +(if (not (eq? g000111 'no)) +((lambda (__ _test _then _else) +(syncase:build-conditional +(chi _test r w) +(chi _then r w) +(chi _else r w))) +(car g000111) +(cadr g000111) +(caddr g000111) +(cadddr g000111)) +((lambda (g000114) +((lambda (g000113) +(if (not (eq? g000113 +'no)) +((lambda (__) +(syntax-error +(wrap e w))) +(car g000113)) +(syntax-error +g000114))) +(syntax-dispatch +g000114 +'(any) +(vector)))) +g000112))) +(syntax-dispatch +g000112 +'(pair (any) +pair +(any) +pair +(any) +pair +(any) +atom) +(vector)))) +g000110))) +(syntax-dispatch +g000110 +'(pair (any) pair (any) pair (any) atom) +(vector)))) +e))) +(global-extend +'core +'set! +(lambda (e r w) +((lambda (g000104) +((lambda (g000105) +((lambda (g000103) +(if (not (eq? g000103 'no)) +((lambda (__ _id _val) +(if (id? _id) +((lambda (val n) +((lambda (g000108) +(if (memv +g000108 +'(lexical)) +(syncase:build-lexical-assignment +n +val) +(if (memv +g000108 +'(global +global-unbound)) +(syncase:build-global-assignment +n +val) +(begin g000108 +(id-error +(wrap _id +w)))))) +(binding-type +(lookup n _id r)))) +(chi _val r w) +(id-var-name _id w)) +(g000105))) +(car g000103) +(cadr g000103) +(caddr g000103)) +(g000105))) +(syntax-dispatch +g000104 +'(pair (any) pair (any) pair (any) atom) +(vector)))) +(lambda () +((lambda (g000107) +((lambda (g000106) +(if (not (eq? g000106 'no)) +((lambda (__) +(syntax-error (wrap e w))) +(car g000106)) +(syntax-error g000107))) +(syntax-dispatch +g000107 +'(any) +(vector)))) +g000104)))) +e))) +(global-extend +'special +'begin +(lambda (e r w k) +((lambda (body) +(if (null? body) +(if (eqv? k chi-top) +(chi (list '#(syntax-object syncase:void (top))) +r +empty-wrap) +(syntax-error +(wrap e w) +"no expressions in body of")) +(syncase:build-sequence +((letrec ((dobody (lambda (body) +(if (null? body) +'() +((lambda (first) +(cons first +(dobody +(cdr body)))) +(k (car body) +r +empty-wrap)))))) +dobody) +body)))) +(chi-sequence e w)))) +(global-extend +'special +'define +(lambda (e r w k) +(if (eqv? k chi-top) +((lambda (n&v) +((lambda (n) +(global-extend 'global n '()) +(syncase:build-global-definition +n +(chi (cadr n&v) r empty-wrap))) +(id-var-name (car n&v) empty-wrap))) +(chi-definition e w)) +(syntax-error +(wrap e w) +"invalid context for definition")))) +(global-extend +'special +'define-syntax +(lambda (e r w k) +(if (eqv? k chi-top) +((lambda (n&v) +(global-extend +'macro +(id-var-name (car n&v) empty-wrap) +(chi-macro-def (cadr n&v) r empty-wrap)) +(chi (list '#(syntax-object syncase:void (top))) +r +empty-wrap)) +(chi-syntax-definition e w)) +(syntax-error +(wrap e w) +"invalid context for definition")))) +(set! expand-syntax +(lambda (x) (chi-top x null-env top-wrap))) +(set! implicit-identifier +(lambda (id sym) +(arg-check id? id 'implicit-identifier) +(arg-check symbol? sym 'implicit-identifier) +(if (syntax-object? id) +(wrap sym (syntax-object-wrap id)) +sym))) +(set! syntax-object->datum (lambda (x) (strip x))) +(set! generate-temporaries +(lambda (ls) +(arg-check list? ls 'generate-temporaries) +(map (lambda (x) (wrap (syncase:new-symbol-hook "g") top-wrap)) ls))) +(set! free-identifier=? +(lambda (x y) +(arg-check id? x 'free-identifier=?) +(arg-check id? y 'free-identifier=?) +(free-id=? x y))) +(set! bound-identifier=? +(lambda (x y) +(arg-check id? x 'bound-identifier=?) +(arg-check id? y 'bound-identifier=?) +(bound-id=? x y))) +(set! identifier? (lambda (x) (id? x))) +(set! syntax-error +(lambda (object . messages) +(for-each +(lambda (x) (arg-check string? x 'syntax-error)) +messages) +((lambda (message) +(syncase:error-hook 'expand-syntax message (strip object))) +(if (null? messages) +"invalid syntax" +(apply string-append messages))))) +(set! syncase:install-global-transformer +(lambda (sym p) (global-extend 'macro sym p))) +((lambda () +(letrec ((match (lambda (e p k w r) +(if (eq? r 'no) +r +((lambda (g000100) +(if (memv g000100 '(any)) +(cons (wrap e w) r) +(if (memv +g000100 +'(free-id)) +(if (if (identifier? +e) +(free-id=? +(wrap e w) +(vector-ref +k +(cdr p))) +#f) +r +'no) +(begin g000100 +(if (syntax-object? +e) +(match* +(syntax-object-expression +e) +p +k +(join-wraps +w +(syntax-object-wrap +e)) +r) +(match* +e +p +k +w +r)))))) +(car p))))) +(match* (lambda (e p k w r) +((lambda (g000101) +(if (memv g000101 '(pair)) +(if (pair? e) +(match +(car e) +(cadr p) +k +w +(match +(cdr e) +(cddr p) +k +w +r)) +'no) +(if (memv g000101 '(each)) +(if (eq? (cadr p) 'any) +((lambda (l) +(if (eq? l 'no) +l +(cons l r))) +(match-each-any +e +w)) +(if (null? e) +(match-empty +(cdr p) +r) +((lambda (l) +(if (eq? l +'no) +l +((letrec ((collect (lambda (l) +(if (null? +(car l)) +r +(cons (map car +l) +(collect +(map cdr +l))))))) +collect) +l))) +(match-each +e +(cdr p) +k +w)))) +(if (memv +g000101 +'(atom)) +(if (equal? +(cdr p) +e) +r +'no) +(if (memv +g000101 +'(vector)) +(if (vector? e) +(match +(vector->list +e) +(cdr p) +k +w +r) +'no) +(begin g000101 +(syncase:void))))))) +(car p)))) +(match-empty (lambda (p r) +((lambda (g000102) +(if (memv g000102 '(any)) +(cons '() r) +(if (memv +g000102 +'(each)) +(match-empty +(cdr p) +r) +(if (memv +g000102 +'(pair)) +(match-empty +(cadr p) +(match-empty +(cddr p) +r)) +(if (memv +g000102 +'(free-id +atom)) +r +(if (memv +g000102 +'(vector)) +(match-empty +(cdr p) +r) +(begin g000102 +(syncase:void)))))))) +(car p)))) +(match-each-any (lambda (e w) +(if (pair? e) +((lambda (l) +(if (eq? l 'no) +l +(cons (wrap (car e) +w) +l))) +(match-each-any +(cdr e) +w)) +(if (null? e) +'() +(if (syntax-object? +e) +(match-each-any +(syntax-object-expression +e) +(join-wraps +w +(syntax-object-wrap +e))) +'no))))) +(match-each (lambda (e p k w) +(if (pair? e) +((lambda (first) +(if (eq? first 'no) +first +((lambda (rest) +(if (eq? rest +'no) +rest +(cons first +rest))) +(match-each +(cdr e) +p +k +w)))) +(match (car e) p k w '())) +(if (null? e) +'() +(if (syntax-object? e) +(match-each +(syntax-object-expression +e) +p +k +(join-wraps +w +(syntax-object-wrap +e))) +'no)))))) +(set! syntax-dispatch +(lambda (expression pattern keys) +(match +expression +pattern +keys +empty-wrap +'()))))))))) +(syncase:install-global-transformer +'let +(lambda (x) +((lambda (g00095) +((lambda (g00096) +((lambda (g00094) +(if (not (eq? g00094 'no)) +(apply +(lambda (__ _x _v _e1 _e2) +(if (syncase:andmap identifier? _x) +(cons (cons '#(syntax-object +lambda +(top)) +(cons _x +(cons _e1 _e2))) +_v) +(g00096))) +g00094) +(g00096))) +(syntax-dispatch +g00095 +'(pair (any) +pair +(each pair (any) pair (any) atom) +pair +(any) +each +any) +(vector)))) +(lambda () +((lambda (g00098) +((lambda (g00099) +((lambda (g00097) +(if (not (eq? g00097 'no)) +(apply +(lambda (__ _f _x _v _e1 _e2) +(if (syncase:andmap +identifier? +(cons _f _x)) +(cons (list '#(syntax-object +letrec +(top)) +(list (list _f +(cons '#(syntax-object +lambda +(top)) +(cons _x +(cons _e1 +_e2))))) +_f) +_v) +(g00099))) +g00097) +(g00099))) +(syntax-dispatch +g00098 +'(pair (any) +pair +(any) +pair +(each pair (any) pair (any) atom) +pair +(any) +each +any) +(vector)))) +(lambda () (syntax-error g00098)))) +g00095)))) +x))) +(syncase:install-global-transformer +'syntax-case +((lambda () +(letrec ((syncase:build-dispatch-call (lambda (args body val) +((lambda (g00046) +((lambda (g00045) +(if (not (eq? g00045 +'no)) +body +((lambda (g00048) +((lambda (g00047) +(if (not (eq? g00047 +'no)) +((lambda (_arg1) +((lambda (g00066) +((lambda (g00065) +(if (not (eq? g00065 +'no)) +((lambda (_body +_val) +(list (list '#(syntax-object +syntax-lambda +(top)) +(list _arg1) +_body) +(list '#(syntax-object +car +(top)) +_val))) +(car g00065) +(cadr g00065)) +(syntax-error +g00066))) +(syntax-dispatch +g00066 +'(pair (any) +pair +(any) +atom) +(vector)))) +(list body +val))) +(car g00047)) +((lambda (g00050) +((lambda (g00049) +(if (not (eq? g00049 +'no)) +((lambda (_arg1 +_arg2) +((lambda (g00064) +((lambda (g00063) +(if (not (eq? g00063 +'no)) +((lambda (_body +_val) +(list (list '#(syntax-object +syntax-lambda +(top)) +(list _arg1 +_arg2) +_body) +(list '#(syntax-object +car +(top)) +_val) +(list '#(syntax-object +cadr +(top)) +_val))) +(car g00063) +(cadr g00063)) +(syntax-error +g00064))) +(syntax-dispatch +g00064 +'(pair (any) +pair +(any) +atom) +(vector)))) +(list body +val))) +(car g00049) +(cadr g00049)) +((lambda (g00052) +((lambda (g00051) +(if (not (eq? g00051 +'no)) +((lambda (_arg1 +_arg2 +_arg3) +((lambda (g00062) +((lambda (g00061) +(if (not (eq? g00061 +'no)) +((lambda (_body +_val) +(list (list '#(syntax-object +syntax-lambda +(top)) +(list _arg1 +_arg2 +_arg3) +_body) +(list '#(syntax-object +car +(top)) +_val) +(list '#(syntax-object +cadr +(top)) +_val) +(list '#(syntax-object +caddr +(top)) +_val))) +(car g00061) +(cadr g00061)) +(syntax-error +g00062))) +(syntax-dispatch +g00062 +'(pair (any) +pair +(any) +atom) +(vector)))) +(list body +val))) +(car g00051) +(cadr g00051) +(caddr +g00051)) +((lambda (g00054) +((lambda (g00053) +(if (not (eq? g00053 +'no)) +((lambda (_arg1 +_arg2 +_arg3 +_arg4) +((lambda (g00060) +((lambda (g00059) +(if (not (eq? g00059 +'no)) +((lambda (_body +_val) +(list (list '#(syntax-object +syntax-lambda +(top)) +(list _arg1 +_arg2 +_arg3 +_arg4) +_body) +(list '#(syntax-object +car +(top)) +_val) +(list '#(syntax-object +cadr +(top)) +_val) +(list '#(syntax-object +caddr +(top)) +_val) +(list '#(syntax-object +cadddr +(top)) +_val))) +(car g00059) +(cadr g00059)) +(syntax-error +g00060))) +(syntax-dispatch +g00060 +'(pair (any) +pair +(any) +atom) +(vector)))) +(list body +val))) +(car g00053) +(cadr g00053) +(caddr +g00053) +(cadddr +g00053)) +((lambda (g00056) +((lambda (g00055) +(if (not (eq? g00055 +'no)) +((lambda (_arg) +((lambda (g00058) +((lambda (g00057) +(if (not (eq? g00057 +'no)) +((lambda (_body +_val) +(list '#(syntax-object +apply +(top)) +(list '#(syntax-object +syntax-lambda +(top)) +_arg +_body) +_val)) +(car g00057) +(cadr g00057)) +(syntax-error +g00058))) +(syntax-dispatch +g00058 +'(pair (any) +pair +(any) +atom) +(vector)))) +(list body +val))) +(car g00055)) +(syntax-error +g00056))) +(syntax-dispatch +g00056 +'(each any) +(vector)))) +g00054))) +(syntax-dispatch +g00054 +'(pair (any) +pair +(any) +pair +(any) +pair +(any) +atom) +(vector)))) +g00052))) +(syntax-dispatch +g00052 +'(pair (any) +pair +(any) +pair +(any) +atom) +(vector)))) +g00050))) +(syntax-dispatch +g00050 +'(pair (any) +pair +(any) +atom) +(vector)))) +g00048))) +(syntax-dispatch +g00048 +'(pair (any) +atom) +(vector)))) +g00046))) +(syntax-dispatch +g00046 +'(atom) +(vector)))) +args))) +(extract-bound-syntax-ids (lambda (pattern keys) +((letrec ((gen (lambda (p +n +ids) +(if (identifier? +p) +(if (key? p +keys) +ids +(cons (list p +n) +ids)) +((lambda (g00068) +((lambda (g00069) +((lambda (g00067) +(if (not (eq? g00067 +'no)) +((lambda (_x +_dots) +(if (ellipsis? +_dots) +(gen _x +(+ n +1) +ids) +(g00069))) +(car g00067) +(cadr g00067)) +(g00069))) +(syntax-dispatch +g00068 +'(pair (any) +pair +(any) +atom) +(vector)))) +(lambda () +((lambda (g00071) +((lambda (g00070) +(if (not (eq? g00070 +'no)) +((lambda (_x +_y) +(gen _x +n +(gen _y +n +ids))) +(car g00070) +(cadr g00070)) +((lambda (g00073) +((lambda (g00072) +(if (not (eq? g00072 +'no)) +((lambda (_x) +(gen _x +n +ids)) +(car g00072)) +((lambda (g00075) +((lambda (g00074) +(if (not (eq? g00074 +'no)) +((lambda (_x) +ids) +(car g00074)) +(syntax-error +g00075))) +(syntax-dispatch +g00075 +'(any) +(vector)))) +g00073))) +(syntax-dispatch +g00073 +'(vector +each +any) +(vector)))) +g00071))) +(syntax-dispatch +g00071 +'(pair (any) +any) +(vector)))) +g00068)))) +p))))) +gen) +pattern +0 +'()))) +(valid-syntax-pattern? (lambda (pattern keys) +(letrec ((check? (lambda (p +ids) +(if (identifier? +p) +(if (eq? ids +'no) +ids +(if (key? p +keys) +ids +(if (if (not (ellipsis? +p)) +(not (memid +p +ids)) +#f) +(cons p +ids) +'no))) +((lambda (g00077) +((lambda (g00078) +((lambda (g00076) +(if (not (eq? g00076 +'no)) +((lambda (_x +_dots) +(if (ellipsis? +_dots) +(check? +_x +ids) +(g00078))) +(car g00076) +(cadr g00076)) +(g00078))) +(syntax-dispatch +g00077 +'(pair (any) +pair +(any) +atom) +(vector)))) +(lambda () +((lambda (g00080) +((lambda (g00079) +(if (not (eq? g00079 +'no)) +((lambda (_x +_y) +(check? +_x +(check? +_y +ids))) +(car g00079) +(cadr g00079)) +((lambda (g00082) +((lambda (g00081) +(if (not (eq? g00081 +'no)) +((lambda (_x) +(check? +_x +ids)) +(car g00081)) +((lambda (g00084) +((lambda (g00083) +(if (not (eq? g00083 +'no)) +((lambda (_x) +ids) +(car g00083)) +(syntax-error +g00084))) +(syntax-dispatch +g00084 +'(any) +(vector)))) +g00082))) +(syntax-dispatch +g00082 +'(vector +each +any) +(vector)))) +g00080))) +(syntax-dispatch +g00080 +'(pair (any) +any) +(vector)))) +g00077)))) +p))))) +(not (eq? (check? +pattern +'()) +'no))))) +(valid-keyword? (lambda (k) +(if (identifier? k) +(not (free-identifier=? +k +'...)) +#f))) +(convert-syntax-dispatch-pattern (lambda (pattern +keys) +((letrec ((gen (lambda (p) +(if (identifier? +p) +(if (key? p +keys) +(cons '#(syntax-object +free-id +(top)) +(key-index +p +keys)) +(list '#(syntax-object +any +(top)))) +((lambda (g00086) +((lambda (g00087) +((lambda (g00085) +(if (not (eq? g00085 +'no)) +((lambda (_x +_dots) +(if (ellipsis? +_dots) +(cons '#(syntax-object +each +(top)) +(gen _x)) +(g00087))) +(car g00085) +(cadr g00085)) +(g00087))) +(syntax-dispatch +g00086 +'(pair (any) +pair +(any) +atom) +(vector)))) +(lambda () +((lambda (g00089) +((lambda (g00088) +(if (not (eq? g00088 +'no)) +((lambda (_x +_y) +(cons '#(syntax-object +pair +(top)) +(cons (gen _x) +(gen _y)))) +(car g00088) +(cadr g00088)) +((lambda (g00091) +((lambda (g00090) +(if (not (eq? g00090 +'no)) +((lambda (_x) +(cons '#(syntax-object +vector +(top)) +(gen _x))) +(car g00090)) +((lambda (g00093) +((lambda (g00092) +(if (not (eq? g00092 +'no)) +((lambda (_x) +(cons '#(syntax-object +atom +(top)) +p)) +(car g00092)) +(syntax-error +g00093))) +(syntax-dispatch +g00093 +'(any) +(vector)))) +g00091))) +(syntax-dispatch +g00091 +'(vector +each +any) +(vector)))) +g00089))) +(syntax-dispatch +g00089 +'(pair (any) +any) +(vector)))) +g00086)))) +p))))) +gen) +pattern))) +(key-index (lambda (p keys) +(- (length keys) +(length (memid p keys))))) +(key? (lambda (p keys) +(if (identifier? p) (memid p keys) #f))) +(memid (lambda (i ids) +(if (not (null? ids)) +(if (bound-identifier=? i (car ids)) +ids +(memid i (cdr ids))) +#f))) +(ellipsis? (lambda (x) +(if (identifier? x) +(free-identifier=? x '...) +#f)))) +(lambda (x) +((lambda (g00030) +((lambda (g00031) +((lambda (g00029) +(if (not (eq? g00029 'no)) +((lambda (__ _val _key) +(if (syncase:andmap valid-keyword? _key) +(list '#(syntax-object +syntax-error +(top)) +_val) +(g00031))) +(car g00029) +(cadr g00029) +(caddr g00029)) +(g00031))) +(syntax-dispatch +g00030 +'(pair (any) +pair +(any) +pair +(each any) +atom) +(vector)))) +(lambda () +((lambda (g00033) +((lambda (g00034) +((lambda (g00032) +(if (not (eq? g00032 'no)) +(apply +(lambda (__ +_val +_key +_pat +_exp) +(if (if (identifier? +_pat) +(if (syncase:andmap +valid-keyword? +_key) +(syncase:andmap +(lambda (x) +(not (free-identifier=? +_pat +x))) +(cons '... +_key)) +#f) +#f) +(list (list '#(syntax-object +syntax-lambda +(top)) +(list (list _pat +0)) +_exp) +_val) +(g00034))) +g00032) +(g00034))) +(syntax-dispatch +g00033 +'(pair (any) +pair +(any) +pair +(each any) +pair +(pair (any) pair (any) atom) +atom) +(vector)))) +(lambda () +((lambda (g00036) +((lambda (g00037) +((lambda (g00035) +(if (not (eq? g00035 'no)) +(apply +(lambda (__ +_val +_key +_pat +_exp +_e1 +_e2 +_e3) +(if (if (syncase:andmap +valid-keyword? +_key) +(valid-syntax-pattern? +_pat +_key) +#f) +((lambda (g00044) +((lambda (g00043) +(if (not (eq? g00043 +'no)) +((lambda (_pattern +_y +_call) +(list '#(syntax-object +let +(top)) +(list (list '#(syntax-object +x +(top)) +_val)) +(list '#(syntax-object +let +(top)) +(list (list _y +(list '#(syntax-object +syntax-dispatch +(top)) +'#(syntax-object +x +(top)) +(list '#(syntax-object +quote +(top)) +_pattern) +(list '#(syntax-object +syntax +(top)) +(list->vector +_key))))) +(list '#(syntax-object +if +(top)) +(list '#(syntax-object +not +(top)) +(list '#(syntax-object +eq? +(top)) +_y +(list '#(syntax-object +quote +(top)) +'#(syntax-object +no +(top))))) +_call +(cons '#(syntax-object +syntax-case +(top)) +(cons '#(syntax-object +x +(top)) +(cons _key +(map (lambda (__e1 +__e2 +__e3) +(cons __e1 +(cons __e2 +__e3))) +_e1 +_e2 +_e3)))))))) +(car g00043) +(cadr g00043) +(caddr +g00043)) +(syntax-error +g00044))) +(syntax-dispatch +g00044 +'(pair (any) +pair +(any) +pair +(any) +atom) +(vector)))) +(list (convert-syntax-dispatch-pattern +_pat +_key) +'#(syntax-object +y +(top)) +(syncase:build-dispatch-call +(extract-bound-syntax-ids +_pat +_key) +_exp +'#(syntax-object +y +(top))))) +(g00037))) +g00035) +(g00037))) +(syntax-dispatch +g00036 +'(pair (any) +pair +(any) +pair +(each any) +pair +(pair (any) +pair +(any) +atom) +each +pair +(any) +pair +(any) +each +any) +(vector)))) +(lambda () +((lambda (g00039) +((lambda (g00040) +((lambda (g00038) +(if (not (eq? g00038 +'no)) +(apply +(lambda (__ +_val +_key +_pat +_fender +_exp +_e1 +_e2 +_e3) +(if (if (syncase:andmap +valid-keyword? +_key) +(valid-syntax-pattern? +_pat +_key) +#f) +((lambda (g00042) +((lambda (g00041) +(if (not (eq? g00041 +'no)) +((lambda (_pattern +_y +_dorest +_call) +(list '#(syntax-object +let +(top)) +(list (list '#(syntax-object +x +(top)) +_val)) +(list '#(syntax-object +let +(top)) +(list (list _dorest +(list '#(syntax-object +lambda +(top)) +'() +(cons '#(syntax-object +syntax-case +(top)) +(cons '#(syntax-object +x +(top)) +(cons _key +(map (lambda (__e1 +__e2 +__e3) +(cons __e1 +(cons __e2 +__e3))) +_e1 +_e2 +_e3))))))) +(list '#(syntax-object +let +(top)) +(list (list _y +(list '#(syntax-object +syntax-dispatch +(top)) +'#(syntax-object +x +(top)) +(list '#(syntax-object +quote +(top)) +_pattern) +(list '#(syntax-object +syntax +(top)) +(list->vector +_key))))) +(list '#(syntax-object +if +(top)) +(list '#(syntax-object +not +(top)) +(list '#(syntax-object +eq? +(top)) +_y +(list '#(syntax-object +quote +(top)) +'#(syntax-object +no +(top))))) +_call +(list _dorest)))))) +(car g00041) +(cadr g00041) +(caddr +g00041) +(cadddr +g00041)) +(syntax-error +g00042))) +(syntax-dispatch +g00042 +'(pair (any) +pair +(any) +pair +(any) +pair +(any) +atom) +(vector)))) +(list (convert-syntax-dispatch-pattern +_pat +_key) +'#(syntax-object +y +(top)) +'#(syntax-object +dorest +(top)) +(syncase:build-dispatch-call +(extract-bound-syntax-ids +_pat +_key) +(list '#(syntax-object +if +(top)) +_fender +_exp +(list '#(syntax-object +dorest +(top)))) +'#(syntax-object +y +(top))))) +(g00040))) +g00038) +(g00040))) +(syntax-dispatch +g00039 +'(pair (any) +pair +(any) +pair +(each any) +pair +(pair (any) +pair +(any) +pair +(any) +atom) +each +pair +(any) +pair +(any) +each +any) +(vector)))) +(lambda () +(syntax-error +g00039)))) +g00036)))) +g00033)))) +g00030)))) +x))))))) diff --git a/scaglob.scm b/scaglob.scm new file mode 100644 index 0000000..32a027c --- /dev/null +++ b/scaglob.scm @@ -0,0 +1,32 @@ +;;; "scaglob.scm" syntax-case initializations +;;; Copyright (C) 1992 R. Kent Dybvig +;;; +;;; Permission to copy this software, in whole or in part, to use this +;;; software for any lawful purpose, and to redistribute this software +;;; is granted subject to the restriction that all copies made of this +;;; software must include this copyright notice in full. This software +;;; is provided AS IS, with NO WARRANTY, EITHER EXPRESS OR IMPLIED, +;;; INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY +;;; OR FITNESS FOR ANY PARTICULAR PURPOSE. IN NO EVENT SHALL THE +;;; AUTHORS BE LIABLE FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES OF ANY +;;; NATURE WHATSOEVER. + +;;; From: Harald Hanche-Olsen <hanche@imf.unit.no> + +;;; init.ss +;;; Robert Hieb & Kent Dybvig +;;; 92/06/18 + +; These initializations are done here rather than "expand.ss" so that +; "expand.ss" can be loaded twice (for bootstrapping purposes). + +(define expand-syntax #f) +(define syntax-dispatch #f) +(define generate-temporaries #f) +(define identifier? #f) +(define syntax-error #f) +(define syntax-object->datum #f) +(define bound-identifier=? #f) +(define free-identifier=? #f) +(define syncase:install-global-transformer #f) +(define implicit-identifier #f) diff --git a/scainit.scm b/scainit.scm new file mode 100644 index 0000000..1103bc6 --- /dev/null +++ b/scainit.scm @@ -0,0 +1,103 @@ +;;; "scainit.scm" Syntax-case macros port to SLIB -*- Scheme -*- +;;; Copyright (C) 1992 R. Kent Dybvig +;;; +;;; Permission to copy this software, in whole or in part, to use this +;;; software for any lawful purpose, and to redistribute this software +;;; is granted subject to the restriction that all copies made of this +;;; software must include this copyright notice in full. This software +;;; is provided AS IS, with NO WARRANTY, EITHER EXPRESS OR IMPLIED, +;;; INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY +;;; OR FITNESS FOR ANY PARTICULAR PURPOSE. IN NO EVENT SHALL THE +;;; AUTHORS BE LIABLE FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES OF ANY +;;; NATURE WHATSOEVER. + +;;; From: Harald Hanche-Olsen <hanche@imf.unit.no> + +;;; compat.ss +;;; Robert Hieb & Kent Dybvig +;;; 92/06/18 + +(require 'common-list-functions) ;to pick up EVERY +(define syncase:andmap comlist:every) + +; In Chez Scheme "(syncase:void)" returns an object that is ignored by the +; REP loop. It is returned whenever a "nonspecified" value is specified +; by the standard. The following should pick up an appropriate value. + +(define syncase:void + (let ((syncase:void-object (if #f #f))) + (lambda () syncase:void-object))) + +(define syncase:eval-hook slib:eval) + +(define syncase:error-hook slib:error) + +(define syncase:new-symbol-hook + (let ((c 0)) + (lambda (string) + (set! c (+ c 1)) + (string->symbol + (string-append string ":Sca" (number->string c)))))) + +(define syncase:put-global-definition-hook #f) +(define syncase:get-global-definition-hook #f) +(let ((*macros* '())) + (set! syncase:put-global-definition-hook + (lambda (symbol binding) + (let ((pair (assq symbol *macros*))) + (if pair + (set-cdr! pair binding) + (set! *macros* (cons (cons symbol binding) *macros*)))))) + (set! syncase:get-global-definition-hook + (lambda (symbol) + (let ((pair (assq symbol *macros*))) + (and pair (cdr pair)))))) + + +;;;! expand.pp requires list* +(define (syncase:list* . args) + (if (null? args) + '() + (let ((r (reverse args))) + (append (reverse (cdr r)) + (car r) ; Last arg + '())))) ; Make sure the last arg is copied + +(define syntax-error syncase:error-hook) +(define impl-error slib:error) + +(define base:eval slib:eval) +(define syncase:eval base:eval) +(define macro:eval base:eval) +(define syncase:expand #f) +(define macro:expand #f) +(define (syncase:expand-install-hook expand) + (set! syncase:eval (lambda (x) (base:eval (expand x)))) + (set! macro:eval syncase:eval) + (set! syncase:expand expand) + (set! macro:expand syncase:expand)) +;;; We Need This for bootstrapping purposes: +(define (syncase:load <pathname>) + (slib:eval-load <pathname> syncase:eval)) +(define macro:load syncase:load) + +(define syncase:sanity-check #f) +;;; LOADING THE SYSTEM ITSELF: +(let ((here (lambda (file) + (in-vicinity (library-vicinity) file))) + (scmhere (lambda (file) + (in-vicinity (library-vicinity) file (scheme-file-suffix))))) + (for-each (lambda (file) (slib:load (here file))) + '("scaoutp" + "scaglob" + "scaexpp")) + (syncase:expand-install-hook expand-syntax) + (syncase:load (here "scamacr")) + (set! syncase:sanity-check + (lambda () + (syncase:load (scmhere "sca-exp")) + (syncase:expand-install-hook expand-syntax) + (syncase:load (scmhere "sca-macr"))))) + +(provide 'syntax-case) +(provide 'macro) diff --git a/scamacr.scm b/scamacr.scm new file mode 100644 index 0000000..016d7fb --- /dev/null +++ b/scamacr.scm @@ -0,0 +1,181 @@ +;;; "scamacr.scm" syntax-case macros for Scheme constructs +;;; Copyright (C) 1992 R. Kent Dybvig +;;; +;;; Permission to copy this software, in whole or in part, to use this +;;; software for any lawful purpose, and to redistribute this software +;;; is granted subject to the restriction that all copies made of this +;;; software must include this copyright notice in full. This software +;;; is provided AS IS, with NO WARRANTY, EITHER EXPRESS OR IMPLIED, +;;; INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY +;;; OR FITNESS FOR ANY PARTICULAR PURPOSE. IN NO EVENT SHALL THE +;;; AUTHORS BE LIABLE FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES OF ANY +;;; NATURE WHATSOEVER. + +;;; Written by Robert Hieb & Kent Dybvig + +;;; This file was munged by a simple minded sed script since it left +;;; its original authors' hands. See syncase.sh for the horrid details. + +;;; macro-defs.ss +;;; Robert Hieb & Kent Dybvig +;;; 92/06/18 + +(define-syntax with-syntax + (lambda (x) + (syntax-case x () + ((_ () e1 e2 ...) + (syntax (begin e1 e2 ...))) + ((_ ((out in)) e1 e2 ...) + (syntax (syntax-case in () (out (begin e1 e2 ...))))) + ((_ ((out in) ...) e1 e2 ...) + (syntax (syntax-case (list in ...) () + ((out ...) (begin e1 e2 ...)))))))) + +(define-syntax syntax-rules + (lambda (x) + (syntax-case x () + ((_ (k ...) ((keyword . pattern) template) ...) + (with-syntax (((dummy ...) + (generate-temporaries (syntax (keyword ...))))) + (syntax (lambda (x) + (syntax-case x (k ...) + ((dummy . pattern) (syntax template)) + ...)))))))) + +(define-syntax or + (lambda (x) + (syntax-case x () + ((_) (syntax #f)) + ((_ e) (syntax e)) + ((_ e1 e2 e3 ...) + (syntax (let ((t e1)) (if t t (or e2 e3 ...)))))))) + +(define-syntax and + (lambda (x) + (syntax-case x () + ((_ e1 e2 e3 ...) (syntax (if e1 (and e2 e3 ...) #f))) + ((_ e) (syntax e)) + ((_) (syntax #t))))) + +(define-syntax cond + (lambda (x) + (syntax-case x (else =>) + ((_ (else e1 e2 ...)) + (syntax (begin e1 e2 ...))) + ((_ (e0)) + (syntax (let ((t e0)) (if t t)))) + ((_ (e0) c1 c2 ...) + (syntax (let ((t e0)) (if t t (cond c1 c2 ...))))) + ((_ (e0 => e1)) (syntax (let ((t e0)) (if t (e1 t))))) + ((_ (e0 => e1) c1 c2 ...) + (syntax (let ((t e0)) (if t (e1 t) (cond c1 c2 ...))))) + ((_ (e0 e1 e2 ...)) (syntax (if e0 (begin e1 e2 ...)))) + ((_ (e0 e1 e2 ...) c1 c2 ...) + (syntax (if e0 (begin e1 e2 ...) (cond c1 c2 ...))))))) + +(define-syntax let* + (lambda (x) + (syntax-case x () + ((let* () e1 e2 ...) + (syntax (let () e1 e2 ...))) + ((let* ((x1 v1) (x2 v2) ...) e1 e2 ...) + (comlist:every identifier? (syntax (x1 x2 ...))) + (syntax (let ((x1 v1)) (let* ((x2 v2) ...) e1 e2 ...))))))) + +(define-syntax case + (lambda (x) + (syntax-case x (else) + ((_ v (else e1 e2 ...)) + (syntax (begin v e1 e2 ...))) + ((_ v ((k ...) e1 e2 ...)) + (syntax (if (memv v '(k ...)) (begin e1 e2 ...)))) + ((_ v ((k ...) e1 e2 ...) c1 c2 ...) + (syntax (let ((x v)) + (if (memv x '(k ...)) + (begin e1 e2 ...) + (case x c1 c2 ...)))))))) + +(define-syntax do + (lambda (orig-x) + (syntax-case orig-x () + ((_ ((var init . step) ...) (e0 e1 ...) c ...) + (with-syntax (((step ...) + (map (lambda (v s) + (syntax-case s () + (() v) + ((e) (syntax e)) + (_ (syntax-error orig-x)))) + (syntax (var ...)) + (syntax (step ...))))) + (syntax-case (syntax (e1 ...)) () + (() (syntax (let doloop ((var init) ...) + (if (not e0) + (begin c ... (doloop step ...)))))) + ((e1 e2 ...) + (syntax (let doloop ((var init) ...) + (if e0 + (begin e1 e2 ...) + (begin c ... (doloop step ...)))))))))))) + +(define-syntax quasiquote + (letrec + ((gen-cons + (lambda (x y) + (syntax-case x (quote) + ((quote x) + (syntax-case y (quote list) + ((quote y) (syntax (quote (x . y)))) + ((list y ...) (syntax (list (quote x) y ...))) + (y (syntax (cons (quote x) y))))) + (x (syntax-case y (quote list) + ((quote ()) (syntax (list x))) + ((list y ...) (syntax (list x y ...))) + (y (syntax (cons x y)))))))) + + (gen-append + (lambda (x y) + (syntax-case x (quote list cons) + ((quote (x1 x2 ...)) + (syntax-case y (quote) + ((quote y) (syntax (quote (x1 x2 ... . y)))) + (y (syntax (append (quote (x1 x2 ...) y)))))) + ((quote ()) y) + ((list x1 x2 ...) + (gen-cons (syntax x1) (gen-append (syntax (list x2 ...)) y))) + (x (syntax-case y (quote list) + ((quote ()) (syntax x)) + (y (syntax (append x y)))))))) + + (gen-vector + (lambda (x) + (syntax-case x (quote list) + ((quote (x ...)) (syntax (quote #(x ...)))) + ((list x ...) (syntax (vector x ...))) + (x (syntax (list->vector x)))))) + + (gen + (lambda (p lev) + (syntax-case p (unquote unquote-splicing quasiquote) + ((unquote p) + (if (= lev 0) + (syntax p) + (gen-cons (syntax (quote unquote)) + (gen (syntax (p)) (- lev 1))))) + (((unquote-splicing p) . q) + (if (= lev 0) + (gen-append (syntax p) (gen (syntax q) lev)) + (gen-cons (gen-cons (syntax (quote unquote-splicing)) + (gen (syntax p) (- lev 1))) + (gen (syntax q) lev)))) + ((quasiquote p) + (gen-cons (syntax (quote quasiquote)) + (gen (syntax (p)) (+ lev 1)))) + ((p . q) + (gen-cons (gen (syntax p) lev) (gen (syntax q) lev))) + (#(x ...) (gen-vector (gen (syntax (x ...)) lev))) + (p (syntax (quote p))))))) + + (lambda (x) + (syntax-case x () + ((- e) (gen (syntax e) 0)))))) + diff --git a/scanf.scm b/scanf.scm new file mode 100644 index 0000000..b1ae30a --- /dev/null +++ b/scanf.scm @@ -0,0 +1,351 @@ +;;;;"scanf.scm" implemenation of formated input +;Copyright (C) 1996 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;; Originally jjb@isye.gatech.edu (John Bartholdi) wrote some public +;;; domain code for a subset of scanf, but it was too difficult to +;;; extend to POSIX pattern compliance. Jan 96, I rewrote the scanf +;;; functions starting from the POSIX man pages. + +(require 'string-port) + +(define (stdio:scan-and-set format-string input-port . args) + (define setters args) + (if (equal? '(#f) args) (set! args #f)) + (cond + ((not (equal? "" format-string)) + (call-with-input-string + format-string + (lambda (format-port) + + (define items '()) + (define chars-scanned 0) + (define assigned-count 0) + + (define (char-non-numeric? c) (not (char-numeric? c))) + + (define (flush-whitespace port) + (do ((c (peek-char port) (peek-char port)) + (i 0 (+ 1 i))) + ((or (eof-object? c) (not (char-whitespace? c))) i) + (read-char port))) + + (define (flush-whitespace-input) + (set! chars-scanned (+ (flush-whitespace input-port) chars-scanned))) + + (define (read-input-char) + (set! chars-scanned (+ 1 chars-scanned)) + (read-char input-port)) + + (define (add-item report-field? next-item) + (cond (args + (cond ((null? setters) + (slib:error 'scanf "not enough variables for format" + format-string)) + ((not next-item) (return)) + ((not report-field?) (loop1)) + (else + (let ((suc ((car setters) next-item))) + (cond ((not (boolean? suc)) + (slib:warn 'scanf "setter returned non-boolean" + suc))) + (set! setters (cdr setters)) + (cond ((not suc) (return)) + ((eqv? -1 report-field?) (loop1)) + (else + (set! assigned-count (+ 1 assigned-count)) + (loop1))))))) + ((not next-item) (return)) + (report-field? (set! items (cons next-item items)) + (loop1)) + (else (loop1)))) + + (define (return) + (cond ((and (zero? chars-scanned) + (eof-object? (peek-char input-port))) + (peek-char input-port)) + (args assigned-count) + (else (reverse items)))) + + (define (read-string width separator?) + (cond (width + (let ((str (make-string width))) + (do ((i 0 (+ 1 i))) + ((>= i width) + str) + (let ((c (peek-char input-port))) + (cond ((eof-object? c) + (set! str (substring str 0 i)) + (set! i width)) + ((separator? c) + (set! str (if (zero? i) "" (substring str 0 i))) + (set! i width)) + (else + (string-set! str i (read-input-char)))))))) + (else + (do ((c (peek-char input-port) (peek-char input-port)) + (l '() (cons c l))) + ((or (eof-object? c) (separator? c)) + (list->string (reverse l))) + (read-input-char))))) + + (define (read-word width separator?) + (let ((l (read-string width separator?))) + (if (zero? (string-length l)) #f l))) + + (define (loop1) + (define fc (read-char format-port)) + (cond + ((eof-object? fc) + (return)) + ((char-whitespace? fc) + (flush-whitespace format-port) + (flush-whitespace-input) + (loop1)) + ((eqv? #\% fc) ; interpret next format + (set! fc (read-char format-port)) + (let ((report-field? (not (eqv? #\* fc))) + (width #f)) + + (define (width--) (if width (set! width (+ -1 width)))) + + (define (read-u) + (string->number (read-string width char-non-numeric?))) + + (define (read-o) + (string->number + (read-string + width + (lambda (c) (not (memv c '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7))))) + 8)) + + (define (read-x) + (string->number + (read-string + width + (lambda (c) (not (memv (char-downcase c) + '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 + #\9 #\a #\b #\c #\d #\e #\f))))) + 16)) + + (define (read-radixed-unsigned) + (let ((c (peek-char input-port))) + (case c + ((#\0) (read-input-char) + (width--) + (set! c (peek-char input-port)) + (case c + ((#\x #\X) (read-input-char) + (width--) + (read-x)) + (else (read-o)))) + (else (read-u))))) + + (define (read-ui) + (let* ((dot? #f) + (mantissa (read-word + width + (lambda (c) + (not (or (char-numeric? c) + (cond (dot? #f) + ((eqv? #\. c) + (set! dot? #t) + #t) + (else #f))))))) + (exponent (cond + ((not mantissa) #f) + ((and (or (not width) (> width 1)) + (memv (peek-char input-port) '(#\E #\e))) + (read-input-char) + (width--) + (let ((expsign + (case (peek-char input-port) + ((#\-) (read-input-char) + (width--) + "-") + ((#\+) (read-input-char) + (width--) + "+") + (else ""))) + (expint + (and + (or (not width) (positive? width)) + (read-word width char-non-numeric?)))) + (and expint (string-append + "e" expsign expint)))) + (else #f)))) + (and mantissa + (string->number + (string-append + "#i" (or mantissa "") (or exponent "")))))) + + (define (read-signed proc) + (case (peek-char input-port) + ((#\-) (read-input-char) + (width--) + (let ((ret (proc))) + (and ret (- ret)))) + ((#\+) (read-input-char) + (width--) + (proc)) + (else (proc)))) + + ;;(trace read-word read-signed read-ui read-radixed-unsigned read-x read-o read-u) + + (cond ((not report-field?) (set! fc (read-char format-port)))) + (if (char-numeric? fc) (set! width 0)) + (do () ((or (eof-object? fc) (char-non-numeric? fc))) + (set! width (+ (* 10 width) (string->number (string fc)))) + (set! fc (read-char format-port))) + (case fc ;ignore h,l,L modifiers. + ((#\h #\l #\L) (set! fc (read-char format-port)))) + (case fc + ((#\n) (if (not report-field?) + (slib:error 'scanf "not saving %n??")) + (add-item -1 chars-scanned)) ;-1 is special flag. + ((#\c #\C) + (if (not width) (set! width 1)) + (let ((str (make-string width))) + (do ((i 0 (+ 1 i))) + ((>= i width) + (add-item report-field? str)) + (let ((c (read-char input-port))) + (cond ((eof-object? c) + (set! str c) + (set! i width)) + (else (string-set! str i c))))))) + ((#\s #\S) + ;;(flush-whitespace-input) + (add-item report-field? (read-word width char-whitespace?))) + ((#\[) + (set! fc (read-char format-port)) + (let ((allbut #f)) + (case fc + ((#\^) (set! allbut #t) + (set! fc (read-char format-port)))) + + (let scanloop ((scanset (list fc))) + (set! fc (read-char format-port)) + (case fc + ((#\-) + (set! fc (peek-char format-port)) + (cond + ((and (char<? (car scanset) fc) + (not (eqv? #\] fc))) + (set! fc (char->integer fc)) + (do ((i (char->integer (car scanset)) (+ 1 i))) + ((> i fc) (scanloop scanset)) + (set! scanset (cons (integer->char i) scanset)))) + (else (scanloop (cons #\- scanset))))) + ((#\]) + (add-item report-field? + (read-word + width + (if allbut (lambda (c) (memv c scanset)) + (lambda (c) (not (memv c scanset))))))) + (else (cond + ((eof-object? fc) + (slib:error 'scanf "unmatched [ in format")) + (else (scanloop (cons fc scanset))))))))) + ((#\o #\O) + ;;(flush-whitespace-input) + (add-item report-field? (read-o))) + ((#\u #\U) + ;;(flush-whitespace-input) + (add-item report-field? (read-u))) + ((#\d #\D) + ;;(flush-whitespace-input) + (add-item report-field? (read-signed read-u))) + ((#\x #\X) + ;;(flush-whitespace-input) + (add-item report-field? (read-x))) + ((#\e #\E #\f #\F #\g #\G) + ;;(flush-whitespace-input) + (add-item report-field? (read-signed read-ui))) + ((#\i) + ;;(flush-whitespace-input) + (add-item report-field? (read-signed read-radixed-unsigned))) + ((#\%) + (cond ((or width (not report-field?)) + (slib:error 'SCANF "%% has modifiers?")) + ((eqv? #\% (read-input-char)) + (loop1)) + (else (return)))) + (else (slib:error 'SCANF + "Unknown format directive:" fc))))) + ((eqv? (peek-char input-port) fc) + (read-input-char) + (loop1)) + (else (return)))) + + (loop1)))) + (args 0) + (else '()))) + +;;;This implements a Scheme-oriented version of SCANF: returns a list of +;;;objects read (rather than set!-ing values). + +(define (scanf-read-list format-string . optarg) + (define input-port + (cond ((null? optarg) (current-input-port)) + ((not (null? (cdr optarg))) + (slib:error 'scanf-read-list 'wrong-number-of-args optarg)) + (else (car optarg)))) + (cond ((input-port? input-port) + (stdio:scan-and-set format-string input-port #f)) + ((string? input-port) + (call-with-input-string + input-port (lambda (input-port) + (stdio:scan-and-set format-string input-port #f)))) + (else (slib:error 'scanf-read-list "argument not port" input-port)))) + +(define (stdio:setter-procedure sexp) + (let ((v (gentemp))) + (cond ((symbol? sexp) `(lambda (,v) (set! ,sexp ,v) #t)) + ((not (and (pair? sexp) (list? sexp))) + (slib:error 'scanf "setter expression not understood" sexp)) + (else + (case (car sexp) + ((vector-ref) `(lambda (,v) (vector-set! ,@(cdr sexp) ,v) #t)) + ((substring) + (require 'rev2-procedures) + `(lambda (,v) (substring-move-left! + ,v 0 (min (string-length ,v) + (- ,(cadddr sexp) ,(caddr sexp))) + ,(cadr sexp) ,(caddr sexp)) + #t)) + ((list-ref) + (require 'rev4-optional-procedures) + `(lambda (,v) (set-car! (list-tail ,@(cdr sexp)) ,v) #t)) + ((car) `(lambda (,v) (set-car! ,@(cdr sexp) ,v) #t)) + ((cdr) `(lambda (,v) (set-cdr! ,@(cdr sexp) ,v) #t)) + (else (slib:error 'scanf "setter not known" sexp))))))) + +(defmacro scanf (format-string . args) + `(stdio:scan-and-set ,format-string (current-input-port) + ,@(map stdio:setter-procedure args))) + +(defmacro sscanf (str format-string . args) + `(call-with-input-string + ,str (lambda (input-port) + (stdio:scan-and-set ,format-string input-port + ,@(map stdio:setter-procedure args))))) + +(defmacro fscanf (input-port format-string . args) + `(stdio:scan-and-set ,format-string ,input-port + ,@(map stdio:setter-procedure args))) diff --git a/scaoutp.scm b/scaoutp.scm new file mode 100644 index 0000000..b9730ca --- /dev/null +++ b/scaoutp.scm @@ -0,0 +1,93 @@ +;;; "scaoutp.scm" syntax-case output +;;; Copyright (C) 1992 R. Kent Dybvig +;;; +;;; Permission to copy this software, in whole or in part, to use this +;;; software for any lawful purpose, and to redistribute this software +;;; is granted subject to the restriction that all copies made of this +;;; software must include this copyright notice in full. This software +;;; is provided AS IS, with NO WARRANTY, EITHER EXPRESS OR IMPLIED, +;;; INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY +;;; OR FITNESS FOR ANY PARTICULAR PURPOSE. IN NO EVENT SHALL THE +;;; AUTHORS BE LIABLE FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES OF ANY +;;; NATURE WHATSOEVER. + +;;; Written by Robert Hieb & Kent Dybvig + +;;; This file was munged by a simple minded sed script since it left +;;; its original authors' hands. See syncase.sh for the horrid details. + +;;; output.ss +;;; Robert Hieb & Kent Dybvig +;;; 92/06/18 + +; The output routines can be tailored to feed a specific system or compiler. +; They are set up here to generate the following subset of standard Scheme: + +; <expression> :== <application> +; | <variable> +; | (set! <variable> <expression>) +; | (define <variable> <expression>) +; | (lambda (<variable>*) <expression>) +; | (lambda <variable> <expression>) +; | (lambda (<variable>+ . <variable>) <expression>) +; | (letrec (<binding>+) <expression>) +; | (if <expression> <expression> <expression>) +; | (begin <expression> <expression>) +; | (quote <datum>) +; <application> :== (<expression>+) +; <binding> :== (<variable> <expression>) +; <variable> :== <symbol> + +; Definitions are generated only at top level. + +(define syncase:build-application + (lambda (fun-exp arg-exps) + `(,fun-exp ,@arg-exps))) + +(define syncase:build-conditional + (lambda (test-exp then-exp else-exp) + `(if ,test-exp ,then-exp ,else-exp))) + +(define syncase:build-lexical-reference (lambda (var) var)) + +(define syncase:build-lexical-assignment + (lambda (var exp) + `(set! ,var ,exp))) + +(define syncase:build-global-reference (lambda (var) var)) + +(define syncase:build-global-assignment + (lambda (var exp) + `(set! ,var ,exp))) + +(define syncase:build-lambda + (lambda (vars exp) + `(lambda ,vars ,exp))) + +(define syncase:build-improper-lambda + (lambda (vars var exp) + `(lambda (,@vars . ,var) ,exp))) + +(define syncase:build-data + (lambda (exp) + `(quote ,exp))) + +(define syncase:build-identifier + (lambda (id) + `(quote ,id))) + +(define syncase:build-sequence + (lambda (exps) + (if (null? (cdr exps)) + (car exps) + `(begin ,(car exps) ,(syncase:build-sequence (cdr exps)))))) + +(define syncase:build-letrec + (lambda (vars val-exps body-exp) + (if (null? vars) + body-exp + `(letrec ,(map list vars val-exps) ,body-exp)))) + +(define syncase:build-global-definition + (lambda (var val) + `(define ,var ,val))) diff --git a/scheme2c.init b/scheme2c.init new file mode 100644 index 0000000..cace8c0 --- /dev/null +++ b/scheme2c.init @@ -0,0 +1,291 @@ +;"scheme2c.init" Initialisation for SLIB for Scheme->C on Sun -*-scheme-*- +;Copyright 1991, 1992, 1993 Aubrey Jaffer +;Copyright 1991 David Love +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;Modified by David Love (d.love@daresbury.ac.uk) 10/12/91 +;; NB this is for the 01nov91 (and, presumably, later ones, +;; although those may not need the bug fixes done at the end). +;; Earlier versions definitely aren't rev4 conformant. Check +;; `ieee-floating-point' and `system' in *features* for non-Sun un*x +;; versions and `system' and the vicinity stuff (at least) for +;; non-un*x versions. + +;; Of course, if you make serious use of library functions you'll want +;; to compile them and use Scheme->C modules. + +(define (software-type) 'UNIX) + +;;; (scheme-implementation-type) should return the name of the scheme +;;; implementation loading this file. + +(define (scheme-implementation-type) 'Scheme->C) + +;;; (scheme-implementation-version) should return a string describing +;;; the version the scheme implementation loading this file. + +(define (scheme-implementation-version) "?01nov91") + +(define (implementation-vicinity) + (case (software-type) + ((UNIX) "/usr/local/lib/scheme/") + ((VMS) "scheme$src:") + ((MS-DOS) "C:\\scheme\\"))) + +;;; (library-vicinity) should be defined to be the pathname of the +;;; directory where files of Scheme library functions reside. + +(define library-vicinity + (let ((library-path + (case (software-type) + ((UNIX) "/usr/local/lib/slib/") + ((VMS) "lib$scheme:") + ((MS-DOS) "C:\\SLIB\\") + (else "")))) + (lambda () library-path))) + +;;; *FEATURES* should be set to a list of symbols describing features +;;; of this implementation. See Template.scm for the list of feature +;;; names. + +(define *features* + '( + source ;can load scheme source files + ;(slib:load-source "filename") +; compiled ;can load compiled files + ;(slib:load-compiled "filename") + rev4-report + ;; Follows rev4 as far as I can tell, modulo '() being false, + ;; number syntax (see doc), incomplete tail recursion (see + ;; docs) and a couple of bugs in some versions -- see below. + rev3-report ;conforms to +; ieee-p1178 ;conforms to + ;; ieee conformance is ruled out by '() being false, if + ;; nothing else. + rev4-optional-procedures + rev3-procedures +; rev2-procedures + multiarg/and- + multiarg-apply + rationalize + object-hash + delay + promise + with-file + transcript + char-ready? + ieee-floating-point + full-continuation + pretty-print + format + trace ;has macros: TRACE and UNTRACE + string-port + system + ;; next two could be added easily to the interpreter +; getenv +; program-arguments + )) + +(define pretty-print pp) + +;;; (OUTPUT-PORT-WIDTH <port>) +(define (output-port-width . arg) 79) + +;;; (OUTPUT-PORT-HEIGHT <port>) +(define (output-port-height . arg) 24) + +;;; (CURRENT-ERROR-PORT) +(define current-error-port + (let ((port (current-output-port))) + (lambda () port))) + +;;; (TMPNAM) makes a temporary file name. +(define tmpnam + (let ((cntr 100)) + (lambda () (set! cntr (+ 1 cntr)) + (let ((tmp (string-append "slib_" (number->string cntr)))) + (if (file-exists? tmp) (tmpnam) tmp))))) + +;;; (FILE-EXISTS? <string>) +(define (file-exists? f) + (case (software-type) + ((UNIX) (zero? (system (string-append "test -f " f)))) + (else (slib:error "FILE-EXISTS? not defined for " software-type)))) + +;;; (DELETE-FILE <string>) +(define (delete-file f) + (case (software-type) + ((UNIX) (zero? (system (string-append "rm " f)))) + (else (slib:error "DELETE-FILE not defined for " software-type)))) + +;;; FORCE-OUTPUT flushes any pending output on optional arg output port +;;; use this definition if your system doesn't have such a procedure. +(define force-output flush-buffer) + +;;; CALL-WITH-INPUT-STRING and CALL-WITH-OUTPUT-STRING are the string +;;; port versions of CALL-WITH-*PUT-FILE. +(define (call-with-output-string f) + (let ((outsp (open-output-string))) + (f outsp) + (let ((s (get-output-string outsp))) +;;; (close-output-port outsp) ;doesn't work + s))) + +(define (call-with-input-string s f) + (let* ((insp (open-input-string s)) + (res (f insp))) + (close-input-port insp) + res)) + +;;; CHAR-CODE-LIMIT is one greater than the largest integer which can +;;; be returned by CHAR->INTEGER. +(define char-code-limit 256) + +;; MOST-POSITIVE-FIXNUM is used in modular.scm +(define most-positive-fixnum 536870911) + +;;; Return argument +(define (identity x) x) + +;;; If your implementation provides eval, SLIB:EVAL is single argument +;;; eval using the top-level (user) environment. +(define slib:eval eval) + +(define-macro defmacro + (lambda (f e) + (let ((key (cadr f)) (pattern (caddr f)) (body (cdddr f))) + (e `(define-macro ,key + (let ((%transformer (lambda ,pattern ,@body))) + (lambda (%form %expr) + (%expr (apply %transformer (cdr %form)) %expr)))) + e)))) + +(define (defmacro? m) (and (getprop m '*expander*) #t)) + +(define macroexpand-1 expand-once) + +(define (macroexpand e) + (if (pair? e) (let ((a (car e))) + (if (and (symbol? a) (getprop a '*expander*)) + (macroexpand (expand-once e)) + e)) + e)) + +(define gentemp + (let ((*gensym-counter* -1)) + (lambda () + (set! *gensym-counter* (+ *gensym-counter* 1)) + (string->symbol + (string-append "slib:G" (number->string *gensym-counter*)))))) + +(define defmacro:eval slib:eval) +(define defmacro:load load) +;;; If your implementation provides R4RS macros: +;(define macro:eval slib:eval) +;(define macro:load load) + +(define (slib:eval-load <pathname> evl) + (if (not (file-exists? <pathname>)) + (set! <pathname> (string-append <pathname> (scheme-file-suffix)))) + (call-with-input-file <pathname> + (lambda (port) + (let ((old-load-pathname *load-pathname*)) + (set! *load-pathname* <pathname>) + (do ((o (read port) (read port))) + ((eof-object? o)) + (evl o)) + (set! *load-pathname* old-load-pathname))))) + +;; define an error procedure for the library +(define (slib:error . args) + (error 'slib-error: "~a" + (apply string-append + (map + (lambda (a) + (format " ~a" a)) + args)))) + +;; define these as appropriate for your system. +(define slib:tab (integer->char 9)) +(define slib:form-feed (integer->char 12)) + +;;; bug fixes for Scheme->C (versions 28sep90, 23feb90, 01nov91): + +(let ((vers (substring (cadr (implementation-information)) 0 7))) + (if (or (string=? vers "28sep90") (string=? vers "23feb90") + (string=? vers "01nov91")) + (begin + ;; GCD fails with 0 as argument + (define old-gcd gcd) + (set! gcd (lambda args + (apply old-gcd (remv! 0 args)))) + + ;; STRING->SYMBOL doesn't allocate a new string + (set! string->symbol + (let ((fred string->symbol)) + (lambda (a) (fred (string-append a))))) + + ;; NUMBER->STRING can generate a leading #? + (set! number->string + (let ((fred number->string)) + (lambda (num . radix) + (let ((joe (apply fred num radix))) + (if (char=? #\# (string-ref joe 0)) + (substring joe 2 (string-length joe)) + joe))))) + + ;; Another bug is bad expansion of LETREC when the body starts with a + ;; DEFINE as shown by test.scm -- not fixed here. + ))) + +(define promise:force force) + +;;; (implementation-vicinity) should be defined to be the pathname of +;;; the directory where any auxillary files to your Scheme +;;; implementation reside. + +(define in-vicinity string-append) + +;;; Define SLIB:EXIT to be the implementation procedure to exit or +;;; return if exitting not supported. +(define slib:exit (lambda args (exit))) + +;;; Here for backward compatability +(define scheme-file-suffix + (let ((suffix (case (software-type) + ((NOSVE) "_scm") + (else ".scm")))) + (lambda () suffix))) + +;;; (SLIB:LOAD-SOURCE "foo") should load "foo.scm" or with whatever +;;; suffix all the module files in SLIB have. See feature 'SOURCE. + +(define (slib:load-source f) (load (string-append f (scheme-file-suffix)))) + +;;; (SLIB:LOAD-COMPILED "foo") should load the file that was produced +;;; by compiling "foo.scm" if this implementation can compile files. +;;; See feature 'COMPILED. + +(define slib:load-compiled load) + +;;; At this point SLIB:LOAD must be able to load SLIB files. + +(define slib:load slib:load-source) + +(slib:load (in-vicinity (library-vicinity) "require")) +; eof diff --git a/scheme48.init b/scheme48.init new file mode 100644 index 0000000..6e6b423 --- /dev/null +++ b/scheme48.init @@ -0,0 +1,239 @@ +;;;"scheme48.init" Initialisation for SLIB for Scheme48 -*-scheme-*- +;;; Copyright (C) 1992, 1993, 1994, 1995 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;; If you know the magic incantation to make a "," command available +;;; as a scheme procedure, you can make a nifty slib function to do +;;; this (like `slib:dump' in "vscm.init"). But for now, type: +;;; make slib48 + +;;; (software-type) should be set to the generic operating system type. +;;; UNIX, VMS, MACOS, AMIGA and MS-DOS are supported. + +(define (software-type) 'UNIX) + +;;; (scheme-implementation-type) should return the name of the scheme +;;; implementation loading this file. + +(define (scheme-implementation-type) 'Scheme48) + +;;; (scheme-implementation-version) should return a string describing +;;; the version the scheme implementation loading this file. + +(define (scheme-implementation-version) "0.36") + +;;; (implementation-vicinity) should be defined to be the pathname of +;;; the directory where any auxiliary files to your Scheme +;;; implementation reside. +; For scheme48, perhaps something like /usr/local/src/scheme48/misc/ ? + +(define (implementation-vicinity) + (case (software-type) + ((UNIX) "=scheme48/") ; Translated + (else (slib:error "unrecognized software-type" software-type)))) + +;;; (library-vicinity) should be defined to be the pathname of the +;;; directory where files of Scheme library functions reside. + +(define (library-vicinity) "/usr/local/lib/slib/") + +;;; *FEATURES* should be set to a list of symbols describing features +;;; of this implementation. See Template.scm for the list of feature +;;; names. + +(define *features* + '( + source ;can load scheme source files + ;(slib:load-source "filename") +; compiled ;can load compiled files + ;(slib:load-compiled "filename") + rev4-report ;conforms to + ieee-p1178 ;conforms to + rev4-optional-procedures + multiarg/and- + multiarg-apply + rationalize + delay ;has delay and force + with-file + char-ready? ;has + values ;proposed multiple values + eval ;slib:eval is single argument eval. + dynamic-wind ;proposed dynamic-wind + full-continuation ;can return multiple times + macro ;R4RS appendix's DEFINE-SYNTAX + )) + +;;; (OUTPUT-PORT-WIDTH <port>) +(define (output-port-width . arg) 79) + +;;; (OUTPUT-PORT-HEIGHT <port>) +(define (output-port-height . arg) 24) + +;;; (CURRENT-ERROR-PORT) +(define current-error-port + (access-scheme-48 'error-output-port)) + +;;; (TMPNAM) makes a temporary file name. +(define tmpnam + (let ((cntr 100)) + (lambda () (set! cntr (+ 1 cntr)) + (let ((tmp (string-append "slib_" (number->string cntr)))) + (if (file-exists? tmp) (tmpnam) tmp))))) + +;;; (FILE-EXISTS? <string>) +(define (file-exists? f) #f) + +;;; (DELETE-FILE <string>) +(define (delete-file f) #f) + +;;; FORCE-OUTPUT flushes any pending output on optional arg output port +;;; use this definition if your system doesn't have such a procedure. +(define (force-output . arg) + ((access-scheme-48 'force-output) + (if (null? arg) (current-output-port) (car arg)))) + +;;; CHAR-CODE-LIMIT is one greater than the largest integer which can +;;; be returned by CHAR->INTEGER. +(define integer->char (access-scheme-48 'ascii->char)) +(define char->integer + (let ((char->integer char->integer) + (code0 (char->integer (integer->char 0)))) + (lambda (char) (- (char->integer char) code0)))) +(define char-code-limit 256) + +;;; Workaround MODULO bug +(define modulo + (let ((modulo modulo)) + (lambda (n1 n2) + (let ((ans (modulo n1 n2))) + (if (= ans n2) (- ans ans) ans))))) + +;;; MOST-POSITIVE-FIXNUM is used in modular.scm +(define most-positive-fixnum #x1FFFFFFF) + +;;; Return argument +(define (identity x) x) + +;;; If your implementation provides eval, SLIB:EVAL is single argument +;;; eval using the top-level (user) environment. +(define slib:eval + (let ((eval eval) + (interaction-environment interaction-environment)) + (lambda (form) + (eval form (interaction-environment))))) + +;;; If your implementation provides R4RS macros: +(define macro:eval slib:eval) +(define macro:load load) + +(define *defmacros* + (list (cons 'defmacro + (lambda (name parms . body) + `(set! *defmacros* (cons (cons ',name (lambda ,parms ,@body)) + *defmacros*)))))) +(define (defmacro? m) (and (assq m *defmacros*) #t)) + +(define (macroexpand-1 e) + (if (pair? e) (let ((a (car e))) + (cond ((symbol? a) (set! a (assq a *defmacros*)) + (if a (apply (cdr a) (cdr e)) e)) + (else e))) + e)) + +(define (macroexpand e) + (if (pair? e) (let ((a (car e))) + (cond ((symbol? a) + (set! a (assq a *defmacros*)) + (if a (macroexpand (apply (cdr a) (cdr e))) e)) + (else e))) + e)) + +(define gentemp + (let ((*gensym-counter* -1)) + (lambda () + (set! *gensym-counter* (+ *gensym-counter* 1)) + (string->symbol + (string-append "slib:G" (number->string *gensym-counter*)))))) + +(define base:eval slib:eval) +(define (defmacro:eval x) (base:eval (defmacro:expand* x))) +(define (defmacro:expand* x) + (require 'defmacroexpand) (apply defmacro:expand* x '())) + +(define (defmacro:load <pathname>) + (slib:eval-load <pathname> defmacro:eval)) + +(define (slib:eval-load <pathname> evl) + (if (not (file-exists? <pathname>)) + (set! <pathname> (string-append <pathname> (scheme-file-suffix)))) + (call-with-input-file <pathname> + (lambda (port) + (let ((old-load-pathname *load-pathname*)) + (set! *load-pathname* <pathname>) + (do ((o (read port) (read port))) + ((eof-object? o)) + (evl o)) + (set! *load-pathname* old-load-pathname))))) + +;;; define an error procedure for the library +(define slib:error (access-scheme-48 'error)) + +;;; define these as appropriate for your system. +(define slib:tab (integer->char 9)) +(define slib:form-feed (integer->char 12)) + +;;; Support for older versions of Scheme. Not enough code for its own file. +(define (last-pair l) (if (pair? (cdr l)) (last-pair (cdr l)) l)) +(define t #t) +(define nil #f) + +;;; Define these if your implementation's syntax can support them and if +;;; they are not already defined. + +(define (1+ n) (+ n 1)) +(define (-1+ n) (+ n -1)) +;(define 1- -1+) + +(define in-vicinity string-append) + +;;; Define SLIB:EXIT to be the implementation procedure to exit or +;;; return if exitting not supported. +(define slib:exit (lambda args #f)) + +;;; Here for backward compatability +(define scheme-file-suffix + (case (software-type) + ((NOSVE) (lambda () "_scm")) + (else (lambda () ".scm")))) + +;;; (SLIB:LOAD-SOURCE "foo") should load "foo.scm" or with whatever +;;; suffix all the module files in SLIB have. See feature 'SOURCE. + +(define (slib:load-source f) (load (string-append f (scheme-file-suffix)))) + +;;; (SLIB:LOAD-COMPILED "foo") should load the file that was produced +;;; by compiling "foo.scm" if this implementation can compile files. +;;; See feature 'COMPILED. + +(define slib:load-compiled load) + +;;; At this point SLIB:LOAD must be able to load SLIB files. + +(define slib:load slib:load-source) + +(slib:load (in-vicinity (library-vicinity) "require")) diff --git a/scmacro.scm b/scmacro.scm new file mode 100644 index 0000000..47bafca --- /dev/null +++ b/scmacro.scm @@ -0,0 +1,119 @@ +;"scmacro.scm", port for Syntactic Closures macro implementation -*- Scheme -*- +;Copyright (C) 1992, 1993, 1994 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;;; Syntaxer Output Interface + +(define syntax-error slib:error) + +(define impl-error slib:error) + +(define (append-map procedure . lists) + (apply append (apply map (cons procedure lists)))) + +(define *counter* 0) + +(define (make-name-generator) + (let ((suffix-promise + (make-promise + (lambda () + (string-append "." + (number->string (begin + (set! *counter* (+ *counter* 1)) + *counter*))))))) + (lambda (identifier) + (string->symbol + (string-append "." + (symbol->string (identifier->symbol identifier)) + (promise:force suffix-promise)))))) + +(define (output/variable name) + name) + +(define (output/literal-unquoted datum) + datum) + +(define (output/literal-quoted datum);was output/constant (inefficient) + `(QUOTE ,datum)) + +(define (output/assignment name value) + `(SET! ,name ,value)) + +(define (output/top-level-definition name value) + `(DEFINE ,name ,value)) + +(define (output/conditional predicate consequent alternative) + `(IF ,predicate ,consequent ,alternative)) + +(define (output/sequence expressions) + (if (null? (cdr expressions)) + (car expressions) + `(BEGIN ,@expressions))) + +(define (output/combination operator operands) + `(,operator ,@operands)) + +(define (output/lambda pattern body) + `(LAMBDA ,pattern ,body)) + +(define (output/delay expression) + `(DELAY ,expression)) + +(define (output/unassigned) + `'*UNASSIGNED*) + +(define (output/unspecific) + `'*UNSPECIFIC*) + +(require 'promise) ; Portable support for force and delay. +(require 'record) +(require 'synchk) ; Syntax checker. + +;;; This file is the macro expander proper. +(slib:load (in-vicinity (library-vicinity) "synclo")) + +;;; These files define the R4RS syntactic environment. +(slib:load (in-vicinity (library-vicinity) "r4rsyn")) +(slib:load (in-vicinity (library-vicinity) "synrul")) + +;;; OK, time to build the databases. +(initialize-scheme-syntactic-environment!) + +;;; MACRO:EXPAND is for you to use. It takes an R4RS expression, macro-expands +;;; it, and returns the result of the macro expansion. +(define (synclo:expand expression) + (set! *counter* 0) + (compile/top-level (list expression) scheme-syntactic-environment)) +(define macro:expand synclo:expand) + +;;; Here are EVAL, EVAL! and LOAD which expand macros. You can replace the +;;; implementation's eval and load with them if you like. +(define base:eval slib:eval) +(define base:load load) + +(define (synclo:eval x) (base:eval (macro:expand x))) +(define macro:eval synclo:eval) + +(define (synclo:load <pathname>) + (slib:eval-load <pathname> synclo:eval)) + +(define macro:load synclo:load) + +(provide 'syntactic-closures) +(provide 'macro) ;Here because we may have + ;(require 'sc-macro) diff --git a/scmactst.scm b/scmactst.scm new file mode 100644 index 0000000..3b71341 --- /dev/null +++ b/scmactst.scm @@ -0,0 +1,160 @@ +;;;"scmactst.scm" test syntactic closures macros +;;; From "sc-macro.doc", A Syntactic Closures Macro Facility by Chris Hanson + +(define errs '()) +(define test + (lambda (expect fun . args) + (write (cons fun args)) + (display " ==> ") + ((lambda (res) + (write res) + (newline) + (cond ((not (equal? expect res)) + (set! errs (cons (list res expect (cons fun args)) errs)) + (display " BUT EXPECTED ") + (write expect) + (newline) + #f) + (else #t))) + (if (procedure? fun) (apply fun args) (car args))))) + +(require 'syntactic-closures) + +(macro:expand + '(define-syntax push + (syntax-rules () + ((push item list) + (set! list (cons item list)))))) + +(test '(set! foo (cons bar foo)) 'push (macro:expand '(push bar foo))) + +(macro:expand + '(define-syntax push1 + (transformer + (lambda (exp env) + (let ((item + (make-syntactic-closure env '() (cadr exp))) + (list + (make-syntactic-closure env '() (caddr exp)))) + `(set! ,list (cons ,item ,list))))))) + +(test '(set! foo (cons bar foo)) 'push1 (macro:expand '(push1 bar foo))) + +(macro:expand + '(define-syntax loop + (transformer + (lambda (exp env) + (let ((body (cdr exp))) + `(call-with-current-continuation + (lambda (exit) + (let f () + ,@(map (lambda (exp) + (make-syntactic-closure env '(exit) + exp)) + body) + (f))))))))) + +(macro:expand + '(define-syntax let1 + (transformer + (lambda (exp env) + (let ((id (cadr exp)) + (init (caddr exp)) + (exp (cadddr exp))) + `((lambda (,id) + ,(make-syntactic-closure env (list id) exp)) + ,(make-syntactic-closure env '() init))))))) + +(test 93 'let1 (macro:eval '(let1 a 90 (+ a 3)))) + +(macro:expand + '(define-syntax loop-until + (syntax-rules + () + ((loop-until id init test return step) + (letrec ((loop + (lambda (id) + (if test return (loop step))))) + (loop init)))))) + +(test (macro:expand '(letrec ((loop (lambda (foo) (if #t 12 (loop 33))))) + (loop 3))) + 'loop + (macro:expand '(loop-until foo 3 #t 12 33))) + +(macro:expand + '(define-syntax loop-until1 + (transformer + (lambda (exp env) + (let ((id (cadr exp)) + (init (caddr exp)) + (test (cadddr exp)) + (return (cadddr (cdr exp))) + (step (cadddr (cddr exp))) + (close + (lambda (exp free) + (make-syntactic-closure env free exp)))) + `(letrec ((loop + ,(capture-syntactic-environment + (lambda (env) + `(lambda (,id) + (,(make-syntactic-closure env '() `if) + ,(close test (list id)) + ,(close return (list id)) + (,(make-syntactic-closure env '() + `loop) + ,(close step (list id))))))))) + (loop ,(close init '())))))))) + +(test (macro:expand '(letrec ((loop (lambda (foo) (if #t 12 (loop 33))))) + (loop 3))) + 'loop1 + (macro:expand '(loop-until1 foo 3 #t 12 33))) + +(test '#t 'identifier (identifier? 'a)) +;;; this needs to setup ENV. +;;;(test '#t 'identifier +;;; (identifier? (macro:expand (make-syntactic-closure env '() 'a)))) +(test #f 'identifier (identifier? "a")) +(test #f 'identifier (identifier? #\a)) +(test #f 'identifier (identifier? 97)) +(test #f 'identifier (identifier? #f)) +(test #f 'identifier (identifier? '(a))) +(test #f 'identifier (identifier? '#(a))) + +(test '(#t #f) + 'syntax + (macro:eval + '(let-syntax + ((foo + (transformer + (lambda (form env) + (capture-syntactic-environment + (lambda (transformer-env) + (identifier=? transformer-env 'x env 'x))))))) + (list (foo) + (let ((x 3)) + (foo)))))) + + +(test '(#f #t) + 'syntax + (macro:eval + '(let-syntax ((bar foo)) + (let-syntax + ((foo + (transformer + (lambda (form env) + (capture-syntactic-environment + (lambda (transformer-env) + (identifier=? transformer-env 'foo + env (cadr form)))))))) + (list (foo foo) + (foo bar)))))) + +(newline) +(cond ((null? errs) (display "Passed all tests")) + (else (display "errors were:") (newline) + (display "(got expected (call))") (newline) + (for-each (lambda (l) (write l) (newline)) errs))) +(newline) diff --git a/sierpinski.scm b/sierpinski.scm new file mode 100644 index 0000000..a4de2d6 --- /dev/null +++ b/sierpinski.scm @@ -0,0 +1,71 @@ +;"sierpinski.scm" Hash function for 2d data which preserves nearness. +;From: jjb@isye.gatech.edu (John Bartholdi) +; +; This code is in the public domain. + +;Date: Fri, 6 May 94 13:22:34 -0500 + +(define MAKE-SIERPINSKI-INDEXER + (lambda (max-coordinate) + (lambda (x y) + (if (not (and (<= 0 x max-coordinate) + (<= 0 y max-coordinate))) + (slib:error 'sierpinski-index + "Coordinate exceeds specified maximum.") + ; + ; The following two mutually recursive procedures + ; correspond to to partitioning successive triangles + ; into two sub-triangles, adjusting the index according + ; to which sub-triangle (x,y) lies in, then rescaling + ; and possibly rotating to continue the recursive + ; decomposition: + ; + (letrec ((loopA + (lambda (resolution x y index) + (cond ((zero? resolution) index) + (else + (let ((finer-index (+ index index))) + (if (> (+ x y) max-coordinate) + ; + ; In the upper sub-triangle: + (loopB resolution + (- max-coordinate y) + x + (+ 1 finer-index)) + ; + ; In the lower sub-triangle: + (loopB resolution + x + y + finer-index))))))) + (loopB + (lambda (resolution x y index) + (let ((new-x (+ x x)) + (new-y (+ y y)) + (finer-index (+ index index))) + (if (> new-y max-coordinate) + ; + ; In the upper sub-triangle: + (loopA (quotient resolution 2) + (- new-y max-coordinate) + (- max-coordinate new-x) + (+ finer-index 1)) + ; + ; In the lower sub-triangle: + (loopA (quotient resolution 2) + new-x + new-y + finer-index)))))) + (if (<= x y) + ; + ; Point in NW triangle of initial square: + (loopA max-coordinate + x + y + 0) + ; + ; Else point in SE triangle of initial square + ; so translate point and increase index: + (loopA max-coordinate + (- max-coordinate x) + (- max-coordinate y) 1))))))) diff --git a/slib.info b/slib.info new file mode 100644 index 0000000..d8ec637 --- /dev/null +++ b/slib.info @@ -0,0 +1,153 @@ +This is Info file slib.info, produced by Makeinfo-1.64 from the input +file slib.texi. + + This file documents SLIB, the portable Scheme library. + + Copyright (C) 1993 Todd R. Eigenschink Copyright (C) 1993, 1994, 1995 +Aubrey Jaffer + + Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + + Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + + Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be stated in a +translation approved by the author. + + +Indirect: +slib.info-1: 885 +slib.info-2: 49910 +slib.info-3: 99287 +slib.info-4: 133361 +slib.info-5: 181826 +slib.info-6: 230781 +slib.info-7: 279927 +slib.info-8: 302856 + +Tag Table: +(Indirect) +Node: Top885 +Node: Overview1831 +Node: Installation2916 +Node: Porting4528 +Node: Coding Standards5779 +Node: Copyrights8112 +Node: Manual Conventions11415 +Node: Data Structures12091 +Node: Arrays12951 +Node: Array Mapping15895 +Node: Association Lists17170 +Node: Collections19428 +Node: Dynamic Data Type25543 +Node: Hash Tables26804 +Node: Hashing28921 +Node: Chapter Ordering33708 +Node: Object35324 +Node: Parameter lists43550 +Node: Priority Queues47932 +Node: Queues48784 +Node: Records49910 +Node: Base Table54316 +Node: Relational Database63453 +Node: Motivations64165 +Node: Creating and Opening Relational Databases69210 +Node: Relational Database Operations71642 +Node: Table Operations74439 +Node: Catalog Representation78947 +Node: Unresolved Issues81845 +Node: Database Utilities84776 +Node: Weight-Balanced Trees99287 +Node: Construction of Weight-Balanced Trees103174 +Node: Basic Operations on Weight-Balanced Trees106624 +Node: Advanced Operations on Weight-Balanced Trees109589 +Node: Indexing Operations on Weight-Balanced Trees115611 +Node: Structures119455 +Node: Macros120767 +Node: Defmacro121352 +Node: R4RS Macros123250 +Node: Macro by Example124479 +Node: Macros That Work127329 +Node: Syntactic Closures133361 +Node: Syntax-Case Macros150768 +Node: Fluid-Let154868 +Node: Yasos155783 +Node: Yasos terms156550 +Node: Yasos interface157574 +Node: Setters159657 +Node: Yasos examples162298 +Node: Numerics165226 +Node: Bit-Twiddling165640 +Node: Modular Arithmetic168832 +Node: Prime Testing and Generation170968 +Node: The Miller-Rabin Test173141 +Node: Prime Factorization177335 +Node: Random Numbers178615 +Node: Cyclic Checksum181826 +Node: Plotting183523 +Node: Root Finding186085 +Node: Procedures188902 +Node: Batch189767 +Node: Common List Functions197328 +Node: List construction197738 +Node: Lists as sets199401 +Node: Lists as sequences204394 +Node: Destructive list operations209056 +Node: Non-List functions211719 +Node: Format213067 +Node: Format Interface213264 +Node: Format Specification215001 +Node: Generic-Write224985 +Node: Line I/O226366 +Node: Multi-Processing227717 +Node: Object-To-String228558 +Node: Pretty-Print228824 +Node: Sorting230781 +Node: Topological Sort236554 +Node: Standard Formatted I/O238252 +Node: Standard Formatted Output238780 +Node: Standard Formatted Input247511 +Node: String-Case254170 +Node: String Ports254664 +Node: String Search255428 +Node: Tektronix Graphics Support256994 +Node: Tree Operations258385 +Node: Standards Support259911 +Node: With-File260605 +Node: Transcripts260881 +Node: Rev2 Procedures261202 +Node: Rev4 Optional Procedures262909 +Node: Multi-argument / and -263479 +Node: Multi-argument Apply264130 +Node: Rationalize264616 +Node: Promises265279 +Node: Dynamic-Wind265696 +Node: Values266952 +Node: Time267760 +Node: CLTime270664 +Node: Session Support272162 +Node: Repl273307 +Node: Quick Print274590 +Node: Debug275703 +Node: Breakpoints276345 +Node: Trace278563 +Node: Getopt279927 +Node: Command Line285745 +Node: System Interface288433 +Node: Require288933 +Node: Vicinity290924 +Node: Configuration293562 +Node: Input/Output295844 +Node: Legacy297444 +Node: System298163 +Node: Optional SLIB Packages300496 +Node: Procedure and Macro Index302856 +Node: Variable Index331571 + +End Tag Table diff --git a/slib.info-1 b/slib.info-1 new file mode 100644 index 0000000..89c4fce --- /dev/null +++ b/slib.info-1 @@ -0,0 +1,1306 @@ +This is Info file slib.info, produced by Makeinfo-1.64 from the input +file slib.texi. + + This file documents SLIB, the portable Scheme library. + + Copyright (C) 1993 Todd R. Eigenschink Copyright (C) 1993, 1994, 1995 +Aubrey Jaffer + + Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + + Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + + Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be stated in a +translation approved by the author. + + +File: slib.info, Node: Top, Next: Overview, Prev: (dir), Up: (dir) + + This file documents SLIB, the portable Scheme library. + +Good Engineering is 1% inspiration and 99% documentation. +========================================================= + + Herein lies the good part. Many thanks to Todd Eigenschink +<eigenstr@CS.Rose-Hulman.Edu> (who thanks Dave Love <D.Love@dl.ac.uk>) +for creating `slib.texi'. I have learned much from their example. + + Aubrey Jaffer jaffer@ai.mit.edu + +* Menu: + +* Overview:: What is SLIB? + +* Data Structures:: Various data structures. +* Macros:: Extensions to Scheme syntax. +* Numerics:: +* Procedures:: Miscellaneous utility procedures. +* Standards Support:: Support for Scheme Standards. +* Session Support:: Debugging, Pathnames, Require, etc. + +* Optional SLIB Packages:: +* Procedure and Macro Index:: +* Variable Index:: + + +File: slib.info, Node: Overview, Next: Data Structures, Prev: Top, Up: Top + +Overview +******** + + SLIB is a portable Scheme library meant to provide compatibility and +utility functions for all standard Scheme implementations, and fixes +several implementations which are non-conforming. SLIB conforms to +`Revised^4 Report on the Algorithmic Language Scheme' and the IEEE +P1178 specification. SLIB supports Unix and similar systems, VMS, and +MS-DOS. + + For a summary of what each file contains, see the file `README'. For +a list of the features that have changed since the last SLIB release, +see the file `ANNOUNCE'. For a list of the features that have changed +over time, see the file `ChangeLog'. + + The maintainer can be reached as `jaffer@ai.mit.edu'. + +* Menu: + +* Installation:: How to install SLIB on your system. +* Porting:: SLIB to new platforms +* Coding Standards:: How to write modules for SLIB. +* Copyrights:: Intellectual propery issues. +* Manual Conventions:: Conventions used in this manual. + + +File: slib.info, Node: Installation, Next: Porting, Prev: Overview, Up: Overview + +Installation +============ + + Check the manifest in `README' to find a configuration file for your +Scheme implementation. Initialization files for most IEEE P1178 +compliant Scheme Implementations are included with this distribution. + + If the Scheme implementation supports `getenv', then the value of the +shell environment variable SCHEME_LIBRARY_PATH will be used for +`(library-vicinity)' if it is defined. Currently, Chez, Elk, +MITScheme, scheme->c, VSCM, and SCM support `getenv'. + + You should check the definitions of `software-type', +`scheme-implementation-version', `implementation-vicinity', and +`library-vicinity' in the initialization file. There are comments in +the file for how to configure it. + + Once this is done you can modify the startup file for your Scheme +implementation to `load' this initialization file. SLIB is then +installed. + + Multiple implementations of Scheme can all use the same SLIB +directory. Simply configure each implementation's initialization file +as outlined above. + + The SCM implementation does not require any initialization file as +SLIB support is already built in to SCM. See the documentation with +SCM for installation instructions. + + SLIB includes methods to create heap images for the VSCM and Scheme48 +implementations. The instructions for creating a VSCM image are in +comments in `vscm.init'. To make a Scheme48 image, `cd' to the SLIB +directory and type `make slib48'. This will also create a shell script +with the name `slib48' which will invoke the saved image. + + +File: slib.info, Node: Porting, Next: Coding Standards, Prev: Installation, Up: Overview + +Porting +======= + + If there is no initialization file for your Scheme implementation, you +will have to create one. Your Scheme implementation must be largely +compliant with `IEEE Std 1178-1990' or `Revised^4 Report on the +Algorithmic Language Scheme' to support SLIB. + + `Template.scm' is an example configuration file. The comments inside +will direct you on how to customize it to reflect your system. Give +your new initialization file the implementation's name with `.init' +appended. For instance, if you were porting `foo-scheme' then the +initialization file might be called `foo.init'. + + Your customized version should then be loaded as part of your scheme +implementation's initialization. It will load `require.scm' (*Note +Require::) from the library; this will allow the use of `provide', +`provided?', and `require' along with the "vicinity" functions +(`vicinity' functions are documented in the section on Require. *Note +Require::). The rest of the library will then be accessible in a +system independent fashion. + + Please mail new working configuration files to `jaffer@ai.mit.edu' so +that they can be included in the SLIB distribution. + + +File: slib.info, Node: Coding Standards, Next: Copyrights, Prev: Porting, Up: Overview + +Coding Standards +================ + + All library packages are written in IEEE P1178 Scheme and assume that +a configuration file and `require.scm' package have already been +loaded. Other versions of Scheme can be supported in library packages +as well by using, for example, `(provided? 'rev3-report)' or `(require +'rev3-report)' (*Note Require::). + + `require.scm' defines `*catalog*', an association list of module +names and filenames. When a new package is added to the library, an +entry should be added to `require.scm'. Local packages can also be +added to `*catalog*' and even shadow entries already in the table. + + The module name and `:' should prefix each symbol defined in the +package. Definitions for external use should then be exported by having +`(define foo module-name:foo)'. + + Submitted code should not duplicate routines which are already in SLIB +files. Use `require' to force those features to be supported in your +package. Care should be taken that there are no circularities in the +`require's and `load's between the library packages. + + Documentation should be provided in Emacs Texinfo format if possible, +But documentation must be provided. + + Your package will be released sooner with SLIB if you send me a file +which tests your code. Please run this test *before* you send me the +code! + +Modifications +------------- + + Please document your changes. A line or two for `ChangeLog' is +sufficient for simple fixes or extensions. Look at the format of +`ChangeLog' to see what information is desired. Please send me `diff' +files from the latest SLIB distribution (remember to send `diff's of +`slib.texi' and `ChangeLog'). This makes for less email traffic and +makes it easier for me to integrate when more than one person is +changing a file (this happens a lot with `slib.texi' and `*.init' +files). + + If someone else wrote a package you want to significantly modify, +please try to contact the author, who may be working on a new version. +This will insure against wasting effort on obsolete versions. + + Please *do not* reformat the source code with your favorite +beautifier, make 10 fixes, and send me the resulting source code. I do +not have the time to fish through 10000 diffs to find your 10 real +fixes. + + +File: slib.info, Node: Copyrights, Next: Manual Conventions, Prev: Coding Standards, Up: Overview + +Copyrights +========== + + This section has instructions for SLIB authors regarding copyrights. + + Each package in SLIB must either be in the public domain, or come +with a statement of terms permitting users to copy, redistribute and +modify it. The comments at the beginning of `require.scm' and +`macwork.scm' illustrate copyright and appropriate terms. + + If your code or changes amount to less than about 10 lines, you do not +need to add your copyright or send a disclaimer. + +Putting code into the Public Domain +----------------------------------- + + In order to put code in the public domain you should sign a copyright +disclaimer and send it to the SLIB maintainer. Contact +jaffer@ai.mit.edu for the address to mail the disclaimer to. + + I, NAME, hereby affirm that I have placed the software package + NAME in the public domain. + + I affirm that I am the sole author and sole copyright holder for + the software package, that I have the right to place this software + package in the public domain, and that I will do nothing to + undermine this status in the future. + + SIGNATURE AND DATE + + This wording assumes that you are the sole author. If you are not the +sole author, the wording needs to be different. If you don't want to be +bothered with sending a letter every time you release or modify a +module, make your letter say that it also applies to your future +revisions of that module. + + Make sure no employer has any claim to the copyright on the work you +are submitting. If there is any doubt, create a copyright disclaimer +and have your employer sign it. Mail the signed disclaimer to the SLIB +maintainer. Contact jaffer@ai.mit.edu for the address to mail the +disclaimer to. An example disclaimer follows. + +Explicit copying terms +---------------------- + +If you submit more than about 10 lines of code which you are not placing +into the Public Domain (by sending me a disclaimer) you need to: + + * Arrange that your name appears in a copyright line for the + appropriate year. Multiple copyright lines are acceptable. + + * With your copyright line, specify any terms you require to be + different from those already in the file. + + * Make sure no employer has any claim to the copyright on the work + you are submitting. If there is any doubt, create a copyright + disclaimer and have your employer sign it. Mail the signed + disclaim to the SLIB maintainer. Contact jaffer@ai.mit.edu for + the address to mail the disclaimer to. + +Example: Company Copyright Disclaimer +------------------------------------- + + This disclaimer should be signed by a vice president or general +manager of the company. If you can't get at them, anyone else +authorized to license out software produced there will do. Here is a +sample wording: + + EMPLOYER Corporation hereby disclaims all copyright interest in + the program PROGRAM written by NAME. + + EMPLOYER Corporation affirms that it has no other intellectual + property interest that would undermine this release, and will do + nothing to undermine it in the future. + + SIGNATURE AND DATE, + NAME, TITLE, EMPLOYER Corporation + + +File: slib.info, Node: Manual Conventions, Prev: Copyrights, Up: Overview + +Manual Conventions +================== + + Things that are labeled as Functions are called for their return +values. Things that are labeled as Procedures are called primarily for +their side effects. + + All examples throughout this text were produced using the `scm' +Scheme implementation. + + At the beginning of each section, there is a line that looks something +like + + `(require 'feature)'. + +This means that, in order to use `feature', you must include the line +`(require 'feature)' somewhere in your code prior to the use of that +feature. `require' will make sure that the feature is loaded. + + +File: slib.info, Node: Data Structures, Next: Macros, Prev: Overview, Up: Top + +Data Structures +*************** + +* Menu: + +* Arrays:: 'array +* Array Mapping:: 'array-for-each +* Association Lists:: 'alist +* Collections:: 'collect +* Dynamic Data Type:: 'dynamic +* Hash Tables:: 'hash-table +* Hashing:: 'hash, 'sierpinski, 'soundex +* Chapter Ordering:: 'chapter-order +* Object:: 'object +* Parameter lists:: 'parameters +* Priority Queues:: 'priority-queue +* Queues:: 'queue +* Records:: 'record +* Base Table:: +* Relational Database:: 'relational-database +* Weight-Balanced Trees:: 'wt-tree +* Structures:: 'struct, 'structure + + +File: slib.info, Node: Arrays, Next: Array Mapping, Prev: Data Structures, Up: Data Structures + +Arrays +====== + + `(require 'array)' + + - Function: array? OBJ + Returns `#t' if the OBJ is an array, and `#f' if not. + + - Function: make-array INITIAL-VALUE BOUND1 BOUND2 ... + Creates and returns an array that has as many dimensins as there + are BOUNDs and fills it with INITIAL-VALUE. + + When constructing an array, BOUND is either an inclusive range of +indices expressed as a two element list, or an upper bound expressed as +a single integer. So + (make-array 'foo 3 3) == (make-array 'foo '(0 2) '(0 2)) + + - Function: make-shared-array ARRAY MAPPER BOUND1 BOUND2 ... + `make-shared-array' can be used to create shared subarrays of other + arrays. The MAPPER is a function that translates coordinates in + the new array into coordinates in the old array. A MAPPER must be + linear, and its range must stay within the bounds of the old + array, but it can be otherwise arbitrary. A simple example: + (define fred (make-array #f 8 8)) + (define freds-diagonal + (make-shared-array fred (lambda (i) (list i i)) 8)) + (array-set! freds-diagonal 'foo 3) + (array-ref fred 3 3) + => FOO + (define freds-center + (make-shared-array fred (lambda (i j) (list (+ 3 i) (+ 3 j))) + 2 2)) + (array-ref freds-center 0 0) + => FOO + + - Function: array-rank OBJ + Returns the number of dimensions of OBJ. If OBJ is not an array, + 0 is returned. + + - Function: array-shape ARRAY + `array-shape' returns a list of inclusive bounds. So: + (array-shape (make-array 'foo 3 5)) + => ((0 2) (0 4)) + + - Function: array-dimensions ARRAY + `array-dimensions' is similar to `array-shape' but replaces + elements with a 0 minimum with one greater than the maximum. So: + (array-dimensions (make-array 'foo 3 5)) + => (3 5) + + - Procedure: array-in-bounds? ARRAY INDEX1 INDEX2 ... + Returns `#t' if its arguments would be acceptable to `array-ref'. + + - Function: array-ref ARRAY INDEX1 INDEX2 ... + Returns the element at the `(INDEX1, INDEX2)' element in ARRAY. + + - Procedure: array-set! ARRAY NEW-VALUE INDEX1 INDEX2 ... + + - Function: array-1d-ref ARRAY INDEX + - Function: array-2d-ref ARRAY INDEX INDEX + - Function: array-3d-ref ARRAY INDEX INDEX INDEX + + - Procedure: array-1d-set! ARRAY NEW-VALUE INDEX + - Procedure: array-2d-set! ARRAY NEW-VALUE INDEX INDEX + - Procedure: array-3d-set! ARRAY NEW-VALUE INDEX INDEX INDEX + + The functions are just fast versions of `array-ref' and `array-set!' +that take a fixed number of arguments, and perform no bounds checking. + + If you comment out the bounds checking code, this is about as +efficient as you could ask for without help from the compiler. + + An exercise left to the reader: implement the rest of APL. + + +File: slib.info, Node: Array Mapping, Next: Association Lists, Prev: Arrays, Up: Data Structures + +Array Mapping +============= + + `(require 'array-for-each)' + + - Function: array-map! ARRAY0 PROC ARRAY1 ... + ARRAY1, ... must have the same number of dimensions as ARRAY0 and + have a range for each index which includes the range for the + corresponding index in ARRAY0. PROC is applied to each tuple of + elements of ARRAY1 ... and the result is stored as the + corresponding element in ARRAY0. The value returned is + unspecified. The order of application is unspecified. + + - Function: array-for-each PROC ARRAY0 ... + PROC is applied to each tuple of elements of ARRAY0 ... in + row-major order. The value returned is unspecified. + + - Function: array-indexes ARRAY + Returns an array of lists of indexes for ARRAY such that, if LI is + a list of indexes for which ARRAY is defined, (equal? LI (apply + array-ref (array-indexes ARRAY) LI)). + + - Function: array-copy! SOURCE DESTINATION + Copies every element from vector or array SOURCE to the + corresponding element of DESTINATION. DESTINATION must have the + same rank as SOURCE, and be at least as large in each dimension. + The order of copying is unspecified. + + +File: slib.info, Node: Association Lists, Next: Collections, Prev: Array Mapping, Up: Data Structures + +Association Lists +================= + + `(require 'alist)' + + Alist functions provide utilities for treating a list of key-value +pairs as an associative database. These functions take an equality +predicate, PRED, as an argument. This predicate should be repeatable, +symmetric, and transitive. + + Alist functions can be used with a secondary index method such as hash +tables for improved performance. + + - Function: predicate->asso PRED + Returns an "association function" (like `assq', `assv', or + `assoc') corresponding to PRED. The returned function returns a + key-value pair whose key is `pred'-equal to its first argument or + `#f' if no key in the alist is PRED-equal to the first argument. + + - Function: alist-inquirer PRED + Returns a procedure of 2 arguments, ALIST and KEY, which returns + the value associated with KEY in ALIST or `#f' if KEY does not + appear in ALIST. + + - Function: alist-associator PRED + Returns a procedure of 3 arguments, ALIST, KEY, and VALUE, which + returns an alist with KEY and VALUE associated. Any previous + value associated with KEY will be lost. This returned procedure + may or may not have side effects on its ALIST argument. An + example of correct usage is: + (define put (alist-associator string-ci=?)) + (define alist '()) + (set! alist (put alist "Foo" 9)) + + - Function: alist-remover PRED + Returns a procedure of 2 arguments, ALIST and KEY, which returns + an alist with an association whose KEY is key removed. This + returned procedure may or may not have side effects on its ALIST + argument. An example of correct usage is: + (define rem (alist-remover string-ci=?)) + (set! alist (rem alist "foo")) + + - Function: alist-map PROC ALIST + Returns a new association list formed by mapping PROC over the + keys and values of ALIST. PROC must be a function of 2 arguments + which returns the new value part. + + - Function: alist-for-each PROC ALIST + Applies PROC to each pair of keys and values of ALIST. PROC must + be a function of 2 arguments. The returned value is unspecified. + + +File: slib.info, Node: Collections, Next: Dynamic Data Type, Prev: Association Lists, Up: Data Structures + +Collections +=========== + + `(require 'collect)' + + Routines for managing collections. Collections are aggregate data +structures supporting iteration over their elements, similar to the +Dylan(TM) language, but with a different interface. They have +"elements" indexed by corresponding "keys", although the keys may be +implicit (as with lists). + + New types of collections may be defined as YASOS objects (*Note +Yasos::). They must support the following operations: + * `(collection? SELF)' (always returns `#t'); + + * `(size SELF)' returns the number of elements in the collection; + + * `(print SELF PORT)' is a specialized print operation for the + collection which prints a suitable representation on the given + PORT or returns it as a string if PORT is `#t'; + + * `(gen-elts SELF)' returns a thunk which on successive invocations + yields elements of SELF in order or gives an error if it is + invoked more than `(size SELF)' times; + + * `(gen-keys SELF)' is like `gen-elts', but yields the collection's + keys in order. + + They might support specialized `for-each-key' and `for-each-elt' +operations. + + - Function: collection? OBJ + A predicate, true initially of lists, vectors and strings. New + sorts of collections must answer `#t' to `collection?'. + + - Procedure: map-elts PROC . COLLECTIONS + - Procedure: do-elts PROC . COLLECTIONS + PROC is a procedure taking as many arguments as there are + COLLECTIONS (at least one). The COLLECTIONS are iterated over in + their natural order and PROC is applied to the elements yielded by + each iteration in turn. The order in which the arguments are + supplied corresponds to te order in which the COLLECTIONS appear. + `do-elts' is used when only side-effects of PROC are of interest + and its return value is unspecified. `map-elts' returns a + collection (actually a vector) of the results of the applications + of PROC. + + Example: + (map-elts + (list 1 2 3) (vector 1 2 3)) + => #(2 4 6) + + - Procedure: map-keys PROC . COLLECTIONS + - Procedure: do-keys PROC . COLLECTIONS + These are analogous to `map-elts' and `do-elts', but each + iteration is over the COLLECTIONS' *keys* rather than their + elements. + + Example: + (map-keys + (list 1 2 3) (vector 1 2 3)) + => #(0 2 4) + + - Procedure: for-each-key COLLECTION PROC + - Procedure: for-each-elt COLLECTION PROC + These are like `do-keys' and `do-elts' but only for a single + collection; they are potentially more efficient. + + - Function: reduce PROC SEED . COLLECTIONS + A generalization of the list-based `comlist:reduce-init' (*Note + Lists as sequences::) to collections which will shadow the + list-based version if `(require 'collect)' follows `(require + 'common-list-functions)' (*Note Common List Functions::). + + Examples: + (reduce + 0 (vector 1 2 3)) + => 6 + (reduce union '() '((a b c) (b c d) (d a))) + => (c b d a). + + - Function: any? PRED . COLLECTIONS + A generalization of the list-based `some' (*Note Lists as + sequences::) to collections. + + Example: + (any? odd? (list 2 3 4 5)) + => #t + + - Function: every? PRED . COLLECTIONS + A generalization of the list-based `every' (*Note Lists as + sequences::) to collections. + + Example: + (every? collection? '((1 2) #(1 2))) + => #t + + - Function: empty? COLLECTION + Returns `#t' iff there are no elements in COLLECTION. + + `(empty? COLLECTION) == (zero? (size COLLECTION))' + + - Function: size COLLECTION + Returns the number of elements in COLLECTION. + + - Function: Setter LIST-REF + See *Note Setters:: for a definition of "setter". N.B. `(setter + list-ref)' doesn't work properly for element 0 of a list. + + Here is a sample collection: `simple-table' which is also a `table'. + (define-predicate TABLE?) + (define-operation (LOOKUP table key failure-object)) + (define-operation (ASSOCIATE! table key value)) ;; returns key + (define-operation (REMOVE! table key)) ;; returns value + + (define (MAKE-SIMPLE-TABLE) + (let ( (table (list)) ) + (object + ;; table behaviors + ((TABLE? self) #t) + ((SIZE self) (size table)) + ((PRINT self port) (format port "#<SIMPLE-TABLE>")) + ((LOOKUP self key failure-object) + (cond + ((assq key table) => cdr) + (else failure-object) + )) + ((ASSOCIATE! self key value) + (cond + ((assq key table) + => (lambda (bucket) (set-cdr! bucket value) key)) + (else + (set! table (cons (cons key value) table)) + key) + )) + ((REMOVE! self key);; returns old value + (cond + ((null? table) (slib:error "TABLE:REMOVE! Key not found: " key)) + ((eq? key (caar table)) + (let ( (value (cdar table)) ) + (set! table (cdr table)) + value) + ) + (else + (let loop ( (last table) (this (cdr table)) ) + (cond + ((null? this) + (slib:error "TABLE:REMOVE! Key not found: " key)) + ((eq? key (caar this)) + (let ( (value (cdar this)) ) + (set-cdr! last (cdr this)) + value) + ) + (else + (loop (cdr last) (cdr this))) + ) ) ) + )) + ;; collection behaviors + ((COLLECTION? self) #t) + ((GEN-KEYS self) (collect:list-gen-elts (map car table))) + ((GEN-ELTS self) (collect:list-gen-elts (map cdr table))) + ((FOR-EACH-KEY self proc) + (for-each (lambda (bucket) (proc (car bucket))) table) + ) + ((FOR-EACH-ELT self proc) + (for-each (lambda (bucket) (proc (cdr bucket))) table) + ) + ) ) ) + + +File: slib.info, Node: Dynamic Data Type, Next: Hash Tables, Prev: Collections, Up: Data Structures + +Dynamic Data Type +================= + + `(require 'dynamic)' + + - Function: make-dynamic OBJ + Create and returns a new "dynamic" whose global value is OBJ. + + - Function: dynamic? OBJ + Returns true if and only if OBJ is a dynamic. No object + satisfying `dynamic?' satisfies any of the other standard type + predicates. + + - Function: dynamic-ref DYN + Return the value of the given dynamic in the current dynamic + environment. + + - Procedure: dynamic-set! DYN OBJ + Change the value of the given dynamic to OBJ in the current + dynamic environment. The returned value is unspecified. + + - Function: call-with-dynamic-binding DYN OBJ THUNK + Invoke and return the value of the given thunk in a new, nested + dynamic environment in which the given dynamic has been bound to a + new location whose initial contents are the value OBJ. This + dynamic environment has precisely the same extent as the + invocation of the thunk and is thus captured by continuations + created within that invocation and re-established by those + continuations when they are invoked. + + The `dynamic-bind' macro is not implemented. + + +File: slib.info, Node: Hash Tables, Next: Hashing, Prev: Dynamic Data Type, Up: Data Structures + +Hash Tables +=========== + + `(require 'hash-table)' + + - Function: predicate->hash PRED + Returns a hash function (like `hashq', `hashv', or `hash') + corresponding to the equality predicate PRED. PRED should be + `eq?', `eqv?', `equal?', `=', `char=?', `char-ci=?', `string=?', or + `string-ci=?'. + + A hash table is a vector of association lists. + + - Function: make-hash-table K + Returns a vector of K empty (association) lists. + + Hash table functions provide utilities for an associative database. +These functions take an equality predicate, PRED, as an argument. PRED +should be `eq?', `eqv?', `equal?', `=', `char=?', `char-ci=?', +`string=?', or `string-ci=?'. + + - Function: predicate->hash-asso PRED + Returns a hash association function of 2 arguments, KEY and + HASHTAB, corresponding to PRED. The returned function returns a + key-value pair whose key is PRED-equal to its first argument or + `#f' if no key in HASHTAB is PRED-equal to the first argument. + + - Function: hash-inquirer PRED + Returns a procedure of 3 arguments, `hashtab' and `key', which + returns the value associated with `key' in `hashtab' or `#f' if + key does not appear in `hashtab'. + + - Function: hash-associator PRED + Returns a procedure of 3 arguments, HASHTAB, KEY, and VALUE, which + modifies HASHTAB so that KEY and VALUE associated. Any previous + value associated with KEY will be lost. + + - Function: hash-remover PRED + Returns a procedure of 2 arguments, HASHTAB and KEY, which + modifies HASHTAB so that the association whose key is KEY is + removed. + + - Function: hash-map PROC HASH-TABLE + Returns a new hash table formed by mapping PROC over the keys and + values of HASH-TABLE. PROC must be a function of 2 arguments + which returns the new value part. + + - Function: hash-for-each PROC HASH-TABLE + Applies PROC to each pair of keys and values of HASH-TABLE. PROC + must be a function of 2 arguments. The returned value is + unspecified. + + +File: slib.info, Node: Hashing, Next: Chapter Ordering, Prev: Hash Tables, Up: Data Structures + +Hashing +======= + + `(require 'hash)' + + These hashing functions are for use in quickly classifying objects. +Hash tables use these functions. + + - Function: hashq OBJ K + - Function: hashv OBJ K + - Function: hash OBJ K + Returns an exact non-negative integer less than K. For each + non-negative integer less than K there are arguments OBJ for which + the hashing functions applied to OBJ and K returns that integer. + + For `hashq', `(eq? obj1 obj2)' implies `(= (hashq obj1 k) (hashq + obj2))'. + + For `hashv', `(eqv? obj1 obj2)' implies `(= (hashv obj1 k) (hashv + obj2))'. + + For `hash', `(equal? obj1 obj2)' implies `(= (hash obj1 k) (hash + obj2))'. + + `hash', `hashv', and `hashq' return in time bounded by a constant. + Notice that items having the same `hash' implies the items have + the same `hashv' implies the items have the same `hashq'. + + `(require 'sierpinski)' + + - Function: make-sierpinski-indexer MAX-COORDINATE + Returns a procedure (eg hash-function) of 2 numeric arguments which + preserves *nearness* in its mapping from NxN to N. + + MAX-COORDINATE is the maximum coordinate (a positive integer) of a + population of points. The returned procedures is a function that + takes the x and y coordinates of a point, (non-negative integers) + and returns an integer corresponding to the relative position of + that point along a Sierpinski curve. (You can think of this as + computing a (pseudo-) inverse of the Sierpinski spacefilling + curve.) + + Example use: Make an indexer (hash-function) for integer points + lying in square of integer grid points [0,99]x[0,99]: + (define space-key (make-sierpinski-indexer 100)) + Now let's compute the index of some points: + (space-key 24 78) => 9206 + (space-key 23 80) => 9172 + + Note that locations (24, 78) and (23, 80) are near in index and + therefore, because the Sierpinski spacefilling curve is + continuous, we know they must also be near in the plane. Nearness + in the plane does not, however, necessarily correspond to nearness + in index, although it *tends* to be so. + + Example applications: + + Sort points by Sierpinski index to get heuristic solution to + *travelling salesman problem*. For details of performance, + see L. Platzman and J. Bartholdi, "Spacefilling curves and the + Euclidean travelling salesman problem", JACM 36(4):719-737 + (October 1989) and references therein. + + + Use Sierpinski index as key by which to store 2-dimensional + data in a 1-dimensional data structure (such as a table). + Then locations that are near each other in 2-d space will + tend to be near each other in 1-d data structure; and + locations that are near in 1-d data structure will be near in + 2-d space. This can significantly speed retrieval from + secondary storage because contiguous regions in the plane + will tend to correspond to contiguous regions in secondary + storage. (This is a standard technique for managing CAD/CAM + or geographic data.) + + + `(require 'soundex)' + + - Function: soundex NAME + Computes the *soundex* hash of NAME. Returns a string of an + initial letter and up to three digits between 0 and 6. Soundex + supposedly has the property that names that sound similar in normal + English pronunciation tend to map to the same key. + + Soundex was a classic algorithm used for manual filing of personal + records before the advent of computers. It performs adequately for + English names but has trouble with other nationalities. + + See Knuth, Vol. 3 `Sorting and searching', pp 391-2 + + To manage unusual inputs, `soundex' omits all non-alphabetic + characters. Consequently, in this implementation: + + (soundex <string of blanks>) => "" + (soundex "") => "" + + Examples from Knuth: + + (map soundex '("Euler" "Gauss" "Hilbert" "Knuth" + "Lloyd" "Lukasiewicz")) + => ("E460" "G200" "H416" "K530" "L300" "L222") + + (map soundex '("Ellery" "Ghosh" "Heilbronn" "Kant" + "Ladd" "Lissajous")) + => ("E460" "G200" "H416" "K530" "L300" "L222") + + Some cases in which the algorithm fails (Knuth): + + (map soundex '("Rogers" "Rodgers")) => ("R262" "R326") + + (map soundex '("Sinclair" "St. Clair")) => ("S524" "S324") + + (map soundex '("Tchebysheff" "Chebyshev")) => ("T212" "C121") + + +File: slib.info, Node: Chapter Ordering, Next: Object, Prev: Hashing, Up: Data Structures + +Chapter Ordering +================ + + `(require 'chapter-order)' + + The `chap:' functions deal with strings which are ordered like +chapter numbers (or letters) in a book. Each section of the string +consists of consecutive numeric or consecutive aphabetic characters of +like case. + + - Function: chap:string<? STRING1 STRING2 + Returns #t if the first non-matching run of alphabetic upper-case + or the first non-matching run of alphabetic lower-case or the first + non-matching run of numeric characters of STRING1 is `string<?' + than the corresponding non-matching run of characters of STRING2. + + (chap:string<? "a.9" "a.10") => #t + (chap:string<? "4c" "4aa") => #t + (chap:string<? "Revised^{3.99}" "Revised^{4}") => #t + + - Function: chap:string>? STRING1 STRING2 + - Function: chap:string<=? STRING1 STRING2 + - Function: chap:string>=? STRING1 STRING2 + Implement the corresponding chapter-order predicates. + + - Function: chap:next-string STRING + Returns the next string in the *chapter order*. If STRING has no + alphabetic or numeric characters, `(string-append STRING "0")' is + returnd. The argument to chap:next-string will always be + `chap:string<?' than the result. + + (chap:next-string "a.9") => "a.10" + (chap:next-string "4c") => "4d" + (chap:next-string "4z") => "4aa" + (chap:next-string "Revised^{4}") => "Revised^{5}" + + +File: slib.info, Node: Object, Next: Parameter lists, Prev: Chapter Ordering, Up: Data Structures + +Macroless Object System +======================= + + `(require 'object)' + + This is the Macroless Object System written by Wade Humeniuk +(whumeniu@datap.ca). Conceptual Tributes: *Note Yasos::, MacScheme's +%object, CLOS, Lack of R4RS macros. + +Concepts +-------- + +OBJECT + An object is an ordered association-list (by `eq?') of methods + (procedures). Methods can be added (`make-method!'), deleted + (`unmake-method!') and retrieved (`get-method'). Objects may + inherit methods from other objects. The object binds to the + environment it was created in, allowing closures to be used to + hide private procedures and data. + +GENERIC-METHOD + A generic-method associates (in terms of `eq?') object's method. + This allows scheme function style to be used for objects. The + calling scheme for using a generic method is `(generic-method + object param1 param2 ...)'. + +METHOD + A method is a procedure that exists in the object. To use a method + get-method must be called to look-up the method. Generic methods + implement the get-method functionality. Methods may be added to an + object associated with any scheme obj in terms of eq? + +GENERIC-PREDICATE + A generic method that returns a boolean value for any scheme obj. + +PREDICATE + A object's method asscociated with a generic-predicate. Returns + `#t'. + +Procedures +---------- + + - Function: make-object ANCESTOR ... + Returns an object. Current object implementation is a tagged + vector. ANCESTORs are optional and must be objects in terms of + object?. ANCESTORs methods are included in the object. Multiple + ANCESTORs might associate the same generic-method with a method. + In this case the method of the ANCESTOR first appearing in the + list is the one returned by `get-method'. + + - Function: object? OBJ + Returns boolean value whether OBJ was created by make-object. + + - Function: make-generic-method EXCEPTION-PROCEDURE + Returns a procedure which be associated with an object's methods. + If EXCEPTION-PROCEDURE is specified then it is used to process + non-objects. + + - Function: make-generic-predicate + Returns a boolean procedure for any scheme object. + + - Function: make-method! OBJECT GENERIC-METHOD METHOD + Associates METHOD to the GENERIC-METHOD in the object. The METHOD + overrides any previous association with the GENERIC-METHOD within + the object. Using `unmake-method!' will restore the object's + previous association with the GENERIC-METHOD. METHOD must be a + procedure. + + - Function: make-predicate! OBJECT GENERIC-PRECIATE + Makes a predicate method associated with the GENERIC-PREDICATE. + + - Function: unmake-method! OBJECT GENERIC-METHOD + Removes an object's association with a GENERIC-METHOD . + + - Function: get-method OBJECT GENERIC-METHOD + Returns the object's method associated (if any) with the + GENERIC-METHOD. If no associated method exists an error is + flagged. + +Examples +-------- + + (require 'object) + + (define instantiate (make-generic-method)) + + (define (make-instance-object . ancestors) + (define self (apply make-object + (map (lambda (obj) (instantiate obj)) ancestors))) + (make-method! self instantiate (lambda (self) self)) + self) + + (define who (make-generic-method)) + (define imigrate! (make-generic-method)) + (define emigrate! (make-generic-method)) + (define describe (make-generic-method)) + (define name (make-generic-method)) + (define address (make-generic-method)) + (define members (make-generic-method)) + + (define society + (let () + (define self (make-instance-object)) + (define population '()) + (make-method! self imigrate! + (lambda (new-person) + (if (not (eq? new-person self)) + (set! population (cons new-person population))))) + (make-method! self emigrate! + (lambda (person) + (if (not (eq? person self)) + (set! population + (comlist:remove-if (lambda (member) + (eq? member person)) + population))))) + (make-method! self describe + (lambda (self) + (map (lambda (person) (describe person)) population))) + (make-method! self who + (lambda (self) (map (lambda (person) (name person)) + population))) + (make-method! self members (lambda (self) population)) + self)) + + (define (make-person %name %address) + (define self (make-instance-object society)) + (make-method! self name (lambda (self) %name)) + (make-method! self address (lambda (self) %address)) + (make-method! self who (lambda (self) (name self))) + (make-method! self instantiate + (lambda (self) + (make-person (string-append (name self) "-son-of") + %address))) + (make-method! self describe + (lambda (self) (list (name self) (address self)))) + (imigrate! self) + self) + +Inverter Documentation +...................... + + Inheritance: + <inverter>::(<number> <description>) + Generic-methods + <inverter>::value => <number>::value + <inverter>::set-value! => <number>::set-value! + <inverter>::describe => <description>::describe + <inverter>::help + <inverter>::invert + <inverter>::inverter? + +Number Documention +.................. + + Inheritance + <number>::() + Slots + <number>::<x> + Generic Methods + <number>::value + <number>::set-value! + +Inverter code +............. + + (require 'object) + + (define value (make-generic-method (lambda (val) val))) + (define set-value! (make-generic-method)) + (define invert (make-generic-method + (lambda (val) + (if (number? val) + (/ 1 val) + (error "Method not supported:" val))))) + (define noop (make-generic-method)) + (define inverter? (make-generic-predicate)) + (define describe (make-generic-method)) + (define help (make-generic-method)) + + (define (make-number x) + (define self (make-object)) + (make-method! self value (lambda (this) x)) + (make-method! self set-value! + (lambda (this new-value) (set! x new-value))) + self) + + (define (make-description str) + (define self (make-object)) + (make-method! self describe (lambda (this) str)) + (make-method! self help (lambda (this) "Help not available")) + self) + + (define (make-inverter) + (define self (make-object + (make-number 1) + (make-description "A number which can be inverted"))) + (define <value> (get-method self value)) + (make-method! self invert (lambda (self) (/ 1 (<value> self)))) + (make-predicate! self inverter?) + (unmake-method! self help) + (make-method! self help + (lambda (self) + (display "Inverter Methods:") (newline) + (display " (value inverter) ==> n") (newline))) + self) + + ;;;; Try it out + + (define invert! (make-generic-method)) + + (define x (make-inverter)) + + (make-method! x invert! (lambda () (set-value! x (/ 1 (value x))))) + + (value x) => 1 + (set-value! x 33) => undefined + (invert! x) => undefined + (value x) => 1/33 + + (unmake-method! x invert!) => undefined + + (invert! x) error--> ERROR: Method not supported: x + + +File: slib.info, Node: Parameter lists, Next: Priority Queues, Prev: Object, Up: Data Structures + +Parameter lists +=============== + + `(require 'parameters)' + +Arguments to procedures in scheme are distinguished from each other by +their position in the procedure call. This can be confusing when a +procedure takes many arguments, many of which are not often used. + +A "parameter-list" is a way of passing named information to a +procedure. Procedures are also defined to set unused parameters to +default values, check parameters, and combine parameter lists. + +A PARAMETER has the form `(parameter-name value1 ...)'. This format +allows for more than one value per parameter-name. + +A PARAMETER-LIST is a list of PARAMETERs, each with a different +PARAMETER-NAME. + + - Function: make-parameter-list PARAMETER-NAMES + Returns an empty parameter-list with slots for PARAMETER-NAMES. + + - Function: parameter-list-ref PARAMETER-LIST PARAMETER-NAME + PARAMETER-NAME must name a valid slot of PARAMETER-LIST. + `parameter-list-ref' returns the value of parameter PARAMETER-NAME + of PARAMETER-LIST. + + - Procedure: adjoin-parameters! PARAMETER-LIST PARAMETER1 ... + Returns PARAMETER-LIST with PARAMETER1 ... merged in. + + - Procedure: parameter-list-expand EXPANDERS PARAMETER-LIST + EXPANDERS is a list of procedures whose order matches the order of + the PARAMETER-NAMEs in the call to `make-parameter-list' which + created PARAMETER-LIST. For each non-false element of EXPANDERS + that procedure is mapped over the corresponding parameter value + and the returned parameter lists are merged into PARAMETER-LIST. + + This process is repeated until PARAMETER-LIST stops growing. The + value returned from `parameter-list-expand' is unspecified. + + - Function: fill-empty-parameters DEFAULTS PARAMETER-LIST + DEFAULTS is a list of lists whose order matches the order of the + PARAMETER-NAMEs in the call to `make-parameter-list' which created + PARAMETER-LIST. `fill-empty-parameters' returns a new + parameter-list with each empty parameter filled with the + corresponding DEFAULT. + + - Function: check-parameters CHECKS PARAMETER-LIST + CHECKS is a list of procedures whose order matches the order of + the PARAMETER-NAMEs in the call to `make-parameter-list' which + created PARAMETER-LIST. + + `check-parameters' returns PARAMETER-LIST if each CHECK of the + corresponding PARAMETER-LIST returns non-false. If some CHECK + returns `#f' an error is signaled. + +In the following procedures ARITIES is a list of symbols. The elements +of `arities' can be: + +`single' + Requires a single parameter. + +`optional' + A single parameter or no parameter is acceptable. + +`boolean' + A single boolean parameter or zero parameters is acceptable. + +`nary' + Any number of parameters are acceptable. + +`nary1' + One or more of parameters are acceptable. + + - Function: parameter-list->arglist POSITIONS ARITIES TYPES + PARAMETER-LIST + Returns PARAMETER-LIST converted to an argument list. Parameters + of ARITY type `single' and `boolean' are converted to the single + value associated with them. The other ARITY types are converted + to lists of the value(s) of type TYPES. + + POSITIONS is a list of positive integers whose order matches the + order of the PARAMETER-NAMEs in the call to `make-parameter-list' + which created PARAMETER-LIST. The integers specify in which + argument position the corresponding parameter should appear. + + - Function: getopt->parameter-list ARGC ARGV OPTNAMES ARITIES TYPES + ALIASES + Returns ARGV converted to a parameter-list. OPTNAMES are the + parameter-names. ALIASES is a list of lists of strings and + elements of OPTNAMES. Each of these strings which have length of + 1 will be treated as a single - option by `getopt'. Longer + strings will be treated as long-named options (*note getopt-: + Getopt.). + + - Function: getopt->arglist ARGC ARGV OPTNAMES POSITIONS ARITIES TYPES + DEFAULTS CHECKS ALIASES + Like `getopt->parameter-list', but converts ARGV to an + argument-list as specified by OPTNAMES, POSITIONS, ARITIES, TYPES, + DEFAULTS, CHECKS, and ALIASES. + + These `getopt' functions can be used with SLIB relational databases. +For an example, *Note make-command-server: Database Utilities. + + +File: slib.info, Node: Priority Queues, Next: Queues, Prev: Parameter lists, Up: Data Structures + +Priority Queues +=============== + + `(require 'priority-queue)' + + - Function: make-heap PRED<? + Returns a binary heap suitable which can be used for priority queue + operations. + + - Function: heap-length HEAP + Returns the number of elements in HEAP. + + - Procedure: heap-insert! HEAP ITEM + Inserts ITEM into HEAP. ITEM can be inserted multiple times. The + value returned is unspecified. + + - Function: heap-extract-max! HEAP + Returns the item which is larger than all others according to the + PRED<? argument to `make-heap'. If there are no items in HEAP, an + error is signaled. + + The algorithm for priority queues was taken from `Introduction to +Algorithms' by T. Cormen, C. Leiserson, R. Rivest. 1989 MIT Press. + + +File: slib.info, Node: Queues, Next: Records, Prev: Priority Queues, Up: Data Structures + +Queues +====== + + `(require 'queue)' + + A "queue" is a list where elements can be added to both the front and +rear, and removed from the front (i.e., they are what are often called +"dequeues"). A queue may also be used like a stack. + + - Function: make-queue + Returns a new, empty queue. + + - Function: queue? OBJ + Returns `#t' if OBJ is a queue. + + - Function: queue-empty? Q + Returns `#t' if the queue Q is empty. + + - Procedure: queue-push! Q DATUM + Adds DATUM to the front of queue Q. + + - Procedure: enquque! Q DATUM + Adds DATUM to the rear of queue Q. + + All of the following functions raise an error if the queue Q is empty. + + - Function: queue-front Q + Returns the datum at the front of the queue Q. + + - Function: queue-rear Q + Returns the datum at the rear of the queue Q. + + - Prcoedure: queue-pop! Q + - Procedure: dequeue! Q + Both of these procedures remove and return the datum at the front + of the queue. `queue-pop!' is used to suggest that the queue is + being used like a stack. + diff --git a/slib.info-2 b/slib.info-2 new file mode 100644 index 0000000..f1c31c5 --- /dev/null +++ b/slib.info-2 @@ -0,0 +1,1193 @@ +This is Info file slib.info, produced by Makeinfo-1.64 from the input +file slib.texi. + + This file documents SLIB, the portable Scheme library. + + Copyright (C) 1993 Todd R. Eigenschink Copyright (C) 1993, 1994, 1995 +Aubrey Jaffer + + Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + + Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + + Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be stated in a +translation approved by the author. + + +File: slib.info, Node: Records, Next: Base Table, Prev: Queues, Up: Data Structures + +Records +======= + + `(require 'record)' + + The Record package provides a facility for user to define their own +record data types. + + - Function: make-record-type TYPE-NAME FIELD-NAMES + Returns a "record-type descriptor", a value representing a new data + type disjoint from all others. The TYPE-NAME argument must be a + string, but is only used for debugging purposes (such as the + printed representation of a record of the new type). The + FIELD-NAMES argument is a list of symbols naming the "fields" of a + record of the new type. It is an error if the list contains any + duplicates. It is unspecified how record-type descriptors are + represented. + + - Function: record-constructor RTD [FIELD-NAMES] + Returns a procedure for constructing new members of the type + represented by RTD. The returned procedure accepts exactly as + many arguments as there are symbols in the given list, + FIELD-NAMES; these are used, in order, as the initial values of + those fields in a new record, which is returned by the constructor + procedure. The values of any fields not named in that list are + unspecified. The FIELD-NAMES argument defaults to the list of + field names in the call to `make-record-type' that created the + type represented by RTD; if the FIELD-NAMES argument is provided, + it is an error if it contains any duplicates or any symbols not in + the default list. + + - Function: record-predicate RTD + Returns a procedure for testing membership in the type represented + by RTD. The returned procedure accepts exactly one argument and + returns a true value if the argument is a member of the indicated + record type; it returns a false value otherwise. + + - Function: record-accessor RTD FIELD-NAME + Returns a procedure for reading the value of a particular field of + a member of the type represented by RTD. The returned procedure + accepts exactly one argument which must be a record of the + appropriate type; it returns the current value of the field named + by the symbol FIELD-NAME in that record. The symbol FIELD-NAME + must be a member of the list of field-names in the call to + `make-record-type' that created the type represented by RTD. + + - Function: record-modifier RTD FIELD-NAME + Returns a procedure for writing the value of a particular field of + a member of the type represented by RTD. The returned procedure + accepts exactly two arguments: first, a record of the appropriate + type, and second, an arbitrary Scheme value; it modifies the field + named by the symbol FIELD-NAME in that record to contain the given + value. The returned value of the modifier procedure is + unspecified. The symbol FIELD-NAME must be a member of the list + of field-names in the call to `make-record-type' that created the + type represented by RTD. + + - Function: record? OBJ + Returns a true value if OBJ is a record of any type and a false + value otherwise. Note that `record?' may be true of any Scheme + value; of course, if it returns true for some particular value, + then `record-type-descriptor' is applicable to that value and + returns an appropriate descriptor. + + - Function: record-type-descriptor RECORD + Returns a record-type descriptor representing the type of the given + record. That is, for example, if the returned descriptor were + passed to `record-predicate', the resulting predicate would return + a true value when passed the given record. Note that it is not + necessarily the case that the returned descriptor is the one that + was passed to `record-constructor' in the call that created the + constructor procedure that created the given record. + + - Function: record-type-name RTD + Returns the type-name associated with the type represented by rtd. + The returned value is `eqv?' to the TYPE-NAME argument given in + the call to `make-record-type' that created the type represented by + RTD. + + - Function: record-type-field-names RTD + Returns a list of the symbols naming the fields in members of the + type represented by RTD. The returned value is `equal?' to the + field-names argument given in the call to `make-record-type' that + created the type represented by RTD. + + +File: slib.info, Node: Base Table, Next: Relational Database, Prev: Records, Up: Data Structures + +Base Table +========== + + A base table implementation using Scheme association lists is +available as the value of the identifier `alist-table' after doing: + + (require 'alist-table) + + Association list base tables are suitable for small databases and +support all Scheme types when temporary and readable/writeable Scheme +types when saved. I hope support for other base table implementations +will be added in the future. + + This rest of this section documents the interface for a base table +implementation from which the *Note Relational Database:: package +constructs a Relational system. It will be of interest primarily to +those wishing to port or write new base-table implementations. + + All of these functions are accessed through a single procedure by +calling that procedure with the symbol name of the operation. A +procedure will be returned if that operation is supported and `#f' +otherwise. For example: + + (require 'alist-table) + (define open-base (alist-table 'make-base)) + make-base => *a procedure* + (define foo (alist-table 'foo)) + foo => #f + + - Function: make-base FILENAME KEY-DIMENSION COLUMN-TYPES + Returns a new, open, low-level database (collection of tables) + associated with FILENAME. This returned database has an empty + table associated with CATALOG-ID. The positive integer + KEY-DIMENSION is the number of keys composed to make a PRIMARY-KEY + for the catalog table. The list of symbols COLUMN-TYPES describes + the types of each column for that table. If the database cannot + be created as specified, `#f' is returned. + + Calling the `close-base' method on this database and possibly other + operations will cause FILENAME to be written to. If FILENAME is + `#f' a temporary, non-disk based database will be created if such + can be supported by the base table implelentation. + + - Function: open-base FILENAME MUTABLE + Returns an open low-level database associated with FILENAME. If + MUTABLE? is `#t', this database will have methods capable of + effecting change to the database. If MUTABLE? is `#f', only + methods for inquiring the database will be available. If the + database cannot be opened as specified `#f' is returned. + + Calling the `close-base' (and possibly other) method on a MUTABLE? + database will cause FILENAME to be written to. + + - Function: write-base LLDB FILENAME + Causes the low-level database LLDB to be written to FILENAME. If + the write is successful, also causes LLDB to henceforth be + associated with FILENAME. Calling the `close-database' (and + possibly other) method on LLDB may cause FILENAME to be written + to. If FILENAME is `#f' this database will be changed to a + temporary, non-disk based database if such can be supported by the + underlying base table implelentation. If the operations completed + successfully, `#t' is returned. Otherwise, `#f' is returned. + + - Function: sync-base LLDB + Causes the file associated with the low-level database LLDB to be + updated to reflect its current state. If the associated filename + is `#f', no action is taken and `#f' is returned. If this + operation completes successfully, `#t' is returned. Otherwise, + `#f' is returned. + + - Function: close-base LLDB + Causes the low-level database LLDB to be written to its associated + file (if any). If the write is successful, subsequent operations + to LLDB will signal an error. If the operations complete + successfully, `#t' is returned. Otherwise, `#f' is returned. + + - Function: make-table LLDB KEY-DIMENSION COLUMN-TYPES + Returns the BASE-ID for a new base table, otherwise returns `#f'. + The base table can then be opened using `(open-table LLDB + BASE-ID)'. The positive integer KEY-DIMENSION is the number of + keys composed to make a PRIMARY-KEY for this table. The list of + symbols COLUMN-TYPES describes the types of each column. + + - Constant: catalog-id + A constant BASE-ID suitable for passing as a parameter to + `open-table'. CATALOG-ID will be used as the base table for the + system catalog. + + - Function: open-table LLDB BASE-ID KEY-DIMENSION COLUMN-TYPES + Returns a HANDLE for an existing base table in the low-level + database LLDB if that table exists and can be opened in the mode + indicated by MUTABLE?, otherwise returns `#f'. + + As with `make-table', the positive integer KEY-DIMENSION is the + number of keys composed to make a PRIMARY-KEY for this table. The + list of symbols COLUMN-TYPES describes the types of each column. + + - Function: kill-table LLDB BASE-ID KEY-DIMENSION COLUMN-TYPES + Returns `#t' if the base table associated with BASE-ID was removed + from the low level database LLDB, and `#f' otherwise. + + - Function: make-keyifier-1 TYPE + Returns a procedure which accepts a single argument which must be + of type TYPE. This returned procedure returns an object suitable + for being a KEY argument in the functions whose descriptions + follow. + + Any 2 arguments of the supported type passed to the returned + function which are not `equal?' must result in returned values + which are not `equal?'. + + - Function: make-list-keyifier KEY-DIMENSION TYPES + The list of symbols TYPES must have at least KEY-DIMENSION + elements. Returns a procedure which accepts a list of length + KEY-DIMENSION and whose types must corresopond to the types named + by TYPES. This returned procedure combines the elements of its + list argument into an object suitable for being a KEY argument in + the functions whose descriptions follow. + + Any 2 lists of supported types (which must at least include + symbols and non-negative integers) passed to the returned function + which are not `equal?' must result in returned values which are not + `equal?'. + + - Function: make-key-extractor KEY-DIMENSION TYPES COLUMN-NUMBER + Returns a procedure which accepts objects produced by application + of the result of `(make-list-keyifier KEY-DIMENSION TYPES)'. This + procedure returns a KEY which is `equal?' to the COLUMN-NUMBERth + element of the list which was passed to create COMBINED-KEY. The + list TYPES must have at least KEY-DIMENSION elements. + + - Function: make-key->list KEY-DIMENSION TYPES + Returns a procedure which accepts objects produced by application + of the result of `(make-list-keyifier KEY-DIMENSION TYPES)'. This + procedure returns a list of KEYs which are elementwise `equal?' to + the list which was passed to create COMBINED-KEY. + +In the following functions, the KEY argument can always be assumed to +be the value returned by a call to a *keyify* routine. + + - Function: for-each-key HANDLE PROCEDURE + Calls PROCEDURE once with each KEY in the table opened in HANDLE + in an unspecified order. An unspecified value is returned. + + - Function: map-key HANDLE PROCEDURE + Returns a list of the values returned by calling PROCEDURE once + with each KEY in the table opened in HANDLE in an unspecified + order. + + - Function: ordered-for-each-key HANDLE PROCEDURE + Calls PROCEDURE once with each KEY in the table opened in HANDLE + in the natural order for the types of the primary key fields of + that table. An unspecified value is returned. + + - Function: present? HANDLE KEY + Returns a non-`#f' value if there is a row associated with KEY in + the table opened in HANDLE and `#f' otherwise. + + - Function: delete HANDLE KEY + Removes the row associated with KEY from the table opened in + HANDLE. An unspecified value is returned. + + - Function: make-getter KEY-DIMENSION TYPES + Returns a procedure which takes arguments HANDLE and KEY. This + procedure returns a list of the non-primary values of the relation + (in the base table opened in HANDLE) whose primary key is KEY if + it exists, and `#f' otherwise. + + - Function: make-putter KEY-DIMENSION TYPES + Returns a procedure which takes arguments HANDLE and KEY and + VALUE-LIST. This procedure associates the primary key KEY with + the values in VALUE-LIST (in the base table opened in HANDLE) and + returns an unspecified value. + + - Function: supported-type? SYMBOL + Returns `#t' if SYMBOL names a type allowed as a column value by + the implementation, and `#f' otherwise. At a minimum, an + implementation must support the types `integer', `symbol', + `string', `boolean', and `base-id'. + + - Function: supported-key-type? SYMBOL + Returns `#t' if SYMBOL names a type allowed as a key value by the + implementation, and `#f' otherwise. At a minimum, an + implementation must support the types `integer', and `symbol'. + +`integer' + Scheme exact integer. + +`symbol' + Scheme symbol. + +`boolean' + `#t' or `#f'. + +`base-id' + Objects suitable for passing as the BASE-ID parameter to + `open-table'. The value of CATALOG-ID must be an acceptable + `base-id'. + + +File: slib.info, Node: Relational Database, Next: Weight-Balanced Trees, Prev: Base Table, Up: Data Structures + +Relational Database +=================== + + `(require 'relational-database)' + + This package implements a database system inspired by the Relational +Model (`E. F. Codd, A Relational Model of Data for Large Shared Data +Banks'). An SLIB relational database implementation can be created +from any *Note Base Table:: implementation. + +* Menu: + +* Motivations:: Database Manifesto +* Creating and Opening Relational Databases:: +* Relational Database Operations:: +* Table Operations:: +* Catalog Representation:: +* Unresolved Issues:: +* Database Utilities:: 'database-utilities + + +File: slib.info, Node: Motivations, Next: Creating and Opening Relational Databases, Prev: Relational Database, Up: Relational Database + +Motivations +----------- + + Most nontrivial programs contain databases: Makefiles, configure +scripts, file backup, calendars, editors, source revision control, CAD +systems, display managers, menu GUIs, games, parsers, debuggers, +profilers, and even error reporting are all rife with databases. Coding +databases is such a common activity in programming that many may not be +aware of how often they do it. + + A database often starts as a dispatch in a program. The author, +perhaps because of the need to make the dispatch configurable, the need +for correlating dispatch in other routines, or because of changes or +growth, devises a data structure to contain the information, a routine +for interpreting that data structure, and perhaps routines for +augmenting and modifying the stored data. The dispatch must be +converted into this form and tested. + + The programmer may need to devise an interactive program for enabling +easy examination and modification of the information contained in this +database. Often, in an attempt to foster modularity and avoid delays in +release, intermediate file formats for the database information are +devised. It often turns out that users prefer modifying these +intermediate files with a text editor to using the interactive program +in order to do operations (such as global changes) not forseen by the +program's author. + + In order to address this need, the concientous software engineer may +even provide a scripting language to allow users to make repetitive +database changes. Users will grumble that they need to read a large +manual and learn yet another programming language (even if it *almost* +has language "xyz" syntax) in order to do simple configuration. + + All of these facilities need to be designed, coded, debugged, +documented, and supported; often causing what was very simple in concept +to become a major developement project. + + This view of databases just outlined is somewhat the reverse of the +view of the originators of the "Relational Model" of database +abstraction. The relational model was devised to unify and allow +interoperation of large multi-user databases running on diverse +platforms. A fairly general purpose "Comprehensive Language" for +database manipulations is mandated (but not specified) as part of the +relational model for databases. + + One aspect of the Relational Model of some importance is that the +"Comprehensive Language" must be expressible in some form which can be +stored in the database. This frees the programmer from having to make +programs data-driven in order to use a database. + + This package includes as one of its basic supported types Scheme +"expression"s. This type allows expressions as defined by the Scheme +standards to be stored in the database. Using `slib:eval' retrieved +expressions can be evaluated (in the top-level environment). Scheme's +`lambda' facilitates closure of environments, modularity, etc. so that +procedures (which could not be stored directly most databases) can +still be effectively retrieved. Since `slib:eval' evaluates +expressions in the top-level environment, built-in and user defined +procedures can be easily accessed by name. + + This package's purpose is to standardize (through a common interface) +database creation and usage in Scheme programs. The relational model's +provision for inclusion of language expressions as data as well as the +description (in tables, of course) of all of its tables assures that +relational databases are powerful enough to assume the roles currently +played by thousands of ad-hoc routines and data formats. + +Such standardization to a relational-like model brings many benefits: + + * Tables, fields, domains, and types can be dealt with by name in + programs. + + * The underlying database implementation can be changed (for + performance or other reasons) by changing a single line of code. + + * The formats of tables can be easily extended or changed without + altering code. + + * Consistency checks are specified as part of the table descriptions. + Changes in checks need only occur in one place. + + * All the configuration information which the developer wishes to + group together is easily grouped, without needing to change + programs aware of only some of these tables. + + * Generalized report generators, interactive entry programs, and + other database utilities can be part of a shared library. The + burden of adding configurability to a program is greatly reduced. + + * Scheme is the "comprehensive language" for these databases. + Scripting for configuration no longer needs to be in a separate + language with additional documentation. + + * Scheme's latent types mesh well with the strict typing and logical + requirements of the relational model. + + * Portable formats allow easy interchange of data. The included + table descriptions help prevent misinterpretation of format. + + +File: slib.info, Node: Creating and Opening Relational Databases, Next: Relational Database Operations, Prev: Motivations, Up: Relational Database + +Creating and Opening Relational Databases +----------------------------------------- + + - Function: make-relational-system BASE-TABLE-IMPLEMENTATION + Returns a procedure implementing a relational database using the + BASE-TABLE-IMPLEMENTATION. + + All of the operations of a base table implementation are accessed + through a procedure defined by `require'ing that implementation. + Similarly, all of the operations of the relational database + implementation are accessed through the procedure returned by + `make-relational-system'. For instance, a new relational database + could be created from the procedure returned by + `make-relational-system' by: + + (require 'alist-table) + (define relational-alist-system + (make-relational-system alist-table)) + (define create-alist-database + (relational-alist-system 'create-database)) + (define my-database + (create-alist-database "mydata.db")) + +What follows are the descriptions of the methods available from +relational system returned by a call to `make-relational-system'. + + - Function: create-database FILENAME + Returns an open, nearly empty relational database associated with + FILENAME. The only tables defined are the system catalog and + domain table. Calling the `close-database' method on this database + and possibly other operations will cause FILENAME to be written + to. If FILENAME is `#f' a temporary, non-disk based database will + be created if such can be supported by the underlying base table + implelentation. If the database cannot be created as specified + `#f' is returned. For the fields and layout of descriptor tables, + *Note Catalog Representation:: + + - Function: open-database FILENAME MUTABLE? + Returns an open relational database associated with FILENAME. If + MUTABLE? is `#t', this database will have methods capable of + effecting change to the database. If MUTABLE? is `#f', only + methods for inquiring the database will be available. Calling the + `close-database' (and possibly other) method on a MUTABLE? + database will cause FILENAME to be written to. If the database + cannot be opened as specified `#f' is returned. + + +File: slib.info, Node: Relational Database Operations, Next: Table Operations, Prev: Creating and Opening Relational Databases, Up: Relational Database + +Relational Database Operations +------------------------------ + +These are the descriptions of the methods available from an open +relational database. A method is retrieved from a database by calling +the database with the symbol name of the operation. For example: + + (define my-database + (create-alist-database "mydata.db")) + (define telephone-table-desc + ((my-database 'create-table) 'telephone-table-desc)) + + - Function: close-database + Causes the relational database to be written to its associated + file (if any). If the write is successful, subsequent operations + to this database will signal an error. If the operations completed + successfully, `#t' is returned. Otherwise, `#f' is returned. + + - Function: write-database FILENAME + Causes the relational database to be written to FILENAME. If the + write is successful, also causes the database to henceforth be + associated with FILENAME. Calling the `close-database' (and + possibly other) method on this database will cause FILENAME to be + written to. If FILENAME is `#f' this database will be changed to + a temporary, non-disk based database if such can be supported by + the underlying base table implelentation. If the operations + completed successfully, `#t' is returned. Otherwise, `#f' is + returned. + + - Function: table-exists? TABLE-NAME + Returns `#t' if TABLE-NAME exists in the system catalog, otherwise + returns `#f'. + + - Function: open-table TABLE-NAME MUTABLE? + Returns a "methods" procedure for an existing relational table in + this database if it exists and can be opened in the mode indicated + by MUTABLE?, otherwise returns `#f'. + +These methods will be present only in databases which are MUTABLE?. + + - Function: delete-table TABLE-NAME + Removes and returns the TABLE-NAME row from the system catalog if + the table or view associated with TABLE-NAME gets removed from the + database, and `#f' otherwise. + + - Function: create-table TABLE-DESC-NAME + Returns a methods procedure for a new (open) relational table for + describing the columns of a new base table in this database, + otherwise returns `#f'. For the fields and layout of descriptor + tables, *Note Catalog Representation::. + + - Function: create-table TABLE-NAME TABLE-DESC-NAME + Returns a methods procedure for a new (open) relational table with + columns as described by TABLE-DESC-NAME, otherwise returns `#f'. + + - Function: create-view ?? + - Function: project-table ?? + - Function: restrict-table ?? + - Function: cart-prod-tables ?? + Not yet implemented. + + +File: slib.info, Node: Table Operations, Next: Catalog Representation, Prev: Relational Database Operations, Up: Relational Database + +Table Operations +---------------- + +These are the descriptions of the methods available from an open +relational table. A method is retrieved from a table by calling the +table with the symbol name of the operation. For example: + + (define telephone-table-desc + ((my-database 'create-table) 'telephone-table-desc)) + (require 'common-list-functions) + (define ndrp (telephone-table-desc 'row:insert)) + (ndrp '(1 #t name #f string)) + (ndrp '(2 #f telephone + (lambda (d) + (and (string? d) (> (string-length d) 2) + (every + (lambda (c) + (memv c '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 + #\+ #\( #\ #\) #\-))) + (string->list d)))) + string)) + +Operations on a single column of a table are retrieved by giving the +column name as the second argument to the methods procedure. For +example: + + (define column-ids ((telephone-table-desc 'get* 'column-number))) + +Some operations described below require primary key arguments. Primary +keys arguments are denoted KEY1 KEY2 .... It is an error to call an +operation for a table which takes primary key arguments with the wrong +number of primary keys for that table. + +The term "row" used below refers to a Scheme list of values (one for +each column) in the order specified in the descriptor (table) for this +table. Missing values appear as `#f'. Primary keys may not be missing. + + - Function: get KEY1 KEY2 ... + Returns the value for the specified column of the row associated + with primary keys KEY1, KEY2 ... if it exists, or `#f' otherwise. + + - Function: get* + Returns a list of the values for the specified column for all rows + in this table. + + - Function: row:retrieve KEY1 KEY2 ... + Returns the row associated with primary keys KEY1, KEY2 ... if it + exists, or `#f' otherwise. + + - Function: row:retrieve* + Returns a list of all rows in this table. + + - Function: row:remove KEY1 KEY2 ... + Removes and returns the row associated with primary keys KEY1, + KEY2 ... if it exists, or `#f' otherwise. + + - Function: row:remove* + Removes and returns a list of all rows in this table. + + - Function: row:delete KEY1 KEY2 ... + Deletes the row associated with primary keys KEY1, KEY2 ... if it + exists. The value returned is unspecified. + + - Function: row:delete* + Deletes all rows in this table. The value returned is + unspecified. The descriptor table and catalog entry for this + table are not affected. + + - Function: row:update ROW + Adds the row, ROW, to this table. If a row for the primary key(s) + specified by ROW already exists in this table, it will be + overwritten. The value returned is unspecified. + + - Function: row:update* ROWS + Adds each row in the list ROWS, to this table. If a row for the + primary key specified by an element of ROWS already exists in this + table, it will be overwritten. The value returned is unspecified. + + - Function: row:insert ROW + Adds the row ROW to this table. If a row for the primary key(s) + specified by ROW already exists in this table an error is + signaled. The value returned is unspecified. + + - Function: row:insert* ROWS + Adds each row in the list ROWS, to this table. If a row for the + primary key specified by an element of ROWS already exists in this + table, an error is signaled. The value returned is unspecified. + + - Function: for-each-row PROC + Calls PROC with each ROW in this table in the natural ordering for + the primary key types. *Real* relational programmers would use + some least-upper-bound join for every row to get them in order; + But we don't have joins yet. + + - Function: close-table + Subsequent operations to this table will signal an error. + + - Constant: column-names + - Constant: column-foreigns + - Constant: column-domains + - Constant: column-types + Return a list of the column names, foreign-key table names, domain + names, or type names respectively for this table. These 4 methods + are different from the others in that the list is returned, rather + than a procedure to obtain the list. + + - Constant: primary-limit + Returns the number of primary keys fields in the relations in this + table. + + +File: slib.info, Node: Catalog Representation, Next: Unresolved Issues, Prev: Table Operations, Up: Relational Database + +Catalog Representation +---------------------- + +Each database (in an implementation) has a "system catalog" which +describes all the user accessible tables in that database (including +itself). + +The system catalog base table has the following fields. `PRI' +indicates a primary key for that table. + + PRI table-name + column-limit the highest column number + coltab-name descriptor table name + bastab-id data base table identifier + user-integrity-rule + view-procedure A scheme thunk which, when called, + produces a handle for the view. coltab + and bastab are specified if and only if + view-procedure is not. + +Descriptors for base tables (not views) are tables (pointed to by +system catalog). Descriptor (base) tables have the fields: + + PRI column-number sequential integers from 1 + primary-key? boolean TRUE for primary key components + column-name + column-integrity-rule + domain-name + +A "primary key" is any column marked as `primary-key?' in the +corresponding descriptor table. All the `primary-key?' columns must +have lower column numbers than any non-`primary-key?' columns. Every +table must have at least one primary key. Primary keys must be +sufficient to distinguish all rows from each other in the table. All of +the system defined tables have a single primary key. + +This package currently supports tables having from 1 to 4 primary keys +if there are non-primary columns, and any (natural) number if *all* +columns are primary keys. If you need more than 4 primary keys, I would +like to hear what you are doing! + +A "domain" is a category describing the allowable values to occur in a +column. It is described by a (base) table with the fields: + + PRI domain-name + foreign-table + domain-integrity-rule + type-id + type-param + +The "type-id" field value is a symbol. This symbol may be used by the +underlying base table implementation in storing that field. + +If the `foreign-table' field is non-`#f' then that field names a table +from the catalog. The values for that domain must match a primary key +of the table referenced by the TYPE-PARAM (or `#f', if allowed). This +package currently does not support composite foreign-keys. + +The types for which support is planned are: + atom + symbol + string [<length>] + number [<base>] + money <currency> + date-time + boolean + + foreign-key <table-name> + expression + virtual <expression> + + +File: slib.info, Node: Unresolved Issues, Next: Database Utilities, Prev: Catalog Representation, Up: Relational Database + +Unresolved Issues +----------------- + + Although `rdms.scm' is not large I found it very difficult to write +(six rewrites). I am not aware of any other examples of a generalized +relational system (although there is little new in CS). I left out +several aspects of the Relational model in order to simplify the job. +The major features lacking (which might be addressed portably) are +views, transaction boundaries, and protection. + + Protection needs a model for specifying priveledges. Given how +operations are accessed from handles it should not be difficult to +restrict table accesses to those allowed for that user. + + The system catalog has a field called `view-procedure'. This should +allow a purely functional implementation of views. This will work but +is unsatisfying for views resulting from a "select"ion (subset of +rows); for whole table operations it will not be possible to reduce the +number of keys scanned over when the selection is specified only by an +opaque procedure. + + Transaction boundaries present the most intriguing area. Transaction +boundaries are actually a feature of the "Comprehensive Language" of the +Relational database and not of the database. Scheme would seem to +provide the opportunity for an extremely clean semantics for transaction +boundaries since the builtin procedures with side effects are small in +number and easily identified. + + These side-effect builtin procedures might all be portably redefined +to versions which properly handled transactions. Compiled library +routines would need to be recompiled as well. Many system extensions +(delete-file, system, etc.) would also need to be redefined. + +There are 2 scope issues that must be resolved for multiprocess +transaction boundaries: + +Process scope + The actions captured by a transaction should be only for the + process which invoked the start of transaction. Although standard + Scheme does not provide process primitives as such, `dynamic-wind' + would provide a workable hook into process switching for many + implementations. + +Shared utilities with state + Some shared utilities have state which should *not* be part of a + transaction. An example would be calling a pseudo-random number + generator. If the success of a transaction depended on the + pseudo-random number and failed, the state of the generator would + be set back. Subsequent calls would keep returning the same + number and keep failing. + + Pseudo-random number generators are not reentrant and so would + require locks in order to operate properly in a multiprocess + environment. Are all examples of utilities whose state should not + part of transactions also non-reentrant? If so, perhaps + suspending transaction capture for the duration of locks would fix + it. + + +File: slib.info, Node: Database Utilities, Prev: Unresolved Issues, Up: Relational Database + +Database Utilities +------------------ + + `(require 'database-utilities)' + +This enhancement wraps a utility layer on `relational-database' which +provides: + * Automatic loading of the appropriate base-table package when + opening a database. + + * Automatic execution of initialization commands stored in database. + + * Transparent execution of database commands stored in `*commands*' + table in database. + +Also included are utilities which provide: + * Data definition from Scheme lists and + + * Report generation + +for any SLIB relational database. + + - Function: create-database FILENAME BASE-TABLE-TYPE + Returns an open, nearly empty enhanced (with `*commands*' table) + relational database (with base-table type BASE-TABLE-TYPE) + associated with FILENAME. + + - Function: open-database FILENAME + - Function: open-database FILENAME BASE-TABLE-TYPE + Returns an open enchanced relational database associated with + FILENAME. The database will be opened with base-table type + BASE-TABLE-TYPE) if supplied. If BASE-TABLE-TYPE is not supplied, + `open-database' will attempt to deduce the correct + base-table-type. If the database can not be opened or if it lacks + the `*commands*' table, `#f' is returned. + + - Function: open-database! FILENAME + - Function: open-database! FILENAME BASE-TABLE-TYPE + Returns *mutable* open enchanced relational database ... + +The table `*commands*' in an "enhanced" relational-database has the +fields (with domains): + PRI name symbol + parameters parameter-list + procedure expression + documentation string + + The `parameters' field is a foreign key (domain `parameter-list') of +the `*catalog-data*' table and should have the value of a table +described by `*parameter-columns*'. This `parameter-list' table +describes the arguments suitable for passing to the associated command. +The intent of this table is to be of a form such that different +user-interfaces (for instance, pull-down menus or plain-text queries) +can operate from the same table. A `parameter-list' table has the +following fields: + PRI index uint + name symbol + arity parameter-arity + domain domain + default expression + documentation string + + The `arity' field can take the values: + +`single' + Requires a single parameter of the specified domain. + +`optional' + A single parameter of the specified domain or zero parameters is + acceptable. + +`boolean' + A single boolean parameter or zero parameters (in which case `#f' + is substituted) is acceptable. + +`nary' + Any number of parameters of the specified domain are acceptable. + The argument passed to the command function is always a list of the + parameters. + +`nary1' + One or more of parameters of the specified domain are acceptable. + The argument passed to the command function is always a list of the + parameters. + + The `domain' field specifies the domain which a parameter or +parameters in the `index'th field must satisfy. + + The `default' field is an expression whose value is either `#f' or a +procedure of no arguments which returns a parameter or parameter list +as appropriate. If the expression's value is `#f' then no default is +appropriate for this parameter. Note that since the `default' +procedure is called every time a default parameter is needed for this +column, "sticky" defaults can be implemented using shared state with +the domain-integrity-rule. + +Invoking Commands +................. + + When an enhanced relational-database is called with a symbol which +matches a NAME in the `*commands*' table, the associated procedure +expression is evaluated and applied to the enhanced +relational-database. A procedure should then be returned which the user +can invoke on (optional) arguments. + + The command `*initialize*' is special. If present in the +`*commands*' table, `open-database' or `open-database!' will return the +value of the `*initialize*' command. Notice that arbitrary code can be +run when the `*initialize*' procedure is automatically applied to the +enhanced relational-database. + + Note also that if you wish to shadow or hide from the user +relational-database methods described in *Note Relational Database +Operations::, this can be done by a dispatch in the closure returned by +the `*initialize*' expression rather than by entries in the +`*commands*' table if it is desired that the underlying methods remain +accessible to code in the `*commands*' table. + + - Function: make-command-server RDB TABLE-NAME + Returns a procedure of 2 arguments, a (symbol) command and a + call-back procedure. When this returned procedure is called, it + looks up COMMAND in table TABLE-NAME and calls the call-back + procedure with arguments: + COMMAND + The COMMAND + + COMMAND-VALUE + The result of evaluating the expression in the PROCEDURE + field of TABLE-NAME and calling it with RDB. + + PARAMETER-NAME + A list of the "official" name of each parameter. Corresponds + to the `name' field of the COMMAND's parameter-table. + + POSITIONS + A list of the positive integer index of each parameter. + Corresponds to the `index' field of the COMMAND's + parameter-table. + + ARITIES + A list of the arities of each parameter. Corresponds to the + `arity' field of the COMMAND's parameter-table. For a + description of `arity' see table above. + + DEFAULTS + A list of the defaults for each parameter. Corresponds to + the `defaults' field of the COMMAND's parameter-table. + + DOMAIN-INTEGRITY-RULES + A list of procedures (one for each parameter) which tests + whether a value for a parameter is acceptable for that + parameter. The procedure should be called with each datum in + the list for `nary' arity parameters. + + ALIASES + A list of lists of `(alias parameter-name)'. There can be + more than one alias per PARAMETER-NAME. + + For information about parameters, *Note Parameter lists::. Here is an +example of setting up a command with arguments and parsing those +arguments from a `getopt' style argument list (*note Getopt::.). + + (require 'database-utilities) + (require 'parameters) + (require 'getopt) + + (define my-rdb (create-database #f 'alist-table)) + + (define-tables my-rdb + '(foo-params + *parameter-columns* + *parameter-columns* + ((1 first-argument single string "hithere" "first argument") + (2 flag boolean boolean #f "a flag"))) + '(foo-pnames + ((name string)) + ((parameter-index uint)) + (("l" 1) + ("a" 2))) + '(my-commands + ((name symbol)) + ((parameters parameter-list) + (parameter-names parameter-name-translation) + (procedure expression) + (documentation string)) + ((foo + foo-params + foo-pnames + (lambda (rdb) (lambda (foo aflag) (print foo aflag))) + "test command arguments")))) + + (define (dbutil:serve-command-line rdb command-table + command argc argv) + (set! argv (if (vector? argv) (vector->list argv) argv)) + ((make-command-server rdb command-table) + command + (lambda (comname comval options positions + arities types defaults dirs aliases) + (apply comval (getopt->arglist argc argv options positions + arities types defaults dirs aliases))))) + + (define (test) + (set! *optind* 1) + (dbutil:serve-command-line + my-rdb 'my-commands 'foo 4 '("dummy" "-l" "foo" "-a"))) + (test) + -| + "foo" #t + + Some commands are defined in all extended relational-databases. The +are called just like *Note Relational Database Operations::. + + - Function: add-domain DOMAIN-ROW + Adds DOMAIN-ROW to the "domains" table if there is no row in the + domains table associated with key `(car DOMAIN-ROW)' and returns + `#t'. Otherwise returns `#f'. + + For the fields and layout of the domain table, *Note Catalog + Representation:: + + - Function: delete-domain DOMAIN-NAME + Removes and returns the DOMAIN-NAME row from the "domains" table. + + - Function: domain-checker DOMAIN + Returns a procedure to check an argument for conformance to domain + DOMAIN. + +Defining Tables +--------------- + + - Procedure: define-tables RDB SPEC-0 ... + Adds tables as specified in SPEC-0 ... to the open + relational-database RDB. Each SPEC has the form: + + (<name> <descriptor-name> <descriptor-name> <rows>) + or + (<name> <primary-key-fields> <other-fields> <rows>) + + where <name> is the table name, <descriptor-name> is the symbol + name of a descriptor table, <primary-key-fields> and + <other-fields> describe the primary keys and other fields + respectively, and <rows> is a list of data rows to be added to the + table. + + <primary-key-fields> and <other-fields> are lists of field + descriptors of the form: + + (<column-name> <domain>) + or + (<column-name> <domain> <column-integrity-rule>) + + where <column-name> is the column name, <domain> is the domain of + the column, and <column-integrity-rule> is an expression whose + value is a procedure of one argument (and returns non-`#f' to + signal an error). + + If <domain> is not a defined domain name and it matches the name of + this table or an already defined (in one of SPEC-0 ...) single key + field table, a foriegn-key domain will be created for it. + + - Procedure: create-report RDB DESTINATION REPORT-NAME TABLE + - Procedure: create-report RDB DESTINATION REPORT-NAME + The symbol REPORT-NAME must be primary key in the table named + `*reports*' in the relational database RDB. DESTINATION is a + port, string, or symbol. If DESTINATION is a: + + port + The table is created as ascii text and written to that port. + + string + The table is created as ascii text and written to the file + named by DESTINATION. + + symbol + DESTINATION is the primary key for a row in the table named + *printers*. + + Each row in the table *reports* has the fields: + + name + The report name. + + default-table + The table to report on if none is specified. + + header, footer + A `format' string. At the beginning and end of each page + respectively, `format' is called with this string and the + (list of) column-names of this table. + + reporter + A `format' string. For each row in the table, `format' is + called with this string and the row. + + minimum-break + The minimum number of lines into which the report lines for a + row can be broken. Use `0' if a row's lines should not be + broken over page boundaries. + + Each row in the table *printers* has the fields: + + name + The printer name. + + print-procedure + The procedure to call to actually print. + + The report is prepared as follows: + + `Format' (*note Format::.) is called with the `header' field + and the (list of) `column-names' of the table. + + `Format' is called with the `reporter' field and (on + successive calls) each record in the natural order for the + table. A count is kept of the number of newlines output by + format. When the number of newlines to be output exceeds the + number of lines per page, the set of lines will be broken if + there are more than `minimum-break' left on this page and the + number of lines for this row is larger or equal to twice + `minimum-break'. + + `Format' is called with the `footer' field and the (list of) + `column-names' of the table. The footer field should not + output a newline. + + A new page is output. + + This entire process repeats until all the rows are output. + +The following example shows a new database with the name of `foo.db' +being created with tables describing processor families and +processor/os/compiler combinations. + +The database command `define-tables' is defined to call `define-tables' +with its arguments. The database is also configured to print `Welcome' +when the database is opened. The database is then closed and reopened. + + (require 'database-utilities) + (define my-rdb (create-database "foo.db" 'alist-table)) + + (define-tables my-rdb + '(*commands* + ((name symbol)) + ((parameters parameter-list) + (procedure expression) + (documentation string)) + ((define-tables + no-parameters + no-parameter-names + (lambda (rdb) (lambda specs (apply define-tables rdb specs))) + "Create or Augment tables from list of specs") + (*initialize* + no-parameters + no-parameter-names + (lambda (rdb) (display "Welcome") (newline) rdb) + "Print Welcome")))) + + ((my-rdb 'define-tables) + '(processor-family + ((family atom)) + ((also-ran processor-family)) + ((m68000 #f) + (m68030 m68000) + (i386 8086) + (8086 #f) + (powerpc #f))) + + '(platform + ((name symbol)) + ((processor processor-family) + (os symbol) + (compiler symbol)) + ((aix powerpc aix -) + (amiga-dice-c m68000 amiga dice-c) + (amiga-aztec m68000 amiga aztec) + (amiga-sas/c-5.10 m68000 amiga sas/c) + (atari-st-gcc m68000 atari gcc) + (atari-st-turbo-c m68000 atari turbo-c) + (borland-c-3.1 8086 ms-dos borland-c) + (djgpp i386 ms-dos gcc) + (linux i386 linux gcc) + (microsoft-c 8086 ms-dos microsoft-c) + (os/2-emx i386 os/2 gcc) + (turbo-c-2 8086 ms-dos turbo-c) + (watcom-9.0 i386 ms-dos watcom)))) + + ((my-rdb 'close-database)) + + (set! my-rdb (open-database "foo.db" 'alist-table)) + -| + Welcome + diff --git a/slib.info-3 b/slib.info-3 new file mode 100644 index 0000000..7109890 --- /dev/null +++ b/slib.info-3 @@ -0,0 +1,859 @@ +This is Info file slib.info, produced by Makeinfo-1.64 from the input +file slib.texi. + + This file documents SLIB, the portable Scheme library. + + Copyright (C) 1993 Todd R. Eigenschink Copyright (C) 1993, 1994, 1995 +Aubrey Jaffer + + Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + + Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + + Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be stated in a +translation approved by the author. + + +File: slib.info, Node: Weight-Balanced Trees, Next: Structures, Prev: Relational Database, Up: Data Structures + +Weight-Balanced Trees +===================== + + `(require 'wt-tree)' + + Balanced binary trees are a useful data structure for maintaining +large sets of ordered objects or sets of associations whose keys are +ordered. MIT Scheme has an comprehensive implementation of +weight-balanced binary trees which has several advantages over the +other data structures for large aggregates: + + * In addition to the usual element-level operations like insertion, + deletion and lookup, there is a full complement of collection-level + operations, like set intersection, set union and subset test, all + of which are implemented with good orders of growth in time and + space. This makes weight balanced trees ideal for rapid + prototyping of functionally derived specifications. + + * An element in a tree may be indexed by its position under the + ordering of the keys, and the ordinal position of an element may + be determined, both with reasonable efficiency. + + * Operations to find and remove minimum element make weight balanced + trees simple to use for priority queues. + + * The implementation is *functional* rather than *imperative*. This + means that operations like `inserting' an association in a tree do + not destroy the old tree, in much the same way that `(+ 1 x)' + modifies neither the constant 1 nor the value bound to `x'. The + trees are referentially transparent thus the programmer need not + worry about copying the trees. Referential transparency allows + space efficiency to be achieved by sharing subtrees. + + These features make weight-balanced trees suitable for a wide range of +applications, especially those that require large numbers of sets or +discrete maps. Applications that have a few global databases and/or +concentrate on element-level operations like insertion and lookup are +probably better off using hash-tables or red-black trees. + + The *size* of a tree is the number of associations that it contains. +Weight balanced binary trees are balanced to keep the sizes of the +subtrees of each node within a constant factor of each other. This +ensures logarithmic times for single-path operations (like lookup and +insertion). A weight balanced tree takes space that is proportional to +the number of associations in the tree. For the current +implementation, the constant of proportionality is six words per +association. + + Weight balanced trees can be used as an implementation for either +discrete sets or discrete maps (associations). Sets are implemented by +ignoring the datum that is associated with the key. Under this scheme +if an associations exists in the tree this indicates that the key of the +association is a member of the set. Typically a value such as `()', +`#t' or `#f' is associated with the key. + + Many operations can be viewed as computing a result that, depending on +whether the tree arguments are thought of as sets or maps, is known by +two different names. An example is `wt-tree/member?', which, when +regarding the tree argument as a set, computes the set membership +operation, but, when regarding the tree as a discrete map, +`wt-tree/member?' is the predicate testing if the map is defined at an +element in its domain. Most names in this package have been chosen +based on interpreting the trees as sets, hence the name +`wt-tree/member?' rather than `wt-tree/defined-at?'. + + The weight balanced tree implementation is a run-time-loadable option. +To use weight balanced trees, execute + + (load-option 'wt-tree) + +once before calling any of the procedures defined here. + +* Menu: + +* Construction of Weight-Balanced Trees:: +* Basic Operations on Weight-Balanced Trees:: +* Advanced Operations on Weight-Balanced Trees:: +* Indexing Operations on Weight-Balanced Trees:: + + +File: slib.info, Node: Construction of Weight-Balanced Trees, Next: Basic Operations on Weight-Balanced Trees, Prev: Weight-Balanced Trees, Up: Weight-Balanced Trees + +Construction of Weight-Balanced Trees +------------------------------------- + + Binary trees require there to be a total order on the keys used to +arrange the elements in the tree. Weight balanced trees are organized +by *types*, where the type is an object encapsulating the ordering +relation. Creating a tree is a two-stage process. First a tree type +must be created from the predicate which gives the ordering. The tree +type is then used for making trees, either empty or singleton trees or +trees from other aggregate structures like association lists. Once +created, a tree `knows' its type and the type is used to test +compatibility between trees in operations taking two trees. Usually a +small number of tree types are created at the beginning of a program +and used many times throughout the program's execution. + + - procedure+: make-wt-tree-type KEY<? + This procedure creates and returns a new tree type based on the + ordering predicate KEY<?. KEY<? must be a total ordering, having + the property that for all key values `a', `b' and `c': + + (key<? a a) => #f + (and (key<? a b) (key<? b a)) => #f + (if (and (key<? a b) (key<? b c)) + (key<? a c) + #t) => #t + + Two key values are assumed to be equal if neither is less than the + other by KEY<?. + + Each call to `make-wt-tree-type' returns a distinct value, and + trees are only compatible if their tree types are `eq?'. A + consequence is that trees that are intended to be used in binary + tree operations must all be created with a tree type originating + from the same call to `make-wt-tree-type'. + + - variable+: number-wt-type + A standard tree type for trees with numeric keys. `Number-wt-type' + could have been defined by + + (define number-wt-type (make-wt-tree-type <)) + + - variable+: string-wt-type + A standard tree type for trees with string keys. `String-wt-type' + could have been defined by + + (define string-wt-type (make-wt-tree-type string<?)) + + - procedure+: make-wt-tree WT-TREE-TYPE + This procedure creates and returns a newly allocated weight + balanced tree. The tree is empty, i.e. it contains no + associations. WT-TREE-TYPE is a weight balanced tree type + obtained by calling `make-wt-tree-type'; the returned tree has + this type. + + - procedure+: singleton-wt-tree WT-TREE-TYPE KEY DATUM + This procedure creates and returns a newly allocated weight + balanced tree. The tree contains a single association, that of + DATUM with KEY. WT-TREE-TYPE is a weight balanced tree type + obtained by calling `make-wt-tree-type'; the returned tree has + this type. + + - procedure+: alist->wt-tree TREE-TYPE ALIST + Returns a newly allocated weight-balanced tree that contains the + same associations as ALIST. This procedure is equivalent to: + + (lambda (type alist) + (let ((tree (make-wt-tree type))) + (for-each (lambda (association) + (wt-tree/add! tree + (car association) + (cdr association))) + alist) + tree)) + + +File: slib.info, Node: Basic Operations on Weight-Balanced Trees, Next: Advanced Operations on Weight-Balanced Trees, Prev: Construction of Weight-Balanced Trees, Up: Weight-Balanced Trees + +Basic Operations on Weight-Balanced Trees +----------------------------------------- + + This section describes the basic tree operations on weight balanced +trees. These operations are the usual tree operations for insertion, +deletion and lookup, some predicates and a procedure for determining the +number of associations in a tree. + + - procedure+: wt-tree? OBJECT + Returns `#t' if OBJECT is a weight-balanced tree, otherwise + returns `#f'. + + - procedure+: wt-tree/empty? WT-TREE + Returns `#t' if WT-TREE contains no associations, otherwise + returns `#f'. + + - procedure+: wt-tree/size WT-TREE + Returns the number of associations in WT-TREE, an exact + non-negative integer. This operation takes constant time. + + - procedure+: wt-tree/add WT-TREE KEY DATUM + Returns a new tree containing all the associations in WT-TREE and + the association of DATUM with KEY. If WT-TREE already had an + association for KEY, the new association overrides the old. The + average and worst-case times required by this operation are + proportional to the logarithm of the number of associations in + WT-TREE. + + - procedure+: wt-tree/add! WT-TREE KEY DATUM + Associates DATUM with KEY in WT-TREE and returns an unspecified + value. If WT-TREE already has an association for KEY, that + association is replaced. The average and worst-case times + required by this operation are proportional to the logarithm of + the number of associations in WT-TREE. + + - procedure+: wt-tree/member? KEY WT-TREE + Returns `#t' if WT-TREE contains an association for KEY, otherwise + returns `#f'. The average and worst-case times required by this + operation are proportional to the logarithm of the number of + associations in WT-TREE. + + - procedure+: wt-tree/lookup WT-TREE KEY DEFAULT + Returns the datum associated with KEY in WT-TREE. If WT-TREE + doesn't contain an association for KEY, DEFAULT is returned. The + average and worst-case times required by this operation are + proportional to the logarithm of the number of associations in + WT-TREE. + + - procedure+: wt-tree/delete WT-TREE KEY + Returns a new tree containing all the associations in WT-TREE, + except that if WT-TREE contains an association for KEY, it is + removed from the result. The average and worst-case times required + by this operation are proportional to the logarithm of the number + of associations in WT-TREE. + + - procedure+: wt-tree/delete! WT-TREE KEY + If WT-TREE contains an association for KEY the association is + removed. Returns an unspecified value. The average and worst-case + times required by this operation are proportional to the logarithm + of the number of associations in WT-TREE. + + +File: slib.info, Node: Advanced Operations on Weight-Balanced Trees, Next: Indexing Operations on Weight-Balanced Trees, Prev: Basic Operations on Weight-Balanced Trees, Up: Weight-Balanced Trees + +Advanced Operations on Weight-Balanced Trees +-------------------------------------------- + + In the following the *size* of a tree is the number of associations +that the tree contains, and a *smaller* tree contains fewer +associations. + + - procedure+: wt-tree/split< WT-TREE BOUND + Returns a new tree containing all and only the associations in + WT-TREE which have a key that is less than BOUND in the ordering + relation of the tree type of WT-TREE. The average and worst-case + times required by this operation are proportional to the logarithm + of the size of WT-TREE. + + - procedure+: wt-tree/split> WT-TREE BOUND + Returns a new tree containing all and only the associations in + WT-TREE which have a key that is greater than BOUND in the + ordering relation of the tree type of WT-TREE. The average and + worst-case times required by this operation are proportional to the + logarithm of size of WT-TREE. + + - procedure+: wt-tree/union WT-TREE-1 WT-TREE-2 + Returns a new tree containing all the associations from both trees. + This operation is asymmetric: when both trees have an association + for the same key, the returned tree associates the datum from + WT-TREE-2 with the key. Thus if the trees are viewed as discrete + maps then `wt-tree/union' computes the map override of WT-TREE-1 by + WT-TREE-2. If the trees are viewed as sets the result is the set + union of the arguments. The worst-case time required by this + operation is proportional to the sum of the sizes of both trees. + If the minimum key of one tree is greater than the maximum key of + the other tree then the time required is at worst proportional to + the logarithm of the size of the larger tree. + + - procedure+: wt-tree/intersection WT-TREE-1 WT-TREE-2 + Returns a new tree containing all and only those associations from + WT-TREE-1 which have keys appearing as the key of an association + in WT-TREE-2. Thus the associated data in the result are those + from WT-TREE-1. If the trees are being used as sets the result is + the set intersection of the arguments. As a discrete map + operation, `wt-tree/intersection' computes the domain restriction + of WT-TREE-1 to (the domain of) WT-TREE-2. The time required by + this operation is never worse that proportional to the sum of the + sizes of the trees. + + - procedure+: wt-tree/difference WT-TREE-1 WT-TREE-2 + Returns a new tree containing all and only those associations from + WT-TREE-1 which have keys that *do not* appear as the key of an + association in WT-TREE-2. If the trees are viewed as sets the + result is the asymmetric set difference of the arguments. As a + discrete map operation, it computes the domain restriction of + WT-TREE-1 to the complement of (the domain of) WT-TREE-2. The + time required by this operation is never worse that proportional to + the sum of the sizes of the trees. + + - procedure+: wt-tree/subset? WT-TREE-1 WT-TREE-2 + Returns `#t' iff the key of each association in WT-TREE-1 is the + key of some association in WT-TREE-2, otherwise returns `#f'. + Viewed as a set operation, `wt-tree/subset?' is the improper subset + predicate. A proper subset predicate can be constructed: + + (define (proper-subset? s1 s2) + (and (wt-tree/subset? s1 s2) + (< (wt-tree/size s1) (wt-tree/size s2)))) + + As a discrete map operation, `wt-tree/subset?' is the subset test + on the domain(s) of the map(s). In the worst-case the time + required by this operation is proportional to the size of + WT-TREE-1. + + - procedure+: wt-tree/set-equal? WT-TREE-1 WT-TREE-2 + Returns `#t' iff for every association in WT-TREE-1 there is an + association in WT-TREE-2 that has the same key, and *vice versa*. + + Viewing the arguments as sets `wt-tree/set-equal?' is the set + equality predicate. As a map operation it determines if two maps + are defined on the same domain. + + This procedure is equivalent to + + (lambda (wt-tree-1 wt-tree-2) + (and (wt-tree/subset? wt-tree-1 wt-tree-2 + (wt-tree/subset? wt-tree-2 wt-tree-1))) + + In the worst-case the time required by this operation is + proportional to the size of the smaller tree. + + - procedure+: wt-tree/fold COMBINER INITIAL WT-TREE + This procedure reduces WT-TREE by combining all the associations, + using an reverse in-order traversal, so the associations are + visited in reverse order. COMBINER is a procedure of three + arguments: a key, a datum and the accumulated result so far. + Provided COMBINER takes time bounded by a constant, `wt-tree/fold' + takes time proportional to the size of WT-TREE. + + A sorted association list can be derived simply: + + (wt-tree/fold (lambda (key datum list) + (cons (cons key datum) list)) + '() + WT-TREE)) + + The data in the associations can be summed like this: + + (wt-tree/fold (lambda (key datum sum) (+ sum datum)) + 0 + WT-TREE) + + - procedure+: wt-tree/for-each ACTION WT-TREE + This procedure traverses the tree in-order, applying ACTION to + each association. The associations are processed in increasing + order of their keys. ACTION is a procedure of two arguments which + take the key and datum respectively of the association. Provided + ACTION takes time bounded by a constant, `wt-tree/for-each' takes + time proportional to in the size of WT-TREE. The example prints + the tree: + + (wt-tree/for-each (lambda (key value) + (display (list key value))) + WT-TREE)) + + +File: slib.info, Node: Indexing Operations on Weight-Balanced Trees, Prev: Advanced Operations on Weight-Balanced Trees, Up: Weight-Balanced Trees + +Indexing Operations on Weight-Balanced Trees +-------------------------------------------- + + Weight balanced trees support operations that view the tree as sorted +sequence of associations. Elements of the sequence can be accessed by +position, and the position of an element in the sequence can be +determined, both in logarthmic time. + + - procedure+: wt-tree/index WT-TREE INDEX + - procedure+: wt-tree/index-datum WT-TREE INDEX + - procedure+: wt-tree/index-pair WT-TREE INDEX + Returns the 0-based INDEXth association of WT-TREE in the sorted + sequence under the tree's ordering relation on the keys. + `wt-tree/index' returns the INDEXth key, `wt-tree/index-datum' + returns the datum associated with the INDEXth key and + `wt-tree/index-pair' returns a new pair `(KEY . DATUM)' which is + the `cons' of the INDEXth key and its datum. The average and + worst-case times required by this operation are proportional to + the logarithm of the number of associations in the tree. + + These operations signal an error if the tree is empty, if + INDEX`<0', or if INDEX is greater than or equal to the number of + associations in the tree. + + Indexing can be used to find the median and maximum keys in the + tree as follows: + + median: (wt-tree/index WT-TREE (quotient (wt-tree/size WT-TREE) 2)) + + maximum: (wt-tree/index WT-TREE (-1+ (wt-tree/size WT-TREE))) + + - procedure+: wt-tree/rank WT-TREE KEY + Determines the 0-based position of KEY in the sorted sequence of + the keys under the tree's ordering relation, or `#f' if the tree + has no association with for KEY. This procedure returns either an + exact non-negative integer or `#f'. The average and worst-case + times required by this operation are proportional to the logarithm + of the number of associations in the tree. + + - procedure+: wt-tree/min WT-TREE + - procedure+: wt-tree/min-datum WT-TREE + - procedure+: wt-tree/min-pair WT-TREE + Returns the association of WT-TREE that has the least key under + the tree's ordering relation. `wt-tree/min' returns the least key, + `wt-tree/min-datum' returns the datum associated with the least + key and `wt-tree/min-pair' returns a new pair `(key . datum)' + which is the `cons' of the minimum key and its datum. The average + and worst-case times required by this operation are proportional + to the logarithm of the number of associations in the tree. + + These operations signal an error if the tree is empty. They could + be written + (define (wt-tree/min tree) (wt-tree/index tree 0)) + (define (wt-tree/min-datum tree) (wt-tree/index-datum tree 0)) + (define (wt-tree/min-pair tree) (wt-tree/index-pair tree 0)) + + - procedure+: wt-tree/delete-min WT-TREE + Returns a new tree containing all of the associations in WT-TREE + except the association with the least key under the WT-TREE's + ordering relation. An error is signalled if the tree is empty. + The average and worst-case times required by this operation are + proportional to the logarithm of the number of associations in the + tree. This operation is equivalent to + + (wt-tree/delete WT-TREE (wt-tree/min WT-TREE)) + + - procedure+: wt-tree/delete-min! WT-TREE + Removes the association with the least key under the WT-TREE's + ordering relation. An error is signalled if the tree is empty. + The average and worst-case times required by this operation are + proportional to the logarithm of the number of associations in the + tree. This operation is equivalent to + + (wt-tree/delete! WT-TREE (wt-tree/min WT-TREE)) + + +File: slib.info, Node: Structures, Prev: Weight-Balanced Trees, Up: Data Structures + +Structures +========== + + `(require 'struct)' (uses defmacros) + + `defmacro's which implement "records" from the book `Essentials of +Programming Languages' by Daniel P. Friedman, M. Wand and C.T. Haynes. +Copyright 1992 Jeff Alexander, Shinnder Lee, and Lewis Patterson + + Matthew McDonald <mafm@cs.uwa.edu.au> added field setters. + + - Macro: define-record TAG (VAR1 VAR2 ...) + Defines several functions pertaining to record-name TAG: + + - Function: make-TAG VAR1 VAR2 ... + + - Function: TAG? OBJ + + - Function: TAG->VAR1 OBJ + + - Function: TAG->VAR2 OBJ + ... + + - Function: set-TAG-VAR1! OBJ VAL + + - Function: set-TAG-VAR2! OBJ VAL + ... + + Here is an example of its use. + + (define-record term (operator left right)) + => #<unspecified> + (define foo (make-term 'plus 1 2)) + => foo + (term-left foo) + => 1 + (set-term-left! foo 2345) + => #<unspecified> + (term-left foo) + => 2345 + + - Macro: variant-case EXP (TAG (VAR1 VAR2 ...) BODY) ... + executes the following for the matching clause: + + ((lambda (VAR1 VAR ...) BODY) + (TAG->VAR1 EXP) + (TAG->VAR2 EXP) ...) + + +File: slib.info, Node: Macros, Next: Numerics, Prev: Data Structures, Up: Top + +Macros +****** + +* Menu: + +* Defmacro:: Supported by all implementations + +* R4RS Macros:: 'macro +* Macro by Example:: 'macro-by-example +* Macros That Work:: 'macros-that-work +* Syntactic Closures:: 'syntactic-closures +* Syntax-Case Macros:: 'syntax-case + +Syntax extensions (macros) included with SLIB. Also *Note Structures::. + +* Fluid-Let:: 'fluid-let +* Yasos:: 'yasos, 'oop, 'collect + + +File: slib.info, Node: Defmacro, Next: R4RS Macros, Prev: Macros, Up: Macros + +Defmacro +======== + + Defmacros are supported by all implementations. + + - Function: gentemp + Returns a new (interned) symbol each time it is called. The symbol + names are implementation-dependent + (gentemp) => scm:G0 + (gentemp) => scm:G1 + + - Function: defmacro:eval E + Returns the `slib:eval' of expanding all defmacros in scheme + expression E. + + - Function: defmacro:load FILENAME + FILENAME should be a string. If filename names an existing file, + the `defmacro:load' procedure reads Scheme source code expressions + and definitions from the file and evaluates them sequentially. + These source code expressions and definitions may contain defmacro + definitions. The `macro:load' procedure does not affect the values + returned by `current-input-port' and `current-output-port'. + + - Function: defmacro? SYM + Returns `#t' if SYM has been defined by `defmacro', `#f' otherwise. + + - Function: macroexpand-1 FORM + - Function: macroexpand FORM + If FORM is a macro call, `macroexpand-1' will expand the macro + call once and return it. A FORM is considered to be a macro call + only if it is a cons whose `car' is a symbol for which a `defmacr' + has been defined. + + `macroexpand' is similar to `macroexpand-1', but repeatedly + expands FORM until it is no longer a macro call. + + - Macro: defmacro NAME LAMBDA-LIST FORM ... + When encountered by `defmacro:eval', `defmacro:macroexpand*', or + `defmacro:load' defines a new macro which will henceforth be + expanded when encountered by `defmacro:eval', + `defmacro:macroexpand*', or `defmacro:load'. + +Defmacroexpand +-------------- + + `(require 'defmacroexpand)' + + - Function: defmacro:expand* E + Returns the result of expanding all defmacros in scheme expression + E. + + +File: slib.info, Node: R4RS Macros, Next: Macro by Example, Prev: Defmacro, Up: Macros + +R4RS Macros +=========== + + `(require 'macro)' is the appropriate call if you want R4RS +high-level macros but don't care about the low level implementation. If +an SLIB R4RS macro implementation is already loaded it will be used. +Otherwise, one of the R4RS macros implemetations is loaded. + + The SLIB R4RS macro implementations support the following uniform +interface: + + - Function: macro:expand SEXPRESSION + Takes an R4RS expression, macro-expands it, and returns the result + of the macro expansion. + + - Function: macro:eval SEXPRESSION + Takes an R4RS expression, macro-expands it, evals the result of the + macro expansion, and returns the result of the evaluation. + + - Procedure: macro:load FILENAME + FILENAME should be a string. If filename names an existing file, + the `macro:load' procedure reads Scheme source code expressions and + definitions from the file and evaluates them sequentially. These + source code expressions and definitions may contain macro + definitions. The `macro:load' procedure does not affect the + values returned by `current-input-port' and `current-output-port'. + + +File: slib.info, Node: Macro by Example, Next: Macros That Work, Prev: R4RS Macros, Up: Macros + +Macro by Example +================ + + `(require 'macro-by-example)' + + A vanilla implementation of `Macro by Example' (Eugene Kohlbecker, +R4RS) by Dorai Sitaram, (dorai@cs.rice.edu) using `defmacro'. + + * generating hygienic global `define-syntax' Macro-by-Example macros + *cheaply*. + + * can define macros which use `...'. + + * needn't worry about a lexical variable in a macro definition + clashing with a variable from the macro use context + + * don't suffer the overhead of redefining the repl if `defmacro' + natively supported (most implementations) + +Caveat +------ + + These macros are not referentially transparent (*note Macros: +(r4rs)Macros.). Lexically scoped macros (i.e., `let-syntax' and +`letrec-syntax') are not supported. In any case, the problem of +referential transparency gains poignancy only when `let-syntax' and +`letrec-syntax' are used. So you will not be courting large-scale +disaster unless you're using system-function names as local variables +with unintuitive bindings that the macro can't use. However, if you +must have the full `r4rs' macro functionality, look to the more +featureful (but also more expensive) versions of syntax-rules available +in slib *Note Macros That Work::, *Note Syntactic Closures::, and *Note +Syntax-Case Macros::. + + - Macro: define-syntax KEYWORD TRANSFORMER-SPEC + The KEYWORD is an identifier, and the TRANSFORMER-SPEC should be + an instance of `syntax-rules'. + + The top-level syntactic environment is extended by binding the + KEYWORD to the specified transformer. + + (define-syntax let* + (syntax-rules () + ((let* () body1 body2 ...) + (let () body1 body2 ...)) + ((let* ((name1 val1) (name2 val2) ...) + body1 body2 ...) + (let ((name1 val1)) + (let* (( name2 val2) ...) + body1 body2 ...))))) + + - Macro: syntax-rules LITERALS SYNTAX-RULE ... + LITERALS is a list of identifiers, and each SYNTAX-RULE should be + of the form + + `(PATTERN TEMPLATE)' + + where the PATTERN and TEMPLATE are as in the grammar above. + + An instance of `syntax-rules' produces a new macro transformer by + specifying a sequence of hygienic rewrite rules. A use of a macro + whose keyword is associated with a transformer specified by + `syntax-rules' is matched against the patterns contained in the + SYNTAX-RULEs, beginning with the leftmost SYNTAX-RULE. When a + match is found, the macro use is trancribed hygienically according + to the template. + + Each pattern begins with the keyword for the macro. This keyword + is not involved in the matching and is not considered a pattern + variable or literal identifier. + + +File: slib.info, Node: Macros That Work, Next: Syntactic Closures, Prev: Macro by Example, Up: Macros + +Macros That Work +================ + + `(require 'macros-that-work)' + + `Macros That Work' differs from the other R4RS macro implementations +in that it does not expand derived expression types to primitive +expression types. + + - Function: macro:expand EXPRESSION + - Function: macwork:expand EXPRESSION + Takes an R4RS expression, macro-expands it, and returns the result + of the macro expansion. + + - Function: macro:eval EXPRESSION + - Function: macwork:eval EXPRESSION + `macro:eval' returns the value of EXPRESSION in the current top + level environment. EXPRESSION can contain macro definitions. + Side effects of EXPRESSION will affect the top level environment. + + - Procedure: macro:load FILENAME + - Procedure: macwork:load FILENAME + FILENAME should be a string. If filename names an existing file, + the `macro:load' procedure reads Scheme source code expressions and + definitions from the file and evaluates them sequentially. These + source code expressions and definitions may contain macro + definitions. The `macro:load' procedure does not affect the + values returned by `current-input-port' and `current-output-port'. + + References: + + The `Revised^4 Report on the Algorithmic Language Scheme' Clinger and +Rees [editors]. To appear in LISP Pointers. Also available as a +technical report from the University of Oregon, MIT AI Lab, and Cornell. + + Macros That Work. Clinger and Rees. POPL '91. + + The supported syntax differs from the R4RS in that vectors are allowed +as patterns and as templates and are not allowed as pattern or template +data. + + transformer spec ==> (syntax-rules literals rules) + + rules ==> () + | (rule . rules) + + rule ==> (pattern template) + + pattern ==> pattern_var ; a symbol not in literals + | symbol ; a symbol in literals + | () + | (pattern . pattern) + | (ellipsis_pattern) + | #(pattern*) ; extends R4RS + | #(pattern* ellipsis_pattern) ; extends R4RS + | pattern_datum + + template ==> pattern_var + | symbol + | () + | (template2 . template2) + | #(template*) ; extends R4RS + | pattern_datum + + template2 ==> template + | ellipsis_template + + pattern_datum ==> string ; no vector + | character + | boolean + | number + + ellipsis_pattern ==> pattern ... + + ellipsis_template ==> template ... + + pattern_var ==> symbol ; not in literals + + literals ==> () + | (symbol . literals) + +Definitions +----------- + +Scope of an ellipsis + Within a pattern or template, the scope of an ellipsis (`...') is + the pattern or template that appears to its left. + +Rank of a pattern variable + The rank of a pattern variable is the number of ellipses within + whose scope it appears in the pattern. + +Rank of a subtemplate + The rank of a subtemplate is the number of ellipses within whose + scope it appears in the template. + +Template rank of an occurrence of a pattern variable + The template rank of an occurrence of a pattern variable within a + template is the rank of that occurrence, viewed as a subtemplate. + +Variables bound by a pattern + The variables bound by a pattern are the pattern variables that + appear within it. + +Referenced variables of a subtemplate + The referenced variables of a subtemplate are the pattern + variables that appear within it. + +Variables opened by an ellipsis template + The variables opened by an ellipsis template are the referenced + pattern variables whose rank is greater than the rank of the + ellipsis template. + +Restrictions +------------ + + No pattern variable appears more than once within a pattern. + + For every occurrence of a pattern variable within a template, the +template rank of the occurrence must be greater than or equal to the +pattern variable's rank. + + Every ellipsis template must open at least one variable. + + For every ellipsis template, the variables opened by an ellipsis +template must all be bound to sequences of the same length. + + The compiled form of a RULE is + + rule ==> (pattern template inserted) + + pattern ==> pattern_var + | symbol + | () + | (pattern . pattern) + | ellipsis_pattern + | #(pattern) + | pattern_datum + + template ==> pattern_var + | symbol + | () + | (template2 . template2) + | #(pattern) + | pattern_datum + + template2 ==> template + | ellipsis_template + + pattern_datum ==> string + | character + | boolean + | number + + pattern_var ==> #(V symbol rank) + + ellipsis_pattern ==> #(E pattern pattern_vars) + + ellipsis_template ==> #(E template pattern_vars) + + inserted ==> () + | (symbol . inserted) + + pattern_vars ==> () + | (pattern_var . pattern_vars) + + rank ==> exact non-negative integer + + where V and E are unforgeable values. + + The pattern variables associated with an ellipsis pattern are the +variables bound by the pattern, and the pattern variables associated +with an ellipsis template are the variables opened by the ellipsis +template. + + If the template contains a big chunk that contains no pattern +variables or inserted identifiers, then the big chunk will be copied +unnecessarily. That shouldn't matter very often. + diff --git a/slib.info-4 b/slib.info-4 new file mode 100644 index 0000000..3d3da19 --- /dev/null +++ b/slib.info-4 @@ -0,0 +1,1248 @@ +This is Info file slib.info, produced by Makeinfo-1.64 from the input +file slib.texi. + + This file documents SLIB, the portable Scheme library. + + Copyright (C) 1993 Todd R. Eigenschink Copyright (C) 1993, 1994, 1995 +Aubrey Jaffer + + Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + + Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + + Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be stated in a +translation approved by the author. + + +File: slib.info, Node: Syntactic Closures, Next: Syntax-Case Macros, Prev: Macros That Work, Up: Macros + +Syntactic Closures +================== + + `(require 'syntactic-closures)' + + - Function: macro:expand EXPRESSION + - Function: synclo:expand EXPRESSION + Returns scheme code with the macros and derived expression types of + EXPRESSION expanded to primitive expression types. + + - Function: macro:eval EXPRESSION + - Function: synclo:eval EXPRESSION + `macro:eval' returns the value of EXPRESSION in the current top + level environment. EXPRESSION can contain macro definitions. + Side effects of EXPRESSION will affect the top level environment. + + - Procedure: macro:load FILENAME + - Procedure: synclo:load FILENAME + FILENAME should be a string. If filename names an existing file, + the `macro:load' procedure reads Scheme source code expressions and + definitions from the file and evaluates them sequentially. These + source code expressions and definitions may contain macro + definitions. The `macro:load' procedure does not affect the + values returned by `current-input-port' and `current-output-port'. + +Syntactic Closure Macro Facility +-------------------------------- + + A Syntactic Closures Macro Facility + + by Chris Hanson + + 9 November 1991 + + This document describes "syntactic closures", a low-level macro +facility for the Scheme programming language. The facility is an +alternative to the low-level macro facility described in the `Revised^4 +Report on Scheme.' This document is an addendum to that report. + + The syntactic closures facility extends the BNF rule for TRANSFORMER +SPEC to allow a new keyword that introduces a low-level macro +transformer: + TRANSFORMER SPEC := (transformer EXPRESSION) + + Additionally, the following procedures are added: + make-syntactic-closure + capture-syntactic-environment + identifier? + identifier=? + + The description of the facility is divided into three parts. The +first part defines basic terminology. The second part describes how +macro transformers are defined. The third part describes the use of +"identifiers", which extend the syntactic closure mechanism to be +compatible with `syntax-rules'. + +Terminology +........... + + This section defines the concepts and data types used by the syntactic +closures facility. + + "Forms" are the syntactic entities out of which programs are + recursively constructed. A form is any expression, any + definition, any syntactic keyword, or any syntactic closure. The + variable name that appears in a `set!' special form is also a + form. Examples of forms: + 17 + #t + car + (+ x 4) + (lambda (x) x) + (define pi 3.14159) + if + define + + An "alias" is an alternate name for a given symbol. It can appear + anywhere in a form that the symbol could be used, and when quoted + it is replaced by the symbol; however, it does not satisfy the + predicate `symbol?'. Macro transformers rarely distinguish + symbols from aliases, referring to both as identifiers. + + A "syntactic" environment maps identifiers to their meanings. + More precisely, it determines whether an identifier is a syntactic + keyword or a variable. If it is a keyword, the meaning is an + interpretation for the form in which that keyword appears. If it + is a variable, the meaning identifies which binding of that + variable is referenced. In short, syntactic environments contain + all of the contextual information necessary for interpreting the + meaning of a particular form. + + A "syntactic closure" consists of a form, a syntactic environment, + and a list of identifiers. All identifiers in the form take their + meaning from the syntactic environment, except those in the given + list. The identifiers in the list are to have their meanings + determined later. A syntactic closure may be used in any context + in which its form could have been used. Since a syntactic closure + is also a form, it may not be used in contexts where a form would + be illegal. For example, a form may not appear as a clause in the + cond special form. A syntactic closure appearing in a quoted + structure is replaced by its form. + +Transformer Definition +...................... + + This section describes the `transformer' special form and the +procedures `make-syntactic-closure' and `capture-syntactic-environment'. + + - Syntax: transformer EXPRESSION + Syntax: It is an error if this syntax occurs except as a + TRANSFORMER SPEC. + + Semantics: The EXPRESSION is evaluated in the standard transformer + environment to yield a macro transformer as described below. This + macro transformer is bound to a macro keyword by the special form + in which the `transformer' expression appears (for example, + `let-syntax'). + + A "macro transformer" is a procedure that takes two arguments, a + form and a syntactic environment, and returns a new form. The + first argument, the "input form", is the form in which the macro + keyword occurred. The second argument, the "usage environment", + is the syntactic environment in which the input form occurred. + The result of the transformer, the "output form", is automatically + closed in the "transformer environment", which is the syntactic + environment in which the `transformer' expression occurred. + + For example, here is a definition of a push macro using + `syntax-rules': + (define-syntax push + (syntax-rules () + ((push item list) + (set! list (cons item list))))) + + Here is an equivalent definition using `transformer': + (define-syntax push + (transformer + (lambda (exp env) + (let ((item + (make-syntactic-closure env '() (cadr exp))) + (list + (make-syntactic-closure env '() (caddr exp)))) + `(set! ,list (cons ,item ,list)))))) + + In this example, the identifiers `set!' and `cons' are closed in + the transformer environment, and thus will not be affected by the + meanings of those identifiers in the usage environment `env'. + + Some macros may be non-hygienic by design. For example, the + following defines a loop macro that implicitly binds `exit' to an + escape procedure. The binding of `exit' is intended to capture + free references to `exit' in the body of the loop, so `exit' must + be left free when the body is closed: + (define-syntax loop + (transformer + (lambda (exp env) + (let ((body (cdr exp))) + `(call-with-current-continuation + (lambda (exit) + (let f () + ,@(map (lambda (exp) + (make-syntactic-closure env '(exit) + exp)) + body) + (f)))))))) + + To assign meanings to the identifiers in a form, use + `make-syntactic-closure' to close the form in a syntactic + environment. + + - Function: make-syntactic-closure ENVIRONMENT FREE-NAMES FORM + ENVIRONMENT must be a syntactic environment, FREE-NAMES must be a + list of identifiers, and FORM must be a form. + `make-syntactic-closure' constructs and returns a syntactic closure + of FORM in ENVIRONMENT, which can be used anywhere that FORM could + have been used. All the identifiers used in FORM, except those + explicitly excepted by FREE-NAMES, obtain their meanings from + ENVIRONMENT. + + Here is an example where FREE-NAMES is something other than the + empty list. It is instructive to compare the use of FREE-NAMES in + this example with its use in the `loop' example above: the examples + are similar except for the source of the identifier being left + free. + (define-syntax let1 + (transformer + (lambda (exp env) + (let ((id (cadr exp)) + (init (caddr exp)) + (exp (cadddr exp))) + `((lambda (,id) + ,(make-syntactic-closure env (list id) exp)) + ,(make-syntactic-closure env '() init)))))) + + `let1' is a simplified version of `let' that only binds a single + identifier, and whose body consists of a single expression. When + the body expression is syntactically closed in its original + syntactic environment, the identifier that is to be bound by + `let1' must be left free, so that it can be properly captured by + the `lambda' in the output form. + + To obtain a syntactic environment other than the usage + environment, use `capture-syntactic-environment'. + + - Function: capture-syntactic-environment PROCEDURE + `capture-syntactic-environment' returns a form that will, when + transformed, call PROCEDURE on the current syntactic environment. + PROCEDURE should compute and return a new form to be transformed, + in that same syntactic environment, in place of the form. + + An example will make this clear. Suppose we wanted to define a + simple `loop-until' keyword equivalent to + (define-syntax loop-until + (syntax-rules () + ((loop-until id init test return step) + (letrec ((loop + (lambda (id) + (if test return (loop step))))) + (loop init))))) + + The following attempt at defining `loop-until' has a subtle bug: + (define-syntax loop-until + (transformer + (lambda (exp env) + (let ((id (cadr exp)) + (init (caddr exp)) + (test (cadddr exp)) + (return (cadddr (cdr exp))) + (step (cadddr (cddr exp))) + (close + (lambda (exp free) + (make-syntactic-closure env free exp)))) + `(letrec ((loop + (lambda (,id) + (if ,(close test (list id)) + ,(close return (list id)) + (loop ,(close step (list id))))))) + (loop ,(close init '()))))))) + + This definition appears to take all of the proper precautions to + prevent unintended captures. It carefully closes the + subexpressions in their original syntactic environment and it + leaves the `id' identifier free in the `test', `return', and + `step' expressions, so that it will be captured by the binding + introduced by the `lambda' expression. Unfortunately it uses the + identifiers `if' and `loop' within that `lambda' expression, so if + the user of `loop-until' just happens to use, say, `if' for the + identifier, it will be inadvertently captured. + + The syntactic environment that `if' and `loop' want to be exposed + to is the one just outside the `lambda' expression: before the + user's identifier is added to the syntactic environment, but after + the identifier loop has been added. + `capture-syntactic-environment' captures exactly that environment + as follows: + (define-syntax loop-until + (transformer + (lambda (exp env) + (let ((id (cadr exp)) + (init (caddr exp)) + (test (cadddr exp)) + (return (cadddr (cdr exp))) + (step (cadddr (cddr exp))) + (close + (lambda (exp free) + (make-syntactic-closure env free exp)))) + `(letrec ((loop + ,(capture-syntactic-environment + (lambda (env) + `(lambda (,id) + (,(make-syntactic-closure env '() `if) + ,(close test (list id)) + ,(close return (list id)) + (,(make-syntactic-closure env '() + `loop) + ,(close step (list id))))))))) + (loop ,(close init '()))))))) + + In this case, having captured the desired syntactic environment, + it is convenient to construct syntactic closures of the + identifiers `if' and the `loop' and use them in the body of the + `lambda'. + + A common use of `capture-syntactic-environment' is to get the + transformer environment of a macro transformer: + (transformer + (lambda (exp env) + (capture-syntactic-environment + (lambda (transformer-env) + ...)))) + +Identifiers +........... + + This section describes the procedures that create and manipulate +identifiers. Previous syntactic closure proposals did not have an +identifier data type - they just used symbols. The identifier data +type extends the syntactic closures facility to be compatible with the +high-level `syntax-rules' facility. + + As discussed earlier, an identifier is either a symbol or an "alias". +An alias is implemented as a syntactic closure whose "form" is an +identifier: + (make-syntactic-closure env '() 'a) + => an "alias" + + Aliases are implemented as syntactic closures because they behave just +like syntactic closures most of the time. The difference is that an +alias may be bound to a new value (for example by `lambda' or +`let-syntax'); other syntactic closures may not be used this way. If +an alias is bound, then within the scope of that binding it is looked +up in the syntactic environment just like any other identifier. + + Aliases are used in the implementation of the high-level facility +`syntax-rules'. A macro transformer created by `syntax-rules' uses a +template to generate its output form, substituting subforms of the +input form into the template. In a syntactic closures implementation, +all of the symbols in the template are replaced by aliases closed in +the transformer environment, while the output form itself is closed in +the usage environment. This guarantees that the macro transformation +is hygienic, without requiring the transformer to know the syntactic +roles of the substituted input subforms. + + - Function: identifier? OBJECT + Returns `#t' if OBJECT is an identifier, otherwise returns `#f'. + Examples: + (identifier? 'a) + => #t + (identifier? (make-syntactic-closure env '() 'a)) + => #t + (identifier? "a") + => #f + (identifier? #\a) + => #f + (identifier? 97) + => #f + (identifier? #f) + => #f + (identifier? '(a)) + => #f + (identifier? '#(a)) + => #f + + The predicate `eq?' is used to determine if two identifers are + "the same". Thus `eq?' can be used to compare identifiers exactly + as it would be used to compare symbols. Often, though, it is + useful to know whether two identifiers "mean the same thing". For + example, the `cond' macro uses the symbol `else' to identify the + final clause in the conditional. A macro transformer for `cond' + cannot just look for the symbol `else', because the `cond' form + might be the output of another macro transformer that replaced the + symbol `else' with an alias. Instead the transformer must look + for an identifier that "means the same thing" in the usage + environment as the symbol `else' means in the transformer + environment. + + - Function: identifier=? ENVIRONMENT1 IDENTIFIER1 ENVIRONMENT2 + IDENTIFIER2 + ENVIRONMENT1 and ENVIRONMENT2 must be syntactic environments, and + IDENTIFIER1 and IDENTIFIER2 must be identifiers. `identifier=?' + returns `#t' if the meaning of IDENTIFIER1 in ENVIRONMENT1 is the + same as that of IDENTIFIER2 in ENVIRONMENT2, otherwise it returns + `#f'. Examples: + + (let-syntax + ((foo + (transformer + (lambda (form env) + (capture-syntactic-environment + (lambda (transformer-env) + (identifier=? transformer-env 'x env 'x))))))) + (list (foo) + (let ((x 3)) + (foo)))) + => (#t #f) + + (let-syntax ((bar foo)) + (let-syntax + ((foo + (transformer + (lambda (form env) + (capture-syntactic-environment + (lambda (transformer-env) + (identifier=? transformer-env 'foo + env (cadr form)))))))) + (list (foo foo) + (foobar)))) + => (#f #t) + +Acknowledgements +................ + + The syntactic closures facility was invented by Alan Bawden and +Jonathan Rees. The use of aliases to implement `syntax-rules' was +invented by Alan Bawden (who prefers to call them "synthetic names"). +Much of this proposal is derived from an earlier proposal by Alan +Bawden. + + +File: slib.info, Node: Syntax-Case Macros, Next: Fluid-Let, Prev: Syntactic Closures, Up: Macros + +Syntax-Case Macros +================== + + `(require 'syntax-case)' + + - Function: macro:expand EXPRESSION + - Function: syncase:expand EXPRESSION + Returns scheme code with the macros and derived expression types of + EXPRESSION expanded to primitive expression types. + + - Function: macro:eval EXPRESSION + - Function: syncase:eval EXPRESSION + `macro:eval' returns the value of EXPRESSION in the current top + level environment. EXPRESSION can contain macro definitions. + Side effects of EXPRESSION will affect the top level environment. + + - Procedure: macro:load FILENAME + - Procedure: syncase:load FILENAME + FILENAME should be a string. If filename names an existing file, + the `macro:load' procedure reads Scheme source code expressions and + definitions from the file and evaluates them sequentially. These + source code expressions and definitions may contain macro + definitions. The `macro:load' procedure does not affect the + values returned by `current-input-port' and `current-output-port'. + + This is version 2.1 of `syntax-case', the low-level macro facility +proposed and implemented by Robert Hieb and R. Kent Dybvig. + + This version is further adapted by Harald Hanche-Olsen +<hanche@imf.unit.no> to make it compatible with, and easily usable +with, SLIB. Mainly, these adaptations consisted of: + + * Removing white space from `expand.pp' to save space in the + distribution. This file is not meant for human readers anyway... + + * Removed a couple of Chez scheme dependencies. + + * Renamed global variables used to minimize the possibility of name + conflicts. + + * Adding an SLIB-specific initialization file. + + * Removing a couple extra files, most notably the documentation (but + see below). + + If you wish, you can see exactly what changes were done by reading the +shell script in the file `syncase.sh'. + + The two PostScript files were omitted in order to not burden the SLIB +distribution with them. If you do intend to use `syntax-case', +however, you should get these files and print them out on a PostScript +printer. They are available with the original `syntax-case' +distribution by anonymous FTP in +`cs.indiana.edu:/pub/scheme/syntax-case'. + + In order to use syntax-case from an interactive top level, execute: + (require 'syntax-case) + (require 'repl) + (repl:top-level macro:eval) + See the section Repl (*Note Repl::) for more information. + + To check operation of syntax-case get +`cs.indiana.edu:/pub/scheme/syntax-case', and type + (require 'syntax-case) + (syncase:sanity-check) + + Beware that `syntax-case' takes a long time to load - about 20s on a +SPARCstation SLC (with SCM) and about 90s on a Macintosh SE/30 (with +Gambit). + +Notes +----- + + All R4RS syntactic forms are defined, including `delay'. Along with +`delay' are simple definitions for `make-promise' (into which `delay' +expressions expand) and `force'. + + `syntax-rules' and `with-syntax' (described in `TR356') are defined. + + `syntax-case' is actually defined as a macro that expands into calls +to the procedure `syntax-dispatch' and the core form `syntax-lambda'; +do not redefine these names. + + Several other top-level bindings not documented in TR356 are created: + the "hooks" in `hooks.ss' + + the `build-' procedures in `output.ss' + + `expand-syntax' (the expander) + + The syntax of define has been extended to allow `(define ID)', which +assigns ID to some unspecified value. + + We have attempted to maintain R4RS compatibility where possible. The +incompatibilities should be confined to `hooks.ss'. Please let us know +if there is some incompatibility that is not flagged as such. + + Send bug reports, comments, suggestions, and questions to Kent Dybvig +(dyb@iuvax.cs.indiana.edu). + +Note from maintainer +-------------------- + + Included with the `syntax-case' files was `structure.scm' which +defines a macro `define-structure'. There is no documentation for this +macro and it is not used by any code in SLIB. + + +File: slib.info, Node: Fluid-Let, Next: Yasos, Prev: Syntax-Case Macros, Up: Macros + +Fluid-Let +========= + + `(require 'fluid-let)' + + - Syntax: fluid-let `(BINDINGS ...)' FORMS... + + (fluid-let ((VARIABLE INIT) ...) + EXPRESSION EXPRESSION ...) + + The INITs are evaluated in the current environment (in some +unspecified order), the current values of the VARIABLEs are saved, the +results are assigned to the VARIABLEs, the EXPRESSIONs are evaluated +sequentially in the current environment, the VARIABLEs are restored to +their original values, and the value of the last EXPRESSION is returned. + + The syntax of this special form is similar to that of `let', but +`fluid-let' temporarily rebinds existing VARIABLEs. Unlike `let', +`fluid-let' creates no new bindings; instead it *assigns* the values of +each INIT to the binding (determined by the rules of lexical scoping) +of its corresponding VARIABLE. + + +File: slib.info, Node: Yasos, Prev: Fluid-Let, Up: Macros + +Yasos +===== + + `(require 'oop)' or `(require 'yasos)' + + `Yet Another Scheme Object System' is a simple object system for +Scheme based on the paper by Norman Adams and Jonathan Rees: `Object +Oriented Programming in Scheme', Proceedings of the 1988 ACM Conference +on LISP and Functional Programming, July 1988 [ACM #552880]. + + Another reference is: + + Ken Dickey. Scheming with Objects `AI Expert' Volume 7, Number 10 +(October 1992), pp. 24-33. + +* Menu: + +* Yasos terms:: Definitions and disclaimer. +* Yasos interface:: The Yasos macros and procedures. +* Setters:: Dylan-like setters in Yasos. +* Yasos examples:: Usage of Yasos and setters. + + +File: slib.info, Node: Yasos terms, Next: Yasos interface, Prev: Yasos, Up: Yasos + +Terms +----- + +"Object" + Any Scheme data object. + +"Instance" + An instance of the OO system; an "object". + +"Operation" + A METHOD. + +*Notes:* + The object system supports multiple inheritance. An instance can + inherit from 0 or more ancestors. In the case of multiple + inherited operations with the same identity, the operation used is + that from the first ancestor which contains it (in the ancestor + `let'). An operation may be applied to any Scheme data + object--not just instances. As code which creates instances is + just code, there are no "classes" and no meta-ANYTHING. Method + dispatch is by a procedure call a la CLOS rather than by `send' + syntax a la Smalltalk. + +*Disclaimer:* + There are a number of optimizations which can be made. This + implementation is expository (although performance should be quite + reasonable). See the L&FP paper for some suggestions. + + +File: slib.info, Node: Yasos interface, Next: Setters, Prev: Yasos terms, Up: Yasos + +Interface +--------- + + - Syntax: define-operation `('OPNAME SELF ARG ...`)' DEFAULT-BODY + Defines a default behavior for data objects which don't handle the + operation OPNAME. The default default behavior (for an empty + DEFAULT-BODY) is to generate an error. + + - Syntax: define-predicate OPNAME? + Defines a predicate OPNAME?, usually used for determining the + "type" of an object, such that `(OPNAME? OBJECT)' returns `#t' if + OBJECT has an operation OPNAME? and `#f' otherwise. + + - Syntax: object `((NAME SELF ARG ...) BODY)' ... + Returns an object (an instance of the object system) with + operations. Invoking `(NAME OBJECT ARG ...' executes the BODY of + the OBJECT with SELF bound to OBJECT and with argument(s) ARG.... + + - Syntax: object-with-ancestors `(('ANCESTOR1 INIT1`)' ...`)' + OPERATION ... + A `let'-like form of `object' for multiple inheritance. It + returns an object inheriting the behaviour of ANCESTOR1 etc. An + operation will be invoked in an ancestor if the object itself does + not provide such a method. In the case of multiple inherited + operations with the same identity, the operation used is the one + found in the first ancestor in the ancestor list. + + - Syntax: operate-as COMPONENT OPERATION SELF ARG ... + Used in an operation definition (of SELF) to invoke the OPERATION + in an ancestor COMPONENT but maintain the object's identity. Also + known as "send-to-super". + + - Procedure: print OBJ PORT + A default `print' operation is provided which is just `(format + PORT OBJ)' (*Note Format::) for non-instances and prints OBJ + preceded by `#<INSTANCE>' for instances. + + - Function: size OBJ + The default method returns the number of elements in OBJ if it is + a vector, string or list, `2' for a pair, `1' for a character and + by default id an error otherwise. Objects such as collections + (*Note Collections::) may override the default in an obvious way. + + +File: slib.info, Node: Setters, Next: Yasos examples, Prev: Yasos interface, Up: Yasos + +Setters +------- + + "Setters" implement "generalized locations" for objects associated +with some sort of mutable state. A "getter" operation retrieves a +value from a generalized location and the corresponding setter +operation stores a value into the location. Only the getter is named - +the setter is specified by a procedure call as below. (Dylan uses +special syntax.) Typically, but not necessarily, getters are access +operations to extract values from Yasos objects (*Note Yasos::). +Several setters are predefined, corresponding to getters `car', `cdr', +`string-ref' and `vector-ref' e.g., `(setter car)' is equivalent to +`set-car!'. + + This implementation of setters is similar to that in Dylan(TM) +(`Dylan: An object-oriented dynamic language', Apple Computer Eastern +Research and Technology). Common LISP provides similar facilities +through `setf'. + + - Function: setter GETTER + Returns the setter for the procedure GETTER. E.g., since + `string-ref' is the getter corresponding to a setter which is + actually `string-set!': + (define foo "foo") + ((setter string-ref) foo 0 #\F) ; set element 0 of foo + foo => "Foo" + + - Syntax: set PLACE NEW-VALUE + If PLACE is a variable name, `set' is equivalent to `set!'. + Otherwise, PLACE must have the form of a procedure call, where the + procedure name refers to a getter and the call indicates an + accessible generalized location, i.e., the call would return a + value. The return value of `set' is usually unspecified unless + used with a setter whose definition guarantees to return a useful + value. + (set (string-ref foo 2) #\O) ; generalized location with getter + foo => "FoO" + (set foo "foo") ; like set! + foo => "foo" + + - Procedure: add-setter GETTER SETTER + Add procedures GETTER and SETTER to the (inaccessible) list of + valid setter/getter pairs. SETTER implements the store operation + corresponding to the GETTER access operation for the relevant + state. The return value is unspecified. + + - Procedure: remove-setter-for GETTER + Removes the setter corresponding to the specified GETTER from the + list of valid setters. The return value is unspecified. + + - Syntax: define-access-operation GETTER-NAME + Shorthand for a Yasos `define-operation' defining an operation + GETTER-NAME that objects may support to return the value of some + mutable state. The default operation is to signal an error. The + return value is unspecified. + + +File: slib.info, Node: Yasos examples, Prev: Setters, Up: Yasos + +Examples +-------- + + (define-operation (print obj port) + (format port + (if (instance? obj) "#<instance>" "~s") + obj)) + + (define-operation (SIZE obj) + (cond + ((vector? obj) (vector-length obj)) + ((list? obj) (length obj)) + ((pair? obj) 2) + ((string? obj) (string-length obj)) + ((char? obj) 1) + (else + (error "Operation not supported: size" obj)))) + + (define-predicate cell?) + (define-operation (fetch obj)) + (define-operation (store! obj newValue)) + + (define (make-cell value) + (object + ((cell? self) #t) + ((fetch self) value) + ((store! self newValue) + (set! value newValue) + newValue) + ((size self) 1) + ((print self port) + (format port "#<Cell: ~s>" (fetch self))))) + + (define-operation (discard obj value) + (format #t "Discarding ~s~%" value)) + + (define (make-filtered-cell value filter) + (object-with-ancestors ((cell (make-cell value))) + ((store! self newValue) + (if (filter newValue) + (store! cell newValue) + (discard self newValue))))) + + (define-predicate array?) + (define-operation (array-ref array index)) + (define-operation (array-set! array index value)) + + (define (make-array num-slots) + (let ((anArray (make-vector num-slots))) + (object + ((array? self) #t) + ((size self) num-slots) + ((array-ref self index) (vector-ref anArray index)) + ((array-set! self index newValue) (vector-set! anArray index newValue)) + ((print self port) (format port "#<Array ~s>" (size self)))))) + + (define-operation (position obj)) + (define-operation (discarded-value obj)) + + (define (make-cell-with-history value filter size) + (let ((pos 0) (most-recent-discard #f)) + (object-with-ancestors + ((cell (make-filtered-call value filter)) + (sequence (make-array size))) + ((array? self) #f) + ((position self) pos) + ((store! self newValue) + (operate-as cell store! self newValue) + (array-set! self pos newValue) + (set! pos (+ pos 1))) + ((discard self value) + (set! most-recent-discard value)) + ((discarded-value self) most-recent-discard) + ((print self port) + (format port "#<Cell-with-history ~s>" (fetch self)))))) + + (define-access-operation fetch) + (add-setter fetch store!) + (define foo (make-cell 1)) + (print foo #f) + => "#<Cell: 1>" + (set (fetch foo) 2) + => + (print foo #f) + => "#<Cell: 2>" + (fetch foo) + => 2 + + +File: slib.info, Node: Numerics, Next: Procedures, Prev: Macros, Up: Top + +Numerics +******** + +* Menu: + +* Bit-Twiddling:: 'logical +* Modular Arithmetic:: 'modular +* Prime Testing and Generation:: 'primes +* Prime Factorization:: 'factor +* Random Numbers:: 'random +* Cyclic Checksum:: 'make-crc +* Plotting:: 'charplot +* Root Finding:: + + +File: slib.info, Node: Bit-Twiddling, Next: Modular Arithmetic, Prev: Numerics, Up: Numerics + +Bit-Twiddling +============= + + `(require 'logical)' + + The bit-twiddling functions are made available through the use of the +`logical' package. `logical' is loaded by inserting `(require +'logical)' before the code that uses these functions. + + - Function: logand N1 N1 + Returns the integer which is the bit-wise AND of the two integer + arguments. + + Example: + (number->string (logand #b1100 #b1010) 2) + => "1000" + + - Function: logior N1 N2 + Returns the integer which is the bit-wise OR of the two integer + arguments. + + Example: + (number->string (logior #b1100 #b1010) 2) + => "1110" + + - Function: logxor N1 N2 + Returns the integer which is the bit-wise XOR of the two integer + arguments. + + Example: + (number->string (logxor #b1100 #b1010) 2) + => "110" + + - Function: lognot N + Returns the integer which is the 2s-complement of the integer + argument. + + Example: + (number->string (lognot #b10000000) 2) + => "-10000001" + (number->string (lognot #b0) 2) + => "-1" + + - Function: logtest J K + (logtest j k) == (not (zero? (logand j k))) + + (logtest #b0100 #b1011) => #f + (logtest #b0100 #b0111) => #t + + - Function: logbit? INDEX J + (logbit? index j) == (logtest (integer-expt 2 index) j) + + (logbit? 0 #b1101) => #t + (logbit? 1 #b1101) => #f + (logbit? 2 #b1101) => #t + (logbit? 3 #b1101) => #t + (logbit? 4 #b1101) => #f + + - Function: ash INT COUNT + Returns an integer equivalent to `(inexact->exact (floor (* INT + (expt 2 COUNT))))'. + + Example: + (number->string (ash #b1 3) 2) + => "1000" + (number->string (ash #b1010 -1) 2) + => "101" + + - Function: logcount N + Returns the number of bits in integer N. If integer is positive, + the 1-bits in its binary representation are counted. If negative, + the 0-bits in its two's-complement binary representation are + counted. If 0, 0 is returned. + + Example: + (logcount #b10101010) + => 4 + (logcount 0) + => 0 + (logcount -2) + => 1 + + - Function: integer-length N + Returns the number of bits neccessary to represent N. + + Example: + (integer-length #b10101010) + => 8 + (integer-length 0) + => 0 + (integer-length #b1111) + => 4 + + - Function: integer-expt N K + Returns N raised to the non-negative integer exponent K. + + Example: + (integer-expt 2 5) + => 32 + (integer-expt -3 3) + => -27 + + - Function: bit-extract N START END + Returns the integer composed of the START (inclusive) through END + (exclusive) bits of N. The STARTth bit becomes the 0-th bit in + the result. + + Example: + (number->string (bit-extract #b1101101010 0 4) 2) + => "1010" + (number->string (bit-extract #b1101101010 4 9) 2) + => "10110" + + +File: slib.info, Node: Modular Arithmetic, Next: Prime Testing and Generation, Prev: Bit-Twiddling, Up: Numerics + +Modular Arithmetic +================== + + `(require 'modular)' + + - Function: extended-euclid N1 N2 + Returns a list of 3 integers `(d x y)' such that d = gcd(N1, N2) = + N1 * x + N2 * y. + + - Function: symmetric:modulus N + Returns `(quotient (+ -1 n) -2)' for positive odd integer N. + + - Function: modulus->integer MODULUS + Returns the non-negative integer characteristic of the ring formed + when MODULUS is used with `modular:' procedures. + + - Function: modular:normalize MODULUS N + Returns the integer `(modulo N (modulus->integer MODULUS))' in the + representation specified by MODULUS. + +The rest of these functions assume normalized arguments; That is, the +arguments are constrained by the following table: + +For all of these functions, if the first argument (MODULUS) is: +`positive?' + Work as before. The result is between 0 and MODULUS. + +`zero?' + The arguments are treated as integers. An integer is returned. + +`negative?' + The arguments and result are treated as members of the integers + modulo `(+ 1 (* -2 MODULUS))', but with "symmetric" + representation; i.e. `(<= (- MODULUS) N MODULUS)'. + +If all the arguments are fixnums the computation will use only fixnums. + + - Function: modular:invertable? MODULUS K + Returns `#t' if there exists an integer n such that K * n == 1 mod + MODULUS, and `#f' otherwise. + + - Function: modular:invert MODULUS K2 + Returns an integer n such that 1 = (n * K2) mod MODULUS. If K2 + has no inverse mod MODULUS an error is signaled. + + - Function: modular:negate MODULUS K2 + Returns (-K2) mod MODULUS. + + - Function: modular:+ MODULUS K2 K3 + Returns (K2 + K3) mod MODULUS. + + - Function: modular:- MODULUS K2 K3 + Returns (K2 - K3) mod MODULUS. + + - Function: modular:* MODULUS K2 K3 + Returns (K2 * K3) mod MODULUS. + + The Scheme code for `modular:*' with negative MODULUS is not + completed for fixnum-only implementations. + + - Function: modular:expt MODULUS K2 K3 + Returns (K2 ^ K3) mod MODULUS. + + +File: slib.info, Node: Prime Testing and Generation, Next: Prime Factorization, Prev: Modular Arithmetic, Up: Numerics + +Prime Testing and Generation +============================ + + `(require 'primes)' + + This package tests and generates prime numbers. The strategy used is +as follows: + + First, use trial division by small primes (primes less than 1000) + to quickly weed out composites with small factors. As a side + benefit, this makes the test precise for numbers up to one million. + + Second, apply the Miller-Rabin primality test to detect (with high + probability) any remaining composites. + + The Miller-Rabin test is a Monte-Carlo test--in other words, it's fast +and it gets the right answer with high probability. For a candidate +that *is* prime, the Miller-Rabin test is certain to report "prime"; it +will never report "composite". However, for a candidate that is +composite, there is a (small) probability that the Miller-Rabin test +will erroneously report "prime". This probability can be made +arbitarily small by adjusting the number of iterations of the +Miller-Rabin test. + + - Function: probably-prime? CANDIDATE + - Function: probably-prime? CANDIDATE ITER + Returns `#t' if `candidate' is probably prime. The optional + parameter `iter' controls the number of iterations of the + Miller-Rabin test. The probability of a composite candidate being + mistaken for a prime is at most `(1/4)^iter'. The default value of + `iter' is 15, which makes the probability less than 1 in 10^9. + + + - Function: primes< START COUNT + - Function: primes< START COUNT ITER + - Function: primes> START COUNT + - Function: primes> START COUNT ITER + Returns a list of the first `count' odd probable primes less (more) + than or equal to `start'. The optional parameter `iter' controls + the number of iterations of the Miller-Rabin test for each + candidate. The probability of a composite candidate being + mistaken for a prime is at most `(1/4)^iter'. The default value + of `iter' is 15, which makes the probability less than 1 in 10^9. + + +* Menu: + +* The Miller-Rabin Test:: How the Miller-Rabin test works + + +File: slib.info, Node: The Miller-Rabin Test, Prev: Prime Testing and Generation, Up: Prime Testing and Generation + +Theory +------ + + Rabin and Miller's result can be summarized as follows. Let `p' (the +candidate prime) be any odd integer greater than 2. Let `b' (the +"base") be an integer in the range `2 ... p-1'. There is a fairly +simple Boolean function--call it `C', for "Composite"--with the +following properties: + If `p' is prime, `C(p, b)' is false for all `b' in the range `2 + ... p-1'. + + If `p' is composite, `C(p, b)' is false for at most 1/4 of all `b' + in the range ` 2 ... p-1'. (If the test fails for base `b', `p' + is called a *strong pseudo-prime to base `b'*.) + + For details of `C', and why it fails for at most 1/4 of the potential +bases, please consult a book on number theory or cryptography such as +"A Course in Number Theory and Cryptography" by Neal Koblitz, published +by Springer-Verlag 1994. + + There is nothing probablistic about this result. It's true for all +`p'. If we had time to test `(1/4)p + 1' different bases, we could +definitively determine the primality of `p'. For large candidates, +that would take much too long--much longer than the simple approach of +dividing by all numbers up to `sqrt(p)'. This is where probability +enters the picture. + + Suppose we have some candidate prime `p'. Pick a random integer `b' +in the range `2 ... p-1'. Compute `C(p,b)'. If `p' is prime, the +result will certainly be false. If `p' is composite, the probability +is at most 1/4 that the result will be false (demonstrating that `p' is +a strong pseudoprime to base `b'). The test can be repeated with other +random bases. If `p' is prime, each test is certain to return false. +If `p' is composite, the probability of `C(p,b)' returning false is at +most 1/4 for each test. Since the `b' are chosen at random, the tests +outcomes are independent. So if `p' is composite and the test is +repeated, say, 15 times, the probability of it returning false all +fifteen times is at most (1/4)^15, or about 10^-9. If the test is +repeated 30 times, the probability of failure drops to at most 8.3e-25. + + Rabin and Miller's result holds for *all* candidates `p'. However, +if the candidate `p' is picked at random, the probability of the +Miller-Rabin test failing is much less than the computed bound. This +is because, for *most* composite numbers, the fraction of bases that +cause the test to fail is much less than 1/4. For example, if you pick +a random odd number less than 1000 and apply the Miller-Rabin test with +only 3 random bases, the computed failure bound is (1/4)^3, or about +1.6e-2. However, the actual probability of failure is much less--about +7.2e-5. If you accidentally pick 703 to test for primality, the +probability of failure is (161/703)^3, or about 1.2e-2, which is almost +as high as the computed bound. This is because 703 is a strong +pseudoprime to 161 bases. But if you pick at random there is only a +small chance of picking 703, and no other number less than 1000 has +that high a percentage of pseudoprime bases. + + The Miller-Rabin test is sometimes used in a slightly different +fashion, where it can, at least in principle, cause problems. The +weaker version uses small prime bases instead of random bases. If you +are picking candidates at random and testing for primality, this works +well since very few composites are strong pseudo-primes to small prime +bases. (For example, there is only one composite less than 2.5e10 that +is a strong pseudo-prime to the bases 2, 3, 5, and 7.) The problem +with this approach is that once a candidate has been picked, the test is +deterministic. This distinction is subtle, but real. With the +randomized test, for *any* candidate you pick--even if your +candidate-picking procedure is strongly biased towards troublesome +numbers, the test will work with high probability. With the +deterministic version, for any particular candidate, the test will +either work (with probability 1), or fail (with probability 1). It +won't fail for very many candidates, but that won't be much consolation +if your candidate-picking procedure is somehow biased toward troublesome +numbers. + + +File: slib.info, Node: Prime Factorization, Next: Random Numbers, Prev: Prime Testing and Generation, Up: Numerics + +Prime Factorization +=================== + + `(require 'factor)' + + - Function: factor K + Returns a list of the prime factors of K. The order of the + factors is unspecified. In order to obtain a sorted list do + `(sort! (factor k) <)'. + + *Note:* The rest of these procedures implement the Solovay-Strassen +primality test. This test has been superseeded by the faster *Note +probably-prime?: Prime Testing and Generation. However these are left +here as they take up little space and may be of use to an +implementation without bignums. + + See Robert Solovay and Volker Strassen, `A Fast Monte-Carlo Test for +Primality', SIAM Journal on Computing, 1977, pp 84-85. + + - Function: jacobi-symbol P Q + Returns the value (+1, -1, or 0) of the Jacobi-Symbol of exact + non-negative integer P and exact positive odd integer Q. + + - Function: prime? P + Returns `#f' if P is composite; `#t' if P is prime. There is a + slight chance `(expt 2 (- prime:trials))' that a composite will + return `#t'. + + - Function: prime:trials + Is the maxinum number of iterations of Solovay-Strassen that will + be done to test a number for primality. + + +File: slib.info, Node: Random Numbers, Next: Cyclic Checksum, Prev: Prime Factorization, Up: Numerics + +Random Numbers +============== + + `(require 'random)' + + - Procedure: random N + - Procedure: random N STATE + Accepts a positive integer or real N and returns a number of the + same type between zero (inclusive) and N (exclusive). The values + returned have a uniform distribution. + + The optional argument STATE must be of the type produced by + `(make-random-state)'. It defaults to the value of the variable + `*random-state*'. This object is used to maintain the state of the + pseudo-random-number generator and is altered as a side effect of + the `random' operation. + + - Variable: *random-state* + Holds a data structure that encodes the internal state of the + random-number generator that `random' uses by default. The nature + of this data structure is implementation-dependent. It may be + printed out and successfully read back in, but may or may not + function correctly as a random-number state object in another + implementation. + + - Procedure: make-random-state + - Procedure: make-random-state STATE + Returns a new object of type suitable for use as the value of the + variable `*random-state*' and as a second argument to `random'. + If argument STATE is given, a copy of it is returned. Otherwise a + copy of `*random-state*' is returned. + + If inexact numbers are support by the Scheme implementation, +`randinex.scm' will be loaded as well. `randinex.scm' contains +procedures for generating inexact distributions. + + - Procedure: random:uniform STATE + Returns an uniformly distributed inexact real random number in the + range between 0 and 1. + + - Procedure: random:solid-sphere! VECT + - Procedure: random:solid-sphere! VECT STATE + Fills VECT with inexact real random numbers the sum of whose + squares is less than 1.0. Thinking of VECT as coordinates in + space of dimension N = `(vector-length VECT)', the coordinates are + uniformly distributed within the unit N-shere. The sum of the + squares of the numbers is returned. + + - Procedure: random:hollow-sphere! VECT + - Procedure: random:hollow-sphere! VECT STATE + Fills VECT with inexact real random numbers the sum of whose + squares is equal to 1.0. Thinking of VECT as coordinates in space + of dimension n = `(vector-length VECT)', the coordinates are + uniformly distributed over the surface of the unit n-shere. + + - Procedure: random:normal + - Procedure: random:normal STATE + Returns an inexact real in a normal distribution with mean 0 and + standard deviation 1. For a normal distribution with mean M and + standard deviation D use `(+ M (* D (random:normal)))'. + + - Procedure: random:normal-vector! VECT + - Procedure: random:normal-vector! VECT STATE + Fills VECT with inexact real random numbers which are independent + and standard normally distributed (i.e., with mean 0 and variance + 1). + + - Procedure: random:exp + - Procedure: random:exp STATE + Returns an inexact real in an exponential distribution with mean + 1. For an exponential distribution with mean U use (* U + (random:exp)). + diff --git a/slib.info-5 b/slib.info-5 new file mode 100644 index 0000000..04d1b28 --- /dev/null +++ b/slib.info-5 @@ -0,0 +1,1536 @@ +This is Info file slib.info, produced by Makeinfo-1.64 from the input +file slib.texi. + + This file documents SLIB, the portable Scheme library. + + Copyright (C) 1993 Todd R. Eigenschink Copyright (C) 1993, 1994, 1995 +Aubrey Jaffer + + Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + + Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + + Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be stated in a +translation approved by the author. + + +File: slib.info, Node: Cyclic Checksum, Next: Plotting, Prev: Random Numbers, Up: Numerics + +Cyclic Checksum +=============== + + `(require 'make-crc)' + + - Function: make-port-crc + - Function: make-port-crc DEGREE + - Function: make-port-crc DEGREE GENERATOR + Returns an expression for a procedure of one argument, a port. + This procedure reads characters from the port until the end of + file and returns the integer checksum of the bytes read. + + The integer DEGREE, if given, specifies the degree of the + polynomial being computed - which is also the number of bits + computed in the checksums. The default value is 32. + + The integer GENERATOR specifies the polynomial being computed. + The power of 2 generating each 1 bit is the exponent of a term of + the polynomial. The bit at position DEGREE is implicit and should + not be part of GENERATOR. This allows systems with numbers + limited to 32 bits to calculate 32 bit checksums. The default + value of GENERATOR when DEGREE is 32 (its default) is: + + (make-port-crc 32 #b00000100110000010001110110110111) + + Creates a procedure to calculate the P1003.2/D11.2 (POSIX.2) 32-bit + checksum from the polynomial: + + 32 26 23 22 16 12 11 + ( x + x + x + x + x + x + x + + + 10 8 7 5 4 2 1 + x + x + x + x + x + x + x + 1 ) mod 2 + + (require 'make-crc) + (define crc32 (slib:eval (make-port-crc))) + (define (file-check-sum file) (call-with-input-file file crc32)) + (file-check-sum (in-vicinity (library-vicinity) "ratize.scm")) + + => 3553047446 + + +File: slib.info, Node: Plotting, Next: Root Finding, Prev: Cyclic Checksum, Up: Numerics + +Plotting on Character Devices +============================= + + `(require 'charplot)' + + The plotting procedure is made available through the use of the +`charplot' package. `charplot' is loaded by inserting `(require +'charplot)' before the code that uses this procedure. + + - Variable: charplot:height + The number of rows to make the plot vertically. + + - Variable: charplot:width + The number of columns to make the plot horizontally. + + - Procedure: plot! COORDS X-LABEL Y-LABEL + COORDS is a list of pairs of x and y coordinates. X-LABEL and + Y-LABEL are strings with which to label the x and y axes. + + Example: + (require 'charplot) + (set! charplot:height 19) + (set! charplot:width 45) + + (define (make-points n) + (if (zero? n) + '() + (cons (cons (/ n 6) (sin (/ n 6))) (make-points (1- n))))) + + (plot! (make-points 37) "x" "Sin(x)") + -| + Sin(x) ______________________________________________ + 1.25|- | + | | + 1|- **** | + | ** ** | + 750.0e-3|- * * | + | * * | + 500.0e-3|- * * | + | * | + 250.0e-3|- * | + | * * | + 0|-------------------*--------------------------| + | * | + -250.0e-3|- * * | + | * * | + -500.0e-3|- * | + | * * | + -750.0e-3|- * * | + | ** ** | + -1|- **** | + |____________:_____._____:_____._____:_________| + x 2 4 + + +File: slib.info, Node: Root Finding, Prev: Plotting, Up: Numerics + +Root Finding +============ + + `(require 'root)' + + - Function: newtown:find-integer-root F DF/DX X0 + Given integer valued procedure F, its derivative (with respect to + its argument) DF/DX, and initial integer value X0 for which + DF/DX(X0) is non-zero, returns an integer X for which F(X) is + closer to zero than either of the integers adjacent to X; or + returns `#f' if such an integer can't be found. + + To find the closest integer to a given integers square root: + + (define (integer-sqrt y) + (newton:find-integer-root + (lambda (x) (- (* x x) y)) + (lambda (x) (* 2 x)) + (ash 1 (quotient (integer-length y) 2)))) + + (integer-sqrt 15) => 4 + + - Function: integer-sqrt Y + Given a non-negative integer Y, returns the rounded square-root of + Y. + + - Function: newton:find-root F DF/DX X0 PREC + Given real valued procedures F, DF/DX of one (real) argument, + initial real value X0 for which DF/DX(X0) is non-zero, and + positive real number PREC, returns a real X for which `abs'(F(X)) + is less than PREC; or returns `#f' if such a real can't be found. + + If `prec' is instead a negative integer, `newton:find-root' + returns the result of -PREC iterations. + +H. J. Orchard, `The Laguerre Method for Finding the Zeros of +Polynomials', IEEE Transactions on Circuits and Systems, Vol. 36, No. +11, November 1989, pp 1377-1381. + + There are 2 errors in Orchard's Table II. Line k=2 for starting + value of 1000+j0 should have Z_k of 1.0475 + j4.1036 and line k=2 + for starting value of 0+j1000 should have Z_k of 1.0988 + j4.0833. + + - Function: laguerre:find-root F DF/DZ DDF/DZ^2 Z0 PREC + Given complex valued procedure F of one (complex) argument, its + derivative (with respect to its argument) DF/DX, its second + derivative DDF/DZ^2, initial complex value Z0, and positive real + number PREC, returns a complex number Z for which + `magnitude'(F(Z)) is less than PREC; or returns `#f' if such a + number can't be found. + + If `prec' is instead a negative integer, `laguerre:find-root' + returns the result of -PREC iterations. + + - Function: laguerre:find-polynomial-root DEG F DF/DZ DDF/DZ^2 Z0 PREC + Given polynomial procedure F of integer degree DEG of one + argument, its derivative (with respect to its argument) DF/DX, its + second derivative DDF/DZ^2, initial complex value Z0, and positive + real number PREC, returns a complex number Z for which + `magnitude'(F(Z)) is less than PREC; or returns `#f' if such a + number can't be found. + + If `prec' is instead a negative integer, + `laguerre:find-polynomial-root' returns the result of -PREC + iterations. + + +File: slib.info, Node: Procedures, Next: Standards Support, Prev: Numerics, Up: Top + +Procedures +********** + + Anything that doesn't fall neatly into any of the other categories +winds up here. + +* Menu: + +* Batch:: 'batch +* Common List Functions:: 'common-list-functions +* Format:: 'format +* Generic-Write:: 'generic-write +* Line I/O:: 'line-i/o +* Multi-Processing:: 'process +* Object-To-String:: 'object->string +* Pretty-Print:: 'pretty-print, 'pprint-file +* Sorting:: 'sort +* Topological Sort:: +* Standard Formatted I/O:: 'printf, 'scanf +* String-Case:: 'string-case +* String Ports:: 'string-port +* String Search:: +* Tektronix Graphics Support:: +* Tree Operations:: 'tree + + +File: slib.info, Node: Batch, Next: Common List Functions, Prev: Procedures, Up: Procedures + +Batch +===== + + `(require 'batch)' + +The batch procedures provide a way to write and execute portable scripts +for a variety of operating systems. Each `batch:' procedure takes as +its first argument a parameter-list (*note Parameter lists::.). This +parameter-list argument PARMS contains named associations. Batch +currently uses 2 of these: + +`batch-port' + The port on which to write lines of the batch file. + +`batch-dialect' + The syntax of batch file to generate. Currently supported are: + * unix + + * dos + + * vms + + * system + + * *unknown* + +`batch.scm' uses 2 enhanced relational tables (*note Database +Utilities::.) to store information linking the names of +`operating-system's to `batch-dialect'es. + + - Function: batch:initialize! DATABASE + Defines `operating-system' and `batch-dialect' tables and adds the + domain `operating-system' to the enhanced relational database + DATABASE. + + - Variable: batch:platform + Is batch's best guess as to which operating-system it is running + under. `batch:platform' is set to `(software-type)' (*note + Configuration::.) unless `(software-type)' is `unix', in which + case finer distinctions are made. + + - Function: batch:call-with-output-script PARMS FILE PROC + PROC should be a procedure of one argument. If FILE is an + output-port, `batch:call-with-output-script' writes an appropriate + header to FILE and then calls PROC with FILE as the only argument. + If FILE is a string, `batch:call-with-output-script' opens a + output-file of name FILE, writes an appropriate header to FILE, + and then calls PROC with the newly opened port as the only + argument. Otherwise, `batch:call-with-output-script' acts as if + it was called with the result of `(current-output-port)' as its + third argument. + + - Function: batch:apply-chop-to-fit PROC ARG1 ARG2 ... LIST + The procedure PROC must accept at least one argument and return + `#t' if successful, `#f' if not. `batch:apply-chop-to-fit' calls + PROC with ARG1, ARG2, ..., and CHUNK, where CHUNK is a subset of + LIST. `batch:apply-chop-to-fit' tries PROC with successively + smaller subsets of LIST until either PROC returns non-false, or + the CHUNKs become empty. + +The rest of the `batch:' procedures write (or execute if +`batch-dialect' is `system') commands to the batch port which has been +added to PARMS or `(copy-tree PARMS)' by the code: + + (adjoin-parameters! PARMS (list 'batch-port PORT)) + + - Function: batch:system PARMS STRING1 STRING2 ... + Calls `batch:try-system' (below) with arguments, but signals an + error if `batch:try-system' returns `#f'. + +These functions return a non-false value if the command was successfully +translated into the batch dialect and `#f' if not. In the case of the +`system' dialect, the value is non-false if the operation suceeded. + + - Function: batch:try-system PARMS STRING1 STRING2 ... + Writes a command to the `batch-port' in PARMS which executes the + program named STRING1 with arguments STRING2 .... + + - Function: batch:run-script PARMS STRING1 STRING2 ... + Writes a command to the `batch-port' in PARMS which executes the + batch script named STRING1 with arguments STRING2 .... + + *Note:* `batch:run-script' and `batch:try-system' are not the same + for some operating systems (VMS). + + - Function: batch:comment PARMS LINE1 ... + Writes comment lines LINE1 ... to the `batch-port' in PARMS. + + - Function: batch:lines->file PARMS FILE LINE1 ... + Writes commands to the `batch-port' in PARMS which create a file + named FILE with contents LINE1 .... + + - Function: batch:delete-file PARMS FILE + Writes a command to the `batch-port' in PARMS which deletes the + file named FILE. + + - Function: batch:rename-file PARMS OLD-NAME NEW-NAME + Writes a command to the `batch-port' in PARMS which renames the + file OLD-NAME to NEW-NAME. + +In addition, batch provides some small utilities very useful for writing +scripts: + + - Function: replace-suffix STR OLD NEW + Returns a new string similar to `str' but with the suffix string + OLD removed and the suffix string NEW appended. If the end of STR + does not match OLD, an error is signaled. + + - Function: string-join JOINER STRING1 ... + Returns a new string consisting of all the strings STRING1 ... in + order appended together with the string JOINER between each + adjacent pair. + + - Function: must-be-first LIST1 LIST2 + Returns a new list consisting of the elements of LIST2 ordered so + that if some elements of LIST1 are `equal?' to elements of LIST2, + then those elements will appear first and in the order of LIST1. + + - Function: must-be-last LIST1 LIST2 + Returns a new list consisting of the elements of LIST1 ordered so + that if some elements of LIST2 are `equal?' to elements of LIST1, + then those elements will appear last and in the order of LIST2. + + - Function: os->batch-dialect OSNAME + Returns its best guess for the `batch-dialect' to be used for the + operating-system named OSNAME. `os->batch-dialect' uses the + tables added to DATABASE by `batch:initialize!'. + +Here is an example of the use of most of batch's procedures: + + (require 'database-utilities) + (require 'parameters) + (require 'batch) + + (define batch (create-database #f 'alist-table)) + (batch:initialize! batch) + + (define my-parameters + (list (list 'batch-dialect (os->batch-dialect batch:platform)) + (list 'platform batch:platform) + (list 'batch-port (current-output-port)))) ;gets filled in later + + (batch:call-with-output-script + my-parameters + "my-batch" + (lambda (batch-port) + (adjoin-parameters! my-parameters (list 'batch-port batch-port)) + (and + (batch:comment my-parameters + "================ Write file with C program.") + (batch:rename-file my-parameters "hello.c" "hello.c~") + (batch:lines->file my-parameters "hello.c" + "#include <stdio.h>" + "int main(int argc, char **argv)" + "{" + " printf(\"hello world\\n\");" + " return 0;" + "}" ) + (batch:system my-parameters "cc" "-c" "hello.c") + (batch:system my-parameters "cc" "-o" "hello" + (replace-suffix "hello.c" ".c" ".o")) + (batch:system my-parameters "hello") + (batch:delete-file my-parameters "hello") + (batch:delete-file my-parameters "hello.c") + (batch:delete-file my-parameters "hello.o") + (batch:delete-file my-parameters "my-batch") + ))) + +Produces the file `my-batch': + + #!/bin/sh + # "my-batch" build script created Sat Jun 10 21:20:37 1995 + # ================ Write file with C program. + mv -f hello.c hello.c~ + rm -f hello.c + echo '#include <stdio.h>'>>hello.c + echo 'int main(int argc, char **argv)'>>hello.c + echo '{'>>hello.c + echo ' printf("hello world\n");'>>hello.c + echo ' return 0;'>>hello.c + echo '}'>>hello.c + cc -c hello.c + cc -o hello hello.o + hello + rm -f hello + rm -f hello.c + rm -f hello.o + rm -f my-batch + +When run, `my-batch' prints: + + bash$ my-batch + mv: hello.c: No such file or directory + hello world + + +File: slib.info, Node: Common List Functions, Next: Format, Prev: Batch, Up: Procedures + +Common List Functions +===================== + + `(require 'common-list-functions)' + + The procedures below follow the Common LISP equivalents apart from +optional arguments in some cases. + +* Menu: + +* List construction:: +* Lists as sets:: +* Lists as sequences:: +* Destructive list operations:: +* Non-List functions:: + + +File: slib.info, Node: List construction, Next: Lists as sets, Prev: Common List Functions, Up: Common List Functions + +List construction +----------------- + + - Function: make-list K . INIT + `make-list' creates and returns a list of K elements. If INIT is + included, all elements in the list are initialized to INIT. + + Example: + (make-list 3) + => (#<unspecified> #<unspecified> #<unspecified>) + (make-list 5 'foo) + => (foo foo foo foo foo) + + - Function: list* X . Y + Works like `list' except that the cdr of the last pair is the last + argument unless there is only one argument, when the result is + just that argument. Sometimes called `cons*'. E.g.: + (list* 1) + => 1 + (list* 1 2 3) + => (1 2 . 3) + (list* 1 2 '(3 4)) + => (1 2 3 4) + (list* ARGS '()) + == (list ARGS) + + - Function: copy-list LST + `copy-list' makes a copy of LST using new pairs and returns it. + Only the top level of the list is copied, i.e., pairs forming + elements of the copied list remain `eq?' to the corresponding + elements of the original; the copy is, however, not `eq?' to the + original, but is `equal?' to it. + + Example: + (copy-list '(foo foo foo)) + => (foo foo foo) + (define q '(foo bar baz bang)) + (define p q) + (eq? p q) + => #t + (define r (copy-list q)) + (eq? q r) + => #f + (equal? q r) + => #t + (define bar '(bar)) + (eq? bar (car (copy-list (list bar 'foo)))) + => #t + + +File: slib.info, Node: Lists as sets, Next: Lists as sequences, Prev: List construction, Up: Common List Functions + +Lists as sets +------------- + + `eq?' is used to test for membership by all the procedures below +which treat lists as sets. + + - Function: adjoin E L + `adjoin' returns the adjoint of the element E and the list L. + That is, if E is in L, `adjoin' returns L, otherwise, it returns + `(cons E L)'. + + Example: + (adjoin 'baz '(bar baz bang)) + => (bar baz bang) + (adjoin 'foo '(bar baz bang)) + => (foo bar baz bang) + + - Function: union L1 L2 + `union' returns the combination of L1 and L2. Duplicates between + L1 and L2 are culled. Duplicates within L1 or within L2 may or + may not be removed. + + Example: + (union '(1 2 3 4) '(5 6 7 8)) + => (4 3 2 1 5 6 7 8) + (union '(1 2 3 4) '(3 4 5 6)) + => (2 1 3 4 5 6) + + - Function: intersection L1 L2 + `intersection' returns all elements that are in both L1 and L2. + + Example: + (intersection '(1 2 3 4) '(3 4 5 6)) + => (3 4) + (intersection '(1 2 3 4) '(5 6 7 8)) + => () + + - Function: set-difference L1 L2 + `set-difference' returns the union of all elements that are in L1 + but not in L2. + + Example: + (set-difference '(1 2 3 4) '(3 4 5 6)) + => (1 2) + (set-difference '(1 2 3 4) '(1 2 3 4 5 6)) + => () + + - Function: member-if PRED LST + `member-if' returns LST if `(PRED ELEMENT)' is `#t' for any + ELEMENT in LST. Returns `#f' if PRED does not apply to any + ELEMENT in LST. + + Example: + (member-if vector? '(1 2 3 4)) + => #f + (member-if number? '(1 2 3 4)) + => (1 2 3 4) + + - Function: some PRED LST . MORE-LSTS + PRED is a boolean function of as many arguments as there are list + arguments to `some' i.e., LST plus any optional arguments. PRED + is applied to successive elements of the list arguments in order. + `some' returns `#t' as soon as one of these applications returns + `#t', and is `#f' if none returns `#t'. All the lists should have + the same length. + + Example: + (some odd? '(1 2 3 4)) + => #t + + (some odd? '(2 4 6 8)) + => #f + + (some > '(2 3) '(1 4)) + => #f + + - Function: every PRED LST . MORE-LSTS + `every' is analogous to `some' except it returns `#t' if every + application of PRED is `#t' and `#f' otherwise. + + Example: + (every even? '(1 2 3 4)) + => #f + + (every even? '(2 4 6 8)) + => #t + + (every > '(2 3) '(1 4)) + => #f + + - Function: notany PRED . LST + `notany' is analogous to `some' but returns `#t' if no application + of PRED returns `#t' or `#f' as soon as any one does. + + - Function: notevery PRED . LST + `notevery' is analogous to `some' but returns `#t' as soon as an + application of PRED returns `#f', and `#f' otherwise. + + Example: + (notevery even? '(1 2 3 4)) + => #t + + (notevery even? '(2 4 6 8)) + => #f + + - Function: find-if PRED LST + `find-if' searches for the first ELEMENT in LST such that `(PRED + ELEMENT)' returns `#t'. If it finds any such ELEMENT in LST, + ELEMENT is returned. Otherwise, `#f' is returned. + + Example: + (find-if number? '(foo 1 bar 2)) + => 1 + + (find-if number? '(foo bar baz bang)) + => #f + + (find-if symbol? '(1 2 foo bar)) + => foo + + - Function: remove ELT LST + `remove' removes all occurrences of ELT from LST using `eqv?' to + test for equality and returns everything that's left. N.B.: other + implementations (Chez, Scheme->C and T, at least) use `equal?' as + the equality test. + + Example: + (remove 1 '(1 2 1 3 1 4 1 5)) + => (2 3 4 5) + + (remove 'foo '(bar baz bang)) + => (bar baz bang) + + - Function: remove-if PRED LST + `remove-if' removes all ELEMENTs from LST where `(PRED ELEMENT)' + is `#t' and returns everything that's left. + + Example: + (remove-if number? '(1 2 3 4)) + => () + + (remove-if even? '(1 2 3 4 5 6 7 8)) + => (1 3 5 7) + + - Function: remove-if-not PRED LST + `remove-if-not' removes all ELEMENTs from LST for which `(PRED + ELEMENT)' is `#f' and returns everything that's left. + + Example: + (remove-if-not number? '(foo bar baz)) + => () + (remove-if-not odd? '(1 2 3 4 5 6 7 8)) + => (1 3 5 7) + + - Function: has-duplicates? LST + returns `#t' if 2 members of LST are `equal?', `#f' otherwise. + Example: + (has-duplicates? '(1 2 3 4)) + => #f + + (has-duplicates? '(2 4 3 4)) + => #t + + +File: slib.info, Node: Lists as sequences, Next: Destructive list operations, Prev: Lists as sets, Up: Common List Functions + +Lists as sequences +------------------ + + - Function: position OBJ LST + `position' returns the 0-based position of OBJ in LST, or `#f' if + OBJ does not occur in LST. + + Example: + (position 'foo '(foo bar baz bang)) + => 0 + (position 'baz '(foo bar baz bang)) + => 2 + (position 'oops '(foo bar baz bang)) + => #f + + - Function: reduce P LST + `reduce' combines all the elements of a sequence using a binary + operation (the combination is left-associative). For example, + using `+', one can add up all the elements. `reduce' allows you to + apply a function which accepts only two arguments to more than 2 + objects. Functional programmers usually refer to this as "foldl". + `collect:reduce' (*Note Collections::) provides a version of + `collect' generalized to collections. + + Example: + (reduce + '(1 2 3 4)) + => 10 + (define (bad-sum . l) (reduce + l)) + (bad-sum 1 2 3 4) + == (reduce + (1 2 3 4)) + == (+ (+ (+ 1 2) 3) 4) + => 10 + (bad-sum) + == (reduce + ()) + => () + (reduce string-append '("hello" "cruel" "world")) + == (string-append (string-append "hello" "cruel") "world") + => "hellocruelworld" + (reduce anything '()) + => () + (reduce anything '(x)) + => x + + What follows is a rather non-standard implementation of `reverse' + in terms of `reduce' and a combinator elsewhere called "C". + + ;;; Contributed by Jussi Piitulainen (jpiitula@ling.helsinki.fi) + + (define commute + (lambda (f) + (lambda (x y) + (f y x)))) + + (define reverse + (lambda (args) + (reduce-init (commute cons) args))) + + - Function: reduce-init P INIT LST + `reduce-init' is the same as reduce, except that it implicitly + inserts INIT at the start of the list. `reduce-init' is preferred + if you want to handle the null list, the one-element, and lists + with two or more elements consistently. It is common to use the + operator's idempotent as the initializer. Functional programmers + usually call this "foldl". + + Example: + (define (sum . l) (reduce-init + 0 l)) + (sum 1 2 3 4) + == (reduce-init + 0 (1 2 3 4)) + == (+ (+ (+ (+ 0 1) 2) 3) 4) + => 10 + (sum) + == (reduce-init + 0 '()) + => 0 + + (reduce-init string-append "@" '("hello" "cruel" "world")) + == + (string-append (string-append (string-append "@" "hello") + "cruel") + "world") + => "@hellocruelworld" + + Given a differentiation of 2 arguments, `diff', the following will + differentiate by any number of variables. + (define (diff* exp . vars) + (reduce-init diff exp vars)) + + Example: + ;;; Real-world example: Insertion sort using reduce-init. + + (define (insert l item) + (if (null? l) + (list item) + (if (< (car l) item) + (cons (car l) (insert (cdr l) item)) + (cons item l)))) + (define (insertion-sort l) (reduce-init insert '() l)) + + (insertion-sort '(3 1 4 1 5) + == (reduce-init insert () (3 1 4 1 5)) + == (insert (insert (insert (insert (insert () 3) 1) 4) 1) 5) + == (insert (insert (insert (insert (3)) 1) 4) 1) 5) + == (insert (insert (insert (1 3) 4) 1) 5) + == (insert (insert (1 3 4) 1) 5) + == (insert (1 1 3 4) 5) + => (1 1 3 4 5) + + - Function: butlast LST N + `butlast' returns all but the last N elements of LST. + + Example: + (butlast '(1 2 3 4) 3) + => (1) + (butlast '(1 2 3 4) 4) + => () + + - Function: nthcdr N LST + `nthcdr' takes N `cdr's of LST and returns the result. Thus + `(nthcdr 3 LST)' == `(cdddr LST)' + + Example: + (nthcdr 2 '(1 2 3 4)) + => (3 4) + (nthcdr 0 '(1 2 3 4)) + => (1 2 3 4) + + - Function: last LST N + `last' returns the last N elements of LST. N must be a + non-negative integer. + + Example: + (last '(foo bar baz bang) 2) + => (baz bang) + (last '(1 2 3) 0) + => 0 + + +File: slib.info, Node: Destructive list operations, Next: Non-List functions, Prev: Lists as sequences, Up: Common List Functions + +Destructive list operations +--------------------------- + + These procedures may mutate the list they operate on, but any such +mutation is undefined. + + - Procedure: nconc ARGS + `nconc' destructively concatenates its arguments. (Compare this + with `append', which copies arguments rather than destroying them.) + Sometimes called `append!' (*Note Rev2 Procedures::). + + Example: You want to find the subsets of a set. Here's the + obvious way: + + (define (subsets set) + (if (null? set) + '(()) + (append (mapcar (lambda (sub) (cons (car set) sub)) + (subsets (cdr set))) + (subsets (cdr set))))) + But that does way more consing than you need. Instead, you could + replace the `append' with `nconc', since you don't have any need + for all the intermediate results. + + Example: + (define x '(a b c)) + (define y '(d e f)) + (nconc x y) + => (a b c d e f) + x + => (a b c d e f) + + `nconc' is the same as `append!' in `sc2.scm'. + + - Procedure: nreverse LST + `nreverse' reverses the order of elements in LST by mutating + `cdr's of the list. Sometimes called `reverse!'. + + Example: + (define foo '(a b c)) + (nreverse foo) + => (c b a) + foo + => (a) + + Some people have been confused about how to use `nreverse', + thinking that it doesn't return a value. It needs to be pointed + out that + (set! lst (nreverse lst)) + + is the proper usage, not + (nreverse lst) + The example should suffice to show why this is the case. + + - Procedure: delete ELT LST + - Procedure: delete-if PRED LST + - Procedure: delete-if-not PRED LST + Destructive versions of `remove' `remove-if', and `remove-if-not'. + + Example: + (define lst '(foo bar baz bang)) + (delete 'foo lst) + => (bar baz bang) + lst + => (foo bar baz bang) + + (define lst '(1 2 3 4 5 6 7 8 9)) + (delete-if odd? lst) + => (2 4 6 8) + lst + => (1 2 4 6 8) + + Some people have been confused about how to use `delete', + `delete-if', and `delete-if', thinking that they dont' return a + value. It needs to be pointed out that + (set! lst (delete el lst)) + + is the proper usage, not + (delete el lst) + The examples should suffice to show why this is the case. + + +File: slib.info, Node: Non-List functions, Prev: Destructive list operations, Up: Common List Functions + +Non-List functions +------------------ + + - Function: and? . ARGS + `and?' checks to see if all its arguments are true. If they are, + `and?' returns `#t', otherwise, `#f'. (In contrast to `and', this + is a function, so all arguments are always evaluated and in an + unspecified order.) + + Example: + (and? 1 2 3) + => #t + (and #f 1 2) + => #f + + - Function: or? . ARGS + `or?' checks to see if any of its arguments are true. If any is + true, `or?' returns `#t', and `#f' otherwise. (To `or' as `and?' + is to `and'.) + + Example: + (or? 1 2 #f) + => #t + (or? #f #f #f) + => #f + + - Function: atom? OBJECT + Returns `#t' if OBJECT is not a pair and `#f' if it is pair. + (Called `atom' in Common LISP.) + (atom? 1) + => #t + (atom? '(1 2)) + => #f + (atom? #(1 2)) ; dubious! + => #t + + - Function: type-of OBJECT + Returns a symbol name for the type of OBJECT. + + - Function: coerce OBJECT RESULT-TYPE + Converts and returns OBJECT of type `char', `number', `string', + `symbol', `list', or `vector' to RESULT-TYPE (which must be one of + these symbols). + + +File: slib.info, Node: Format, Next: Generic-Write, Prev: Common List Functions, Up: Procedures + +Format +====== + + `(require 'format)' + +* Menu: + +* Format Interface:: +* Format Specification:: + + +File: slib.info, Node: Format Interface, Next: Format Specification, Prev: Format, Up: Format + +Format Interface +---------------- + + - Function: format DESTINATION FORMAT-STRING . ARGUMENTS + An almost complete implementation of Common LISP format description + according to the CL reference book `Common LISP' from Guy L. + Steele, Digital Press. Backward compatible to most of the + available Scheme format implementations. + + Returns `#t', `#f' or a string; has side effect of printing + according to FORMAT-STRING. If DESTINATION is `#t', the output is + to the current output port and `#t' is returned. If DESTINATION + is `#f', a formatted string is returned as the result of the call. + NEW: If DESTINATION is a string, DESTINATION is regarded as the + format string; FORMAT-STRING is then the first argument and the + output is returned as a string. If DESTINATION is a number, the + output is to the current error port if available by the + implementation. Otherwise DESTINATION must be an output port and + `#t' is returned. + + FORMAT-STRING must be a string. In case of a formatting error + format returns `#f' and prints a message on the current output or + error port. Characters are output as if the string were output by + the `display' function with the exception of those prefixed by a + tilde (~). For a detailed description of the FORMAT-STRING syntax + please consult a Common LISP format reference manual. For a test + suite to verify this format implementation load `formatst.scm'. + Please send bug reports to `lutzeb@cs.tu-berlin.de'. + + Note: `format' is not reentrant, i.e. only one `format'-call may + be executed at a time. + + + +File: slib.info, Node: Format Specification, Prev: Format Interface, Up: Format + +Format Specification (Format version 3.0) +----------------------------------------- + + Please consult a Common LISP format reference manual for a detailed +description of the format string syntax. For a demonstration of the +implemented directives see `formatst.scm'. + + This implementation supports directive parameters and modifiers (`:' +and `@' characters). Multiple parameters must be separated by a comma +(`,'). Parameters can be numerical parameters (positive or negative), +character parameters (prefixed by a quote character (`''), variable +parameters (`v'), number of rest arguments parameter (`#'), empty and +default parameters. Directive characters are case independent. The +general form of a directive is: + +DIRECTIVE ::= ~{DIRECTIVE-PARAMETER,}[:][@]DIRECTIVE-CHARACTER + +DIRECTIVE-PARAMETER ::= [ [-|+]{0-9}+ | 'CHARACTER | v | # ] + +Implemented CL Format Control Directives +........................................ + + Documentation syntax: Uppercase characters represent the corresponding +control directive characters. Lowercase characters represent control +directive parameter descriptions. + +`~A' + Any (print as `display' does). + `~@A' + left pad. + + `~MINCOL,COLINC,MINPAD,PADCHARA' + full padding. + +`~S' + S-expression (print as `write' does). + `~@S' + left pad. + + `~MINCOL,COLINC,MINPAD,PADCHARS' + full padding. + +`~D' + Decimal. + `~@D' + print number sign always. + + `~:D' + print comma separated. + + `~MINCOL,PADCHAR,COMMACHARD' + padding. + +`~X' + Hexadecimal. + `~@X' + print number sign always. + + `~:X' + print comma separated. + + `~MINCOL,PADCHAR,COMMACHARX' + padding. + +`~O' + Octal. + `~@O' + print number sign always. + + `~:O' + print comma separated. + + `~MINCOL,PADCHAR,COMMACHARO' + padding. + +`~B' + Binary. + `~@B' + print number sign always. + + `~:B' + print comma separated. + + `~MINCOL,PADCHAR,COMMACHARB' + padding. + +`~NR' + Radix N. + `~N,MINCOL,PADCHAR,COMMACHARR' + padding. + +`~@R' + print a number as a Roman numeral. + +`~:R' + print a number as an ordinal English number. + +`~:@R' + print a number as a cardinal English number. + +`~P' + Plural. + `~@P' + prints `y' and `ies'. + + `~:P' + as `~P but jumps 1 argument backward.' + + `~:@P' + as `~@P but jumps 1 argument backward.' + +`~C' + Character. + `~@C' + prints a character as the reader can understand it (i.e. `#\' + prefixing). + + `~:C' + prints a character as emacs does (eg. `^C' for ASCII 03). + +`~F' + Fixed-format floating-point (prints a flonum like MMM.NNN). + `~WIDTH,DIGITS,SCALE,OVERFLOWCHAR,PADCHARF' + `~@F' + If the number is positive a plus sign is printed. + +`~E' + Exponential floating-point (prints a flonum like MMM.NNN`E'EE). + `~WIDTH,DIGITS,EXPONENTDIGITS,SCALE,OVERFLOWCHAR,PADCHAR,EXPONENTCHARE' + `~@E' + If the number is positive a plus sign is printed. + +`~G' + General floating-point (prints a flonum either fixed or + exponential). + `~WIDTH,DIGITS,EXPONENTDIGITS,SCALE,OVERFLOWCHAR,PADCHAR,EXPONENTCHARG' + `~@G' + If the number is positive a plus sign is printed. + +`~$' + Dollars floating-point (prints a flonum in fixed with signs + separated). + `~DIGITS,SCALE,WIDTH,PADCHAR$' + `~@$' + If the number is positive a plus sign is printed. + + `~:@$' + A sign is always printed and appears before the padding. + + `~:$' + The sign appears before the padding. + +`~%' + Newline. + `~N%' + print N newlines. + +`~&' + print newline if not at the beginning of the output line. + `~N&' + prints `~&' and then N-1 newlines. + +`~|' + Page Separator. + `~N|' + print N page separators. + +`~~' + Tilde. + `~N~' + print N tildes. + +`~'<newline> + Continuation Line. + `~:'<newline> + newline is ignored, white space left. + + `~@'<newline> + newline is left, white space ignored. + +`~T' + Tabulation. + `~@T' + relative tabulation. + + `~COLNUM,COLINCT' + full tabulation. + +`~?' + Indirection (expects indirect arguments as a list). + `~@?' + extracts indirect arguments from format arguments. + +`~(STR~)' + Case conversion (converts by `string-downcase'). + `~:(STR~)' + converts by `string-capitalize'. + + `~@(STR~)' + converts by `string-capitalize-first'. + + `~:@(STR~)' + converts by `string-upcase'. + +`~*' + Argument Jumping (jumps 1 argument forward). + `~N*' + jumps N arguments forward. + + `~:*' + jumps 1 argument backward. + + `~N:*' + jumps N arguments backward. + + `~@*' + jumps to the 0th argument. + + `~N@*' + jumps to the Nth argument (beginning from 0) + +`~[STR0~;STR1~;...~;STRN~]' + Conditional Expression (numerical clause conditional). + `~N[' + take argument from N. + + `~@[' + true test conditional. + + `~:[' + if-else-then conditional. + + `~;' + clause separator. + + `~:;' + default clause follows. + +`~{STR~}' + Iteration (args come from the next argument (a list)). + `~N{' + at most N iterations. + + `~:{' + args from next arg (a list of lists). + + `~@{' + args from the rest of arguments. + + `~:@{' + args from the rest args (lists). + +`~^' + Up and out. + `~N^' + aborts if N = 0 + + `~N,M^' + aborts if N = M + + `~N,M,K^' + aborts if N <= M <= K + +Not Implemented CL Format Control Directives +............................................ + +`~:A' + print `#f' as an empty list (see below). + +`~:S' + print `#f' as an empty list (see below). + +`~<~>' + Justification. + +`~:^' + (sorry I don't understand its semantics completely) + +Extended, Replaced and Additional Control Directives +.................................................... + +`~MINCOL,PADCHAR,COMMACHAR,COMMAWIDTHD' +`~MINCOL,PADCHAR,COMMACHAR,COMMAWIDTHX' +`~MINCOL,PADCHAR,COMMACHAR,COMMAWIDTHO' +`~MINCOL,PADCHAR,COMMACHAR,COMMAWIDTHB' +`~N,MINCOL,PADCHAR,COMMACHAR,COMMAWIDTHR' + COMMAWIDTH is the number of characters between two comma + characters. + +`~I' + print a R4RS complex number as `~F~@Fi' with passed parameters for + `~F'. + +`~Y' + Pretty print formatting of an argument for scheme code lists. + +`~K' + Same as `~?.' + +`~!' + Flushes the output if format DESTINATION is a port. + +`~_' + Print a `#\space' character + `~N_' + print N `#\space' characters. + +`~/' + Print a `#\tab' character + `~N/' + print N `#\tab' characters. + +`~NC' + Takes N as an integer representation for a character. No arguments + are consumed. N is converted to a character by `integer->char'. N + must be a positive decimal number. + +`~:S' + Print out readproof. Prints out internal objects represented as + `#<...>' as strings `"#<...>"' so that the format output can always + be processed by `read'. + +`~:A' + Print out readproof. Prints out internal objects represented as + `#<...>' as strings `"#<...>"' so that the format output can always + be processed by `read'. + +`~Q' + Prints information and a copyright notice on the format + implementation. + `~:Q' + prints format version. + +`~F, ~E, ~G, ~$' + may also print number strings, i.e. passing a number as a string + and format it accordingly. + +Configuration Variables +....................... + + Format has some configuration variables at the beginning of +`format.scm' to suit the systems and users needs. There should be no +modification necessary for the configuration that comes with SLIB. If +modification is desired the variable should be set after the format +code is loaded. Format detects automatically if the running scheme +system implements floating point numbers and complex numbers. + +FORMAT:SYMBOL-CASE-CONV + Symbols are converted by `symbol->string' so the case type of the + printed symbols is implementation dependent. + `format:symbol-case-conv' is a one arg closure which is either + `#f' (no conversion), `string-upcase', `string-downcase' or + `string-capitalize'. (default `#f') + +FORMAT:IOBJ-CASE-CONV + As FORMAT:SYMBOL-CASE-CONV but applies for the representation of + implementation internal objects. (default `#f') + +FORMAT:EXPCH + The character prefixing the exponent value in `~E' printing. + (default `#\E') + +Compatibility With Other Format Implementations +............................................... + +SLIB format 2.x: + See `format.doc'. + +SLIB format 1.4: + Downward compatible except for padding support and `~A', `~S', + `~P', `~X' uppercase printing. SLIB format 1.4 uses C-style + `printf' padding support which is completely replaced by the CL + `format' padding style. + +MIT C-Scheme 7.1: + Downward compatible except for `~', which is not documented + (ignores all characters inside the format string up to a newline + character). (7.1 implements `~a', `~s', ~NEWLINE, `~~', `~%', + numerical and variable parameters and `:/@' modifiers in the CL + sense). + +Elk 1.5/2.0: + Downward compatible except for `~A' and `~S' which print in + uppercase. (Elk implements `~a', `~s', `~~', and `~%' (no + directive parameters or modifiers)). + +Scheme->C 01nov91: + Downward compatible except for an optional destination parameter: + S2C accepts a format call without a destination which returns a + formatted string. This is equivalent to a #f destination in S2C. + (S2C implements `~a', `~s', `~c', `~%', and `~~' (no directive + parameters or modifiers)). + + This implementation of format is solely useful in the SLIB context +because it requires other components provided by SLIB. + + +File: slib.info, Node: Generic-Write, Next: Line I/O, Prev: Format, Up: Procedures + +Generic-Write +============= + + `(require 'generic-write)' + + `generic-write' is a procedure that transforms a Scheme data value +(or Scheme program expression) into its textual representation and +prints it. The interface to the procedure is sufficiently general to +easily implement other useful formatting procedures such as pretty +printing, output to a string and truncated output. + + - Procedure: generic-write OBJ DISPLAY? WIDTH OUTPUT + OBJ + Scheme data value to transform. + + DISPLAY? + Boolean, controls whether characters and strings are quoted. + + WIDTH + Extended boolean, selects format: + #f + single line format + + integer > 0 + pretty-print (value = max nb of chars per line) + + OUTPUT + Procedure of 1 argument of string type, called repeatedly with + successive substrings of the textual representation. This + procedure can return `#f' to stop the transformation. + + The value returned by `generic-write' is undefined. + + Examples: + (write obj) == (generic-write obj #f #f DISPLAY-STRING) + (display obj) == (generic-write obj #t #f DISPLAY-STRING) + + where + DISPLAY-STRING == + (lambda (s) (for-each write-char (string->list s)) #t) + + +File: slib.info, Node: Line I/O, Next: Multi-Processing, Prev: Generic-Write, Up: Procedures + +Line I/O +======== + + `(require 'line-i/o)' + + - Function: read-line + - Function: read-line PORT + Returns a string of the characters up to, but not including a + newline or end of file, updating PORT to point to the character + following the newline. If no characters are available, an end of + file object is returned. PORT may be omitted, in which case it + defaults to the value returned by `current-input-port'. + + - Function: read-line! STRING + - Function: read-line! STRING PORT + Fills STRING with characters up to, but not including a newline or + end of file, updating the port to point to the last character read + or following the newline if it was read. If no characters are + available, an end of file object is returned. If a newline or end + of file was found, the number of characters read is returned. + Otherwise, `#f' is returned. PORT may be omitted, in which case + it defaults to the value returned by `current-input-port'. + + - Function: write-line STRING + - Function: write-line STRING PORT + Writes STRING followed by a newline to the given port and returns + an unspecified value. Port may be omited, in which case it + defaults to the value returned by `current-input-port'. + + +File: slib.info, Node: Multi-Processing, Next: Object-To-String, Prev: Line I/O, Up: Procedures + +Multi-Processing +================ + + `(require 'process)' + + - Procedure: add-process! PROC + Adds proc, which must be a procedure (or continuation) capable of + accepting accepting one argument, to the `process:queue'. The + value returned is unspecified. The argument to PROC should be + ignored. If PROC returns, the process is killed. + + - Procedure: process:schedule! + Saves the current process on `process:queue' and runs the next + process from `process:queue'. The value returned is unspecified. + + - Procedure: kill-process! + Kills the current process and runs the next process from + `process:queue'. If there are no more processes on + `process:queue', `(slib:exit)' is called (*Note System::). + + +File: slib.info, Node: Object-To-String, Next: Pretty-Print, Prev: Multi-Processing, Up: Procedures + +Object-To-String +================ + + `(require 'object->string)' + + - Function: object->string OBJ + Returns the textual representation of OBJ as a string. + + +File: slib.info, Node: Pretty-Print, Next: Sorting, Prev: Object-To-String, Up: Procedures + +Pretty-Print +============ + + `(require 'pretty-print)' + + - Procedure: pretty-print OBJ + - Procedure: pretty-print OBJ PORT + `pretty-print's OBJ on PORT. If PORT is not specified, + `current-output-port' is used. + + Example: + (pretty-print '((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))) + -| ((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)) + + `(require 'pprint-file)' + + - Procedure: pprint-file INFILE + - Procedure: pprint-file INFILE OUTFILE + Pretty-prints all the code in INFILE. If OUTFILE is specified, + the output goes to OUTFILE, otherwise it goes to + `(current-output-port)'. + + - Function: pprint-filter-file INFILE PROC OUTFILE + - Function: pprint-filter-file INFILE PROC + INFILE is a port or a string naming an existing file. Scheme + source code expressions and definitions are read from the port (or + file) and PROC is applied to them sequentially. + + OUTFILE is a port or a string. If no OUTFILE is specified then + `current-output-port' is assumed. These expanded expressions are + then `pretty-print'ed to this port. + + Whitepsace and comments (introduced by `;') which are not part of + scheme expressions are reproduced in the output. This procedure + does not affect the values returned by `current-input-port' and + `current-output-port'. + + `pprint-filter-file' can be used to pre-compile macro-expansion and +thus can reduce loading time. The following will write into +`exp-code.scm' the result of expanding all defmacros in `code.scm'. + (require 'pprint-file) + (require 'defmacroexpand) + (defmacro:load "my-macros.scm") + (pprint-filter-file "code.scm" defmacro:expand* "exp-code.scm") + diff --git a/slib.info-6 b/slib.info-6 new file mode 100644 index 0000000..05d8377 --- /dev/null +++ b/slib.info-6 @@ -0,0 +1,1410 @@ +This is Info file slib.info, produced by Makeinfo-1.64 from the input +file slib.texi. + + This file documents SLIB, the portable Scheme library. + + Copyright (C) 1993 Todd R. Eigenschink Copyright (C) 1993, 1994, 1995 +Aubrey Jaffer + + Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + + Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + + Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be stated in a +translation approved by the author. + + +File: slib.info, Node: Sorting, Next: Topological Sort, Prev: Pretty-Print, Up: Procedures + +Sorting +======= + + `(require 'sort)' + + Many Scheme systems provide some kind of sorting functions. They do +not, however, always provide the *same* sorting functions, and those +that I have had the opportunity to test provided inefficient ones (a +common blunder is to use quicksort which does not perform well). + + Because `sort' and `sort!' are not in the standard, there is very +little agreement about what these functions look like. For example, +Dybvig says that Chez Scheme provides + (merge predicate list1 list2) + (merge! predicate list1 list2) + (sort predicate list) + (sort! predicate list) + +while MIT Scheme 7.1, following Common LISP, offers unstable + (sort list predicate) + +TI PC Scheme offers + (sort! list/vector predicate?) + +and Elk offers + (sort list/vector predicate?) + (sort! list/vector predicate?) + + Here is a comprehensive catalogue of the variations I have found. + + 1. Both `sort' and `sort!' may be provided. + + 2. `sort' may be provided without `sort!'. + + 3. `sort!' may be provided without `sort'. + + 4. Neither may be provided. + + 5. The sequence argument may be either a list or a vector. + + 6. The sequence argument may only be a list. + + 7. The sequence argument may only be a vector. + + 8. The comparison function may be expected to behave like `<'. + + 9. The comparison function may be expected to behave like `<='. + + 10. The interface may be `(sort predicate? sequence)'. + + 11. The interface may be `(sort sequence predicate?)'. + + 12. The interface may be `(sort sequence &optional (predicate? <))'. + + 13. The sort may be stable. + + 14. The sort may be unstable. + + All of this variation really does not help anybody. A nice simple +merge sort is both stable and fast (quite a lot faster than *quick* +sort). + + I am providing this source code with no restrictions at all on its use +(but please retain D.H.D.Warren's credit for the original idea). You +may have to rename some of these functions in order to use them in a +system which already provides incompatible or inferior sorts. For each +of the functions, only the top-level define needs to be edited to do +that. + + I could have given these functions names which would not clash with +any Scheme that I know of, but I would like to encourage implementors to +converge on a single interface, and this may serve as a hint. The +argument order for all functions has been chosen to be as close to +Common LISP as made sense, in order to avoid NIH-itis. + + Each of the five functions has a required *last* parameter which is a +comparison function. A comparison function `f' is a function of 2 +arguments which acts like `<'. For example, + + (not (f x x)) + (and (f x y) (f y z)) == (f x z) + + The standard functions `<', `>', `char<?', `char>?', `char-ci<?', +`char-ci>?', `string<?', `string>?', `string-ci<?', and `string-ci>?' +are suitable for use as comparison functions. Think of `(less? x y)' +as saying when `x' must *not* precede `y'. + + - Function: sorted? SEQUENCE LESS? + Returns `#t' when the sequence argument is in non-decreasing order + according to LESS? (that is, there is no adjacent pair `... x y + ...' for which `(less? y x)'). + + Returns `#f' when the sequence contains at least one out-of-order + pair. It is an error if the sequence is neither a list nor a + vector. + + - Function: merge LIST1 LIST2 LESS? + This merges two lists, producing a completely new list as result. + I gave serious consideration to producing a Common-LISP-compatible + version. However, Common LISP's `sort' is our `sort!' (well, in + fact Common LISP's `stable-sort' is our `sort!', merge sort is + *fast* as well as stable!) so adapting CL code to Scheme takes a + bit of work anyway. I did, however, appeal to CL to determine the + *order* of the arguments. + + - Procedure: merge! LIST1 LIST2 LESS? + Merges two lists, re-using the pairs of LIST1 and LIST2 to build + the result. If the code is compiled, and LESS? constructs no new + pairs, no pairs at all will be allocated. The first pair of the + result will be either the first pair of LIST1 or the first pair of + LIST2, but you can't predict which. + + The code of `merge' and `merge!' could have been quite a bit + simpler, but they have been coded to reduce the amount of work + done per iteration. (For example, we only have one `null?' test + per iteration.) + + - Function: sort SEQUENCE LESS? + Accepts either a list or a vector, and returns a new sequence + which is sorted. The new sequence is the same type as the input. + Always `(sorted? (sort sequence less?) less?)'. The original + sequence is not altered in any way. The new sequence shares its + *elements* with the old one; no elements are copied. + + - Procedure: sort! SEQUENCE LESS? + Returns its sorted result in the original boxes. If the original + sequence is a list, no new storage is allocated at all. If the + original sequence is a vector, the sorted elements are put back in + the same vector. + + Some people have been confused about how to use `sort!', thinking + that it doesn't return a value. It needs to be pointed out that + (set! slist (sort! slist <)) + + is the proper usage, not + (sort! slist <) + + Note that these functions do *not* accept a CL-style `:key' argument. +A simple device for obtaining the same expressiveness is to define + (define (keyed less? key) + (lambda (x y) (less? (key x) (key y)))) + +and then, when you would have written + (sort a-sequence #'my-less :key #'my-key) + +in Common LISP, just write + (sort! a-sequence (keyed my-less? my-key)) + +in Scheme. + + +File: slib.info, Node: Topological Sort, Next: Standard Formatted I/O, Prev: Sorting, Up: Procedures + +Topological Sort +================ + + `(require 'topological-sort)' or `(require 'tsort)' + +The algorithm is inspired by Cormen, Leiserson and Rivest (1990) +`Introduction to Algorithms', chapter 23. + + - Function: tsort DAG PRED + - Function: topological-sort DAG PRED + where + DAG + is a list of sublists. The car of each sublist is a vertex. + The cdr is the adjacency list of that vertex, i.e. a list of + all vertices to which there exists an edge from the car + vertex. + + PRED + is one of `eq?', `eqv?', `equal?', `=', `char=?', + `char-ci=?', `string=?', or `string-ci=?'. + + Sort the directed acyclic graph DAG so that for every edge from + vertex U to V, U will come before V in the resulting list of + vertices. + + Time complexity: O (|V| + |E|) + + Example (from Cormen): + Prof. Bumstead topologically sorts his clothing when getting + dressed. The first argument to `tsort' describes which + garments he needs to put on before others. (For example, + Prof Bumstead needs to put on his shirt before he puts on his + tie or his belt.) `tsort' gives the correct order of + dressing: + + (require 'tsort) + (tsort '((shirt tie belt) + (tie jacket) + (belt jacket) + (watch) + (pants shoes belt) + (undershorts pants shoes) + (socks shoes)) + eq?) + => + (socks undershorts pants shoes watch shirt belt tie jacket) + + +File: slib.info, Node: Standard Formatted I/O, Next: String-Case, Prev: Topological Sort, Up: Procedures + +Standard Formatted I/O +====================== + +* Menu: + +* Standard Formatted Output:: +* Standard Formatted Input:: + +stdio +----- + + `(require 'stdio)' + + `require's `printf' and `scanf' and additionally defines the symbols: + + - Variable: stdin + Defined to be `(current-input-port)'. + + - Variable: stdout + Defined to be `(current-output-port)'. + + - Variable: stderr + Defined to be `(current-error-port)'. + + +File: slib.info, Node: Standard Formatted Output, Next: Standard Formatted Input, Prev: Standard Formatted I/O, Up: Standard Formatted I/O + +Standard Formatted Output +------------------------- + + `(require 'printf)' + + - Procedure: printf FORMAT ARG1 ... + - Procedure: fprintf PORT FORMAT ARG1 ... + - Procedure: sprintf STR FORMAT ARG1 ... + Each function converts, formats, and outputs its ARG1 ... + arguments according to the control string FORMAT argument and + returns the number of characters output. + + `printf' sends its output to the port `(current-output-port)'. + `fprintf' sends its output to the port PORT. `sprintf' + `string-set!'s locations of the non-constant string argument STR + to the output characters. + + *Note:* sprintf should be changed to a macro so a `substring' + expression could be used for the STR argument. + + The string FORMAT contains plain characters which are copied to + the output stream, and conversion specifications, each of which + results in fetching zero or more of the arguments ARG1 .... The + results are undefined if there are an insufficient number of + arguments for the format. If FORMAT is exhausted while some of the + ARG1 ... arguments remain unused, the excess ARG1 ... arguments + are ignored. + + The conversion specifications in a format string have the form: + + % [ FLAGS ] [ WIDTH ] [ . PRECISION ] [ TYPE ] CONVERSION + + An output conversion specifications consist of an initial `%' + character followed in sequence by: + + * Zero or more "flag characters" that modify the normal + behavior of the conversion specification. + + `-' + Left-justify the result in the field. Normally the + result is right-justified. + + `+' + For the signed `%d' and `%i' conversions and all inexact + conversions, prefix a plus sign if the value is positive. + + ` ' + For the signed `%d' and `%i' conversions, if the result + doesn't start with a plus or minus sign, prefix it with + a space character instead. Since the `+' flag ensures + that the result includes a sign, this flag is ignored if + both are specified. + + `#' + For inexact conversions, `#' specifies that the result + should always include a decimal point, even if no digits + follow it. For the `%g' and `%G' conversions, this also + forces trailing zeros after the decimal point to be + printed where they would otherwise be elided. + + For the `%o' conversion, force the leading digit to be + `0', as if by increasing the precision. For `%x' or + `%X', prefix a leading `0x' or `0X' (respectively) to + the result. This doesn't do anything useful for the + `%d', `%i', or `%u' conversions. Using this flag + produces output which can be parsed by the `scanf' + functions with the `%i' conversion (*note Standard + Formatted Input::.). + + `0' + Pad the field with zeros instead of spaces. The zeros + are placed after any indication of sign or base. This + flag is ignored if the `-' flag is also specified, or if + a precision is specified for an exact converson. + + * An optional decimal integer specifying the "minimum field + width". If the normal conversion produces fewer characters + than this, the field is padded (with spaces or zeros per the + `0' flag) to the specified width. This is a *minimum* width; + if the normal conversion produces more characters than this, + the field is *not* truncated. + + Alternatively, if the field width is `*', the next argument + in the argument list (before the actual value to be printed) + is used as the field width. The width value must be an + integer. If the value is negative it is as though the `-' + flag is set (see above) and the absolute value is used as the + field width. + + * An optional "precision" to specify the number of digits to be + written for numeric conversions and the maximum field width + for string conversions. The precision is specified by a + period (`.') followed optionally by a decimal integer (which + defaults to zero if omitted). + + Alternatively, if the precision is `.*', the next argument in + the argument list (before the actual value to be printed) is + used as the precision. The value must be an integer, and is + ignored if negative. If you specify `*' for both the field + width and precision, the field width argument precedes the + precision argument. The `.*' precision is an enhancement. C + library versions may not accept this syntax. + + For the `%f', `%e', and `%E' conversions, the precision + specifies how many digits follow the decimal-point character. + The default precision is `6'. If the precision is + explicitly `0', the decimal point character is suppressed. + + For the `%g' and `%G' conversions, the precision specifies how + many significant digits to print. Significant digits are the + first digit before the decimal point, and all the digits + after it. If the precision is `0' or not specified for `%g' + or `%G', it is treated like a value of `1'. If the value + being printed cannot be expressed accurately in the specified + number of digits, the value is rounded to the nearest number + that fits. + + For exact conversions, if a precision is supplied it + specifies the minimum number of digits to appear; leading + zeros are produced if necessary. If a precision is not + supplied, the number is printed with as many digits as + necessary. Converting an exact `0' with an explicit + precision of zero produces no characters. + + * An optional one of `l', `h' or `L', which is ignored for + numeric conversions. It is an error to specify these + modifiers for non-numeric conversions. + + * A character that specifies the conversion to be applied. + +Exact Conversions +................. + + `d', `i' + Print an integer as a signed decimal number. `%d' and `%i' + are synonymous for output, but are different when used with + `scanf' for input (*note Standard Formatted Input::.). + + `o' + Print an integer as an unsigned octal number. + + `u' + Print an integer as an unsigned decimal number. + + `x', `X' + Print an integer as an unsigned hexadecimal number. `%x' + prints using the digits `0123456789abcdef'. `%X' prints + using the digits `0123456789ABCDEF'. + +Inexact Conversions +................... + + *Note:* Inexact conversions are not supported yet. + + `f' + Print a floating-point number in fixed-point notation. + + `e', `E' + Print a floating-point number in exponential notation. `%e' + prints `e' between mantissa and exponont. `%E' prints `E' + between mantissa and exponont. + + `g', `G' + Print a floating-point number in either normal or exponential + notation, whichever is more appropriate for its magnitude. + `%g' prints `e' between mantissa and exponont. `%G' prints + `E' between mantissa and exponont. + +Other Conversions +................. + + `c' + Print a single character. The `-' flag is the only one which + can be specified. It is an error to specify a precision. + + `s' + Print a string. The `-' flag is the only one which can be + specified. A precision specifies the maximum number of + characters to output; otherwise all characters in the string + are output. + + `a', `A' + Print a scheme expression. The `-' flag left-justifies the + output. The `#' flag specifies that strings and characters + should be quoted as by `write' (which can be read using + `read'); otherwise, output is as `display' prints. A + precision specifies the maximum number of characters to + output; otherwise as many characters as needed are output. + + *Note:* `%a' and `%A' are SLIB extensions. + + `%' + Print a literal `%' character. No argument is consumed. It + is an error to specifiy flags, field width, precision, or + type modifiers with `%%'. + + +File: slib.info, Node: Standard Formatted Input, Prev: Standard Formatted Output, Up: Standard Formatted I/O + +Standard Formatted Input +------------------------ + + `(require 'scanf)' + + - Function: scanf-read-list FORMAT + - Function: scanf-read-list FORMAT PORT + - Function: scanf-read-list FORMAT STRING + + - Macro: scanf FORMAT ARG1 ... + - Macro: fscanf PORT FORMAT ARG1 ... + - Macro: sscanf STR FORMAT ARG1 ... + Each function reads characters, interpreting them according to the + control string FORMAT argument. + + `scanf-read-list' returns a list of the items specified as far as + the input matches FORMAT. `scanf', `fscanf', and `sscanf' return + the number of items successfully matched and stored. `scanf', + `fscanf', and `sscanf' also set the location corresponding to ARG1 + ... using the methods: + + symbol + `set!' + + car expression + `set-car!' + + cdr expression + `set-cdr!' + + vector-ref expression + `vector-set!' + + substring expression + `substring-move-left!' + + The argument to a `substring' expression in ARG1 ... must be a + non-constant string. Characters will be stored starting at the + position specified by the second argument to `substring'. The + number of characters stored will be limited by either the position + specified by the third argument to `substring' or the length of the + matched string, whichever is less. + + The control string, FORMAT, contains conversion specifications and + other characters used to direct interpretation of input sequences. + The control string contains: + + * White-space characters (blanks, tabs, newlines, or formfeeds) + that cause input to be read (and discarded) up to the next + non-white-space character. + + * An ordinary character (not `%') that must match the next + character of the input stream. + + * Conversion specifications, consisting of the character `%', an + optional assignment suppressing character `*', an optional + numerical maximum-field width, an optional `l', `h' or `L' + which is ignored, and a conversion code. + + Unless the specification contains the `n' conversion character + (described below), a conversion specification directs the + conversion of the next input field. The result of a conversion + specification is returned in the position of the corresponding + argument points, unless `*' indicates assignment suppression. + Assignment suppression provides a way to describe an input field + to be skipped. An input field is defined as a string of + characters; it extends to the next inappropriate character or + until the field width, if specified, is exhausted. + + *Note:* This specification of format strings differs from the + `ANSI C' and `POSIX' specifications. In SLIB, white space + before an input field is not skipped unless white space + appears before the conversion specification in the format + string. In order to write format strings which work + identically with `ANSI C' and SLIB, prepend whitespace to all + conversion specifications except `[' and `c'. + + The conversion code indicates the interpretation of the input + field; For a suppressed field, no value is returned. The + following conversion codes are legal: + + `%' + A single % is expected in the input at this point; no value + is returned. + + `d', `D' + A decimal integer is expected. + + `u', `U' + An unsigned decimal integer is expected. + + `o', `O' + An octal integer is expected. + + `x', `X' + A hexadecimal integer is expected. + + `i' + An integer is expected. Returns the value of the next input + item, interpreted according to C conventions; a leading `0' + implies octal, a leading `0x' implies hexadecimal; otherwise, + decimal is assumed. + + `n' + Returns the total number of bytes (including white space) + read by `scanf'. No input is consumed by `%n'. + + `f', `F', `e', `E', `g', `G' + A floating-point number is expected. The input format for + floating-point numbers is an optionally signed string of + digits, possibly containing a radix character `.', followed + by an optional exponent field consisting of an `E' or an `e', + followed by an optional `+', `-', or space, followed by an + integer. + + `c', `C' + WIDTH characters are expected. The normal + skip-over-white-space is suppressed in this case; to read the + next non-space character, use `%1s'. If a field width is + given, a string is returned; up to the indicated number of + characters is read. + + `s', `S' + A character string is expected The input field is terminated + by a white-space character. `scanf' cannot read a null + string. + + `[' + Indicates string data and the normal + skip-over-leading-white-space is suppressed. The left + bracket is followed by a set of characters, called the + scanset, and a right bracket; the input field is the maximal + sequence of input characters consisting entirely of + characters in the scanset. `^', when it appears as the first + character in the scanset, serves as a complement operator and + redefines the scanset as the set of all characters not + contained in the remainder of the scanset string. + Construction of the scanset follows certain conventions. A + range of characters may be represented by the construct + first-last, enabling `[0123456789]' to be expressed `[0-9]'. + Using this convention, first must be lexically less than or + equal to last; otherwise, the dash stands for itself. The + dash also stands for itself when it is the first or the last + character in the scanset. To include the right square + bracket as an element of the scanset, it must appear as the + first character (possibly preceded by a `^') of the scanset, + in which case it will not be interpreted syntactically as the + closing bracket. At least one character must match for this + conversion to succeed. + + The `scanf' functions terminate their conversions at end-of-file, + at the end of the control string, or when an input character + conflicts with the control string. In the latter case, the + offending character is left unread in the input stream. + + +File: slib.info, Node: String-Case, Next: String Ports, Prev: Standard Formatted I/O, Up: Procedures + +String-Case +=========== + + `(require 'string-case)' + + - Procedure: string-upcase STR + - Procedure: string-downcase STR + - Procedure: string-capitalize STR + The obvious string conversion routines. These are non-destructive. + + - Function: string-upcase! STR + - Function: string-downcase! STR + - Function: string-captialize! STR + The destructive versions of the functions above. + + +File: slib.info, Node: String Ports, Next: String Search, Prev: String-Case, Up: Procedures + +String Ports +============ + + `(require 'string-port)' + + - Procedure: call-with-output-string PROC + PROC must be a procedure of one argument. This procedure calls + PROC with one argument: a (newly created) output port. When the + function returns, the string composed of the characters written + into the port is returned. + + - Procedure: call-with-input-string STRING PROC + PROC must be a procedure of one argument. This procedure calls + PROC with one argument: an (newly created) input port from which + STRING's contents may be read. When PROC returns, the port is + closed and the value yielded by the procedure PROC is returned. + + +File: slib.info, Node: String Search, Next: Tektronix Graphics Support, Prev: String Ports, Up: Procedures + +String Search +============= + + `(require 'string-search)' + + - Procedure: string-index STRING CHAR + Returns the index of the first occurence of CHAR within STRING, or + `#f' if the STRING does not contain a character CHAR. + + - procedure: substring? PATTERN STRING + Searches STRING to see if some substring of STRING is equal to + PATTERN. `substring?' returns the index of the first character of + the first substring of STRING that is equal to PATTERN; or `#f' if + STRING does not contain PATTERN. + + (substring? "rat" "pirate") => 2 + (substring? "rat" "outrage") => #f + (substring? "" any-string) => 0 + + - Procedure: find-string-from-port? STR IN-PORT MAX-NO-CHARS + - Procedure: find-string-from-port? STR IN-PORT + Looks for a string STR within the first MAX-NO-CHARS chars of the + input port IN-PORT. MAX-NO-CHARS may be omitted: in that case, + the search span is limited by the end of the input stream. When + the STR is found, the function returns the number of characters it + has read from the port, and the port is set to read the first char + after that (that is, after the STR) The function returns `#f' when + the STR isn't found. + + `find-string-from-port?' reads the port *strictly* sequentially, + and does not perform any buffering. So `find-string-from-port?' + can be used even if the IN-PORT is open to a pipe or other + communication channel. + + +File: slib.info, Node: Tektronix Graphics Support, Next: Tree Operations, Prev: String Search, Up: Procedures + +Tektronix Graphics Support +========================== + + *Note:* The Tektronix graphics support files need more work, and are +not complete. + +Tektronix 4000 Series Graphics +------------------------------ + + The Tektronix 4000 series graphics protocol gives the user a 1024 by +1024 square drawing area. The origin is in the lower left corner of the +screen. Increasing y is up and increasing x is to the right. + + The graphics control codes are sent over the current-output-port and +can be mixed with regular text and ANSI or other terminal control +sequences. + + - Procedure: tek40:init + + - Procedure: tek40:graphics + + - Procedure: tek40:text + + - Procedure: tek40:linetype LINETYPE + + - Procedure: tek40:move X Y + + - Procedure: tek40:draw X Y + + - Procedure: tek40:put-text X Y STR + + - Procedure: tek40:reset + +Tektronix 4100 Series Graphics +------------------------------ + + The graphics control codes are sent over the current-output-port and +can be mixed with regular text and ANSI or other terminal control +sequences. + + - Procedure: tek41:init + + - Procedure: tek41:reset + + - Procedure: tek41:graphics + + - Procedure: tek41:move X Y + + - Procedure: tek41:draw X Y + + - Procedure: tek41:point X Y NUMBER + + - Procedure: tek41:encode-x-y X Y + + - Procedure: tek41:encode-int NUMBER + + +File: slib.info, Node: Tree Operations, Prev: Tektronix Graphics Support, Up: Procedures + +Tree operations +=============== + + `(require 'tree)' + + These are operations that treat lists a representations of trees. + + - Function: subst NEW OLD TREE + - Function: substq NEW OLD TREE + - Function: substv NEW OLD TREE + `subst' makes a copy of TREE, substituting NEW for every subtree + or leaf of TREE which is `equal?' to OLD and returns a modified + tree. The original TREE is unchanged, but may share parts with + the result. + + `substq' and `substv' are similar, but test against OLD using + `eq?' and `eqv?' respectively. + + Examples: + (substq 'tempest 'hurricane '(shakespeare wrote (the hurricane))) + => (shakespeare wrote (the tempest)) + (substq 'foo '() '(shakespeare wrote (twelfth night))) + => (shakespeare wrote (twelfth night . foo) . foo) + (subst '(a . cons) '(old . pair) + '((old . spice) ((old . shoes) old . pair) (old . pair))) + => ((old . spice) ((old . shoes) a . cons) (a . cons)) + + - Function: copy-tree TREE + Makes a copy of the nested list structure TREE using new pairs and + returns it. All levels are copied, so that none of the pairs in + the tree are `eq?' to the original ones - only the leaves are. + + Example: + (define bar '(bar)) + (copy-tree (list bar 'foo)) + => ((bar) foo) + (eq? bar (car (copy-tree (list bar 'foo)))) + => #f + + +File: slib.info, Node: Standards Support, Next: Session Support, Prev: Procedures, Up: Top + +Standards Support +***************** + +* Menu: + +* With-File:: 'with-file +* Transcripts:: 'transcript +* Rev2 Procedures:: 'rev2-procedures +* Rev4 Optional Procedures:: 'rev4-optional-procedures +* Multi-argument / and -:: 'multiarg/and- +* Multi-argument Apply:: 'multiarg-apply +* Rationalize:: 'rationalize +* Promises:: 'promise +* Dynamic-Wind:: 'dynamic-wind +* Values:: 'values +* Time:: 'time +* CLTime:: 'common-lisp-time + + +File: slib.info, Node: With-File, Next: Transcripts, Prev: Standards Support, Up: Standards Support + +With-File +========= + + `(require 'with-file)' + + - Function: with-input-from-file FILE THUNK + - Function: with-output-to-file FILE THUNK + Description found in R4RS. + + +File: slib.info, Node: Transcripts, Next: Rev2 Procedures, Prev: With-File, Up: Standards Support + +Transcripts +=========== + + `(require 'transcript)' + + - Function: transcript-on FILENAME + - Function: transcript-off FILENAME + Redefines `read-char', `read', `write-char', `write', `display', + and `newline'. + + +File: slib.info, Node: Rev2 Procedures, Next: Rev4 Optional Procedures, Prev: Transcripts, Up: Standards Support + +Rev2 Procedures +=============== + + `(require 'rev2-procedures)' + + The procedures below were specified in the `Revised^2 Report on +Scheme'. *N.B.*: The symbols `1+' and `-1+' are not `R4RS' syntax. +Scheme->C, for instance, barfs on this module. + + - Procedure: substring-move-left! STRING1 START1 END1 STRING2 START2 + - Procedure: substring-move-right! STRING1 START1 END1 STRING2 START2 + STRING1 and STRING2 must be a strings, and START1, START2 and END1 + must be exact integers satisfying + + 0 <= START1 <= END1 <= (string-length STRING1) + 0 <= START2 <= END1 - START1 + START2 <= (string-length STRING2) + + `substring-move-left!' and `substring-move-right!' store + characters of STRING1 beginning with index START1 (inclusive) and + ending with index END1 (exclusive) into STRING2 beginning with + index START2 (inclusive). + + `substring-move-left!' stores characters in time order of + increasing indices. `substring-move-right!' stores characters in + time order of increasing indeces. + + - Procedure: substring-fill! STRING START END CHAR + Fills the elements START-END of STRING with the character CHAR. + + - Function: string-null? STR + == `(= 0 (string-length STR))' + + - Procedure: append! . PAIRS + Destructively appends its arguments. Equivalent to `nconc'. + + - Function: 1+ N + Adds 1 to N. + + - Function: -1+ N + Subtracts 1 from N. + + - Function: <? + - Function: <=? + - Function: =? + - Function: >? + - Function: >=? + These are equivalent to the procedures of the same name but + without the trailing `?'. + + +File: slib.info, Node: Rev4 Optional Procedures, Next: Multi-argument / and -, Prev: Rev2 Procedures, Up: Standards Support + +Rev4 Optional Procedures +======================== + + `(require 'rev4-optional-procedures)' + + For the specification of these optional procedures, *Note Standard +procedures: (r4rs)Standard procedures. + + - Function: list-tail L P + + - Function: string->list S + + - Function: list->string L + + - Function: string-copy + + - Procedure: string-fill! S OBJ + + - Function: list->vector L + + - Function: vector->list S + + - Procedure: vector-fill! S OBJ + + +File: slib.info, Node: Multi-argument / and -, Next: Multi-argument Apply, Prev: Rev4 Optional Procedures, Up: Standards Support + +Multi-argument / and - +====================== + + `(require 'mutliarg/and-)' + + For the specification of these optional forms, *Note Numerical +operations: (r4rs)Numerical operations. The `two-arg:'* forms are only +defined if the implementation does not support the many-argument forms. + + - Function: two-arg:/ N1 N2 + The original two-argument version of `/'. + + - Function: / DIVIDENT . DIVISORS + + - Function: two-arg:- N1 N2 + The original two-argument version of `-'. + + - Function: - MINUEND . SUBTRAHENDS + + +File: slib.info, Node: Multi-argument Apply, Next: Rationalize, Prev: Multi-argument / and -, Up: Standards Support + +Multi-argument Apply +==================== + + `(require 'multiarg-apply)' + +For the specification of this optional form, *Note Control features: +(r4rs)Control features. + + - Function: two-arg:apply PROC L + The implementation's native `apply'. Only defined for + implementations which don't support the many-argument version. + + - Function: apply PROC . ARGS + + +File: slib.info, Node: Rationalize, Next: Promises, Prev: Multi-argument Apply, Up: Standards Support + +Rationalize +=========== + + `(require 'rationalize)' + + The procedure rationalize is interesting because most programming +languages do not provide anything analogous to it. For simplicity, we +present an algorithm which computes the correct result for exact +arguments (provided the implementation supports exact rational numbers +of unlimited precision), and produces a reasonable answer for inexact +arguments when inexact arithmetic is implemented using floating-point. +We thank Alan Bawden for contributing this algorithm. + + - Function: rationalize X E + + +File: slib.info, Node: Promises, Next: Dynamic-Wind, Prev: Rationalize, Up: Standards Support + +Promises +======== + + `(require 'promise)' + + - Function: make-promise PROC + + Change occurrences of `(delay EXPRESSION)' to `(make-promise (lambda +() EXPRESSION))' and `(define force promise:force)' to implement +promises if your implementation doesn't support them (*note Control +features: (r4rs)Control features.). + + +File: slib.info, Node: Dynamic-Wind, Next: Values, Prev: Promises, Up: Standards Support + +Dynamic-Wind +============ + + `(require 'dynamic-wind)' + + This facility is a generalization of Common LISP `unwind-protect', +designed to take into account the fact that continuations produced by +`call-with-current-continuation' may be reentered. + + - Procedure: dynamic-wind THUNK1 THUNK2 THUNK3 + The arguments THUNK1, THUNK2, and THUNK3 must all be procedures of + no arguments (thunks). + + `dynamic-wind' calls THUNK1, THUNK2, and then THUNK3. The value + returned by THUNK2 is returned as the result of `dynamic-wind'. + THUNK3 is also called just before control leaves the dynamic + context of THUNK2 by calling a continuation created outside that + context. Furthermore, THUNK1 is called before reentering the + dynamic context of THUNK2 by calling a continuation created inside + that context. (Control is inside the context of THUNK2 if THUNK2 + is on the current return stack). + + *Warning:* There is no provision for dealing with errors or + interrupts. If an error or interrupt occurs while using + `dynamic-wind', the dynamic environment will be that in effect at + the time of the error or interrupt. + + +File: slib.info, Node: Values, Next: Time, Prev: Dynamic-Wind, Up: Standards Support + +Values +====== + + `(require 'values)' + + - Function: values OBJ ... + `values' takes any number of arguments, and passes (returns) them + to its continuation. + + - Function: call-with-values THUNK PROC + THUNK must be a procedure of no arguments, and PROC must be a + procedure. `call-with-values' calls THUNK with a continuation + that, when passed some values, calls PROC with those values as + arguments. + + Except for continuations created by the `call-with-values' + procedure, all continuations take exactly one value, as now; the + effect of passing no value or more than one value to continuations + that were not created by the `call-with-values' procedure is + unspecified. + + +File: slib.info, Node: Time, Next: CLTime, Prev: Values, Up: Standards Support + +Time +==== + + The procedures `current-time', `difftime', and `offset-time' are +supported by all implementations (SLIB provides them if feature +`('current-time)' is missing. `current-time' returns a "calendar time" +(caltime) which can be a number or other type. + + - Function: current-time + Returns the time since 00:00:00 GMT, January 1, 1970, measured in + seconds. Note that the reference time is different from the + reference time for `get-universal-time' in *Note CLTime::. On + implementations which cannot support actual times, `current-time' + will increment a counter and return its value when called. + + - Function: difftime CALTIME1 CALTIME0 + Returns the difference (number of seconds) between twe calendar + times: CALTIME1 - CALTIME0. CALTIME0 can also be a number. + + - Function: offset-time CALTIME OFFSET + Returns the calendar time of CALTIME offset by OFFSET number of + seconds `(+ caltime offset)'. + + (require 'posix-time) + + These procedures are intended to be compatible with Posix time +conversion functions. + + - Variable: *timezone* + contains the difference, in seconds, between UTC and local + standard time (for example, in the U.S. Eastern time zone (EST), + timezone is 5*60*60). `*timezone*' is initialized by `tzset'. + + - Function: tzset + initializes the *TIMEZONE* variable from the TZ environment + variable. This function is automatically called by the other time + conversion functions that depend on the time zone. + + - Function: gmtime CALTIME + converts the calendar time CALTIME to a vector of integers + representing the time expressed as Coordinated Universal Time + (UTC). + + - Function: localtime CALTIME + converts the calendar time CALTIME to a vector of integers + expressed relative to the user's time zone. `localtime' sets the + variable *TIMEZONE* with the difference between Coordinated + Universal Time (UTC) and local standard time in seconds by calling + `tzset'. The elements of the returned vector are as follows: + + 0. seconds (0 - 61) + + 1. minutes (0 - 59) + + 2. hours since midnight + + 3. day of month + + 4. month (0 - 11). Note difference from + `decode-universal-time'. + + 5. year (A.D.) + + 6. day of week (0 - 6) + + 7. day of year (0 - 365) + + 8. 1 for daylight savings, 0 for regular time + + - Function: mktime UNIVTIME + Converts a vector of integers in Coordinated Universal Time (UTC) + format to calendar time (caltime) format. + + - Function: asctime UNIVTIME + Converts the vector of integers CALTIME in Coordinated Universal + Time (UTC) format into a string of the form `"Wed Jun 30 21:49:08 + 1993"'. + + - Function: ctime CALTIME + Equivalent to `(time:asctime (time:localtime CALTIME))'. + + +File: slib.info, Node: CLTime, Prev: Time, Up: Standards Support + +CLTime +====== + + - Function: get-decoded-time + Equivalent to `(decode-universal-time (get-universal-time))'. + + - Function: get-universal-time + Returns the current time as "Universal Time", number of seconds + since 00:00:00 Jan 1, 1900 GMT. Note that the reference time is + different from `current-time'. + + - Function: decode-universal-time UNIVTIME + Converts UNIVTIME to "Decoded Time" format. Nine values are + returned: + 0. seconds (0 - 61) + + 1. minutes (0 - 59) + + 2. hours since midnight + + 3. day of month + + 4. month (1 - 12). Note difference from `gmtime' and + `localtime'. + + 5. year (A.D.) + + 6. day of week (0 - 6) + + 7. #t for daylight savings, #f otherwise + + 8. hours west of GMT (-24 - +24) + + Notice that the values returned by `decode-universal-time' do not + match the arguments to `encode-universal-time'. + + - Function: encode-universal-time SECOND MINUTE HOUR DATE MONTH YEAR + - Function: encode-universal-time SECOND MINUTE HOUR DATE MONTH YEAR + TIME-ZONE + Converts the arguments in Decoded Time format to Universal Time + format. If TIME-ZONE is not specified, the returned time is + adjusted for daylight saving time. Otherwise, no adjustment is + performed. + + Notice that the values returned by `decode-universal-time' do not + match the arguments to `encode-universal-time'. + + +File: slib.info, Node: Session Support, Next: Optional SLIB Packages, Prev: Standards Support, Up: Top + +Session Support +*************** + +* Menu: + +* Repl:: Macros at top-level +* Quick Print:: Loop-safe Output +* Debug:: To err is human ... +* Breakpoints:: Pause execution +* Trace:: 'trace +* Getopt:: Command Line option parsing +* Command Line:: A command line reader for Scheme shells +* System Interface:: 'system and 'getenv + +Certain features are so simple, system-dependent, or widely subcribed +that they are supported by all implementations as part of the +`*.init' files. + +The features described in the following sections are provided by all +implementations. + +* Require:: Module Management +* Vicinity:: Pathname Management +* Configuration:: Characteristics of Scheme Implementation +* Input/Output:: Things not provided by the Scheme specs. +* Legacy:: +* System:: LOADing, EVALing, ERRORing, and EXITing + + +File: slib.info, Node: Repl, Next: Quick Print, Prev: Session Support, Up: Session Support + +Repl +==== + + `(require 'repl)' + + Here is a read-eval-print-loop which, given an eval, evaluates forms. + + - Procedure: repl:top-level REPL:EVAL + `read's, `repl:eval's and `write's expressions from + `(current-input-port)' to `(current-output-port)' until an + end-of-file is encountered. `load', `slib:eval', `slib:error', + and `repl:quit' dynamically bound during `repl:top-level'. + + - Procedure: repl:quit + Exits from the invocation of `repl:top-level'. + + The `repl:' procedures establish, as much as is possible to do +portably, a top level environment supporting macros. `repl:top-level' +uses `dynamic-wind' to catch error conditions and interrupts. If your +implementation supports this you are all set. + + Otherwise, if there is some way your implementation can catch error +conditions and interrupts, then have them call `slib:error'. It will +display its arguments and reenter `repl:top-level'. `slib:error' +dynamically bound by `repl:top-level'. + + To have your top level loop always use macros, add any interrupt +catching lines and the following lines to your Scheme init file: + (require 'macro) + (require 'repl) + (repl:top-level macro:eval) + + +File: slib.info, Node: Quick Print, Next: Debug, Prev: Repl, Up: Session Support + +Quick Print +=========== + + `(require 'qp)' + +When displaying error messages and warnings, it is paramount that the +output generated for circular lists and large data structures be +limited. This section supplies a procedure to do this. It could be +much improved. + + Notice that the neccessity for truncating output eliminates + Common-Lisp's *Note Format:: from consideration; even when + variables `*print-level*' and `*print-level*' are set, huge + strings and bit-vectors are *not* limited. + + - Procedure: qp ARG1 ... + - Procedure: qpn ARG1 ... + - Procedure: qpr ARG1 ... + `qp' writes its arguments, separated by spaces, to + `(current-output-port)'. `qp' compresses printing by substituting + `...' for substructure it does not have sufficient room to print. + `qpn' is like `qp' but outputs a newline before returning. `qpr' + is like `qpn' except that it returns its last argument. + + - Variable: *qp-width* + `*qp-width*' is the largest number of characters that `qp' should + use. + + +File: slib.info, Node: Debug, Next: Breakpoints, Prev: Quick Print, Up: Session Support + +Debug +===== + + `(require 'debug)' + +Requiring `debug' automatically requires `trace' and `break'. + +An application with its own datatypes may want to substitute its own +printer for `qp'. This example shows how to do this: + + (define qpn (lambda args) ...) + (provide 'qp) + (require 'debug) + + - Procedure: trace-all FILE + Traces (*note Trace::.) all procedures `define'd at top-level in + file `file'. + + - Procedure: break-all FILE + Breakpoints (*note Breakpoints::.) all procedures `define'd at + top-level in file `file'. + + +File: slib.info, Node: Breakpoints, Next: Trace, Prev: Debug, Up: Session Support + +Breakpoints +=========== + + `(require 'break)' + + - Function: init-debug + If your Scheme implementation does not support `break' or `abort', + a message will appear when you `(require 'break)' or `(require + 'debug)' telling you to type `(init-debug)'. This is in order to + establish a top-level continuation. Typing `(init-debug)' at top + level sets up a continuation for `break'. + + - Function: breakpoint ARG1 ... + Returns from the top level continuation and pushes the + continuation from which it was called on a continuation stack. + + - Function: continue + Pops the topmost continuation off of the continuation stack and + returns an unspecified value to it. + + - Function: continue ARG1 ... + Pops the topmost continuation off of the continuation stack and + returns ARG1 ... to it. + + - Macro: break PROC1 ... + Redefines the top-level named procedures given as arguments so that + `breakpoint' is called before calling PROC1 .... + + - Macro: break + With no arguments, makes sure that all the currently broken + identifiers are broken (even if those identifiers have been + redefined) and returns a list of the broken identifiers. + + - Macro: unbreak PROC1 ... + Turns breakpoints off for its arguments. + + - Macro: unbreak + With no arguments, unbreaks all currently broken identifiers and + returns a list of these formerly broken identifiers. + + The following routines are the procedures which actually do the +tracing when this module is supplied by SLIB, rather than natively. If +defmacros are not natively supported by your implementation, these might +be more convenient to use. + + - Function: breakf PROC + - Function: breakf PROC NAME + - Function: debug:breakf PROC + - Function: debug:breakf PROC NAME + To break, type + (set! SYMBOL (breakf SYMBOL)) + + or + (set! SYMBOL (breakf SYMBOL 'SYMBOL)) + + or + (define SYMBOL (breakf FUNCTION)) + + or + (define SYMBOL (breakf FUNCTION 'SYMBOL)) + + - Function: unbreakf PROC + - Function: debug:unbreakf PROC + To unbreak, type + (set! SYMBOL (unbreakf SYMBOL)) + + +File: slib.info, Node: Trace, Next: Getopt, Prev: Breakpoints, Up: Session Support + +Tracing +======= + + `(require 'trace)' + + - Macro: trace PROC1 ... + Traces the top-level named procedures given as arguments. + + - Macro: trace + With no arguments, makes sure that all the currently traced + identifiers are traced (even if those identifiers have been + redefined) and returns a list of the traced identifiers. + + - Macro: untrace PROC1 ... + Turns tracing off for its arguments. + + - Macro: untrace + With no arguments, untraces all currently traced identifiers and + returns a list of these formerly traced identifiers. + + The following routines are the procedures which actually do the +tracing when this module is supplied by SLIB, rather than natively. If +defmacros are not natively supported by your implementation, these might +be more convenient to use. + + - Function: tracef PROC + - Function: tracef PROC NAME + - Function: debug:tracef PROC + - Function: debug:tracef PROC NAME + To trace, type + (set! SYMBOL (tracef SYMBOL)) + + or + (set! SYMBOL (tracef SYMBOL 'SYMBOL)) + + or + (define SYMBOL (tracef FUNCTION)) + + or + (define SYMBOL (tracef FUNCTION 'SYMBOL)) + + - Function: untracef PROC + - Function: debug:untracef PROC + To untrace, type + (set! SYMBOL (untracef SYMBOL)) + diff --git a/slib.info-7 b/slib.info-7 new file mode 100644 index 0000000..2ed9fcd --- /dev/null +++ b/slib.info-7 @@ -0,0 +1,615 @@ +This is Info file slib.info, produced by Makeinfo-1.64 from the input +file slib.texi. + + This file documents SLIB, the portable Scheme library. + + Copyright (C) 1993 Todd R. Eigenschink Copyright (C) 1993, 1994, 1995 +Aubrey Jaffer + + Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + + Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + + Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be stated in a +translation approved by the author. + + +File: slib.info, Node: Getopt, Next: Command Line, Prev: Trace, Up: Session Support + +Getopt +====== + + `(require 'getopt)' + + This routine implements Posix command line argument parsing. Notice +that returning values through global variables means that `getopt' is +*not* reentrant. + + - Variable: *optind* + Is the index of the current element of the command line. It is + initially one. In order to parse a new command line or reparse an + old one, *OPTING* must be reset. + + - Variable: *optarg* + Is set by getopt to the (string) option-argument of the current + option. + + - Procedure: getopt ARGC ARGV OPTSTRING + Returns the next option letter in ARGV (starting from `(vector-ref + argv *optind*)') that matches a letter in OPTSTRING. ARGV is a + vector or list of strings, the 0th of which getopt usually + ignores. ARGC is the argument count, usually the length of ARGV. + OPTSTRING is a string of recognized option characters; if a + character is followed by a colon, the option takes an argument + which may be immediately following it in the string or in the next + element of ARGV. + + *OPTIND* is the index of the next element of the ARGV vector to be + processed. It is initialized to 1 by `getopt.scm', and `getopt' + updates it when it finishes with each element of ARGV. + + `getopt' returns the next option character from ARGV that matches + a character in OPTSTRING, if there is one that matches. If the + option takes an argument, `getopt' sets the variable *OPTARG* to + the option-argument as follows: + + * If the option was the last character in the string pointed to + by an element of ARGV, then *OPTARG* contains the next + element of ARGV, and *OPTIND* is incremented by 2. If the + resulting value of *OPTIND* is greater than or equal to ARGC, + this indicates a missing option argument, and `getopt' + returns an error indication. + + * Otherwise, *OPTARG* is set to the string following the option + character in that element of ARGV, and *OPTIND* is + incremented by 1. + + If, when `getopt' is called, the string `(vector-ref argv + *optind*)' either does not begin with the character `#\-' or is + just `"-"', `getopt' returns `#f' without changing *OPTIND*. If + `(vector-ref argv *optind*)' is the string `"--"', `getopt' + returns `#f' after incrementing *OPTIND*. + + If `getopt' encounters an option character that is not contained in + OPTSTRING, it returns the question-mark `#\?' character. If it + detects a missing option argument, it returns the colon character + `#\:' if the first character of OPTSTRING was a colon, or a + question-mark character otherwise. In either case, `getopt' sets + the variable GETOPT:OPT to the option character that caused the + error. + + The special option `"--"' can be used to delimit the end of the + options; `#f' is returned, and `"--"' is skipped. + + RETURN VALUE + + `getopt' returns the next option character specified on the command + line. A colon `#\:' is returned if `getopt' detects a missing + argument and the first character of OPTSTRING was a colon `#\:'. + + A question-mark `#\?' is returned if `getopt' encounters an option + character not in OPTSTRING or detects a missing argument and the + first character of OPTSTRING was not a colon `#\:'. + + Otherwise, `getopt' returns `#f' when all command line options + have been parsed. + + Example: + #! /usr/local/bin/scm + ;;;This code is SCM specific. + (define argv (program-arguments)) + (require 'getopt) + + (define opts ":a:b:cd") + (let loop ((opt (getopt (length argv) argv opts))) + (case opt + ((#\a) (print "option a: " *optarg*)) + ((#\b) (print "option b: " *optarg*)) + ((#\c) (print "option c")) + ((#\d) (print "option d")) + ((#\?) (print "error" getopt:opt)) + ((#\:) (print "missing arg" getopt:opt)) + ((#f) (if (< *optind* (length argv)) + (print "argv[" *optind* "]=" + (list-ref argv *optind*))) + (set! *optind* (+ *optind* 1)))) + (if (< *optind* (length argv)) + (loop (getopt (length argv) argv opts)))) + + (slib:exit) + +Getopt- +======= + + - Function: getopt- ARGC ARGV OPTSTRING + The procedure `getopt--' is an extended version of `getopt' which + parses "long option names" of the form `--hold-the-onions' and + `--verbosity-level=extreme'. `Getopt--' behaves as `getopt' + except for non-empty options beginning with `--'. + + Options beginning with `--' are returned as strings rather than + characters. If a value is assigned (using `=') to a long option, + `*optarg*' is set to the value. The `=' and value are not + returned as part of the option string. + + No information is passed to `getopt--' concerning which long + options should be accepted or whether such options can take + arguments. If a long option did not have an argument, `*optarg' + will be set to `#f'. The caller is responsible for detecting and + reporting errors. + + (define opts ":-:b:") + (define argc 5) + (define argv '("foo" "-b9" "--f1" "--2=" "--g3=35234.342" "--")) + (define *optind* 1) + (define *optarg* #f) + (require 'qp) + (do ((i 5 (+ -1 i))) + ((zero? i)) + (define opt (getopt-- argc argv opts)) + (print *optind* opt *optarg*))) + -| + 2 #\b "9" + 3 "f1" #f + 4 "2" "" + 5 "g3" "35234.342" + 5 #f "35234.342" + + +File: slib.info, Node: Command Line, Next: System Interface, Prev: Getopt, Up: Session Support + +Command Line +============ + + `(require 'read-command)' + + - Function: read-command PORT + - Function: read-command + `read-command' converts a "command line" into a list of strings + suitable for parsing by `getopt'. The syntax of command lines + supported resembles that of popular "shell"s. `read-command' + updates PORT to point to the first character past the command + delimiter. + + If an end of file is encountered in the input before any + characters are found that can begin an object or comment, then an + end of file object is returned. + + The PORT argument may be omitted, in which case it defaults to the + value returned by `current-input-port'. + + The fields into which the command line is split are delimited by + whitespace as defined by `char-whitespace?'. The end of a command + is delimited by end-of-file or unescaped semicolon (;) or newline. + Any character can be literally included in a field by escaping it + with a backslach (\). + + The initial character and types of fields recognized are: + `\' + The next character has is taken literally and not interpreted + as a field delimiter. If \ is the last character before a + newline, that newline is just ignored. Processing continues + from the characters after the newline as though the backslash + and newline were not there. + + `"' + The characters up to the next unescaped " are taken literally, + according to [R4RS] rules for literal strings (*note Strings: + (r4rs)Strings.). + + `(', `%'' + One scheme expression is `read' starting with this character. + The `read' expression is evaluated, converted to a string + (using `display'), and replaces the expression in the returned + field. + + `;' + Semicolon delimits a command. Using semicolons more than one + command can appear on a line. Escaped semicolons and + semicolons inside strings do not delimit commands. + + The comment field differs from the previous fields in that it must + be the first character of a command or appear after whitespace in + order to be recognized. # can be part of fields if these + conditions are not met. For instance, `ab#c' is just the field + ab#c. + + `#' + Introduces a comment. The comment continues to the end of + the line on which the semicolon appears. Comments are + treated as whitespace by `read-dommand-line' and backslashes + before newlines in comments are also ignored. + + +File: slib.info, Node: System Interface, Next: Require, Prev: Command Line, Up: Session Support + +System Interface +================ + + If `(provided? 'getenv)': + + - Function: getenv NAME + Looks up NAME, a string, in the program environment. If NAME is + found a string of its value is returned. Otherwise, `#f' is + returned. + + If `(provided? 'system)': + + - Function: system COMMAND-STRING + Executes the COMMAND-STRING on the computer and returns the + integer status code. + + +File: slib.info, Node: Require, Next: Vicinity, Prev: System Interface, Up: Session Support + +Require +======= + + These variables and procedures are provided by all implementations. + + - Variable: *features* + Is a list of symbols denoting features supported in this + implementation. + + - Variable: *modules* + Is a list of pathnames denoting files which have been loaded. + + - Variable: *catalog* + Is an association list of features (symbols) and pathnames which + will supply those features. The pathname can be either a string + or a pair. If pathname is a pair then the first element should be + a macro feature symbol, `source', or `compiled'. The cdr of the + pathname should be either a string or a list. + + In the following three functions if FEATURE is not a symbol it is +assumed to be a pathname. + + - Function: provided? FEATURE + Returns `#t' if FEATURE is a member of `*features*' or `*modules*' + or if FEATURE is supported by a file already loaded and `#f' + otherwise. + + - Procedure: require FEATURE + If `(not (provided? FEATURE))' it is loaded if FEATURE is a + pathname or if `(assq FEATURE *catalog*)'. Otherwise an error is + signaled. + + - Procedure: provide FEATURE + Assures that FEATURE is contained in `*features*' if FEATURE is a + symbol and `*modules*' otherwise. + + - Function: require:feature->path FEATURE + Returns `#t' if FEATURE is a member of `*features*' or `*modules*' + or if FEATURE is supported by a file already loaded. Returns a + path if one was found in `*catalog*' under the feature name, and + `#f' otherwise. The path can either be a string suitable as an + argument to load or a pair as described above for *catalog*. + + Below is a list of features that are automatically determined by +`require'. For each item, `(provided? 'FEATURE)' will return `#t' if +that feature is available, and `#f' if not. + + * 'inexact + + * 'rational + + * 'real + + * 'complex + + * 'bignum + + +File: slib.info, Node: Vicinity, Next: Configuration, Prev: Require, Up: Session Support + +Vicinity +======== + + A vicinity is a descriptor for a place in the file system. Vicinities +hide from the programmer the concepts of host, volume, directory, and +version. Vicinities express only the concept of a file environment +where a file name can be resolved to a file in a system independent +manner. Vicinities can even be used on "flat" file systems (which have +no directory structure) by having the vicinity express constraints on +the file name. On most systems a vicinity would be a string. All of +these procedures are file system dependent. + + These procedures are provided by all implementations. + + - Function: make-vicinity FILENAME + Returns the vicinity of FILENAME for use by `in-vicinity'. + + - Function: program-vicinity + Returns the vicinity of the currently loading Scheme code. For an + interpreter this would be the directory containing source code. + For a compiled system (with multiple files) this would be the + directory where the object or executable files are. If no file is + currently loading it the result is undefined. *Warning:* + `program-vicinity' can return incorrectl values if your program + escapes back into a `load'. + + - Function: library-vicinity + Returns the vicinity of the shared Scheme library. + + - Function: implementation-vicinity + Returns the vicinity of the underlying Scheme implementation. This + vicinity will likely contain startup code and messages and a + compiler. + + - Function: user-vicinity + Returns the vicinity of the current directory of the user. On most + systems this is `""' (the empty string). + + - Function: in-vicinity VICINITY FILENAME + Returns a filename suitable for use by `slib:load', + `slib:load-source', `slib:load-compiled', `open-input-file', + `open-output-file', etc. The returned filename is FILENAME in + VICINITY. `in-vicinity' should allow FILENAME to override + VICINITY when FILENAME is an absolute pathname and VICINITY is + equal to the value of `(user-vicinity)'. The behavior of + `in-vicinity' when FILENAME is absolute and VICINITY is not equal + to the value of `(user-vicinity)' is unspecified. For most systems + `in-vicinity' can be `string-append'. + + - Function: sub-vicinity VICINITY NAME + Returns the vicinity of VICINITY restricted to NAME. This is used + for large systems where names of files in subsystems could + conflict. On systems with directory structure `sub-vicinity' will + return a pathname of the subdirectory NAME of VICINITY. + + +File: slib.info, Node: Configuration, Next: Input/Output, Prev: Vicinity, Up: Session Support + +Configuration +============= + + These constants and procedures describe characteristics of the Scheme +and underlying operating system. They are provided by all +implementations. + + - Constant: char-code-limit + An integer 1 larger that the largest value which can be returned by + `char->integer'. + + - Constant: most-positive-fixnum + The immediate integer closest to positive infinity. + + - Constant: slib:tab + The tab character. + + - Constant: slib:form-feed + The form-feed character. + + - Function: software-type + Returns a symbol denoting the generic operating system type. For + instance, `unix', `vms', `macos', `amiga', or `ms-dos'. + + - Function: slib:report-version + Displays the versions of SLIB and the underlying Scheme + implementation and the name of the operating system. An + unspecified value is returned. + + (slib:report-version) => slib "2a3" on scm "4e1" on unix + + - Function: slib:report + Displays the information of `(slib:report-version)' followed by + almost all the information neccessary for submitting a problem + report. An unspecified value is returned. + + - Function: slib:report #T + provides a more verbose listing. + + - Function: slib:report FILENAME + Writes the report to file `filename'. + + (slib:report) + => + slib "2a3" on scm "4e1" on unix + (implementation-vicinity) is "/usr/local/src/scm/" + (library-vicinity) is "/usr/local/lib/slib/" + (scheme-file-suffix) is ".scm" + implementation *features* : + bignum complex real rational + inexact vicinity ed getenv + tmpnam system abort transcript + with-file ieee-p1178 rev4-report rev4-optional-procedures + hash object-hash delay eval + dynamic-wind multiarg-apply multiarg/and- logical + defmacro string-port source array-for-each + array full-continuation char-ready? line-i/o + i/o-extensions pipe + implementation *catalog* : + (rev4-optional-procedures . "/usr/local/lib/slib/sc4opt") + ... + + +File: slib.info, Node: Input/Output, Next: Legacy, Prev: Configuration, Up: Session Support + +Input/Output +============ + + These procedures are provided by all implementations. + + - Procedure: file-exists? FILENAME + Returns `#t' if the specified file exists. Otherwise, returns + `#f'. If the underlying implementation does not support this + feature then `#f' is always returned. + + - Procedure: delete-file FILENAME + Deletes the file specified by FILENAME. If FILENAME can not be + deleted, `#f' is returned. Otherwise, `#t' is returned. + + - Procedure: tmpnam + Returns a pathname for a file which will likely not be used by any + other process. Successive calls to `(tmpnam)' will return + different pathnames. + + - Procedure: current-error-port + Returns the current port to which diagnostic and error output is + directed. + + - Procedure: force-output + - Procedure: force-output PORT + Forces any pending output on PORT to be delivered to the output + device and returns an unspecified value. The PORT argument may be + omitted, in which case it defaults to the value returned by + `(current-output-port)'. + + - Procedure: output-port-width + - Procedure: output-port-width PORT + Returns the width of PORT, which defaults to + `(current-output-port)' if absent. If the width cannot be + determined 79 is returned. + + - Procedure: output-port-height + - Procedure: output-port-height PORT + Returns the height of PORT, which defaults to + `(current-output-port)' if absent. If the height cannot be + determined 24 is returned. + + +File: slib.info, Node: Legacy, Next: System, Prev: Input/Output, Up: Session Support + +Legacy +====== + + - Function: identity X + IDENTITY returns its argument. + + Example: + (identity 3) + => 3 + (identity '(foo bar)) + => (foo bar) + (map identity LST) + == (copy-list LST) + + These were present in Scheme until R4RS (*note Language changes: +(r4rs)Notes.). + + - Constant: t + Derfined as `#t'. + + - Constant: nil + Defined as `#f'. + + - Function: last-pair L + Returns the last pair in the list L. Example: + (last-pair (cons 1 2)) + => (1 . 2) + (last-pair '(1 2)) + => (2) + == (cons 2 '()) + + +File: slib.info, Node: System, Prev: Legacy, Up: Session Support + +System +====== + + These procedures are provided by all implementations. + + - Procedure: slib:load-source NAME + Loads a file of Scheme source code from NAME with the default + filename extension used in SLIB. For instance if the filename + extension used in SLIB is `.scm' then `(slib:load-source "foo")' + will load from file `foo.scm'. + + - Procedure: slib:load-compiled NAME + On implementations which support separtely loadable compiled + modules, loads a file of compiled code from NAME with the + implementation's filename extension for compiled code appended. + + - Procedure: slib:load NAME + Loads a file of Scheme source or compiled code from NAME with the + appropriate suffixes appended. If both source and compiled code + are present with the appropriate names then the implementation + will load just one. It is up to the implementation to choose + which one will be loaded. + + If an implementation does not support compiled code then + `slib:load' will be identical to `slib:load-source'. + + - Procedure: slib:eval OBJ + `eval' returns the value of OBJ evaluated in the current top level + environment. + + - Procedure: slib:eval-load FILENAME EVAL + FILENAME should be a string. If filename names an existing file, + the Scheme source code expressions and definitions are read from + the file and EVAL called with them sequentially. The + `slib:eval-load' procedure does not affect the values returned by + `current-input-port' and `current-output-port'. + + - Procedure: slib:error ARG1 ARG2 ... + Outputs an error message containing the arguments, aborts + evaluation of the current form and responds in a system dependent + way to the error. Typical responses are to abort the program or + to enter a read-eval-print loop. + + - Procedure: slib:exit N + - Procedure: slib:exit + Exits from the Scheme session returning status N to the system. + If N is omitted or `#t', a success status is returned to the + system (if possible). If N is `#f' a failure is returned to the + system (if possible). If N is an integer, then N is returned to + the system (if possible). If the Scheme session cannot exit an + unspecified value is returned from `slib:exit'. + + +File: slib.info, Node: Optional SLIB Packages, Next: Procedure and Macro Index, Prev: Session Support, Up: Top + +Optional SLIB Packages +********************** + + Several Scheme packages have been written using SLIB. There are +several reasons why a package might not be included in the SLIB +distribution: + * Because it requires special hardware or software which is not + universal. + + * Because it is large and of limited interest to most Scheme users. + + * Because it has copying terms different enough from the other SLIB + packages that its inclusion would cause confusion. + + * Because it is an application program, rather than a library module. + + * Because I have been too busy to integrate it. + + Once an optional package is installed (and an entry added to +`*catalog*', the `require' mechanism allows it to be called up and used +as easily as any other SLIB package. Some optional packages (for which +`*catalog*' already has entries) available from SLIB sites are: + +SLIB-PSD is a portable debugger for Scheme (requires emacs editor). + ftp-swiss.ai.mit.edu:pub/scm/slib-psd1-3.tar.gz + prep.ai.mit.edu:pub/gnu/jacal/slib-psd1-3.tar.gz + ftp.maths.tcd.ie:pub/bosullvn/jacal/slib-psd1-3.tar.gz + ftp.cs.indiana.edu:/pub/scheme-repository/utl/slib-psd1-3.tar.gz + + With PSD, you can run a Scheme program in an Emacs buffer, set + breakpoints, single step evaluation and access and modify the + program's variables. It works by instrumenting the original source + code, so it should run with any R4RS compliant Scheme. It has been + tested with SCM, Elk 1.5, and the sci interpreter in the Scheme->C + system, but should work with other Schemes with a minimal amount + of porting, if at all. Includes documentation and user's manual. + Written by Pertti Kellom\"aki, pk@cs.tut.fi. The Lisp Pointers + article describing PSD (Lisp Pointers VI(1):15-23, January-March + 1993) is available as + http://www.cs.tut.fi/staff/pk/scheme/psd/article/article.html + +SLIB-SCHELOG is an embedding of Prolog in Scheme. + ftp-swiss.ai.mit.edu:pub/scm/slib-schelog.tar.gz + prep.ai.mit.edu:pub/gnu/jacal/slib-schelog.tar.gz + ftp.maths.tcd.ie:pub/bosullvn/jacal/slib-schelog.tar.gz + ftp.cs.indiana.edu:/pub/scheme-repository/utl/slib-schelog.tar.gz + diff --git a/slib.info-8 b/slib.info-8 new file mode 100644 index 0000000..670e9c1 --- /dev/null +++ b/slib.info-8 @@ -0,0 +1,570 @@ +This is Info file slib.info, produced by Makeinfo-1.64 from the input +file slib.texi. + + This file documents SLIB, the portable Scheme library. + + Copyright (C) 1993 Todd R. Eigenschink Copyright (C) 1993, 1994, 1995 +Aubrey Jaffer + + Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + + Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + + Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be stated in a +translation approved by the author. + + +File: slib.info, Node: Procedure and Macro Index, Next: Variable Index, Prev: Optional SLIB Packages, Up: Top + +Procedure and Macro Index +************************* + + This is an alphabetical list of all the procedures and macros in SLIB. + +* Menu: + +* -: Multi-argument / and -. +* -1+: Rev2 Procedures. +* /: Multi-argument / and -. +* 1+: Rev2 Procedures. +* <=?: Rev2 Procedures. +* <?: Rev2 Procedures. +* =?: Rev2 Procedures. +* >=?: Rev2 Procedures. +* >?: Rev2 Procedures. +* TAG: Structures. +* add-domain: Database Utilities. +* add-process!: Multi-Processing. +* add-setter: Setters. +* adjoin: Lists as sets. +* adjoin-parameters!: Parameter lists. +* alist->wt-tree: Construction of Weight-Balanced Trees. +* alist-associator: Association Lists. +* alist-for-each: Association Lists. +* alist-inquirer: Association Lists. +* alist-map: Association Lists. +* alist-remover: Association Lists. +* and?: Non-List functions. +* any?: Collections. +* append!: Rev2 Procedures. +* apply: Multi-argument Apply. +* array-1d-ref: Arrays. +* array-1d-set!: Arrays. +* array-2d-ref: Arrays. +* array-2d-set!: Arrays. +* array-3d-ref: Arrays. +* array-3d-set!: Arrays. +* array-copy!: Array Mapping. +* array-dimensions: Arrays. +* array-for-each: Array Mapping. +* array-in-bounds?: Arrays. +* array-indexes: Array Mapping. +* array-map!: Array Mapping. +* array-rank: Arrays. +* array-ref: Arrays. +* array-set!: Arrays. +* array-shape: Arrays. +* array?: Arrays. +* asctime: Time. +* ash: Bit-Twiddling. +* atom?: Non-List functions. +* batch:apply-chop-to-fit: Batch. +* batch:call-with-output-script: Batch. +* batch:comment: Batch. +* batch:delete-file: Batch. +* batch:initialize!: Batch. +* batch:lines->file: Batch. +* batch:rename-file: Batch. +* batch:run-script: Batch. +* batch:system: Batch. +* batch:try-system: Batch. +* bit-extract: Bit-Twiddling. +* break: Breakpoints. +* break-all: Debug. +* breakf: Breakpoints. +* breakpoint: Breakpoints. +* butlast: Lists as sequences. +* call-with-dynamic-binding: Dynamic Data Type. +* call-with-input-string: String Ports. +* call-with-output-string: String Ports. +* call-with-values: Values. +* capture-syntactic-environment: Syntactic Closures. +* cart-prod-tables: Relational Database Operations. +* chap:next-string: Chapter Ordering. +* chap:string<=?: Chapter Ordering. +* chap:string<?: Chapter Ordering. +* chap:string>=?: Chapter Ordering. +* chap:string>?: Chapter Ordering. +* check-parameters: Parameter lists. +* close-base: Base Table. +* close-database: Relational Database Operations. +* close-table: Table Operations. +* coerce: Non-List functions. +* collection?: Collections. +* continue: Breakpoints. +* copy-list: List construction. +* copy-tree: Tree Operations. +* create-database <1>: Database Utilities. +* create-database: Creating and Opening Relational Databases. +* create-report: Database Utilities. +* create-table: Relational Database Operations. +* create-view: Relational Database Operations. +* ctime: Time. +* current-error-port: Input/Output. +* current-time: Time. +* debug:breakf: Breakpoints. +* debug:tracef: Trace. +* debug:unbreakf: Breakpoints. +* debug:untracef: Trace. +* decode-universal-time: CLTime. +* define-access-operation: Setters. +* define-operation: Yasos interface. +* define-predicate: Yasos interface. +* define-record: Structures. +* define-syntax: Macro by Example. +* define-tables: Database Utilities. +* defmacro: Defmacro. +* defmacro:eval: Defmacro. +* defmacro:expand*: Defmacro. +* defmacro:load: Defmacro. +* defmacro?: Defmacro. +* delete <1>: Destructive list operations. +* delete: Base Table. +* delete-domain: Database Utilities. +* delete-file: Input/Output. +* delete-if: Destructive list operations. +* delete-if-not: Destructive list operations. +* delete-table: Relational Database Operations. +* dequeue!: Queues. +* difftime: Time. +* do-elts: Collections. +* do-keys: Collections. +* domain-checker: Database Utilities. +* dynamic-ref: Dynamic Data Type. +* dynamic-set!: Dynamic Data Type. +* dynamic-wind: Dynamic-Wind. +* dynamic?: Dynamic Data Type. +* empty?: Collections. +* encode-universal-time: CLTime. +* enquque!: Queues. +* every: Lists as sets. +* every?: Collections. +* extended-euclid: Modular Arithmetic. +* factor: Prime Factorization. +* file-exists?: Input/Output. +* fill-empty-parameters: Parameter lists. +* find-if: Lists as sets. +* find-string-from-port?: String Search. +* fluid-let: Fluid-Let. +* for-each-elt: Collections. +* for-each-key <1>: Base Table. +* for-each-key: Collections. +* for-each-row: Table Operations. +* force-output: Input/Output. +* format: Format Interface. +* fprintf: Standard Formatted Output. +* fscanf: Standard Formatted Input. +* generic-write: Generic-Write. +* gentemp: Defmacro. +* get: Table Operations. +* get*: Table Operations. +* get-decoded-time: CLTime. +* get-method: Object. +* get-universal-time: CLTime. +* getenv: System Interface. +* getopt: Getopt. +* getopt-: Getopt. +* getopt->arglist: Parameter lists. +* getopt->parameter-list: Parameter lists. +* gmtime: Time. +* has-duplicates?: Lists as sets. +* hash: Hashing. +* hash-associator: Hash Tables. +* hash-for-each: Hash Tables. +* hash-inquirer: Hash Tables. +* hash-map: Hash Tables. +* hash-remover: Hash Tables. +* hashq: Hashing. +* hashv: Hashing. +* heap-extract-max!: Priority Queues. +* heap-insert!: Priority Queues. +* heap-length: Priority Queues. +* identifier=?: Syntactic Closures. +* identifier?: Syntactic Closures. +* identity: Legacy. +* implementation-vicinity: Vicinity. +* in-vicinity: Vicinity. +* init-debug: Breakpoints. +* integer-expt: Bit-Twiddling. +* integer-length: Bit-Twiddling. +* integer-sqrt: Root Finding. +* intersection: Lists as sets. +* jacobi-symbol: Prime Factorization. +* kill-process!: Multi-Processing. +* kill-table: Base Table. +* laguerre:find-polynomial-root: Root Finding. +* laguerre:find-root: Root Finding. +* last: Lists as sequences. +* last-pair: Legacy. +* library-vicinity: Vicinity. +* list*: List construction. +* list->string: Rev4 Optional Procedures. +* list->vector: Rev4 Optional Procedures. +* list-tail: Rev4 Optional Procedures. +* load-option: Weight-Balanced Trees. +* localtime: Time. +* logand: Bit-Twiddling. +* logbit?: Bit-Twiddling. +* logcount: Bit-Twiddling. +* logior: Bit-Twiddling. +* lognot: Bit-Twiddling. +* logtest: Bit-Twiddling. +* logxor: Bit-Twiddling. +* macro:eval <1>: Syntax-Case Macros. +* macro:eval <2>: Syntactic Closures. +* macro:eval <3>: Macros That Work. +* macro:eval: R4RS Macros. +* macro:expand <1>: Syntax-Case Macros. +* macro:expand <2>: Syntactic Closures. +* macro:expand <3>: Macros That Work. +* macro:expand: R4RS Macros. +* macro:load <1>: Syntax-Case Macros. +* macro:load <2>: Syntactic Closures. +* macro:load <3>: Macros That Work. +* macro:load: R4RS Macros. +* macroexpand: Defmacro. +* macroexpand-1: Defmacro. +* macwork:eval: Macros That Work. +* macwork:expand: Macros That Work. +* macwork:load: Macros That Work. +* make-: Structures. +* make-array: Arrays. +* make-base: Base Table. +* make-command-server: Database Utilities. +* make-dynamic: Dynamic Data Type. +* make-generic-method: Object. +* make-generic-predicate: Object. +* make-getter: Base Table. +* make-hash-table: Hash Tables. +* make-heap: Priority Queues. +* make-key->list: Base Table. +* make-key-extractor: Base Table. +* make-keyifier-1: Base Table. +* make-list: List construction. +* make-list-keyifier: Base Table. +* make-method!: Object. +* make-object: Object. +* make-parameter-list: Parameter lists. +* make-port-crc: Cyclic Checksum. +* make-predicate!: Object. +* make-promise: Promises. +* make-putter: Base Table. +* make-queue: Queues. +* make-random-state: Random Numbers. +* make-record-type: Records. +* make-relational-system: Creating and Opening Relational Databases. +* make-shared-array: Arrays. +* make-sierpinski-indexer: Hashing. +* make-syntactic-closure: Syntactic Closures. +* make-table: Base Table. +* make-vicinity: Vicinity. +* make-wt-tree: Construction of Weight-Balanced Trees. +* make-wt-tree-type: Construction of Weight-Balanced Trees. +* map-elts: Collections. +* map-key: Base Table. +* map-keys: Collections. +* member-if: Lists as sets. +* merge: Sorting. +* merge!: Sorting. +* mktime: Time. +* modular:: Modular Arithmetic. +* modular:*: Modular Arithmetic. +* modular:+: Modular Arithmetic. +* modular:expt: Modular Arithmetic. +* modular:invert: Modular Arithmetic. +* modular:invertable?: Modular Arithmetic. +* modular:negate: Modular Arithmetic. +* modular:normalize: Modular Arithmetic. +* modulus->integer: Modular Arithmetic. +* must-be-first: Batch. +* must-be-last: Batch. +* nconc: Destructive list operations. +* newton:find-root: Root Finding. +* newtown:find-integer-root: Root Finding. +* notany: Lists as sets. +* notevery: Lists as sets. +* nreverse: Destructive list operations. +* nthcdr: Lists as sequences. +* object: Yasos interface. +* object->string: Object-To-String. +* object-with-ancestors: Yasos interface. +* object?: Object. +* offset-time: Time. +* open-base: Base Table. +* open-database <1>: Database Utilities. +* open-database: Creating and Opening Relational Databases. +* open-database!: Database Utilities. +* open-table <1>: Relational Database Operations. +* open-table: Base Table. +* operate-as: Yasos interface. +* or?: Non-List functions. +* ordered-for-each-key: Base Table. +* os->batch-dialect: Batch. +* output-port-height: Input/Output. +* output-port-width: Input/Output. +* parameter-list->arglist: Parameter lists. +* parameter-list-expand: Parameter lists. +* parameter-list-ref: Parameter lists. +* plot!: Plotting. +* position: Lists as sequences. +* pprint-file: Pretty-Print. +* pprint-filter-file: Pretty-Print. +* predicate->asso: Association Lists. +* predicate->hash: Hash Tables. +* predicate->hash-asso: Hash Tables. +* present?: Base Table. +* pretty-print: Pretty-Print. +* prime:trials: Prime Factorization. +* prime?: Prime Factorization. +* primes<: Prime Testing and Generation. +* primes>: Prime Testing and Generation. +* print: Yasos interface. +* printf: Standard Formatted Output. +* probably-prime?: Prime Testing and Generation. +* process:schedule!: Multi-Processing. +* program-vicinity: Vicinity. +* project-table: Relational Database Operations. +* provide: Require. +* provided?: Require. +* qp: Quick Print. +* qpn: Quick Print. +* qpr: Quick Print. +* queue-empty?: Queues. +* queue-front: Queues. +* queue-pop!: Queues. +* queue-push!: Queues. +* queue-rear: Queues. +* queue?: Queues. +* random: Random Numbers. +* random:exp: Random Numbers. +* random:hollow-sphere!: Random Numbers. +* random:normal: Random Numbers. +* random:normal-vector!: Random Numbers. +* random:solid-sphere!: Random Numbers. +* random:uniform: Random Numbers. +* rationalize: Rationalize. +* read-command: Command Line. +* read-line: Line I/O. +* read-line!: Line I/O. +* record-accessor: Records. +* record-constructor: Records. +* record-modifier: Records. +* record-predicate: Records. +* record-type-descriptor: Records. +* record-type-field-names: Records. +* record-type-name: Records. +* record?: Records. +* reduce <1>: Lists as sequences. +* reduce: Collections. +* reduce-init: Lists as sequences. +* remove: Lists as sets. +* remove-if: Lists as sets. +* remove-if-not: Lists as sets. +* remove-setter-for: Setters. +* repl:quit: Repl. +* repl:top-level: Repl. +* replace-suffix: Batch. +* require: Require. +* require:feature->path: Require. +* restrict-table: Relational Database Operations. +* row:delete: Table Operations. +* row:delete*: Table Operations. +* row:insert: Table Operations. +* row:insert*: Table Operations. +* row:remove: Table Operations. +* row:remove*: Table Operations. +* row:retrieve: Table Operations. +* row:retrieve*: Table Operations. +* row:update: Table Operations. +* row:update*: Table Operations. +* scanf: Standard Formatted Input. +* scanf-read-list: Standard Formatted Input. +* set: Setters. +* set-: Structures. +* set-difference: Lists as sets. +* setter: Setters. +* Setter: Collections. +* singleton-wt-tree: Construction of Weight-Balanced Trees. +* size <1>: Yasos interface. +* size: Collections. +* slib:error: System. +* slib:eval: System. +* slib:eval-load: System. +* slib:exit: System. +* slib:load: System. +* slib:load-compiled: System. +* slib:load-source: System. +* slib:report: Configuration. +* slib:report-version: Configuration. +* software-type: Configuration. +* some: Lists as sets. +* sort: Sorting. +* sort!: Sorting. +* sorted?: Sorting. +* soundex: Hashing. +* sprintf: Standard Formatted Output. +* sscanf: Standard Formatted Input. +* string->list: Rev4 Optional Procedures. +* string-capitalize: String-Case. +* string-captialize!: String-Case. +* string-copy: Rev4 Optional Procedures. +* string-downcase: String-Case. +* string-downcase!: String-Case. +* string-fill!: Rev4 Optional Procedures. +* string-index: String Search. +* string-join: Batch. +* string-null?: Rev2 Procedures. +* string-upcase: String-Case. +* string-upcase!: String-Case. +* sub-vicinity: Vicinity. +* subst: Tree Operations. +* substq: Tree Operations. +* substring-fill!: Rev2 Procedures. +* substring-move-left!: Rev2 Procedures. +* substring-move-right!: Rev2 Procedures. +* substring?: String Search. +* substv: Tree Operations. +* supported-key-type?: Base Table. +* supported-type?: Base Table. +* symmetric:modulus: Modular Arithmetic. +* sync-base: Base Table. +* syncase:eval: Syntax-Case Macros. +* syncase:expand: Syntax-Case Macros. +* syncase:load: Syntax-Case Macros. +* synclo:eval: Syntactic Closures. +* synclo:expand: Syntactic Closures. +* synclo:load: Syntactic Closures. +* syntax-rules: Macro by Example. +* system: System Interface. +* table-exists?: Relational Database Operations. +* tek40:draw: Tektronix Graphics Support. +* tek40:graphics: Tektronix Graphics Support. +* tek40:init: Tektronix Graphics Support. +* tek40:linetype: Tektronix Graphics Support. +* tek40:move: Tektronix Graphics Support. +* tek40:put-text: Tektronix Graphics Support. +* tek40:reset: Tektronix Graphics Support. +* tek40:text: Tektronix Graphics Support. +* tek41:draw: Tektronix Graphics Support. +* tek41:encode-int: Tektronix Graphics Support. +* tek41:encode-x-y: Tektronix Graphics Support. +* tek41:graphics: Tektronix Graphics Support. +* tek41:init: Tektronix Graphics Support. +* tek41:move: Tektronix Graphics Support. +* tek41:point: Tektronix Graphics Support. +* tek41:reset: Tektronix Graphics Support. +* tmpnam: Input/Output. +* topological-sort: Topological Sort. +* trace: Trace. +* trace-all: Debug. +* tracef: Trace. +* transcript-off: Transcripts. +* transcript-on: Transcripts. +* transformer: Syntactic Closures. +* tsort: Topological Sort. +* two-arg:-: Multi-argument / and -. +* two-arg:/: Multi-argument / and -. +* two-arg:apply: Multi-argument Apply. +* type-of: Non-List functions. +* tzset: Time. +* unbreak: Breakpoints. +* unbreakf: Breakpoints. +* union: Lists as sets. +* unmake-method!: Object. +* untrace: Trace. +* untracef: Trace. +* user-vicinity: Vicinity. +* values: Values. +* variant-case: Structures. +* vector->list: Rev4 Optional Procedures. +* vector-fill!: Rev4 Optional Procedures. +* with-input-from-file: With-File. +* with-output-to-file: With-File. +* write-base: Base Table. +* write-database: Relational Database Operations. +* write-line: Line I/O. +* wt-tree/add: Basic Operations on Weight-Balanced Trees. +* wt-tree/add!: Basic Operations on Weight-Balanced Trees. +* wt-tree/delete: Basic Operations on Weight-Balanced Trees. +* wt-tree/delete!: Basic Operations on Weight-Balanced Trees. +* wt-tree/delete-min: Indexing Operations on Weight-Balanced Trees. +* wt-tree/delete-min!: Indexing Operations on Weight-Balanced Trees. +* wt-tree/difference: Advanced Operations on Weight-Balanced Trees. +* wt-tree/empty?: Basic Operations on Weight-Balanced Trees. +* wt-tree/fold: Advanced Operations on Weight-Balanced Trees. +* wt-tree/for-each: Advanced Operations on Weight-Balanced Trees. +* wt-tree/index: Indexing Operations on Weight-Balanced Trees. +* wt-tree/index-datum: Indexing Operations on Weight-Balanced Trees. +* wt-tree/index-pair: Indexing Operations on Weight-Balanced Trees. +* wt-tree/intersection: Advanced Operations on Weight-Balanced Trees. +* wt-tree/lookup: Basic Operations on Weight-Balanced Trees. +* wt-tree/member?: Basic Operations on Weight-Balanced Trees. +* wt-tree/min: Indexing Operations on Weight-Balanced Trees. +* wt-tree/min-datum: Indexing Operations on Weight-Balanced Trees. +* wt-tree/min-pair: Indexing Operations on Weight-Balanced Trees. +* wt-tree/rank: Indexing Operations on Weight-Balanced Trees. +* wt-tree/set-equal?: Advanced Operations on Weight-Balanced Trees. +* wt-tree/size: Basic Operations on Weight-Balanced Trees. +* wt-tree/split<: Advanced Operations on Weight-Balanced Trees. +* wt-tree/split>: Advanced Operations on Weight-Balanced Trees. +* wt-tree/subset?: Advanced Operations on Weight-Balanced Trees. +* wt-tree/union: Advanced Operations on Weight-Balanced Trees. +* wt-tree?: Basic Operations on Weight-Balanced Trees. + + +File: slib.info, Node: Variable Index, Prev: Procedure and Macro Index, Up: Top + +Variable Index +************** + + This is an alphabetical list of all the global variables in SLIB. + +* Menu: + +* *catalog*: Require. +* *features*: Require. +* *modules*: Require. +* *optarg*: Getopt. +* *optind*: Getopt. +* *qp-width*: Quick Print. +* *random-state*: Random Numbers. +* *timezone*: Time. +* batch:platform: Batch. +* catalog-id: Base Table. +* char-code-limit: Configuration. +* charplot:height: Plotting. +* charplot:width: Plotting. +* column-domains: Table Operations. +* column-foreigns: Table Operations. +* column-names: Table Operations. +* column-types: Table Operations. +* most-positive-fixnum: Configuration. +* nil: Legacy. +* number-wt-type: Construction of Weight-Balanced Trees. +* primary-limit: Table Operations. +* slib:form-feed: Configuration. +* slib:tab: Configuration. +* stderr: Standard Formatted I/O. +* stdin: Standard Formatted I/O. +* stdout: Standard Formatted I/O. +* string-wt-type: Construction of Weight-Balanced Trees. +* t: Legacy. + + diff --git a/slib.texi b/slib.texi new file mode 100644 index 0000000..1d41fdc --- /dev/null +++ b/slib.texi @@ -0,0 +1,9058 @@ +\input texinfo @c -*-texinfo-*- +@c %**start of header +@setfilename slib.info +@settitle SLIB +@setchapternewpage on +@c Choices for setchapternewpage are {on,off,odd}. +@paragraphindent 2 +@c %**end of header + +@iftex +@finalout +@c DL: lose the egregious vertical whitespace, esp. around examples +@c but paras in @defun-like things don't have parindent +@parskip 4pt plus 1pt +@end iftex + +@ifinfo +This file documents SLIB, the portable Scheme library. + +Copyright (C) 1993 Todd R. Eigenschink +Copyright (C) 1993, 1994, 1995 Aubrey Jaffer + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +@ignore +Permission is granted to process this file through TeX and print the +results, provided the printed document carries copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). + +@end ignore +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation approved +by the author. +@end ifinfo + +@titlepage +@title SLIB +@subtitle The Portable Scheme Library +@subtitle Version 2a3 +@subtitle June 1995 +@author by Todd R. Eigenschink, Dave Love, and Aubrey Jaffer + +@page +@vskip 0pt plus 1filll +Copyright @copyright{} 1993, 1994, 1995 Todd R. Eigenschink and Aubrey Jaffer + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation approved +by the author. +@end titlepage + + + + + +@node Top, Overview, (dir), (dir) + +@ifinfo +This file documents SLIB, the portable Scheme library. + +@heading Good Engineering is 1% inspiration and 99% documentation. + +Herein lies the good part. Many thanks to Todd Eigenschink +<eigenstr@@CS.Rose-Hulman.Edu> (who thanks Dave Love <D.Love@@dl.ac.uk>) +for creating @file{slib.texi}. I have learned much from their example. + +Aubrey Jaffer +jaffer@@ai.mit.edu +@end ifinfo + + +@menu +* Overview:: What is SLIB? + +* Data Structures:: Various data structures. +* Macros:: Extensions to Scheme syntax. +* Numerics:: +* Procedures:: Miscellaneous utility procedures. +* Standards Support:: Support for Scheme Standards. +* Session Support:: Debugging, Pathnames, Require, etc. + +* Optional SLIB Packages:: +* Procedure and Macro Index:: +* Variable Index:: +@end menu + + +@node Overview, Data Structures, Top, Top +@chapter Overview + +SLIB is a portable Scheme library meant to provide compatibility and +utility functions for all standard Scheme implementations, and fixes +several implementations which are non-conforming. SLIB conforms to +@cite{Revised^4 Report on the Algorithmic Language Scheme} and the IEEE +P1178 specification. SLIB supports Unix and similar systems, VMS, and +MS-DOS.@refill + +For a summary of what each file contains, see the file @file{README}. +For a list of the features that have changed since the last SLIB +release, see the file @file{ANNOUNCE}. For a list of the features that +have changed over time, see the file @file{ChangeLog}. + +The maintainer can be reached as @samp{jaffer@@ai.mit.edu}. + +@menu +* Installation:: How to install SLIB on your system. +* Porting:: SLIB to new platforms +* Coding Standards:: How to write modules for SLIB. +* Copyrights:: Intellectual propery issues. +* Manual Conventions:: Conventions used in this manual. +@end menu + +@node Installation, Porting, Overview, Overview +@section Installation + +Check the manifest in @file{README} to find a configuration file for +your Scheme implementation. Initialization files for most IEEE P1178 +compliant Scheme Implementations are included with this distribution. + +If the Scheme implementation supports @code{getenv}, then the value of +the shell environment variable @var{SCHEME_LIBRARY_PATH} will be used +for @code{(library-vicinity)} if it is defined. Currently, Chez, Elk, +MITScheme, scheme->c, VSCM, and SCM support @code{getenv}. + +You should check the definitions of @code{software-type}, +@code{scheme-implementation-version}, +@iftex +@* +@end iftex +@code{implementation-vicinity}, +and @code{library-vicinity} in the initialization file. There are +comments in the file for how to configure it. + +Once this is done you can modify the startup file for your Scheme +implementation to @code{load} this initialization file. SLIB is then +installed. + +Multiple implementations of Scheme can all use the same SLIB directory. +Simply configure each implementation's initialization file as outlined +above. + +The SCM implementation does not require any initialization file as SLIB +support is already built in to SCM. See the documentation with SCM for +installation instructions. + +SLIB includes methods to create heap images for the VSCM and Scheme48 +implementations. The instructions for creating a VSCM image are in +comments in @file{vscm.init}. To make a Scheme48 image, @code{cd} to +the SLIB directory and type @code{make slib48}. This will also create a +shell script with the name @code{slib48} which will invoke the saved +image. + +@node Porting, Coding Standards, Installation, Overview +@section Porting + +If there is no initialization file for your Scheme implementation, you +will have to create one. Your Scheme implementation must be largely +compliant with @cite{IEEE Std 1178-1990} or @cite{Revised^4 Report on +the Algorithmic Language Scheme} to support SLIB. + +@file{Template.scm} is an example configuration file. The comments +inside will direct you on how to customize it to reflect your system. +Give your new initialization file the implementation's name with +@file{.init} appended. For instance, if you were porting +@code{foo-scheme} then the initialization file might be called +@file{foo.init}. + +Your customized version should then be loaded as part of your scheme +implementation's initialization. It will load @file{require.scm} +(@xref{Require}) from the library; this will allow the use of +@code{provide}, @code{provided?}, and @code{require} along with the +@dfn{vicinity} functions (@code{vicinity} functions are documented in +the section on Require. @xref{Require}). The rest of the library will +then be accessible in a system independent fashion.@refill + +Please mail new working configuration files to @code{jaffer@@ai.mit.edu} +so that they can be included in the SLIB distribution.@refill + +@node Coding Standards, Copyrights, Porting, Overview +@section Coding Standards + +All library packages are written in IEEE P1178 Scheme and assume that a +configuration file and @file{require.scm} package have already been +loaded. Other versions of Scheme can be supported in library packages +as well by using, for example, @code{(provided? 'rev3-report)} or +@code{(require 'rev3-report)} (@xref{Require}).@refill + +@file{require.scm} defines @code{*catalog*}, an association list of +module names and filenames. When a new package is added to the library, +an entry should be added to @file{require.scm}. Local packages can also +be added to @code{*catalog*} and even shadow entries already in the +table.@refill + +The module name and @samp{:} should prefix each symbol defined in the +package. Definitions for external use should then be exported by having +@code{(define foo module-name:foo)}.@refill + +Submitted code should not duplicate routines which are already in SLIB +files. Use @code{require} to force those features to be supported in +your package. Care should be taken that there are no circularities in +the @code{require}s and @code{load}s between the library +packages.@refill + +Documentation should be provided in Emacs Texinfo format if possible, +But documentation must be provided. + +Your package will be released sooner with SLIB if you send me a file +which tests your code. Please run this test @emph{before} you send me +the code! + +@subheading Modifications + +Please document your changes. A line or two for @file{ChangeLog} is +sufficient for simple fixes or extensions. Look at the format of +@file{ChangeLog} to see what information is desired. Please send me +@code{diff} files from the latest SLIB distribution (remember to send +@code{diff}s of @file{slib.texi} and @file{ChangeLog}). This makes for +less email traffic and makes it easier for me to integrate when more +than one person is changing a file (this happens a lot with +@file{slib.texi} and @samp{*.init} files). + +If someone else wrote a package you want to significantly modify, please +try to contact the author, who may be working on a new version. This +will insure against wasting effort on obsolete versions. + +Please @emph{do not} reformat the source code with your favorite +beautifier, make 10 fixes, and send me the resulting source code. I do +not have the time to fish through 10000 diffs to find your 10 real fixes. + +@node Copyrights, Manual Conventions, Coding Standards, Overview +@section Copyrights + +This section has instructions for SLIB authors regarding copyrights. + +Each package in SLIB must either be in the public domain, or come with a +statement of terms permitting users to copy, redistribute and modify it. +The comments at the beginning of @file{require.scm} and +@file{macwork.scm} illustrate copyright and appropriate terms. + +If your code or changes amount to less than about 10 lines, you do not +need to add your copyright or send a disclaimer. + +@subheading Putting code into the Public Domain + +In order to put code in the public domain you should sign a copyright +disclaimer and send it to the SLIB maintainer. Contact +jaffer@@ai.mit.edu for the address to mail the disclaimer to. + +@quotation +I, @var{name}, hereby affirm that I have placed the software package +@var{name} in the public domain. + +I affirm that I am the sole author and sole copyright holder for the +software package, that I have the right to place this software package +in the public domain, and that I will do nothing to undermine this +status in the future. + +@flushright + @var{signature and date} +@end flushright +@end quotation + +This wording assumes that you are the sole author. If you are not the +sole author, the wording needs to be different. If you don't want to be +bothered with sending a letter every time you release or modify a +module, make your letter say that it also applies to your future +revisions of that module. + +Make sure no employer has any claim to the copyright on the work you are +submitting. If there is any doubt, create a copyright disclaimer and +have your employer sign it. Mail the signed disclaimer to the SLIB +maintainer. Contact jaffer@@ai.mit.edu for the address to mail the +disclaimer to. An example disclaimer follows. + +@subheading Explicit copying terms + +@noindent +If you submit more than about 10 lines of code which you are not placing +into the Public Domain (by sending me a disclaimer) you need to: + +@itemize @bullet +@item +Arrange that your name appears in a copyright line for the appropriate +year. Multiple copyright lines are acceptable. +@item +With your copyright line, specify any terms you require to be different +from those already in the file. +@item +Make sure no employer has any claim to the copyright on the work you are +submitting. If there is any doubt, create a copyright disclaimer and +have your employer sign it. Mail the signed disclaim to the SLIB +maintainer. Contact jaffer@@ai.mit.edu for the address to mail the +disclaimer to. +@end itemize + +@subheading Example: Company Copyright Disclaimer + +This disclaimer should be signed by a vice president or general manager +of the company. If you can't get at them, anyone else authorized to +license out software produced there will do. Here is a sample wording: + +@quotation +@var{employer} Corporation hereby disclaims all copyright +interest in the program @var{program} written by @var{name}. + +@var{employer} Corporation affirms that it has no other intellectual +property interest that would undermine this release, and will do nothing +to undermine it in the future. + +@flushleft +@var{signature and date}, +@var{name}, @var{title}, @var{employer} Corporation +@end flushleft +@end quotation + +@node Manual Conventions, , Copyrights, Overview +@section Manual Conventions + +Things that are labeled as Functions are called for their return values. +Things that are labeled as Procedures are called primarily for their +side effects. + +All examples throughout this text were produced using the @code{scm} +Scheme implementation. + +At the beginning of each section, there is a line that looks something +like + +@code{(require 'feature)}. + +@noindent +This means that, in order to use @code{feature}, you must include the +line @code{(require 'feature)} somewhere in your code prior to the use +of that feature. @code{require} will make sure that the feature is +loaded.@refill + + + + + +@node Data Structures, Macros, Overview, Top +@chapter Data Structures + + + +@menu +* Arrays:: 'array +* Array Mapping:: 'array-for-each +* Association Lists:: 'alist +* Collections:: 'collect +* Dynamic Data Type:: 'dynamic +* Hash Tables:: 'hash-table +* Hashing:: 'hash, 'sierpinski, 'soundex +* Chapter Ordering:: 'chapter-order +* Object:: 'object +* Parameter lists:: 'parameters +* Priority Queues:: 'priority-queue +* Queues:: 'queue +* Records:: 'record +* Base Table:: +* Relational Database:: 'relational-database +* Weight-Balanced Trees:: 'wt-tree +* Structures:: 'struct, 'structure +@end menu + + + + +@node Arrays, Array Mapping, Data Structures, Data Structures +@section Arrays + +@code{(require 'array)} + +@defun array? obj +Returns @code{#t} if the @var{obj} is an array, and @code{#f} if not. +@end defun + +@defun make-array initial-value bound1 bound2 @dots{} +Creates and returns an array that has as many dimensins as there are +@var{bound}s and fills it with @var{initial-value}.@refill +@end defun + +When constructing an array, @var{bound} is either an inclusive range of +indices expressed as a two element list, or an upper bound expressed as +a single integer. So@refill +@lisp +(make-array 'foo 3 3) @equiv{} (make-array 'foo '(0 2) '(0 2)) +@end lisp + +@defun make-shared-array array mapper bound1 bound2 @dots{} +@code{make-shared-array} can be used to create shared subarrays of other +arrays. The @var{mapper} is a function that translates coordinates in +the new array into coordinates in the old array. A @var{mapper} must be +linear, and its range must stay within the bounds of the old array, but +it can be otherwise arbitrary. A simple example:@refill +@lisp +(define fred (make-array #f 8 8)) +(define freds-diagonal + (make-shared-array fred (lambda (i) (list i i)) 8)) +(array-set! freds-diagonal 'foo 3) +(array-ref fred 3 3) + @result{} FOO +(define freds-center + (make-shared-array fred (lambda (i j) (list (+ 3 i) (+ 3 j))) + 2 2)) +(array-ref freds-center 0 0) + @result{} FOO +@end lisp +@end defun + +@defun array-rank obj +Returns the number of dimensions of @var{obj}. If @var{obj} is not an +array, 0 is returned. +@end defun + +@defun array-shape array +@code{array-shape} returns a list of inclusive bounds. So: +@lisp +(array-shape (make-array 'foo 3 5)) + @result{} ((0 2) (0 4)) +@end lisp +@end defun + +@defun array-dimensions array +@code{array-dimensions} is similar to @code{array-shape} but replaces +elements with a 0 minimum with one greater than the maximum. So: +@lisp +(array-dimensions (make-array 'foo 3 5)) + @result{} (3 5) +@end lisp +@end defun + +@deffn Procedure array-in-bounds? array index1 index2 @dots{} +Returns @code{#t} if its arguments would be acceptable to +@code{array-ref}. +@end deffn + +@defun array-ref array index1 index2 @dots{} +Returns the element at the @code{(@var{index1}, @var{index2})} element +in @var{array}.@refill +@end defun + +@deffn Procedure array-set! array new-value index1 index2 @dots{} +@end deffn + +@defun array-1d-ref array index +@defunx array-2d-ref array index index +@defunx array-3d-ref array index index index +@end defun + +@deffn Procedure array-1d-set! array new-value index +@deffnx Procedure array-2d-set! array new-value index index +@deffnx Procedure array-3d-set! array new-value index index index +@end deffn + +The functions are just fast versions of @code{array-ref} and +@code{array-set!} that take a fixed number of arguments, and perform no +bounds checking.@refill + +If you comment out the bounds checking code, this is about as efficient +as you could ask for without help from the compiler. + +An exercise left to the reader: implement the rest of APL. + + + +@node Array Mapping, Association Lists, Arrays, Data Structures +@section Array Mapping + +@code{(require 'array-for-each)} + +@defun array-map! array0 proc array1 @dots{} +@var{array1}, @dots{} must have the same number of dimensions as +@var{array0} and have a range for each index which includes the range +for the corresponding index in @var{array0}. @var{proc} is applied to +each tuple of elements of @var{array1} @dots{} and the result is stored +as the corresponding element in @var{array0}. The value returned is +unspecified. The order of application is unspecified. +@end defun + +@defun array-for-each @var{proc} @var{array0} @dots{} +@var{proc} is applied to each tuple of elements of @var{array0} @dots{} +in row-major order. The value returned is unspecified. +@end defun + +@defun array-indexes @var{array} +Returns an array of lists of indexes for @var{array} such that, if +@var{li} is a list of indexes for which @var{array} is defined, (equal? +@var{li} (apply array-ref (array-indexes @var{array}) @var{li})). +@end defun + +@defun array-copy! source destination +Copies every element from vector or array @var{source} to the +corresponding element of @var{destination}. @var{destination} must have +the same rank as @var{source}, and be at least as large in each +dimension. The order of copying is unspecified. +@end defun + + +@node Association Lists, Collections, Array Mapping, Data Structures +@section Association Lists + +@code{(require 'alist)} + +Alist functions provide utilities for treating a list of key-value pairs +as an associative database. These functions take an equality predicate, +@var{pred}, as an argument. This predicate should be repeatable, +symmetric, and transitive.@refill + +Alist functions can be used with a secondary index method such as hash +tables for improved performance. + +@defun predicate->asso pred +Returns an @dfn{association function} (like @code{assq}, @code{assv}, or +@code{assoc}) corresponding to @var{pred}. The returned function +returns a key-value pair whose key is @code{pred}-equal to its first +argument or @code{#f} if no key in the alist is @var{pred}-equal to the +first argument.@refill +@end defun + +@defun alist-inquirer pred +Returns a procedure of 2 arguments, @var{alist} and @var{key}, which +returns the value associated with @var{key} in @var{alist} or @code{#f} if +@var{key} does not appear in @var{alist}.@refill +@end defun + +@defun alist-associator pred +Returns a procedure of 3 arguments, @var{alist}, @var{key}, and +@var{value}, which returns an alist with @var{key} and @var{value} +associated. Any previous value associated with @var{key} will be +lost. This returned procedure may or may not have side effects on its +@var{alist} argument. An example of correct usage is:@refill +@lisp +(define put (alist-associator string-ci=?)) +(define alist '()) +(set! alist (put alist "Foo" 9)) +@end lisp +@end defun + +@defun alist-remover pred +Returns a procedure of 2 arguments, @var{alist} and @var{key}, which +returns an alist with an association whose @var{key} is key removed. +This returned procedure may or may not have side effects on its +@var{alist} argument. An example of correct usage is:@refill +@lisp +(define rem (alist-remover string-ci=?)) +(set! alist (rem alist "foo")) +@end lisp +@end defun + +@defun alist-map proc alist +Returns a new association list formed by mapping @var{proc} over the +keys and values of @var{alist}. @var{proc} must be a function of 2 +arguments which returns the new value part. +@end defun + +@defun alist-for-each proc alist +Applies @var{proc} to each pair of keys and values of @var{alist}. +@var{proc} must be a function of 2 arguments. The returned value is +unspecified. +@end defun + + +@node Collections, Dynamic Data Type, Association Lists, Data Structures +@section Collections + +@c Much of the documentation in this section was written by Dave Love +@c (d.love@dl.ac.uk) -- don't blame Ken Dickey for its faults. +@c but we can blame him for not writing it! + +@code{(require 'collect)} + +Routines for managing collections. Collections are aggregate data +structures supporting iteration over their elements, similar to the +Dylan(TM) language, but with a different interface. They have +@dfn{elements} indexed by corresponding @dfn{keys}, although the keys +may be implicit (as with lists).@refill + +New types of collections may be defined as YASOS objects (@xref{Yasos}). +They must support the following operations: +@itemize @bullet +@item +@code{(collection? @var{self})} (always returns @code{#t}); + +@item +@code{(size @var{self})} returns the number of elements in the collection; + +@item +@code{(print @var{self} @var{port})} is a specialized print operation +for the collection which prints a suitable representation on the given +@var{port} or returns it as a string if @var{port} is @code{#t};@refill + +@item +@code{(gen-elts @var{self})} returns a thunk which on successive +invocations yields elements of @var{self} in order or gives an error if +it is invoked more than @code{(size @var{self})} times;@refill + +@item +@code{(gen-keys @var{self})} is like @code{gen-elts}, but yields the +collection's keys in order. + +@end itemize +They might support specialized @code{for-each-key} and +@code{for-each-elt} operations.@refill + +@defun collection? obj +A predicate, true initially of lists, vectors and strings. New sorts of +collections must answer @code{#t} to @code{collection?}.@refill +@end defun + +@deffn Procedure map-elts proc . collections +@deffnx Procedure do-elts proc . collections +@var{proc} is a procedure taking as many arguments as there are +@var{collections} (at least one). The @var{collections} are iterated +over in their natural order and @var{proc} is applied to the elements +yielded by each iteration in turn. The order in which the arguments are +supplied corresponds to te order in which the @var{collections} appear. +@code{do-elts} is used when only side-effects of @var{proc} are of +interest and its return value is unspecified. @code{map-elts} returns a +collection (actually a vector) of the results of the applications of +@var{proc}.@refill + +Example: +@lisp +(map-elts + (list 1 2 3) (vector 1 2 3)) + @result{} #(2 4 6) +@end lisp +@end deffn + +@deffn Procedure map-keys proc . collections +@deffnx Procedure do-keys proc . collections +These are analogous to @code{map-elts} and @code{do-elts}, but each +iteration is over the @var{collections}' @emph{keys} rather than their +elements.@refill + +Example: +@lisp +(map-keys + (list 1 2 3) (vector 1 2 3)) + @result{} #(0 2 4) +@end lisp +@end deffn + +@deffn Procedure for-each-key collection proc +@deffnx Procedure for-each-elt collection proc +These are like @code{do-keys} and @code{do-elts} but only for a single +collection; they are potentially more efficient. +@end deffn + +@defun reduce proc seed . collections +A generalization of the list-based @code{comlist:reduce-init} +(@xref{Lists as sequences}) to collections which will shadow the +list-based version if @code{(require 'collect)} follows @code{(require +'common-list-functions)} (@xref{Common List Functions}).@refill + +Examples: +@lisp +(reduce + 0 (vector 1 2 3)) + @result{} 6 +(reduce union '() '((a b c) (b c d) (d a))) + @result{} (c b d a). +@end lisp +@end defun + +@defun any? pred . collections +A generalization of the list-based @code{some} (@xref{Lists as +sequences}) to collections.@refill + +Example: +@lisp +(any? odd? (list 2 3 4 5)) + @result{} #t +@end lisp +@end defun + +@defun every? pred . collections +A generalization of the list-based @code{every} (@xref{Lists as +sequences}) to collections.@refill + +Example: +@lisp +(every? collection? '((1 2) #(1 2))) + @result{} #t +@end lisp +@end defun + +@defun empty? collection +Returns @code{#t} iff there are no elements in @var{collection}. + +@code{(empty? @var{collection}) @equiv{} (zero? (size @var{collection}))} +@end defun + +@defun size collection +Returns the number of elements in @var{collection}. +@end defun + +@defun Setter list-ref +See @xref{Setters} for a definition of @dfn{setter}. N.B. +@code{(setter list-ref)} doesn't work properly for element 0 of a +list.@refill +@end defun + +Here is a sample collection: @code{simple-table} which is also a +@code{table}.@refill +@lisp +(define-predicate TABLE?) +(define-operation (LOOKUP table key failure-object)) +(define-operation (ASSOCIATE! table key value)) ;; returns key +(define-operation (REMOVE! table key)) ;; returns value + +(define (MAKE-SIMPLE-TABLE) + (let ( (table (list)) ) + (object + ;; table behaviors + ((TABLE? self) #t) + ((SIZE self) (size table)) + ((PRINT self port) (format port "#<SIMPLE-TABLE>")) + ((LOOKUP self key failure-object) + (cond + ((assq key table) => cdr) + (else failure-object) + )) + ((ASSOCIATE! self key value) + (cond + ((assq key table) + => (lambda (bucket) (set-cdr! bucket value) key)) + (else + (set! table (cons (cons key value) table)) + key) + )) + ((REMOVE! self key);; returns old value + (cond + ((null? table) (slib:error "TABLE:REMOVE! Key not found: " key)) + ((eq? key (caar table)) + (let ( (value (cdar table)) ) + (set! table (cdr table)) + value) + ) + (else + (let loop ( (last table) (this (cdr table)) ) + (cond + ((null? this) + (slib:error "TABLE:REMOVE! Key not found: " key)) + ((eq? key (caar this)) + (let ( (value (cdar this)) ) + (set-cdr! last (cdr this)) + value) + ) + (else + (loop (cdr last) (cdr this))) + ) ) ) + )) + ;; collection behaviors + ((COLLECTION? self) #t) + ((GEN-KEYS self) (collect:list-gen-elts (map car table))) + ((GEN-ELTS self) (collect:list-gen-elts (map cdr table))) + ((FOR-EACH-KEY self proc) + (for-each (lambda (bucket) (proc (car bucket))) table) + ) + ((FOR-EACH-ELT self proc) + (for-each (lambda (bucket) (proc (cdr bucket))) table) + ) + ) ) ) +@end lisp + + + + + +@node Dynamic Data Type, Hash Tables, Collections, Data Structures +@section Dynamic Data Type + +@code{(require 'dynamic)} + +@defun make-dynamic obj +Create and returns a new @dfn{dynamic} whose global value is @var{obj}. +@end defun + +@defun dynamic? obj +Returns true if and only if @var{obj} is a dynamic. No object +satisfying @code{dynamic?} satisfies any of the other standard type +predicates.@refill +@end defun + +@defun dynamic-ref dyn +Return the value of the given dynamic in the current dynamic +environment. +@end defun + +@deffn Procedure dynamic-set! dyn obj +Change the value of the given dynamic to @var{obj} in the current +dynamic environment. The returned value is unspecified.@refill +@end deffn + +@defun call-with-dynamic-binding dyn obj thunk +Invoke and return the value of the given thunk in a new, nested dynamic +environment in which the given dynamic has been bound to a new location +whose initial contents are the value @var{obj}. This dynamic +environment has precisely the same extent as the invocation of the thunk +and is thus captured by continuations created within that invocation and +re-established by those continuations when they are invoked.@refill +@end defun + +The @code{dynamic-bind} macro is not implemented. + + + + +@node Hash Tables, Hashing, Dynamic Data Type, Data Structures +@section Hash Tables + +@code{(require 'hash-table)} + +@defun predicate->hash pred +Returns a hash function (like @code{hashq}, @code{hashv}, or +@code{hash}) corresponding to the equality predicate @var{pred}. +@var{pred} should be @code{eq?}, @code{eqv?}, @code{equal?}, @code{=}, +@code{char=?}, @code{char-ci=?}, @code{string=?}, or +@code{string-ci=?}.@refill +@end defun + +A hash table is a vector of association lists. + +@defun make-hash-table k +Returns a vector of @var{k} empty (association) lists. +@end defun + +Hash table functions provide utilities for an associative database. +These functions take an equality predicate, @var{pred}, as an argument. +@var{pred} should be @code{eq?}, @code{eqv?}, @code{equal?}, @code{=}, +@code{char=?}, @code{char-ci=?}, @code{string=?}, or +@code{string-ci=?}.@refill + +@defun predicate->hash-asso pred +Returns a hash association function of 2 arguments, @var{key} and +@var{hashtab}, corresponding to @var{pred}. The returned function +returns a key-value pair whose key is @var{pred}-equal to its first +argument or @code{#f} if no key in @var{hashtab} is @var{pred}-equal to +the first argument.@refill +@end defun + +@defun hash-inquirer pred +Returns a procedure of 3 arguments, @code{hashtab} and @code{key}, which +returns the value associated with @code{key} in @code{hashtab} or +@code{#f} if key does not appear in @code{hashtab}.@refill +@end defun + +@defun hash-associator pred +Returns a procedure of 3 arguments, @var{hashtab}, @var{key}, and +@var{value}, which modifies @var{hashtab} so that @var{key} and +@var{value} associated. Any previous value associated with @var{key} +will be lost.@refill +@end defun + +@defun hash-remover pred +Returns a procedure of 2 arguments, @var{hashtab} and @var{key}, which +modifies @var{hashtab} so that the association whose key is @var{key} is +removed.@refill +@end defun + +@defun hash-map proc hash-table +Returns a new hash table formed by mapping @var{proc} over the +keys and values of @var{hash-table}. @var{proc} must be a function of 2 +arguments which returns the new value part. +@end defun + +@defun hash-for-each proc hash-table +Applies @var{proc} to each pair of keys and values of @var{hash-table}. +@var{proc} must be a function of 2 arguments. The returned value is +unspecified. +@end defun + + + + + +@node Hashing, Chapter Ordering, Hash Tables, Data Structures +@section Hashing + +@code{(require 'hash)} + +These hashing functions are for use in quickly classifying objects. +Hash tables use these functions. + +@defun hashq obj k +@defunx hashv obj k +@defunx hash obj k +Returns an exact non-negative integer less than @var{k}. For each +non-negative integer less than @var{k} there are arguments @var{obj} for +which the hashing functions applied to @var{obj} and @var{k} returns +that integer.@refill + +For @code{hashq}, @code{(eq? obj1 obj2)} implies @code{(= (hashq obj1 k) +(hashq obj2))}.@refill + +For @code{hashv}, @code{(eqv? obj1 obj2)} implies @code{(= (hashv obj1 k) +(hashv obj2))}.@refill + +For @code{hash}, @code{(equal? obj1 obj2)} implies @code{(= (hash obj1 k) +(hash obj2))}.@refill + +@code{hash}, @code{hashv}, and @code{hashq} return in time bounded by a +constant. Notice that items having the same @code{hash} implies the +items have the same @code{hashv} implies the items have the same +@code{hashq}.@refill +@end defun + + +@code{(require 'sierpinski)} + +@defun make-sierpinski-indexer max-coordinate +Returns a procedure (eg hash-function) of 2 numeric arguments which +preserves @emph{nearness} in its mapping from NxN to N. + +@var{max-coordinate} is the maximum coordinate (a positive integer) of a +population of points. The returned procedures is a function that takes +the x and y coordinates of a point, (non-negative integers) and returns +an integer corresponding to the relative position of that point along a +Sierpinski curve. (You can think of this as computing a (pseudo-) +inverse of the Sierpinski spacefilling curve.) + +Example use: Make an indexer (hash-function) for integer points lying in +square of integer grid points [0,99]x[0,99]: +@example +(define space-key (make-sierpinski-indexer 100)) +@end example +Now let's compute the index of some points: +@example +(space-key 24 78) @result{} 9206 +(space-key 23 80) @result{} 9172 +@end example + +Note that locations (24, 78) and (23, 80) are near in index and +therefore, because the Sierpinski spacefilling curve is continuous, we +know they must also be near in the plane. Nearness in the plane does +not, however, necessarily correspond to nearness in index, although it +@emph{tends} to be so. + +Example applications: +@table @asis + +@item +Sort points by Sierpinski index to get heuristic solution to +@emph{travelling salesman problem}. For details of performance, +see L. Platzman and J. Bartholdi, "Spacefilling curves and the +Euclidean travelling salesman problem", JACM 36(4):719--737 +(October 1989) and references therein. + +@item +Use Sierpinski index as key by which to store 2-dimensional data +in a 1-dimensional data structure (such as a table). Then +locations that are near each other in 2-d space will tend to +be near each other in 1-d data structure; and locations that +are near in 1-d data structure will be near in 2-d space. This +can significantly speed retrieval from secondary storage because +contiguous regions in the plane will tend to correspond to +contiguous regions in secondary storage. (This is a standard +technique for managing CAD/CAM or geographic data.) + +@end table +@end defun + + + +@code{(require 'soundex)} + +@defun soundex name +Computes the @emph{soundex} hash of @var{name}. Returns a string of an +initial letter and up to three digits between 0 and 6. Soundex +supposedly has the property that names that sound similar in normal +English pronunciation tend to map to the same key. + +Soundex was a classic algorithm used for manual filing of personal +records before the advent of computers. It performs adequately for +English names but has trouble with other nationalities. + +See Knuth, Vol. 3 @cite{Sorting and searching}, pp 391--2 + +To manage unusual inputs, @code{soundex} omits all non-alphabetic +characters. Consequently, in this implementation: + +@example +(soundex <string of blanks>) @result{} "" +(soundex "") @result{} "" +@end example + +Examples from Knuth: + +@example +(map soundex '("Euler" "Gauss" "Hilbert" "Knuth" + "Lloyd" "Lukasiewicz")) + @result{} ("E460" "G200" "H416" "K530" "L300" "L222") + +(map soundex '("Ellery" "Ghosh" "Heilbronn" "Kant" + "Ladd" "Lissajous")) + @result{} ("E460" "G200" "H416" "K530" "L300" "L222") +@end example + +Some cases in which the algorithm fails (Knuth): + +@example +(map soundex '("Rogers" "Rodgers")) @result{} ("R262" "R326") + +(map soundex '("Sinclair" "St. Clair")) @result{} ("S524" "S324") + +(map soundex '("Tchebysheff" "Chebyshev")) @result{} ("T212" "C121") +@end example +@end defun + +@node Chapter Ordering, Object, Hashing, Data Structures +@section Chapter Ordering + +@code{(require 'chapter-order)} + +The @samp{chap:} functions deal with strings which are ordered like +chapter numbers (or letters) in a book. Each section of the string +consists of consecutive numeric or consecutive aphabetic characters of +like case. + +@defun chap:string<? string1 string2 +Returns #t if the first non-matching run of alphabetic upper-case or the +first non-matching run of alphabetic lower-case or the first +non-matching run of numeric characters of @var{string1} is +@code{string<?} than the corresponding non-matching run of characters of +@var{string2}. + +@example +(chap:string<? "a.9" "a.10") @result{} #t +(chap:string<? "4c" "4aa") @result{} #t +(chap:string<? "Revised^@{3.99@}" "Revised^@{4@}") @result{} #t +@end example + +@defunx chap:string>? string1 string2 +@defunx chap:string<=? string1 string2 +@defunx chap:string>=? string1 string2 +Implement the corresponding chapter-order predicates. +@end defun + +@defun chap:next-string string +Returns the next string in the @emph{chapter order}. If @var{string} +has no alphabetic or numeric characters, +@code{(string-append @var{string} "0")} is returnd. The argument to +chap:next-string will always be @code{chap:string<?} than the result. + +@example +(chap:next-string "a.9") @result{} "a.10" +(chap:next-string "4c") @result{} "4d" +(chap:next-string "4z") @result{} "4aa" +(chap:next-string "Revised^@{4@}") @result{} "Revised^@{5@}" + +@end example +@end defun + +@node Object, Parameter lists, Chapter Ordering, Data Structures +@section Macroless Object System + +@code{(require 'object)} + +This is the Macroless Object System written by Wade Humeniuk +(whumeniu@@datap.ca). Conceptual Tributes: @ref{Yasos}, MacScheme's +%object, CLOS, Lack of R4RS macros. + +@subsection Concepts +@table @asis + +@item OBJECT +An object is an ordered association-list (by @code{eq?}) of methods +(procedures). Methods can be added (@code{make-method!}), deleted +(@code{unmake-method!}) and retrieved (@code{get-method}). Objects may +inherit methods from other objects. The object binds to the environment +it was created in, allowing closures to be used to hide private +procedures and data. + +@item GENERIC-METHOD +A generic-method associates (in terms of @code{eq?}) object's method. +This allows scheme function style to be used for objects. The calling +scheme for using a generic method is @code{(generic-method object param1 +param2 ...)}. + +@item METHOD +A method is a procedure that exists in the object. To use a method +get-method must be called to look-up the method. Generic methods +implement the get-method functionality. Methods may be added to an +object associated with any scheme obj in terms of eq? + +@item GENERIC-PREDICATE +A generic method that returns a boolean value for any scheme obj. + +@item PREDICATE +A object's method asscociated with a generic-predicate. Returns +@code{#t}. +@end table + +@subsection Procedures + +@defun make-object ancestor @dots{} +Returns an object. Current object implementation is a tagged vector. +@var{ancestor}s are optional and must be objects in terms of object?. +@var{ancestor}s methods are included in the object. Multiple +@var{ancestor}s might associate the same generic-method with a method. +In this case the method of the @var{ancestor} first appearing in the +list is the one returned by @code{get-method}. +@end defun + +@defun object? obj +Returns boolean value whether @var{obj} was created by make-object. +@end defun + +@defun make-generic-method exception-procedure +Returns a procedure which be associated with an object's methods. If +@var{exception-procedure} is specified then it is used to process +non-objects. +@end defun + +@defun make-generic-predicate +Returns a boolean procedure for any scheme object. +@end defun + +@defun make-method! object generic-method method +Associates @var{method} to the @var{generic-method} in the object. The +@var{method} overrides any previous association with the +@var{generic-method} within the object. Using @code{unmake-method!} +will restore the object's previous association with the +@var{generic-method}. @var{method} must be a procedure. +@end defun + +@defun make-predicate! object generic-preciate +Makes a predicate method associated with the @var{generic-predicate}. +@end defun + +@defun unmake-method! object generic-method +Removes an object's association with a @var{generic-method} . +@end defun + +@defun get-method object generic-method +Returns the object's method associated (if any) with the +@var{generic-method}. If no associated method exists an error is +flagged. +@end defun + +@subsection Examples + +@example +(require 'object) + +(define instantiate (make-generic-method)) + +(define (make-instance-object . ancestors) + (define self (apply make-object + (map (lambda (obj) (instantiate obj)) ancestors))) + (make-method! self instantiate (lambda (self) self)) + self) + +(define who (make-generic-method)) +(define imigrate! (make-generic-method)) +(define emigrate! (make-generic-method)) +(define describe (make-generic-method)) +(define name (make-generic-method)) +(define address (make-generic-method)) +(define members (make-generic-method)) + +(define society + (let () + (define self (make-instance-object)) + (define population '()) + (make-method! self imigrate! + (lambda (new-person) + (if (not (eq? new-person self)) + (set! population (cons new-person population))))) + (make-method! self emigrate! + (lambda (person) + (if (not (eq? person self)) + (set! population + (comlist:remove-if (lambda (member) + (eq? member person)) + population))))) + (make-method! self describe + (lambda (self) + (map (lambda (person) (describe person)) population))) + (make-method! self who + (lambda (self) (map (lambda (person) (name person)) + population))) + (make-method! self members (lambda (self) population)) + self)) + +(define (make-person %name %address) + (define self (make-instance-object society)) + (make-method! self name (lambda (self) %name)) + (make-method! self address (lambda (self) %address)) + (make-method! self who (lambda (self) (name self))) + (make-method! self instantiate + (lambda (self) + (make-person (string-append (name self) "-son-of") + %address))) + (make-method! self describe + (lambda (self) (list (name self) (address self)))) + (imigrate! self) + self) +@end example + +@subsubsection Inverter Documentation +Inheritance: +@lisp + <inverter>::(<number> <description>) +@end lisp +Generic-methods +@lisp + <inverter>::value @result{} <number>::value + <inverter>::set-value! @result{} <number>::set-value! + <inverter>::describe @result{} <description>::describe + <inverter>::help + <inverter>::invert + <inverter>::inverter? +@end lisp + +@subsubsection Number Documention +Inheritance +@lisp + <number>::() +@end lisp +Slots +@lisp + <number>::<x> +@end lisp +Generic Methods +@lisp + <number>::value + <number>::set-value! +@end lisp + +@subsubsection Inverter code +@example +(require 'object) + +(define value (make-generic-method (lambda (val) val))) +(define set-value! (make-generic-method)) +(define invert (make-generic-method + (lambda (val) + (if (number? val) + (/ 1 val) + (error "Method not supported:" val))))) +(define noop (make-generic-method)) +(define inverter? (make-generic-predicate)) +(define describe (make-generic-method)) +(define help (make-generic-method)) + +(define (make-number x) + (define self (make-object)) + (make-method! self value (lambda (this) x)) + (make-method! self set-value! + (lambda (this new-value) (set! x new-value))) + self) + +(define (make-description str) + (define self (make-object)) + (make-method! self describe (lambda (this) str)) + (make-method! self help (lambda (this) "Help not available")) + self) + +(define (make-inverter) + (define self (make-object + (make-number 1) + (make-description "A number which can be inverted"))) + (define <value> (get-method self value)) + (make-method! self invert (lambda (self) (/ 1 (<value> self)))) + (make-predicate! self inverter?) + (unmake-method! self help) + (make-method! self help + (lambda (self) + (display "Inverter Methods:") (newline) + (display " (value inverter) ==> n") (newline))) + self) + +;;;; Try it out + +(define invert! (make-generic-method)) + +(define x (make-inverter)) + +(make-method! x invert! (lambda () (set-value! x (/ 1 (value x))))) + +(value x) @result{} 1 +(set-value! x 33) @result{} undefined +(invert! x) @result{} undefined +(value x) @result{} 1/33 + +(unmake-method! x invert!) @result{} undefined + +(invert! x) @error{} ERROR: Method not supported: x +@end example + +@node Parameter lists, Priority Queues, Object, Data Structures +@section Parameter lists + +@code{(require 'parameters)} + +@noindent +Arguments to procedures in scheme are distinguished from each other by +their position in the procedure call. This can be confusing when a +procedure takes many arguments, many of which are not often used. + +@noindent +A @dfn{parameter-list} is a way of passing named information to a +procedure. Procedures are also defined to set unused parameters to +default values, check parameters, and combine parameter lists. + +@noindent +A @var{parameter} has the form @code{(@r{parameter-name} @r{value1} +@dots{})}. This format allows for more than one value per +parameter-name. + +@noindent +A @var{parameter-list} is a list of @var{parameter}s, each with a +different @var{parameter-name}. + +@deffn Function make-parameter-list parameter-names +Returns an empty parameter-list with slots for @var{parameter-names}. +@end deffn + +@deffn Function parameter-list-ref parameter-list parameter-name +@var{parameter-name} must name a valid slot of @var{parameter-list}. +@code{parameter-list-ref} returns the value of parameter +@var{parameter-name} of @var{parameter-list}. +@end deffn + +@deffn Procedure adjoin-parameters! parameter-list parameter1 @dots{} +Returns @var{parameter-list} with @var{parameter1} @dots{} merged in. +@end deffn + +@deffn Procedure parameter-list-expand expanders parameter-list +@var{expanders} is a list of procedures whose order matches the order of +the @var{parameter-name}s in the call to @code{make-parameter-list} +which created @var{parameter-list}. For each non-false element of +@var{expanders} that procedure is mapped over the corresponding +parameter value and the returned parameter lists are merged into +@var{parameter-list}. + +This process is repeated until @var{parameter-list} stops growing. The +value returned from @code{parameter-list-expand} is unspecified. +@end deffn + +@deffn Function fill-empty-parameters defaults parameter-list +@var{defaults} is a list of lists whose order matches the order of the +@var{parameter-name}s in the call to @code{make-parameter-list} which +created @var{parameter-list}. @code{fill-empty-parameters} returns a +new parameter-list with each empty parameter filled with the +corresponding @var{default}. +@end deffn + +@deffn Function check-parameters checks parameter-list +@var{checks} is a list of procedures whose order matches the order of +the @var{parameter-name}s in the call to @code{make-parameter-list} +which created @var{parameter-list}. + +@code{check-parameters} returns @var{parameter-list} if each @var{check} +of the corresponding @var{parameter-list} returns non-false. If some +@var{check} returns @code{#f} an error is signaled. +@end deffn + +@noindent +In the following procedures @var{arities} is a list of symbols. The +elements of @code{arities} can be: + +@table @code +@item single +Requires a single parameter. +@item optional +A single parameter or no parameter is acceptable. +@item boolean +A single boolean parameter or zero parameters is acceptable. +@item nary +Any number of parameters are acceptable. +@item nary1 +One or more of parameters are acceptable. +@end table + +@deffn Function parameter-list->arglist positions arities types parameter-list +Returns @var{parameter-list} converted to an argument list. Parameters +of @var{arity} type @code{single} and @code{boolean} are converted to +the single value associated with them. The other @var{arity} types are +converted to lists of the value(s) of type @var{types}. + +@var{positions} is a list of positive integers whose order matches the +order of the @var{parameter-name}s in the call to +@code{make-parameter-list} which created @var{parameter-list}. The +integers specify in which argument position the corresponding parameter +should appear. +@end deffn + +@deffn Function getopt->parameter-list argc argv optnames arities types aliases +Returns @var{argv} converted to a parameter-list. @var{optnames} are +the parameter-names. @var{aliases} is a list of lists of strings and +elements of @var{optnames}. Each of these strings which have length of +1 will be treated as a single @key{-} option by @code{getopt}. Longer +strings will be treated as long-named options (@pxref{Getopt, getopt--}). +@end deffn + +@deffn Function getopt->arglist argc argv optnames positions arities types defaults checks aliases +Like @code{getopt->parameter-list}, but converts @var{argv} to an +argument-list as specified by @var{optnames}, @var{positions}, +@var{arities}, @var{types}, @var{defaults}, @var{checks}, and +@var{aliases}. +@end deffn + +These @code{getopt} functions can be used with SLIB relational +databases. For an example, @xref{Database Utilities, +make-command-server}. + +@node Priority Queues, Queues, Parameter lists, Data Structures +@section Priority Queues + +@code{(require 'priority-queue)} + +@defun make-heap pred<? +Returns a binary heap suitable which can be used for priority queue +operations. +@end defun + +@defun heap-length heap +Returns the number of elements in @var{heap}.@refill +@end defun + +@deffn Procedure heap-insert! heap item +Inserts @var{item} into @var{heap}. @var{item} can be inserted multiple +times. The value returned is unspecified.@refill +@end deffn + +@defun heap-extract-max! heap +Returns the item which is larger than all others according to the +@var{pred<?} argument to @code{make-heap}. If there are no items in +@var{heap}, an error is signaled.@refill +@end defun + +The algorithm for priority queues was taken from @cite{Introduction to +Algorithms} by T. Cormen, C. Leiserson, R. Rivest. 1989 MIT Press. + + + +@node Queues, Records, Priority Queues, Data Structures +@section Queues + +@code{(require 'queue)} + +A @dfn{queue} is a list where elements can be added to both the front +and rear, and removed from the front (i.e., they are what are often +called @dfn{dequeues}). A queue may also be used like a stack.@refill + +@defun make-queue +Returns a new, empty queue. +@end defun + +@defun queue? obj +Returns @code{#t} if @var{obj} is a queue. +@end defun + +@defun queue-empty? q +Returns @code{#t} if the queue @var{q} is empty. +@end defun + +@deffn Procedure queue-push! q datum +Adds @var{datum} to the front of queue @var{q}. +@end deffn + +@deffn Procedure enquque! q datum +Adds @var{datum} to the rear of queue @var{q}. +@end deffn + +All of the following functions raise an error if the queue @var{q} is +empty.@refill + +@defun queue-front q +Returns the datum at the front of the queue @var{q}. +@end defun + +@defun queue-rear q +Returns the datum at the rear of the queue @var{q}. +@end defun + +@deffn Prcoedure queue-pop! q +@deffnx Procedure dequeue! q +Both of these procedures remove and return the datum at the front of the +queue. @code{queue-pop!} is used to suggest that the queue is being +used like a stack.@refill +@end deffn + + + + + +@node Records, Base Table, Queues, Data Structures +@section Records + +@code{(require 'record)} + +The Record package provides a facility for user to define their own +record data types. + +@defun make-record-type type-name field-names +Returns a @dfn{record-type descriptor}, a value representing a new data +type disjoint from all others. The @var{type-name} argument must be a +string, but is only used for debugging purposes (such as the printed +representation of a record of the new type). The @var{field-names} +argument is a list of symbols naming the @dfn{fields} of a record of the +new type. It is an error if the list contains any duplicates. It is +unspecified how record-type descriptors are represented.@refill +@end defun + +@c @defun make-record-sub-type type-name field-names rtd +@c Returns a @dfn{record-type descriptor}, a value representing a new data +@c type, disjoint from all others. The @var{type-name} argument must be a +@c string. The @var{field-names} argument is a list of symbols naming the +@c additional @dfn{fields} to be appended to @var{field-names} of +@c @var{rtd}. It is an error if the combinded list contains any +@c duplicates.@refill +@c +@c Record-modifiers and record-accessors for @var{rtd} work for the new +@c record-sub-type as well. But record-modifiers and record-accessors for +@c the new record-sub-type will not neccessarily work for @var{rtd}.@refill +@c @end defun + +@defun record-constructor rtd [field-names] +Returns a procedure for constructing new members of the type represented +by @var{rtd}. The returned procedure accepts exactly as many arguments +as there are symbols in the given list, @var{field-names}; these are +used, in order, as the initial values of those fields in a new record, +which is returned by the constructor procedure. The values of any +fields not named in that list are unspecified. The @var{field-names} +argument defaults to the list of field names in the call to +@code{make-record-type} that created the type represented by @var{rtd}; +if the @var{field-names} argument is provided, it is an error if it +contains any duplicates or any symbols not in the default list.@refill +@end defun + +@defun record-predicate rtd +Returns a procedure for testing membership in the type represented by +@var{rtd}. The returned procedure accepts exactly one argument and +returns a true value if the argument is a member of the indicated record +type; it returns a false value otherwise.@refill +@end defun + +@c @defun record-sub-predicate rtd +@c Returns a procedure for testing membership in the type represented by +@c @var{rtd} or its parents. The returned procedure accepts exactly one +@c argument and returns a true value if the argument is a member of the +@c indicated record type or its parents; it returns a false value +@c otherwise.@refill +@c @end defun + +@defun record-accessor rtd field-name +Returns a procedure for reading the value of a particular field of a +member of the type represented by @var{rtd}. The returned procedure +accepts exactly one argument which must be a record of the appropriate +type; it returns the current value of the field named by the symbol +@var{field-name} in that record. The symbol @var{field-name} must be a +member of the list of field-names in the call to @code{make-record-type} +that created the type represented by @var{rtd}.@refill +@end defun + + +@defun record-modifier rtd field-name +Returns a procedure for writing the value of a particular field of a +member of the type represented by @var{rtd}. The returned procedure +accepts exactly two arguments: first, a record of the appropriate type, +and second, an arbitrary Scheme value; it modifies the field named by +the symbol @var{field-name} in that record to contain the given value. +The returned value of the modifier procedure is unspecified. The symbol +@var{field-name} must be a member of the list of field-names in the call +to @code{make-record-type} that created the type represented by +@var{rtd}.@refill +@end defun + +@defun record? obj +Returns a true value if @var{obj} is a record of any type and a false +value otherwise. Note that @code{record?} may be true of any Scheme +value; of course, if it returns true for some particular value, then +@code{record-type-descriptor} is applicable to that value and returns an +appropriate descriptor.@refill +@end defun + +@defun record-type-descriptor record +Returns a record-type descriptor representing the type of the given +record. That is, for example, if the returned descriptor were passed to +@code{record-predicate}, the resulting predicate would return a true +value when passed the given record. Note that it is not necessarily the +case that the returned descriptor is the one that was passed to +@code{record-constructor} in the call that created the constructor +procedure that created the given record.@refill +@end defun + +@defun record-type-name rtd +Returns the type-name associated with the type represented by rtd. The +returned value is @code{eqv?} to the @var{type-name} argument given in +the call to @code{make-record-type} that created the type represented by +@var{rtd}.@refill +@end defun + +@defun record-type-field-names rtd +Returns a list of the symbols naming the fields in members of the type +represented by @var{rtd}. The returned value is @code{equal?} to the +field-names argument given in the call to @code{make-record-type} that +created the type represented by @var{rtd}.@refill +@end defun + + + +@node Base Table, Relational Database, Records, Data Structures +@section Base Table + +A base table implementation using Scheme association lists is available +as the value of the identifier @code{alist-table} after doing: + +@example +(require 'alist-table) +@end example + + +Association list base tables are suitable for small databases and +support all Scheme types when temporary and readable/writeable Scheme +types when saved. I hope support for other base table implementations +will be added in the future. + +This rest of this section documents the interface for a base table +implementation from which the @ref{Relational Database} package +constructs a Relational system. It will be of interest primarily to +those wishing to port or write new base-table implementations. + +All of these functions are accessed through a single procedure by +calling that procedure with the symbol name of the operation. A +procedure will be returned if that operation is supported and @code{#f} +otherwise. For example: + +@example +@group +(require 'alist-table) +(define open-base (alist-table 'make-base)) +make-base @result{} *a procedure* +(define foo (alist-table 'foo)) +foo @result{} #f +@end group +@end example + +@defun make-base filename key-dimension column-types +Returns a new, open, low-level database (collection of tables) +associated with @var{filename}. This returned database has an empty +table associated with @var{catalog-id}. The positive integer +@var{key-dimension} is the number of keys composed to make a +@var{primary-key} for the catalog table. The list of symbols +@var{column-types} describes the types of each column for that table. +If the database cannot be created as specified, @code{#f} is returned. + +Calling the @code{close-base} method on this database and possibly other +operations will cause @var{filename} to be written to. If +@var{filename} is @code{#f} a temporary, non-disk based database will be +created if such can be supported by the base table implelentation. +@end defun + +@defun open-base filename mutable +Returns an open low-level database associated with @var{filename}. If +@var{mutable?} is @code{#t}, this database will have methods capable of +effecting change to the database. If @var{mutable?} is @code{#f}, only +methods for inquiring the database will be available. If the database +cannot be opened as specified @code{#f} is returned. + +Calling the @code{close-base} (and possibly other) method on a +@var{mutable?} database will cause @var{filename} to be written to. +@end defun + +@defun write-base lldb filename +Causes the low-level database @var{lldb} to be written to +@var{filename}. If the write is successful, also causes @var{lldb} to +henceforth be associated with @var{filename}. Calling the +@code{close-database} (and possibly other) method on @var{lldb} may +cause @var{filename} to be written to. If @var{filename} is @code{#f} +this database will be changed to a temporary, non-disk based database if +such can be supported by the underlying base table implelentation. If +the operations completed successfully, @code{#t} is returned. +Otherwise, @code{#f} is returned. +@end defun + +@defun sync-base lldb +Causes the file associated with the low-level database @var{lldb} to be +updated to reflect its current state. If the associated filename is +@code{#f}, no action is taken and @code{#f} is returned. If this +operation completes successfully, @code{#t} is returned. Otherwise, +@code{#f} is returned. +@end defun + +@defun close-base lldb +Causes the low-level database @var{lldb} to be written to its associated +file (if any). If the write is successful, subsequent operations to +@var{lldb} will signal an error. If the operations complete +successfully, @code{#t} is returned. Otherwise, @code{#f} is returned. +@end defun + +@defun make-table lldb key-dimension column-types +Returns the @var{base-id} for a new base table, otherwise returns +@code{#f}. The base table can then be opened using @code{(open-table +@var{lldb} @var{base-id})}. The positive integer @var{key-dimension} is +the number of keys composed to make a @var{primary-key} for this table. +The list of symbols @var{column-types} describes the types of each +column. +@end defun + +@defvr Constant catalog-id +A constant @var{base-id} suitable for passing as a parameter to +@code{open-table}. @var{catalog-id} will be used as the base table for +the system catalog. +@end defvr + +@defun open-table lldb base-id key-dimension column-types +Returns a @var{handle} for an existing base table in the low-level +database @var{lldb} if that table exists and can be opened in the mode +indicated by @var{mutable?}, otherwise returns @code{#f}. + +As with @code{make-table}, the positive integer @var{key-dimension} is +the number of keys composed to make a @var{primary-key} for this table. +The list of symbols @var{column-types} describes the types of each +column. +@end defun + +@defun kill-table lldb base-id key-dimension column-types +Returns @code{#t} if the base table associated with @var{base-id} was +removed from the low level database @var{lldb}, and @code{#f} otherwise. +@end defun + +@defun make-keyifier-1 type +Returns a procedure which accepts a single argument which must be of +type @var{type}. This returned procedure returns an object suitable for +being a @var{key} argument in the functions whose descriptions follow. + +Any 2 arguments of the supported type passed to the returned function +which are not @code{equal?} must result in returned values which are not +@code{equal?}. +@end defun + +@defun make-list-keyifier key-dimension types +The list of symbols @var{types} must have at least @var{key-dimension} +elements. Returns a procedure which accepts a list of length +@var{key-dimension} and whose types must corresopond to the types named +by @var{types}. This returned procedure combines the elements of its +list argument into an object suitable for being a @var{key} argument in +the functions whose descriptions follow. + +Any 2 lists of supported types (which must at least include symbols and +non-negative integers) passed to the returned function which are not +@code{equal?} must result in returned values which are not +@code{equal?}. +@end defun + +@defun make-key-extractor key-dimension types column-number +Returns a procedure which accepts objects produced by application of the +result of @code{(make-list-keyifier @var{key-dimension} @var{types})}. +This procedure returns a @var{key} which is @code{equal?} to the +@var{column-number}th element of the list which was passed to create +@var{combined-key}. The list @var{types} must have at least +@var{key-dimension} elements. +@end defun + +@defun make-key->list key-dimension types +Returns a procedure which accepts objects produced by application of the +result of @code{(make-list-keyifier @var{key-dimension} @var{types})}. +This procedure returns a list of @var{key}s which are elementwise +@code{equal?} to the list which was passed to create @var{combined-key}. +@end defun + +@noindent +In the following functions, the @var{key} argument can always be assumed +to be the value returned by a call to a @emph{keyify} routine. + +@defun for-each-key handle procedure +Calls @var{procedure} once with each @var{key} in the table opened in +@var{handle} in an unspecified order. An unspecified value is returned. +@end defun + +@defun map-key handle procedure +Returns a list of the values returned by calling @var{procedure} once +with each @var{key} in the table opened in @var{handle} in an +unspecified order. +@end defun + +@defun ordered-for-each-key handle procedure +Calls @var{procedure} once with each @var{key} in the table opened in +@var{handle} in the natural order for the types of the primary key +fields of that table. An unspecified value is returned. +@end defun + +@defun present? handle key +Returns a non-@code{#f} value if there is a row associated with +@var{key} in the table opened in @var{handle} and @code{#f} otherwise. +@end defun + +@defun delete handle key +Removes the row associated with @var{key} from the table opened in +@var{handle}. An unspecified value is returned. +@end defun + +@defun make-getter key-dimension types +Returns a procedure which takes arguments @var{handle} and @var{key}. +This procedure returns a list of the non-primary values of the relation +(in the base table opened in @var{handle}) whose primary key is +@var{key} if it exists, and @code{#f} otherwise. +@end defun + +@defun make-putter key-dimension types +Returns a procedure which takes arguments @var{handle} and @var{key} and +@var{value-list}. This procedure associates the primary key @var{key} +with the values in @var{value-list} (in the base table opened in +@var{handle}) and returns an unspecified value. +@end defun + +@defun supported-type? symbol +Returns @code{#t} if @var{symbol} names a type allowed as a column value +by the implementation, and @code{#f} otherwise. At a minimum, an +implementation must support the types @code{integer}, @code{symbol}, +@code{string}, @code{boolean}, and @code{base-id}. +@end defun + +@defun supported-key-type? symbol +Returns @code{#t} if @var{symbol} names a type allowed as a key value by +the implementation, and @code{#f} otherwise. At a minimum, an +implementation must support the types @code{integer}, and @code{symbol}. +@end defun + +@table @code +@item integer +Scheme exact integer. +@item symbol +Scheme symbol. +@item boolean +@code{#t} or @code{#f}. +@item base-id +Objects suitable for passing as the @var{base-id} parameter to +@code{open-table}. The value of @var{catalog-id} must be an acceptable +@code{base-id}. +@end table + +@node Relational Database, Weight-Balanced Trees, Base Table, Data Structures +@section Relational Database + +@code{(require 'relational-database)} + +This package implements a database system inspired by the Relational +Model (@cite{E. F. Codd, A Relational Model of Data for Large Shared +Data Banks}). An SLIB relational database implementation can be created +from any @ref{Base Table} implementation. + +@menu +* Motivations:: Database Manifesto +* Creating and Opening Relational Databases:: +* Relational Database Operations:: +* Table Operations:: +* Catalog Representation:: +* Unresolved Issues:: +* Database Utilities:: 'database-utilities +@end menu + +@node Motivations, Creating and Opening Relational Databases, Relational Database, Relational Database +@subsection Motivations + +Most nontrivial programs contain databases: Makefiles, configure +scripts, file backup, calendars, editors, source revision control, CAD +systems, display managers, menu GUIs, games, parsers, debuggers, +profilers, and even error reporting are all rife with databases. Coding +databases is such a common activity in programming that many may not be +aware of how often they do it. + +A database often starts as a dispatch in a program. The author, perhaps +because of the need to make the dispatch configurable, the need for +correlating dispatch in other routines, or because of changes or growth, +devises a data structure to contain the information, a routine for +interpreting that data structure, and perhaps routines for augmenting +and modifying the stored data. The dispatch must be converted into this +form and tested. + +The programmer may need to devise an interactive program for enabling +easy examination and modification of the information contained in this +database. Often, in an attempt to foster modularity and avoid delays in +release, intermediate file formats for the database information are +devised. It often turns out that users prefer modifying these +intermediate files with a text editor to using the interactive program +in order to do operations (such as global changes) not forseen by the +program's author. + +In order to address this need, the concientous software engineer may +even provide a scripting language to allow users to make repetitive +database changes. Users will grumble that they need to read a large +manual and learn yet another programming language (even if it +@emph{almost} has language "xyz" syntax) in order to do simple +configuration. + +All of these facilities need to be designed, coded, debugged, +documented, and supported; often causing what was very simple in concept +to become a major developement project. + +This view of databases just outlined is somewhat the reverse of the view +of the originators of the @dfn{Relational Model} of database +abstraction. The relational model was devised to unify and allow +interoperation of large multi-user databases running on diverse +platforms. A fairly general purpose "Comprehensive Language" for +database manipulations is mandated (but not specified) as part of the +relational model for databases. + +One aspect of the Relational Model of some importance is that the +"Comprehensive Language" must be expressible in some form which can be +stored in the database. This frees the programmer from having to make +programs data-driven in order to use a database. + +This package includes as one of its basic supported types Scheme +@dfn{expression}s. This type allows expressions as defined by the +Scheme standards to be stored in the database. Using @code{slib:eval} +retrieved expressions can be evaluated (in the top-level environment). +Scheme's @code{lambda} facilitates closure of environments, modularity, +etc. so that procedures (which could not be stored directly most +databases) can still be effectively retrieved. Since @code{slib:eval} +evaluates expressions in the top-level environment, built-in and user +defined procedures can be easily accessed by name. + +This package's purpose is to standardize (through a common interface) +database creation and usage in Scheme programs. The relational model's +provision for inclusion of language expressions as data as well as the +description (in tables, of course) of all of its tables assures that +relational databases are powerful enough to assume the roles currently +played by thousands of ad-hoc routines and data formats. + +@noindent +Such standardization to a relational-like model brings many benefits: + +@itemize @bullet +@item +Tables, fields, domains, and types can be dealt with by name in +programs. +@item +The underlying database implementation can be changed (for +performance or other reasons) by changing a single line of code. +@item +The formats of tables can be easily extended or changed without +altering code. +@item +Consistency checks are specified as part of the table descriptions. +Changes in checks need only occur in one place. +@item +All the configuration information which the developer wishes to group +together is easily grouped, without needing to change programs aware of +only some of these tables. +@item +Generalized report generators, interactive entry programs, and other +database utilities can be part of a shared library. The burden of +adding configurability to a program is greatly reduced. +@item +Scheme is the "comprehensive language" for these databases. Scripting +for configuration no longer needs to be in a separate language with +additional documentation. +@item +Scheme's latent types mesh well with the strict typing and logical +requirements of the relational model. +@item +Portable formats allow easy interchange of data. The included table +descriptions help prevent misinterpretation of format. +@end itemize + +@node Creating and Opening Relational Databases, Relational Database Operations, Motivations, Relational Database +@subsection Creating and Opening Relational Databases + +@defun make-relational-system base-table-implementation + +Returns a procedure implementing a relational database using the +@var{base-table-implementation}. + +All of the operations of a base table implementation are accessed +through a procedure defined by @code{require}ing that implementation. +Similarly, all of the operations of the relational database +implementation are accessed through the procedure returned by +@code{make-relational-system}. For instance, a new relational database +could be created from the procedure returned by +@code{make-relational-system} by: + +@example +(require 'alist-table) +(define relational-alist-system + (make-relational-system alist-table)) +(define create-alist-database + (relational-alist-system 'create-database)) +(define my-database + (create-alist-database "mydata.db")) +@end example +@end defun + +@noindent +What follows are the descriptions of the methods available from +relational system returned by a call to @code{make-relational-system}. + +@defun create-database filename + +Returns an open, nearly empty relational database associated with +@var{filename}. The only tables defined are the system catalog and +domain table. Calling the @code{close-database} method on this database +and possibly other operations will cause @var{filename} to be written +to. If @var{filename} is @code{#f} a temporary, non-disk based database +will be created if such can be supported by the underlying base table +implelentation. If the database cannot be created as specified +@code{#f} is returned. For the fields and layout of descriptor tables, +@xref{Catalog Representation} +@end defun + +@defun open-database filename mutable? + +Returns an open relational database associated with @var{filename}. If +@var{mutable?} is @code{#t}, this database will have methods capable of +effecting change to the database. If @var{mutable?} is @code{#f}, only +methods for inquiring the database will be available. Calling the +@code{close-database} (and possibly other) method on a @var{mutable?} +database will cause @var{filename} to be written to. If the database +cannot be opened as specified @code{#f} is returned. +@end defun + +@node Relational Database Operations, Table Operations, Creating and Opening Relational Databases, Relational Database +@subsection Relational Database Operations + +@noindent +These are the descriptions of the methods available from an open +relational database. A method is retrieved from a database by calling +the database with the symbol name of the operation. For example: + +@example +(define my-database + (create-alist-database "mydata.db")) +(define telephone-table-desc + ((my-database 'create-table) 'telephone-table-desc)) +@end example + +@defun close-database +Causes the relational database to be written to its associated file (if +any). If the write is successful, subsequent operations to this +database will signal an error. If the operations completed +successfully, @code{#t} is returned. Otherwise, @code{#f} is returned. +@end defun + +@defun write-database filename +Causes the relational database to be written to @var{filename}. If the +write is successful, also causes the database to henceforth be +associated with @var{filename}. Calling the @code{close-database} (and +possibly other) method on this database will cause @var{filename} to be +written to. If @var{filename} is @code{#f} this database will be +changed to a temporary, non-disk based database if such can be supported +by the underlying base table implelentation. If the operations +completed successfully, @code{#t} is returned. Otherwise, @code{#f} is +returned. +@end defun + +@defun table-exists? table-name +Returns @code{#t} if @var{table-name} exists in the system catalog, +otherwise returns @code{#f}. +@end defun + +@defun open-table table-name mutable? +Returns a @dfn{methods} procedure for an existing relational table in +this database if it exists and can be opened in the mode indicated by +@var{mutable?}, otherwise returns @code{#f}. +@end defun + +@noindent +These methods will be present only in databases which are +@var{mutable?}. + +@defun delete-table table-name +Removes and returns the @var{table-name} row from the system catalog if +the table or view associated with @var{table-name} gets removed from the +database, and @code{#f} otherwise. +@end defun + +@defun create-table table-desc-name +Returns a methods procedure for a new (open) relational table for +describing the columns of a new base table in this database, otherwise +returns @code{#f}. For the fields and layout of descriptor tables, +@xref{Catalog Representation}. + +@defunx create-table table-name table-desc-name +Returns a methods procedure for a new (open) relational table with +columns as described by @var{table-desc-name}, otherwise returns +@code{#f}. +@end defun + +@defun create-view ?? +@defunx project-table ?? +@defunx restrict-table ?? +@defunx cart-prod-tables ?? +Not yet implemented. +@end defun + +@node Table Operations, Catalog Representation, Relational Database Operations, Relational Database +@subsection Table Operations + +@noindent +These are the descriptions of the methods available from an open +relational table. A method is retrieved from a table by calling +the table with the symbol name of the operation. For example: + +@example +@group +(define telephone-table-desc + ((my-database 'create-table) 'telephone-table-desc)) +(require 'common-list-functions) +(define ndrp (telephone-table-desc 'row:insert)) +(ndrp '(1 #t name #f string)) +(ndrp '(2 #f telephone + (lambda (d) + (and (string? d) (> (string-length d) 2) + (every + (lambda (c) + (memv c '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 + #\+ #\( #\ #\) #\-))) + (string->list d)))) + string)) +@end group +@end example + +@noindent +Operations on a single column of a table are retrieved by giving the +column name as the second argument to the methods procedure. For +example: + +@example +(define column-ids ((telephone-table-desc 'get* 'column-number))) +@end example + +@noindent +Some operations described below require primary key arguments. Primary +keys arguments are denoted @var{key1} @var{key2} @dots{}. It is an +error to call an operation for a table which takes primary key arguments +with the wrong number of primary keys for that table. + +@noindent +The term @dfn{row} used below refers to a Scheme list of values (one for +each column) in the order specified in the descriptor (table) for this +table. Missing values appear as @code{#f}. Primary keys may not +be missing. + +@defun get key1 key2 @dots{} +Returns the value for the specified column of the row associated with +primary keys @var{key1}, @var{key2} @dots{} if it exists, or @code{#f} +otherwise. + +@defunx get* +Returns a list of the values for the specified column for all rows in +this table. + +@defunx row:retrieve key1 key2 @dots{} +Returns the row associated with primary keys @var{key1}, @var{key2} +@dots{} if it exists, or @code{#f} otherwise. + +@defunx row:retrieve* +Returns a list of all rows in this table. +@end defun + +@defun row:remove key1 key2 @dots{} +Removes and returns the row associated with primary keys @var{key1}, +@var{key2} @dots{} if it exists, or @code{#f} otherwise. + +@defunx row:remove* +Removes and returns a list of all rows in this table. +@end defun + +@defun row:delete key1 key2 @dots{} +Deletes the row associated with primary keys @var{key1}, @var{key2} +@dots{} if it exists. The value returned is unspecified. + +@defunx row:delete* +Deletes all rows in this table. The value returned is unspecified. The +descriptor table and catalog entry for this table are not affected. +@end defun + +@defun row:update row +Adds the row, @var{row}, to this table. If a row for the primary key(s) +specified by @var{row} already exists in this table, it will be +overwritten. The value returned is unspecified. + +@defunx row:update* rows +Adds each row in the list @var{rows}, to this table. If a row for the +primary key specified by an element of @var{rows} already exists in this +table, it will be overwritten. The value returned is unspecified. +@end defun + +@defun row:insert row +Adds the row @var{row} to this table. If a row for the primary key(s) +specified by @var{row} already exists in this table an error is +signaled. The value returned is unspecified. + +@defunx row:insert* rows +Adds each row in the list @var{rows}, to this table. If a row for the +primary key specified by an element of @var{rows} already exists in this +table, an error is signaled. The value returned is unspecified. +@end defun + +@defun for-each-row proc +Calls @var{proc} with each @var{row} in this table in the natural +ordering for the primary key types. @emph{Real} relational programmers +would use some least-upper-bound join for every row to get them in +order; But we don't have joins yet. +@end defun + +@defun close-table +Subsequent operations to this table will signal an error. +@end defun + +@defvr Constant column-names +@defvrx Constant column-foreigns +@defvrx Constant column-domains +@defvrx Constant column-types +Return a list of the column names, foreign-key table names, domain +names, or type names respectively for this table. These 4 methods are +different from the others in that the list is returned, rather than a +procedure to obtain the list. + +@defvrx Constant primary-limit +Returns the number of primary keys fields in the relations in this +table. +@end defvr + +@node Catalog Representation, Unresolved Issues, Table Operations, Relational Database +@subsection Catalog Representation + +@noindent +Each database (in an implementation) has a @dfn{system catalog} which +describes all the user accessible tables in that database (including +itself). + +@noindent +The system catalog base table has the following fields. @code{PRI} +indicates a primary key for that table. + +@example +@group +PRI table-name + column-limit the highest column number + coltab-name descriptor table name + bastab-id data base table identifier + user-integrity-rule + view-procedure A scheme thunk which, when called, + produces a handle for the view. coltab + and bastab are specified if and only if + view-procedure is not. +@end group +@end example + +@noindent +Descriptors for base tables (not views) are tables (pointed to by +system catalog). Descriptor (base) tables have the fields: + +@example +@group +PRI column-number sequential integers from 1 + primary-key? boolean TRUE for primary key components + column-name + column-integrity-rule + domain-name +@end group +@end example + +@noindent +A @dfn{primary key} is any column marked as @code{primary-key?} in the +corresponding descriptor table. All the @code{primary-key?} columns +must have lower column numbers than any non-@code{primary-key?} columns. +Every table must have at least one primary key. Primary keys must be +sufficient to distinguish all rows from each other in the table. All of +the system defined tables have a single primary key. + +@noindent +This package currently supports tables having from 1 to 4 primary keys +if there are non-primary columns, and any (natural) number if @emph{all} +columns are primary keys. If you need more than 4 primary keys, I would +like to hear what you are doing! + +@noindent +A @dfn{domain} is a category describing the allowable values to occur in +a column. It is described by a (base) table with the fields: + +@example +@group +PRI domain-name + foreign-table + domain-integrity-rule + type-id + type-param +@end group +@end example + +@noindent +The @dfn{type-id} field value is a symbol. This symbol may be used by +the underlying base table implementation in storing that field. + +@noindent +If the @code{foreign-table} field is non-@code{#f} then that field names +a table from the catalog. The values for that domain must match a +primary key of the table referenced by the @var{type-param} (or +@code{#f}, if allowed). This package currently does not support +composite foreign-keys. + +@noindent +The types for which support is planned are: +@example +@group + atom + symbol + string [<length>] + number [<base>] + money <currency> + date-time + boolean + + foreign-key <table-name> + expression + virtual <expression> +@end group +@end example + +@node Unresolved Issues, Database Utilities, Catalog Representation, Relational Database +@subsection Unresolved Issues + +Although @file{rdms.scm} is not large I found it very difficult to write +(six rewrites). I am not aware of any other examples of a generalized +relational system (although there is little new in CS). I left out +several aspects of the Relational model in order to simplify the job. +The major features lacking (which might be addressed portably) are +views, transaction boundaries, and protection. + +Protection needs a model for specifying priveledges. Given how +operations are accessed from handles it should not be difficult to +restrict table accesses to those allowed for that user. + +The system catalog has a field called @code{view-procedure}. This +should allow a purely functional implementation of views. This will +work but is unsatisfying for views resulting from a @dfn{select}ion +(subset of rows); for whole table operations it will not be possible to +reduce the number of keys scanned over when the selection is specified +only by an opaque procedure. + +Transaction boundaries present the most intriguing area. Transaction +boundaries are actually a feature of the "Comprehensive Language" of the +Relational database and not of the database. Scheme would seem to +provide the opportunity for an extremely clean semantics for transaction +boundaries since the builtin procedures with side effects are small in +number and easily identified. + +These side-effect builtin procedures might all be portably redefined to +versions which properly handled transactions. Compiled library routines +would need to be recompiled as well. Many system extensions +(delete-file, system, etc.) would also need to be redefined. + +@noindent +There are 2 scope issues that must be resolved for multiprocess +transaction boundaries: + +@table @asis +@item Process scope +The actions captured by a transaction should be only for the process +which invoked the start of transaction. Although standard Scheme does +not provide process primitives as such, @code{dynamic-wind} would +provide a workable hook into process switching for many implementations. +@item Shared utilities with state +Some shared utilities have state which should @emph{not} be part of a +transaction. An example would be calling a pseudo-random number +generator. If the success of a transaction depended on the +pseudo-random number and failed, the state of the generator would be set +back. Subsequent calls would keep returning the same number and keep +failing. + +Pseudo-random number generators are not reentrant and so would require +locks in order to operate properly in a multiprocess environment. Are +all examples of utilities whose state should not part of transactions +also non-reentrant? If so, perhaps suspending transaction capture for +the duration of locks would fix it. +@end table + +@node Database Utilities, , Unresolved Issues, Relational Database +@subsection Database Utilities + +@code{(require 'database-utilities)} + +@noindent +This enhancement wraps a utility layer on @code{relational-database} +which provides: +@itemize @bullet +@item +Automatic loading of the appropriate base-table package when opening a +database. +@item +Automatic execution of initialization commands stored in database. +@item +Transparent execution of database commands stored in @code{*commands*} +table in database. +@end itemize + +@noindent +Also included are utilities which provide: +@itemize @bullet +@item +Data definition from Scheme lists and +@item +Report generation +@end itemize +@noindent +for any SLIB relational database. + +@defun create-database filename base-table-type +Returns an open, nearly empty enhanced (with @code{*commands*} table) +relational database (with base-table type @var{base-table-type}) +associated with @var{filename}. +@end defun + +@defun open-database filename +@defunx open-database filename base-table-type +Returns an open enchanced relational database associated with +@var{filename}. The database will be opened with base-table type +@var{base-table-type}) if supplied. If @var{base-table-type} is not +supplied, @code{open-database} will attempt to deduce the correct +base-table-type. If the database can not be opened or if it lacks the +@code{*commands*} table, @code{#f} is returned. + +@defunx open-database! filename +@defunx open-database! filename base-table-type +Returns @emph{mutable} open enchanced relational database @dots{} +@end defun + +@noindent +The table @code{*commands*} in an @dfn{enhanced} relational-database has +the fields (with domains): +@example +@group +PRI name symbol + parameters parameter-list + procedure expression + documentation string +@end group +@end example + +The @code{parameters} field is a foreign key (domain +@code{parameter-list}) of the @code{*catalog-data*} table and should +have the value of a table described by @code{*parameter-columns*}. This +@code{parameter-list} table describes the arguments suitable for passing +to the associated command. The intent of this table is to be of a form +such that different user-interfaces (for instance, pull-down menus or +plain-text queries) can operate from the same table. A +@code{parameter-list} table has the following fields: +@example +@group +PRI index uint + name symbol + arity parameter-arity + domain domain + default expression + documentation string +@end group +@end example + +The @code{arity} field can take the values: + +@table @code +@item single +Requires a single parameter of the specified domain. +@item optional +A single parameter of the specified domain or zero parameters is +acceptable. +@item boolean +A single boolean parameter or zero parameters (in which case @code{#f} +is substituted) is acceptable. +@item nary +Any number of parameters of the specified domain are acceptable. The +argument passed to the command function is always a list of the +parameters. +@item nary1 +One or more of parameters of the specified domain are acceptable. The +argument passed to the command function is always a list of the +parameters. +@end table + +The @code{domain} field specifies the domain which a parameter or +parameters in the @code{index}th field must satisfy. + +The @code{default} field is an expression whose value is either +@code{#f} or a procedure of no arguments which returns a parameter or +parameter list as appropriate. If the expression's value is @code{#f} +then no default is appropriate for this parameter. Note that since the +@code{default} procedure is called every time a default parameter is +needed for this column, @dfn{sticky} defaults can be implemented using +shared state with the domain-integrity-rule. + +@subsubheading Invoking Commands + +When an enhanced relational-database is called with a symbol which +matches a @var{name} in the @code{*commands*} table, the associated +procedure expression is evaluated and applied to the enhanced +relational-database. A procedure should then be returned which the user +can invoke on (optional) arguments. + +The command @code{*initialize*} is special. If present in the +@code{*commands*} table, @code{open-database} or @code{open-database!} +will return the value of the @code{*initialize*} command. Notice that +arbitrary code can be run when the @code{*initialize*} procedure is +automatically applied to the enhanced relational-database. + +Note also that if you wish to shadow or hide from the user +relational-database methods described in @ref{Relational Database +Operations}, this can be done by a dispatch in the closure returned by +the @code{*initialize*} expression rather than by entries in the +@code{*commands*} table if it is desired that the underlying methods +remain accessible to code in the @code{*commands*} table. + +@defun make-command-server rdb table-name +Returns a procedure of 2 arguments, a (symbol) command and a call-back +procedure. When this returned procedure is called, it looks up +@var{command} in table @var{table-name} and calls the call-back +procedure with arguments: +@table @var +@item command +The @var{command} +@item command-value +The result of evaluating the expression in the @var{procedure} field of +@var{table-name} and calling it with @var{rdb}. +@item parameter-name +A list of the @dfn{official} name of each parameter. Corresponds to the +@code{name} field of the @var{command}'s parameter-table. +@item positions +A list of the positive integer index of each parameter. Corresponds to +the @code{index} field of the @var{command}'s parameter-table. +@item arities +A list of the arities of each parameter. Corresponds to the +@code{arity} field of the @var{command}'s parameter-table. For a +description of @code{arity} see table above. +@item defaults +A list of the defaults for each parameter. Corresponds to +the @code{defaults} field of the @var{command}'s parameter-table. +@item domain-integrity-rules +A list of procedures (one for each parameter) which tests whether a +value for a parameter is acceptable for that parameter. The procedure +should be called with each datum in the list for @code{nary} arity +parameters. +@item aliases +A list of lists of @code{(@r{alias} @r{parameter-name})}. There can be +more than one alias per @var{parameter-name}. +@end table +@end defun + +For information about parameters, @xref{Parameter lists}. Here is an +example of setting up a command with arguments and parsing those +arguments from a @code{getopt} style argument list (@pxref{Getopt}). + +@example +(require 'database-utilities) +(require 'parameters) +(require 'getopt) + +(define my-rdb (create-database #f 'alist-table)) + +(define-tables my-rdb + '(foo-params + *parameter-columns* + *parameter-columns* + ((1 first-argument single string "hithere" "first argument") + (2 flag boolean boolean #f "a flag"))) + '(foo-pnames + ((name string)) + ((parameter-index uint)) + (("l" 1) + ("a" 2))) + '(my-commands + ((name symbol)) + ((parameters parameter-list) + (parameter-names parameter-name-translation) + (procedure expression) + (documentation string)) + ((foo + foo-params + foo-pnames + (lambda (rdb) (lambda (foo aflag) (print foo aflag))) + "test command arguments")))) + +(define (dbutil:serve-command-line rdb command-table + command argc argv) + (set! argv (if (vector? argv) (vector->list argv) argv)) + ((make-command-server rdb command-table) + command + (lambda (comname comval options positions + arities types defaults dirs aliases) + (apply comval (getopt->arglist argc argv options positions + arities types defaults dirs aliases))))) + +(define (test) + (set! *optind* 1) + (dbutil:serve-command-line + my-rdb 'my-commands 'foo 4 '("dummy" "-l" "foo" "-a"))) +(test) +@print{} +"foo" #t +@end example + +Some commands are defined in all extended relational-databases. The are +called just like @ref{Relational Database Operations}. + +@defun add-domain domain-row +Adds @var{domain-row} to the @dfn{domains} table if there is no row in +the domains table associated with key @code{(car @var{domain-row})} and +returns @code{#t}. Otherwise returns @code{#f}. + +For the fields and layout of the domain table, @xref{Catalog +Representation} +@end defun + +@defun delete-domain domain-name +Removes and returns the @var{domain-name} row from the @dfn{domains} +table. +@end defun + +@defun domain-checker domain +Returns a procedure to check an argument for conformance to domain +@var{domain}. +@end defun + +@subheading Defining Tables + +@deffn Procedure define-tables rdb spec-0 @dots{} +Adds tables as specified in @var{spec-0} @dots{} to the open +relational-database @var{rdb}. Each @var{spec} has the form: + +@lisp +(@r{<name>} @r{<descriptor-name>} @r{<descriptor-name>} @r{<rows>}) +@end lisp +or +@lisp +(@r{<name>} @r{<primary-key-fields>} @r{<other-fields>} @r{<rows>}) +@end lisp + +where @r{<name>} is the table name, @r{<descriptor-name>} is the symbol +name of a descriptor table, @r{<primary-key-fields>} and +@r{<other-fields>} describe the primary keys and other fields +respectively, and @r{<rows>} is a list of data rows to be added to the +table. + +@r{<primary-key-fields>} and @r{<other-fields>} are lists of field +descriptors of the form: + +@lisp +(@r{<column-name>} @r{<domain>}) +@end lisp +or +@lisp +(@r{<column-name>} @r{<domain>} @r{<column-integrity-rule>}) +@end lisp + +where @r{<column-name>} is the column name, @r{<domain>} is the domain +of the column, and @r{<column-integrity-rule>} is an expression whose +value is a procedure of one argument (and returns non-@code{#f} to +signal an error). + +If @r{<domain>} is not a defined domain name and it matches the name of +this table or an already defined (in one of @var{spec-0} @dots{}) single +key field table, a foriegn-key domain will be created for it. +@end deffn + + +@deffn Procedure create-report rdb destination report-name table +@deffnx Procedure create-report rdb destination report-name +The symbol @var{report-name} must be primary key in the table named +@code{*reports*} in the relational database @var{rdb}. +@var{destination} is a port, string, or symbol. If @var{destination} is +a: + +@table @asis +@item port +The table is created as ascii text and written to that port. +@item string +The table is created as ascii text and written to the file named by +@var{destination}. +@item symbol +@var{destination} is the primary key for a row in the table named *printers*. +@end table + +Each row in the table *reports* has the fields: + +@table @asis +@item name +The report name. +@item default-table +The table to report on if none is specified. +@item header, footer +A @code{format} string. At the beginning and end of each page +respectively, @code{format} is called with this string and the (list of) +column-names of this table. +@item reporter +A @code{format} string. For each row in the table, @code{format} is +called with this string and the row. +@item minimum-break +The minimum number of lines into which the report lines for a row can be +broken. Use @code{0} if a row's lines should not be broken over page +boundaries. +@end table + +Each row in the table *printers* has the fields: + +@table @asis +@item name +The printer name. +@item print-procedure +The procedure to call to actually print. +@end table + +The report is prepared as follows: + +@itemize +@item +@code{Format} (@pxref{Format}) is called with the @code{header} field +and the (list of) @code{column-names} of the table. +@item +@code{Format} is called with the @code{reporter} field and (on +successive calls) each record in the natural order for the table. A +count is kept of the number of newlines output by format. When the +number of newlines to be output exceeds the number of lines per page, +the set of lines will be broken if there are more than +@code{minimum-break} left on this page and the number of lines for this +row is larger or equal to twice @code{minimum-break}. +@item +@code{Format} is called with the @code{footer} field and the (list of) +@code{column-names} of the table. The footer field should not output a +newline. +@item +A new page is output. +@item +This entire process repeats until all the rows are output. +@end itemize +@end deffn + +@noindent +The following example shows a new database with the name of +@file{foo.db} being created with tables describing processor families +and processor/os/compiler combinations. + +@noindent +The database command @code{define-tables} is defined to call +@code{define-tables} with its arguments. The database is also +configured to print @samp{Welcome} when the database is opened. The +database is then closed and reopened. + +@example +(require 'database-utilities) +(define my-rdb (create-database "foo.db" 'alist-table)) + +(define-tables my-rdb + '(*commands* + ((name symbol)) + ((parameters parameter-list) + (procedure expression) + (documentation string)) + ((define-tables + no-parameters + no-parameter-names + (lambda (rdb) (lambda specs (apply define-tables rdb specs))) + "Create or Augment tables from list of specs") + (*initialize* + no-parameters + no-parameter-names + (lambda (rdb) (display "Welcome") (newline) rdb) + "Print Welcome")))) + +((my-rdb 'define-tables) + '(processor-family + ((family atom)) + ((also-ran processor-family)) + ((m68000 #f) + (m68030 m68000) + (i386 8086) + (8086 #f) + (powerpc #f))) + + '(platform + ((name symbol)) + ((processor processor-family) + (os symbol) + (compiler symbol)) + ((aix powerpc aix -) + (amiga-dice-c m68000 amiga dice-c) + (amiga-aztec m68000 amiga aztec) + (amiga-sas/c-5.10 m68000 amiga sas/c) + (atari-st-gcc m68000 atari gcc) + (atari-st-turbo-c m68000 atari turbo-c) + (borland-c-3.1 8086 ms-dos borland-c) + (djgpp i386 ms-dos gcc) + (linux i386 linux gcc) + (microsoft-c 8086 ms-dos microsoft-c) + (os/2-emx i386 os/2 gcc) + (turbo-c-2 8086 ms-dos turbo-c) + (watcom-9.0 i386 ms-dos watcom)))) + +((my-rdb 'close-database)) + +(set! my-rdb (open-database "foo.db" 'alist-table)) +@print{} +Welcome +@end example + +@node Weight-Balanced Trees, Structures, Relational Database, Data Structures +@section Weight-Balanced Trees + +@code{(require 'wt-tree)} + +@cindex trees, balanced binary +@cindex balanced binary trees +@cindex binary trees +@cindex weight-balanced binary trees +Balanced binary trees are a useful data structure for maintaining large +sets of ordered objects or sets of associations whose keys are ordered. +MIT Scheme has an comprehensive implementation of weight-balanced binary +trees which has several advantages over the other data structures for +large aggregates: + +@itemize @bullet +@item +In addition to the usual element-level operations like insertion, +deletion and lookup, there is a full complement of collection-level +operations, like set intersection, set union and subset test, all of +which are implemented with good orders of growth in time and space. +This makes weight balanced trees ideal for rapid prototyping of +functionally derived specifications. + +@item +An element in a tree may be indexed by its position under the ordering +of the keys, and the ordinal position of an element may be determined, +both with reasonable efficiency. + +@item +Operations to find and remove minimum element make weight balanced trees +simple to use for priority queues. + +@item +The implementation is @emph{functional} rather than @emph{imperative}. +This means that operations like `inserting' an association in a tree do +not destroy the old tree, in much the same way that @code{(+ 1 x)} +modifies neither the constant 1 nor the value bound to @code{x}. The +trees are referentially transparent thus the programmer need not worry +about copying the trees. Referential transparency allows space +efficiency to be achieved by sharing subtrees. + +@end itemize + +These features make weight-balanced trees suitable for a wide range of +applications, especially those that +require large numbers of sets or discrete maps. Applications that have +a few global databases and/or concentrate on element-level operations like +insertion and lookup are probably better off using hash-tables or +red-black trees. + +The @emph{size} of a tree is the number of associations that it +contains. Weight balanced binary trees are balanced to keep the sizes +of the subtrees of each node within a constant factor of each other. +This ensures logarithmic times for single-path operations (like lookup +and insertion). A weight balanced tree takes space that is proportional +to the number of associations in the tree. For the current +implementation, the constant of proportionality is six words per +association. + +@cindex binary trees, as sets +@cindex binary trees, as discrete maps +@cindex sets, using binary trees +@cindex discrete maps, using binary trees +Weight balanced trees can be used as an implementation for either +discrete sets or discrete maps (associations). Sets are implemented by +ignoring the datum that is associated with the key. Under this scheme +if an associations exists in the tree this indicates that the key of the +association is a member of the set. Typically a value such as +@code{()}, @code{#t} or @code{#f} is associated with the key. + +Many operations can be viewed as computing a result that, depending on +whether the tree arguments are thought of as sets or maps, is known by +two different names. +An example is @code{wt-tree/member?}, which, when +regarding the tree argument as a set, computes the set membership operation, but, +when regarding the tree as a discrete map, @code{wt-tree/member?} is the +predicate testing if the map is defined at an element in its domain. +Most names in this package have been chosen based on interpreting the +trees as sets, hence the name @code{wt-tree/member?} rather than +@code{wt-tree/defined-at?}. + + +@cindex run-time-loadable option +@cindex option, run-time-loadable +The weight balanced tree implementation is a run-time-loadable option. +To use weight balanced trees, execute + +@example +(load-option 'wt-tree) +@end example +@findex load-option + +@noindent +once before calling any of the procedures defined here. + + +@menu +* Construction of Weight-Balanced Trees:: +* Basic Operations on Weight-Balanced Trees:: +* Advanced Operations on Weight-Balanced Trees:: +* Indexing Operations on Weight-Balanced Trees:: +@end menu + +@node Construction of Weight-Balanced Trees, Basic Operations on Weight-Balanced Trees, Weight-Balanced Trees, Weight-Balanced Trees +@subsection Construction of Weight-Balanced Trees + +Binary trees require there to be a total order on the keys used to +arrange the elements in the tree. Weight balanced trees are organized +by @emph{types}, where the type is an object encapsulating the ordering +relation. Creating a tree is a two-stage process. First a tree type +must be created from the predicate which gives the ordering. The tree type +is then used for making trees, either empty or singleton trees or trees +from other aggregate structures like association lists. Once created, a +tree `knows' its type and the type is used to test compatibility between +trees in operations taking two trees. Usually a small number of tree +types are created at the beginning of a program and used many times +throughout the program's execution. + +@deffn {procedure+} make-wt-tree-type key<? +This procedure creates and returns a new tree type based on the ordering +predicate @var{key<?}. +@var{Key<?} must be a total ordering, having the property that for all +key values @code{a}, @code{b} and @code{c}: + +@example +(key<? a a) @result{} #f +(and (key<? a b) (key<? b a)) @result{} #f +(if (and (key<? a b) (key<? b c)) + (key<? a c) + #t) @result{} #t +@end example + +@noindent +Two key values are assumed to be equal if neither is less than the other +by @var{key<?}. + +Each call to @code{make-wt-tree-type} returns a distinct value, and +trees are only compatible if their tree types are @code{eq?}. +A consequence is +that trees that are intended to be used in binary tree operations must all be +created with a tree type originating from the same call to +@code{make-wt-tree-type}. +@end deffn + +@defvr {variable+} number-wt-type +A standard tree type for trees with numeric keys. @code{Number-wt-type} +could have been defined by + +@example +(define number-wt-type (make-wt-tree-type <)) +@end example +@end defvr + +@defvr {variable+} string-wt-type +A standard tree type for trees with string keys. @code{String-wt-type} +could have been defined by + +@example +(define string-wt-type (make-wt-tree-type string<?)) +@end example +@end defvr + + + +@deffn {procedure+} make-wt-tree wt-tree-type +This procedure creates and returns a newly allocated weight balanced +tree. The tree is empty, i.e. it contains no associations. +@var{Wt-tree-type} is a weight balanced tree type obtained by calling +@code{make-wt-tree-type}; the returned tree has this type. +@end deffn + +@deffn {procedure+} singleton-wt-tree wt-tree-type key datum +This procedure creates and returns a newly allocated weight balanced +tree. The tree contains a single association, that of @var{datum} with +@var{key}. @var{Wt-tree-type} is a weight balanced tree type obtained +by calling @code{make-wt-tree-type}; the returned tree has this type. +@end deffn + +@deffn {procedure+} alist->wt-tree tree-type alist +Returns a newly allocated weight-balanced tree that contains the same +associations as @var{alist}. This procedure is equivalent to: + +@example +(lambda (type alist) + (let ((tree (make-wt-tree type))) + (for-each (lambda (association) + (wt-tree/add! tree + (car association) + (cdr association))) + alist) + tree)) +@end example +@end deffn + + + +@node Basic Operations on Weight-Balanced Trees, Advanced Operations on Weight-Balanced Trees, Construction of Weight-Balanced Trees, Weight-Balanced Trees +@subsection Basic Operations on Weight-Balanced Trees + +This section describes the basic tree operations on weight balanced +trees. These operations are the usual tree operations for insertion, +deletion and lookup, some predicates and a procedure for determining the +number of associations in a tree. + +@deffn {procedure+} wt-tree? object +Returns @code{#t} if @var{object} is a weight-balanced tree, otherwise +returns @code{#f}. +@end deffn + +@deffn {procedure+} wt-tree/empty? wt-tree +Returns @code{#t} if @var{wt-tree} contains no associations, otherwise +returns @code{#f}. +@end deffn + +@deffn {procedure+} wt-tree/size wt-tree +Returns the number of associations in @var{wt-tree}, an exact +non-negative integer. This operation takes constant time. +@end deffn + + +@deffn {procedure+} wt-tree/add wt-tree key datum +Returns a new tree containing all the associations in @var{wt-tree} and +the association of @var{datum} with @var{key}. If @var{wt-tree} already +had an association for @var{key}, the new association overrides the old. +The average and worst-case times required by this operation are +proportional to the logarithm of the number of associations in +@var{wt-tree}. +@end deffn + +@deffn {procedure+} wt-tree/add! wt-tree key datum +Associates @var{datum} with @var{key} in @var{wt-tree} and returns an +unspecified value. If @var{wt-tree} already has an association for +@var{key}, that association is replaced. The average and worst-case +times required by this operation are proportional to the logarithm of +the number of associations in @var{wt-tree}. +@end deffn + +@deffn {procedure+} wt-tree/member? key wt-tree +Returns @code{#t} if @var{wt-tree} contains an association for +@var{key}, otherwise returns @code{#f}. The average and worst-case +times required by this operation are proportional to the logarithm of +the number of associations in @var{wt-tree}. +@end deffn + +@deffn {procedure+} wt-tree/lookup wt-tree key default +Returns the datum associated with @var{key} in @var{wt-tree}. If +@var{wt-tree} doesn't contain an association for @var{key}, +@var{default} is returned. The average and worst-case times required by +this operation are proportional to the logarithm of the number of +associations in @var{wt-tree}. +@end deffn + +@deffn {procedure+} wt-tree/delete wt-tree key +Returns a new tree containing all the associations in @var{wt-tree}, +except that if @var{wt-tree} contains an association for @var{key}, it +is removed from the result. The average and worst-case times required +by this operation are proportional to the logarithm of the number of +associations in @var{wt-tree}. +@end deffn + +@deffn {procedure+} wt-tree/delete! wt-tree key +If @var{wt-tree} contains an association for @var{key} the association +is removed. Returns an unspecified value. The average and worst-case +times required by this operation are proportional to the logarithm of +the number of associations in @var{wt-tree}. +@end deffn + + +@node Advanced Operations on Weight-Balanced Trees, Indexing Operations on Weight-Balanced Trees, Basic Operations on Weight-Balanced Trees, Weight-Balanced Trees +@subsection Advanced Operations on Weight-Balanced Trees + +In the following the @emph{size} of a tree is the number of associations +that the tree contains, and a @emph{smaller} tree contains fewer +associations. + +@deffn {procedure+} wt-tree/split< wt-tree bound +Returns a new tree containing all and only the associations in +@var{wt-tree} which have a key that is less than @var{bound} in the +ordering relation of the tree type of @var{wt-tree}. The average and +worst-case times required by this operation are proportional to the +logarithm of the size of @var{wt-tree}. +@end deffn + +@deffn {procedure+} wt-tree/split> wt-tree bound +Returns a new tree containing all and only the associations in +@var{wt-tree} which have a key that is greater than @var{bound} in the +ordering relation of the tree type of @var{wt-tree}. The average and +worst-case times required by this operation are proportional to the +logarithm of size of @var{wt-tree}. +@end deffn + +@deffn {procedure+} wt-tree/union wt-tree-1 wt-tree-2 +Returns a new tree containing all the associations from both trees. +This operation is asymmetric: when both trees have an association for +the same key, the returned tree associates the datum from @var{wt-tree-2} +with the key. Thus if the trees are viewed as discrete maps then +@code{wt-tree/union} computes the map override of @var{wt-tree-1} by +@var{wt-tree-2}. If the trees are viewed as sets the result is the set +union of the arguments. +The worst-case time required by this operation +is proportional to the sum of the sizes of both trees. +If the minimum key of one tree is greater than the maximum key of +the other tree then the time required is at worst proportional to +the logarithm of the size of the larger tree. +@end deffn + +@deffn {procedure+} wt-tree/intersection wt-tree-1 wt-tree-2 +Returns a new tree containing all and only those associations from +@var{wt-tree-1} which have keys appearing as the key of an association +in @var{wt-tree-2}. Thus the associated data in the result are those +from @var{wt-tree-1}. If the trees are being used as sets the result is +the set intersection of the arguments. As a discrete map operation, +@code{wt-tree/intersection} computes the domain restriction of +@var{wt-tree-1} to (the domain of) @var{wt-tree-2}. +The time required by this operation is never worse that proportional to +the sum of the sizes of the trees. +@end deffn + +@deffn {procedure+} wt-tree/difference wt-tree-1 wt-tree-2 +Returns a new tree containing all and only those associations from +@var{wt-tree-1} which have keys that @emph{do not} appear as the key of +an association in @var{wt-tree-2}. If the trees are viewed as sets the +result is the asymmetric set difference of the arguments. As a discrete +map operation, it computes the domain restriction of @var{wt-tree-1} to +the complement of (the domain of) @var{wt-tree-2}. +The time required by this operation is never worse that proportional to +the sum of the sizes of the trees. +@end deffn + + +@deffn {procedure+} wt-tree/subset? wt-tree-1 wt-tree-2 +Returns @code{#t} iff the key of each association in @var{wt-tree-1} is +the key of some association in @var{wt-tree-2}, otherwise returns @code{#f}. +Viewed as a set operation, @code{wt-tree/subset?} is the improper subset +predicate. +A proper subset predicate can be constructed: + +@example +(define (proper-subset? s1 s2) + (and (wt-tree/subset? s1 s2) + (< (wt-tree/size s1) (wt-tree/size s2)))) +@end example + +As a discrete map operation, @code{wt-tree/subset?} is the subset +test on the domain(s) of the map(s). In the worst-case the time +required by this operation is proportional to the size of +@var{wt-tree-1}. +@end deffn + + +@deffn {procedure+} wt-tree/set-equal? wt-tree-1 wt-tree-2 +Returns @code{#t} iff for every association in @var{wt-tree-1} there is +an association in @var{wt-tree-2} that has the same key, and @emph{vice +versa}. + +Viewing the arguments as sets @code{wt-tree/set-equal?} is the set +equality predicate. As a map operation it determines if two maps are +defined on the same domain. + +This procedure is equivalent to + +@example +(lambda (wt-tree-1 wt-tree-2) + (and (wt-tree/subset? wt-tree-1 wt-tree-2 + (wt-tree/subset? wt-tree-2 wt-tree-1))) +@end example + +In the worst-case the time required by this operation is proportional to +the size of the smaller tree. +@end deffn + + +@deffn {procedure+} wt-tree/fold combiner initial wt-tree +This procedure reduces @var{wt-tree} by combining all the associations, +using an reverse in-order traversal, so the associations are visited in +reverse order. @var{Combiner} is a procedure of three arguments: a key, +a datum and the accumulated result so far. Provided @var{combiner} +takes time bounded by a constant, @code{wt-tree/fold} takes time +proportional to the size of @var{wt-tree}. + +A sorted association list can be derived simply: + +@example +(wt-tree/fold (lambda (key datum list) + (cons (cons key datum) list)) + '() + @var{wt-tree})) +@end example + +The data in the associations can be summed like this: + +@example +(wt-tree/fold (lambda (key datum sum) (+ sum datum)) + 0 + @var{wt-tree}) +@end example +@end deffn + +@deffn {procedure+} wt-tree/for-each action wt-tree +This procedure traverses the tree in-order, applying @var{action} to +each association. +The associations are processed in increasing order of their keys. +@var{Action} is a procedure of two arguments which take the key and +datum respectively of the association. +Provided @var{action} takes time bounded by a constant, +@code{wt-tree/for-each} takes time proportional to in the size of +@var{wt-tree}. +The example prints the tree: + +@example +(wt-tree/for-each (lambda (key value) + (display (list key value))) + @var{wt-tree})) +@end example +@end deffn + + +@node Indexing Operations on Weight-Balanced Trees, , Advanced Operations on Weight-Balanced Trees, Weight-Balanced Trees +@subsection Indexing Operations on Weight-Balanced Trees + +Weight balanced trees support operations that view the tree as sorted +sequence of associations. Elements of the sequence can be accessed by +position, and the position of an element in the sequence can be +determined, both in logarthmic time. + +@deffn {procedure+} wt-tree/index wt-tree index +@deffnx {procedure+} wt-tree/index-datum wt-tree index +@deffnx {procedure+} wt-tree/index-pair wt-tree index +Returns the 0-based @var{index}th association of @var{wt-tree} in the +sorted sequence under the tree's ordering relation on the keys. +@code{wt-tree/index} returns the @var{index}th key, +@code{wt-tree/index-datum} returns the datum associated with the +@var{index}th key and @code{wt-tree/index-pair} returns a new pair +@code{(@var{key} . @var{datum})} which is the @code{cons} of the @var{index}th +key and its datum. The average and worst-case times required by this +operation are proportional to the logarithm of the number of +associations in the tree. + +These operations signal an error if the tree is empty, if +@var{index}@code{<0}, or if @var{index} is greater than or equal to the +number of associations in the tree. + +Indexing can be used to find the median and maximum keys in the tree as +follows: + +@example +median: (wt-tree/index @var{wt-tree} (quotient (wt-tree/size @var{wt-tree}) 2)) + +maximum: (wt-tree/index @var{wt-tree} (-1+ (wt-tree/size @var{wt-tree}))) +@end example +@end deffn + +@deffn {procedure+} wt-tree/rank wt-tree key +Determines the 0-based position of @var{key} in the sorted sequence of +the keys under the tree's ordering relation, or @code{#f} if the tree +has no association with for @var{key}. This procedure returns either an +exact non-negative integer or @code{#f}. The average and worst-case +times required by this operation are proportional to the logarithm of +the number of associations in the tree. +@end deffn + +@deffn {procedure+} wt-tree/min wt-tree +@deffnx {procedure+} wt-tree/min-datum wt-tree +@deffnx {procedure+} wt-tree/min-pair wt-tree +Returns the association of @var{wt-tree} that has the least key under the tree's ordering relation. +@code{wt-tree/min} returns the least key, +@code{wt-tree/min-datum} returns the datum associated with the +least key and @code{wt-tree/min-pair} returns a new pair +@code{(key . datum)} which is the @code{cons} of the minimum key and its datum. +The average and worst-case times required by this operation are +proportional to the logarithm of the number of associations in the tree. + +These operations signal an error if the tree is empty. +They could be written +@example +(define (wt-tree/min tree) (wt-tree/index tree 0)) +(define (wt-tree/min-datum tree) (wt-tree/index-datum tree 0)) +(define (wt-tree/min-pair tree) (wt-tree/index-pair tree 0)) +@end example +@end deffn + +@deffn {procedure+} wt-tree/delete-min wt-tree +Returns a new tree containing all of the associations in @var{wt-tree} +except the association with the least key under the @var{wt-tree}'s +ordering relation. An error is signalled if the tree is empty. The +average and worst-case times required by this operation are proportional +to the logarithm of the number of associations in the tree. This +operation is equivalent to + +@example +(wt-tree/delete @var{wt-tree} (wt-tree/min @var{wt-tree})) +@end example +@end deffn + + +@deffn {procedure+} wt-tree/delete-min! wt-tree +Removes the association with the least key under the @var{wt-tree}'s +ordering relation. An error is signalled if the tree is empty. The +average and worst-case times required by this operation are proportional +to the logarithm of the number of associations in the tree. This +operation is equivalent to + +@example +(wt-tree/delete! @var{wt-tree} (wt-tree/min @var{wt-tree})) +@end example +@end deffn + + + +@node Structures, , Weight-Balanced Trees, Data Structures +@section Structures + +@code{(require 'struct)} (uses defmacros) + +@code{defmacro}s which implement @dfn{records} from the book +@cite{Essentials of Programming Languages} by Daniel P. Friedman, M. +Wand and C.T. Haynes. Copyright 1992 Jeff Alexander, Shinnder Lee, and +Lewis Patterson@refill + +Matthew McDonald <mafm@@cs.uwa.edu.au> added field setters. + +@defmac define-record tag (var1 var2 @dots{}) +Defines several functions pertaining to record-name @var{tag}: + +@defun make-@var{tag} var1 var2 @dots{} +@end defun +@defun @var{tag}? obj +@end defun +@defun @var{tag}->var1 obj +@end defun +@defun @var{tag}->var2 obj +@end defun +@dots{} +@defun set-@var{@var{tag}}-var1! obj val +@end defun +@defun set-@var{@var{tag}}-var2! obj val +@end defun +@dots{} + +Here is an example of its use. + +@example +(define-record term (operator left right)) +@result{} #<unspecified> +(define foo (make-term 'plus 1 2)) +@result{} foo +(term-left foo) +@result{} 1 +(set-term-left! foo 2345) +@result{} #<unspecified> +(term-left foo) +@result{} 2345 +@end example +@end defmac + +@defmac variant-case exp (tag (var1 var2 @dots{}) body) @dots{} +executes the following for the matching clause: + +@example +((lambda (@var{var1} @var{var} @dots{}) @var{body}) + (@var{tag->var1} @var{exp}) + (@var{tag->var2} @var{exp}) @dots{}) +@end example +@end defmac + +@node Macros, Numerics, Data Structures, Top +@chapter Macros +@menu +* Defmacro:: Supported by all implementations + +* R4RS Macros:: 'macro +* Macro by Example:: 'macro-by-example +* Macros That Work:: 'macros-that-work +* Syntactic Closures:: 'syntactic-closures +* Syntax-Case Macros:: 'syntax-case + +Syntax extensions (macros) included with SLIB. Also @xref{Structures}. + +* Fluid-Let:: 'fluid-let +* Yasos:: 'yasos, 'oop, 'collect +@end menu + + +@node Defmacro, R4RS Macros, Macros, Macros +@section Defmacro + +Defmacros are supported by all implementations. +@c See also @code{gentemp}, in @ref{Macros}. + +@defun gentemp +Returns a new (interned) symbol each time it is called. The symbol +names are implementation-dependent +@lisp +(gentemp) @result{} scm:G0 +(gentemp) @result{} scm:G1 +@end lisp +@end defun + +@defun defmacro:eval e +Returns the @code{slib:eval} of expanding all defmacros in scheme +expression @var{e}. +@end defun + +@defun defmacro:load filename +@var{filename} should be a string. If filename names an existing file, +the @code{defmacro:load} procedure reads Scheme source code expressions +and definitions from the file and evaluates them sequentially. These +source code expressions and definitions may contain defmacro +definitions. The @code{macro:load} procedure does not affect the values +returned by @code{current-input-port} and +@code{current-output-port}.@refill +@end defun + +@defun defmacro? sym +Returns @code{#t} if @var{sym} has been defined by @code{defmacro}, +@code{#f} otherwise. +@end defun + +@defun macroexpand-1 form +@defunx macroexpand form +If @var{form} is a macro call, @code{macroexpand-1} will expand the +macro call once and return it. A @var{form} is considered to be a macro +call only if it is a cons whose @code{car} is a symbol for which a +@code{defmacr} has been defined. + +@code{macroexpand} is similar to @code{macroexpand-1}, but repeatedly +expands @var{form} until it is no longer a macro call. +@end defun + +@defmac defmacro name lambda-list form @dots{} +When encountered by @code{defmacro:eval}, @code{defmacro:macroexpand*}, +or @code{defmacro:load} defines a new macro which will henceforth be +expanded when encountered by @code{defmacro:eval}, +@code{defmacro:macroexpand*}, or @code{defmacro:load}. +@end defmac + +@subsection Defmacroexpand +@code{(require 'defmacroexpand)} + +@defun defmacro:expand* e +Returns the result of expanding all defmacros in scheme expression +@var{e}. +@end defun + +@node R4RS Macros, Macro by Example, Defmacro, Macros +@section R4RS Macros + +@code{(require 'macro)} is the appropriate call if you want R4RS +high-level macros but don't care about the low level implementation. If +an SLIB R4RS macro implementation is already loaded it will be used. +Otherwise, one of the R4RS macros implemetations is loaded. + +The SLIB R4RS macro implementations support the following uniform +interface: + +@defun macro:expand sexpression +Takes an R4RS expression, macro-expands it, and returns the result of +the macro expansion. +@end defun + +@defun macro:eval sexpression +Takes an R4RS expression, macro-expands it, evals the result of the +macro expansion, and returns the result of the evaluation. +@end defun + +@deffn Procedure macro:load filename +@var{filename} should be a string. If filename names an existing file, +the @code{macro:load} procedure reads Scheme source code expressions and +definitions from the file and evaluates them sequentially. These source +code expressions and definitions may contain macro definitions. The +@code{macro:load} procedure does not affect the values returned by +@code{current-input-port} and @code{current-output-port}.@refill +@end deffn + +@node Macro by Example, Macros That Work, R4RS Macros, Macros +@section Macro by Example + +@code{(require 'macro-by-example)} + +A vanilla implementation of @cite{Macro by Example} (Eugene Kohlbecker, +R4RS) by Dorai Sitaram, (dorai@@cs.rice.edu) using @code{defmacro}. + +@itemize @bullet + +@item +generating hygienic global @code{define-syntax} Macro-by-Example macros +@strong{cheaply}. + +@item +can define macros which use @code{...}. + +@item +needn't worry about a lexical variable in a macro definition +clashing with a variable from the macro use context + +@item +don't suffer the overhead of redefining the repl if @code{defmacro} +natively supported (most implementations) + +@end itemize +@subsection Caveat +These macros are not referentially transparent (@pxref{Macros, , ,r4rs, +Revised(4) Scheme}). Lexically scoped macros (i.e., @code{let-syntax} +and @code{letrec-syntax}) are not supported. In any case, the problem +of referential transparency gains poignancy only when @code{let-syntax} +and @code{letrec-syntax} are used. So you will not be courting +large-scale disaster unless you're using system-function names as local +variables with unintuitive bindings that the macro can't use. However, +if you must have the full @cite{r4rs} macro functionality, look to the +more featureful (but also more expensive) versions of syntax-rules +available in slib @ref{Macros That Work}, @ref{Syntactic Closures}, and +@ref{Syntax-Case Macros}. + +@defmac define-syntax keyword transformer-spec +The @var{keyword} is an identifier, and the @var{transformer-spec} +should be an instance of @code{syntax-rules}. + +The top-level syntactic environment is extended by binding the +@var{keyword} to the specified transformer. + +@example +(define-syntax let* + (syntax-rules () + ((let* () body1 body2 ...) + (let () body1 body2 ...)) + ((let* ((name1 val1) (name2 val2) ...) + body1 body2 ...) + (let ((name1 val1)) + (let* (( name2 val2) ...) + body1 body2 ...))))) +@end example +@end defmac + +@defmac syntax-rules literals syntax-rule @dots{} +@var{literals} is a list of identifiers, and each @var{syntax-rule} +should be of the form + +@code{(@var{pattern} @var{template})} + +where the @var{pattern} and @var{template} are as in the grammar above. + +An instance of @code{syntax-rules} produces a new macro transformer by +specifying a sequence of hygienic rewrite rules. A use of a macro whose +keyword is associated with a transformer specified by +@code{syntax-rules} is matched against the patterns contained in the +@var{syntax-rule}s, beginning with the leftmost @var{syntax-rule}. +When a match is found, the macro use is trancribed hygienically +according to the template. + +Each pattern begins with the keyword for the macro. This keyword is not +involved in the matching and is not considered a pattern variable or +literal identifier. +@end defmac + +@node Macros That Work, Syntactic Closures, Macro by Example, Macros +@section Macros That Work + +@code{(require 'macros-that-work)} + +@cite{Macros That Work} differs from the other R4RS macro +implementations in that it does not expand derived expression types to +primitive expression types. + +@defun macro:expand expression +@defunx macwork:expand expression +Takes an R4RS expression, macro-expands it, and returns the result of +the macro expansion. +@end defun + +@defun macro:eval expression +@defunx macwork:eval expression +@code{macro:eval} returns the value of @var{expression} in the current +top level environment. @var{expression} can contain macro definitions. +Side effects of @var{expression} will affect the top level +environment.@refill +@end defun + +@deffn Procedure macro:load filename +@deffnx Procedure macwork:load filename +@var{filename} should be a string. If filename names an existing file, +the @code{macro:load} procedure reads Scheme source code expressions and +definitions from the file and evaluates them sequentially. These source +code expressions and definitions may contain macro definitions. The +@code{macro:load} procedure does not affect the values returned by +@code{current-input-port} and @code{current-output-port}.@refill +@end deffn + +References: + +The @cite{Revised^4 Report on the Algorithmic Language Scheme} Clinger +and Rees [editors]. To appear in LISP Pointers. Also available as a +technical report from the University of Oregon, MIT AI Lab, and +Cornell.@refill + +@center Macros That Work. Clinger and Rees. POPL '91. + +The supported syntax differs from the R4RS in that vectors are allowed +as patterns and as templates and are not allowed as pattern or template +data. + +@example +transformer spec @expansion{} (syntax-rules literals rules) + +rules @expansion{} () + | (rule . rules) + +rule @expansion{} (pattern template) + +pattern @expansion{} pattern_var ; a symbol not in literals + | symbol ; a symbol in literals + | () + | (pattern . pattern) + | (ellipsis_pattern) + | #(pattern*) ; extends R4RS + | #(pattern* ellipsis_pattern) ; extends R4RS + | pattern_datum + +template @expansion{} pattern_var + | symbol + | () + | (template2 . template2) + | #(template*) ; extends R4RS + | pattern_datum + +template2 @expansion{} template + | ellipsis_template + +pattern_datum @expansion{} string ; no vector + | character + | boolean + | number + +ellipsis_pattern @expansion{} pattern ... + +ellipsis_template @expansion{} template ... + +pattern_var @expansion{} symbol ; not in literals + +literals @expansion{} () + | (symbol . literals) +@end example + +@subsection Definitions + +@table @asis + +@item Scope of an ellipsis +Within a pattern or template, the scope of an ellipsis (@code{...}) is +the pattern or template that appears to its left. + +@item Rank of a pattern variable +The rank of a pattern variable is the number of ellipses within whose +scope it appears in the pattern. + +@item Rank of a subtemplate +The rank of a subtemplate is the number of ellipses within whose scope +it appears in the template. + +@item Template rank of an occurrence of a pattern variable +The template rank of an occurrence of a pattern variable within a +template is the rank of that occurrence, viewed as a subtemplate. + +@item Variables bound by a pattern +The variables bound by a pattern are the pattern variables that appear +within it. + +@item Referenced variables of a subtemplate +The referenced variables of a subtemplate are the pattern variables that +appear within it. + +@item Variables opened by an ellipsis template +The variables opened by an ellipsis template are the referenced pattern +variables whose rank is greater than the rank of the ellipsis template. + +@end table + +@subsection Restrictions + +No pattern variable appears more than once within a pattern. + +For every occurrence of a pattern variable within a template, the +template rank of the occurrence must be greater than or equal to the +pattern variable's rank. + +Every ellipsis template must open at least one variable. + +For every ellipsis template, the variables opened by an ellipsis +template must all be bound to sequences of the same length. + +The compiled form of a @var{rule} is + +@example +rule @expansion{} (pattern template inserted) + +pattern @expansion{} pattern_var + | symbol + | () + | (pattern . pattern) + | ellipsis_pattern + | #(pattern) + | pattern_datum + +template @expansion{} pattern_var + | symbol + | () + | (template2 . template2) + | #(pattern) + | pattern_datum + +template2 @expansion{} template + | ellipsis_template + +pattern_datum @expansion{} string + | character + | boolean + | number + +pattern_var @expansion{} #(V symbol rank) + +ellipsis_pattern @expansion{} #(E pattern pattern_vars) + +ellipsis_template @expansion{} #(E template pattern_vars) + +inserted @expansion{} () + | (symbol . inserted) + +pattern_vars @expansion{} () + | (pattern_var . pattern_vars) + +rank @expansion{} exact non-negative integer +@end example + +where V and E are unforgeable values. + +The pattern variables associated with an ellipsis pattern are the +variables bound by the pattern, and the pattern variables associated +with an ellipsis template are the variables opened by the ellipsis +template. + +If the template contains a big chunk that contains no pattern variables +or inserted identifiers, then the big chunk will be copied +unnecessarily. That shouldn't matter very often. + + + + + +@node Syntactic Closures, Syntax-Case Macros, Macros That Work, Macros +@section Syntactic Closures + +@code{(require 'syntactic-closures)} + +@defun macro:expand expression +@defunx synclo:expand expression +Returns scheme code with the macros and derived expression types of +@var{expression} expanded to primitive expression types.@refill +@end defun + +@defun macro:eval expression +@defunx synclo:eval expression +@code{macro:eval} returns the value of @var{expression} in the current +top level environment. @var{expression} can contain macro definitions. +Side effects of @var{expression} will affect the top level +environment.@refill +@end defun + +@deffn Procedure macro:load filename +@deffnx Procedure synclo:load filename +@var{filename} should be a string. If filename names an existing file, +the @code{macro:load} procedure reads Scheme source code expressions and +definitions from the file and evaluates them sequentially. These +source code expressions and definitions may contain macro definitions. +The @code{macro:load} procedure does not affect the values returned by +@code{current-input-port} and @code{current-output-port}.@refill +@end deffn + +@subsection Syntactic Closure Macro Facility + +@center A Syntactic Closures Macro Facility +@center by Chris Hanson +@center 9 November 1991 + +This document describes @dfn{syntactic closures}, a low-level macro +facility for the Scheme programming language. The facility is an +alternative to the low-level macro facility described in the +@cite{Revised^4 Report on Scheme.} This document is an addendum to that +report. + +The syntactic closures facility extends the BNF rule for +@var{transformer spec} to allow a new keyword that introduces a +low-level macro transformer:@refill +@example +@var{transformer spec} := (transformer @var{expression}) +@end example + +Additionally, the following procedures are added: +@lisp +make-syntactic-closure +capture-syntactic-environment +identifier? +identifier=? +@end lisp + +The description of the facility is divided into three parts. The first +part defines basic terminology. The second part describes how macro +transformers are defined. The third part describes the use of +@dfn{identifiers}, which extend the syntactic closure mechanism to be +compatible with @code{syntax-rules}.@refill + +@subsubsection Terminology + +This section defines the concepts and data types used by the syntactic +closures facility. + +@itemize + +@item @dfn{Forms} are the syntactic entities out of which programs are +recursively constructed. A form is any expression, any definition, any +syntactic keyword, or any syntactic closure. The variable name that +appears in a @code{set!} special form is also a form. Examples of +forms:@refill +@lisp +17 +#t +car +(+ x 4) +(lambda (x) x) +(define pi 3.14159) +if +define +@end lisp + +@item An @dfn{alias} is an alternate name for a given symbol. It can +appear anywhere in a form that the symbol could be used, and when quoted +it is replaced by the symbol; however, it does not satisfy the predicate +@code{symbol?}. Macro transformers rarely distinguish symbols from +aliases, referring to both as identifiers.@refill + +@item A @dfn{syntactic} environment maps identifiers to their +meanings. More precisely, it determines whether an identifier is a +syntactic keyword or a variable. If it is a keyword, the meaning is an +interpretation for the form in which that keyword appears. If it is a +variable, the meaning identifies which binding of that variable is +referenced. In short, syntactic environments contain all of the +contextual information necessary for interpreting the meaning of a +particular form.@refill + +@item A @dfn{syntactic closure} consists of a form, a syntactic +environment, and a list of identifiers. All identifiers in the form +take their meaning from the syntactic environment, except those in the +given list. The identifiers in the list are to have their meanings +determined later. A syntactic closure may be used in any context in +which its form could have been used. Since a syntactic closure is also +a form, it may not be used in contexts where a form would be illegal. +For example, a form may not appear as a clause in the cond special form. +A syntactic closure appearing in a quoted structure is replaced by its +form.@refill + +@end itemize + +@subsubsection Transformer Definition + +This section describes the @code{transformer} special form and the +procedures @code{make-syntactic-closure} and +@code{capture-syntactic-environment}.@refill + +@deffn Syntax transformer expression + +Syntax: It is an error if this syntax occurs except as a +@var{transformer spec}.@refill + +Semantics: The @var{expression} is evaluated in the standard transformer +environment to yield a macro transformer as described below. This macro +transformer is bound to a macro keyword by the special form in which the +@code{transformer} expression appears (for example, +@code{let-syntax}).@refill + +A @dfn{macro transformer} is a procedure that takes two arguments, a +form and a syntactic environment, and returns a new form. The first +argument, the @dfn{input form}, is the form in which the macro keyword +occurred. The second argument, the @dfn{usage environment}, is the +syntactic environment in which the input form occurred. The result of +the transformer, the @dfn{output form}, is automatically closed in the +@dfn{transformer environment}, which is the syntactic environment in +which the @code{transformer} expression occurred.@refill + +For example, here is a definition of a push macro using +@code{syntax-rules}:@refill +@lisp +(define-syntax push + (syntax-rules () + ((push item list) + (set! list (cons item list))))) +@end lisp + +Here is an equivalent definition using @code{transformer}: +@lisp +(define-syntax push + (transformer + (lambda (exp env) + (let ((item + (make-syntactic-closure env '() (cadr exp))) + (list + (make-syntactic-closure env '() (caddr exp)))) + `(set! ,list (cons ,item ,list)))))) +@end lisp + +In this example, the identifiers @code{set!} and @code{cons} are closed +in the transformer environment, and thus will not be affected by the +meanings of those identifiers in the usage environment +@code{env}.@refill + +Some macros may be non-hygienic by design. For example, the following +defines a loop macro that implicitly binds @code{exit} to an escape +procedure. The binding of @code{exit} is intended to capture free +references to @code{exit} in the body of the loop, so @code{exit} must +be left free when the body is closed:@refill +@lisp +(define-syntax loop + (transformer + (lambda (exp env) + (let ((body (cdr exp))) + `(call-with-current-continuation + (lambda (exit) + (let f () + ,@@(map (lambda (exp) + (make-syntactic-closure env '(exit) + exp)) + body) + (f)))))))) +@end lisp + +To assign meanings to the identifiers in a form, use +@code{make-syntactic-closure} to close the form in a syntactic +environment.@refill +@end deffn + +@defun make-syntactic-closure environment free-names form + +@var{environment} must be a syntactic environment, @var{free-names} must +be a list of identifiers, and @var{form} must be a form. +@code{make-syntactic-closure} constructs and returns a syntactic closure +of @var{form} in @var{environment}, which can be used anywhere that +@var{form} could have been used. All the identifiers used in +@var{form}, except those explicitly excepted by @var{free-names}, obtain +their meanings from @var{environment}.@refill + +Here is an example where @var{free-names} is something other than the +empty list. It is instructive to compare the use of @var{free-names} in +this example with its use in the @code{loop} example above: the examples +are similar except for the source of the identifier being left +free.@refill +@lisp +(define-syntax let1 + (transformer + (lambda (exp env) + (let ((id (cadr exp)) + (init (caddr exp)) + (exp (cadddr exp))) + `((lambda (,id) + ,(make-syntactic-closure env (list id) exp)) + ,(make-syntactic-closure env '() init)))))) +@end lisp + +@code{let1} is a simplified version of @code{let} that only binds a +single identifier, and whose body consists of a single expression. When +the body expression is syntactically closed in its original syntactic +environment, the identifier that is to be bound by @code{let1} must be +left free, so that it can be properly captured by the @code{lambda} in +the output form.@refill + +To obtain a syntactic environment other than the usage environment, use +@code{capture-syntactic-environment}.@refill +@end defun + +@defun capture-syntactic-environment procedure + +@code{capture-syntactic-environment} returns a form that will, when +transformed, call @var{procedure} on the current syntactic environment. +@var{procedure} should compute and return a new form to be transformed, +in that same syntactic environment, in place of the form.@refill + +An example will make this clear. Suppose we wanted to define a simple +@code{loop-until} keyword equivalent to@refill +@lisp +(define-syntax loop-until + (syntax-rules () + ((loop-until id init test return step) + (letrec ((loop + (lambda (id) + (if test return (loop step))))) + (loop init))))) +@end lisp + +The following attempt at defining @code{loop-until} has a subtle bug: +@lisp +(define-syntax loop-until + (transformer + (lambda (exp env) + (let ((id (cadr exp)) + (init (caddr exp)) + (test (cadddr exp)) + (return (cadddr (cdr exp))) + (step (cadddr (cddr exp))) + (close + (lambda (exp free) + (make-syntactic-closure env free exp)))) + `(letrec ((loop + (lambda (,id) + (if ,(close test (list id)) + ,(close return (list id)) + (loop ,(close step (list id))))))) + (loop ,(close init '()))))))) +@end lisp + +This definition appears to take all of the proper precautions to prevent +unintended captures. It carefully closes the subexpressions in their +original syntactic environment and it leaves the @code{id} identifier +free in the @code{test}, @code{return}, and @code{step} expressions, so +that it will be captured by the binding introduced by the @code{lambda} +expression. Unfortunately it uses the identifiers @code{if} and +@code{loop} within that @code{lambda} expression, so if the user of +@code{loop-until} just happens to use, say, @code{if} for the +identifier, it will be inadvertently captured.@refill + +The syntactic environment that @code{if} and @code{loop} want to be +exposed to is the one just outside the @code{lambda} expression: before +the user's identifier is added to the syntactic environment, but after +the identifier loop has been added. +@code{capture-syntactic-environment} captures exactly that environment +as follows:@refill +@lisp +(define-syntax loop-until + (transformer + (lambda (exp env) + (let ((id (cadr exp)) + (init (caddr exp)) + (test (cadddr exp)) + (return (cadddr (cdr exp))) + (step (cadddr (cddr exp))) + (close + (lambda (exp free) + (make-syntactic-closure env free exp)))) + `(letrec ((loop + ,(capture-syntactic-environment + (lambda (env) + `(lambda (,id) + (,(make-syntactic-closure env '() `if) + ,(close test (list id)) + ,(close return (list id)) + (,(make-syntactic-closure env '() + `loop) + ,(close step (list id))))))))) + (loop ,(close init '()))))))) +@end lisp + +In this case, having captured the desired syntactic environment, it is +convenient to construct syntactic closures of the identifiers @code{if} +and the @code{loop} and use them in the body of the +@code{lambda}.@refill + +A common use of @code{capture-syntactic-environment} is to get the +transformer environment of a macro transformer:@refill +@lisp +(transformer + (lambda (exp env) + (capture-syntactic-environment + (lambda (transformer-env) + ...)))) +@end lisp +@end defun + +@subsubsection Identifiers + +This section describes the procedures that create and manipulate +identifiers. Previous syntactic closure proposals did not have an +identifier data type -- they just used symbols. The identifier data +type extends the syntactic closures facility to be compatible with the +high-level @code{syntax-rules} facility.@refill + +As discussed earlier, an identifier is either a symbol or an +@dfn{alias}. An alias is implemented as a syntactic closure whose +@dfn{form} is an identifier:@refill +@lisp +(make-syntactic-closure env '() 'a) + @result{} an @dfn{alias} +@end lisp + +Aliases are implemented as syntactic closures because they behave just +like syntactic closures most of the time. The difference is that an +alias may be bound to a new value (for example by @code{lambda} or +@code{let-syntax}); other syntactic closures may not be used this way. +If an alias is bound, then within the scope of that binding it is looked +up in the syntactic environment just like any other identifier.@refill + +Aliases are used in the implementation of the high-level facility +@code{syntax-rules}. A macro transformer created by @code{syntax-rules} +uses a template to generate its output form, substituting subforms of +the input form into the template. In a syntactic closures +implementation, all of the symbols in the template are replaced by +aliases closed in the transformer environment, while the output form +itself is closed in the usage environment. This guarantees that the +macro transformation is hygienic, without requiring the transformer to +know the syntactic roles of the substituted input subforms. + +@defun identifier? object +Returns @code{#t} if @var{object} is an identifier, otherwise returns +@code{#f}. Examples:@refill +@lisp +(identifier? 'a) + @result{} #t +(identifier? (make-syntactic-closure env '() 'a)) + @result{} #t +(identifier? "a") + @result{} #f +(identifier? #\a) + @result{} #f +(identifier? 97) + @result{} #f +(identifier? #f) + @result{} #f +(identifier? '(a)) + @result{} #f +(identifier? '#(a)) + @result{} #f +@end lisp + +The predicate @code{eq?} is used to determine if two identifers are +``the same''. Thus @code{eq?} can be used to compare identifiers +exactly as it would be used to compare symbols. Often, though, it is +useful to know whether two identifiers ``mean the same thing''. For +example, the @code{cond} macro uses the symbol @code{else} to identify +the final clause in the conditional. A macro transformer for +@code{cond} cannot just look for the symbol @code{else}, because the +@code{cond} form might be the output of another macro transformer that +replaced the symbol @code{else} with an alias. Instead the transformer +must look for an identifier that ``means the same thing'' in the usage +environment as the symbol @code{else} means in the transformer +environment.@refill +@end defun + +@defun identifier=? environment1 identifier1 environment2 identifier2 +@var{environment1} and @var{environment2} must be syntactic +environments, and @var{identifier1} and @var{identifier2} must be +identifiers. @code{identifier=?} returns @code{#t} if the meaning of +@var{identifier1} in @var{environment1} is the same as that of +@var{identifier2} in @var{environment2}, otherwise it returns @code{#f}. +Examples:@refill + +@lisp +(let-syntax + ((foo + (transformer + (lambda (form env) + (capture-syntactic-environment + (lambda (transformer-env) + (identifier=? transformer-env 'x env 'x))))))) + (list (foo) + (let ((x 3)) + (foo)))) + @result{} (#t #f) +@end lisp + +@lisp +(let-syntax ((bar foo)) + (let-syntax + ((foo + (transformer + (lambda (form env) + (capture-syntactic-environment + (lambda (transformer-env) + (identifier=? transformer-env 'foo + env (cadr form)))))))) + (list (foo foo) + (foobar)))) + @result{} (#f #t) +@end lisp +@end defun + +@subsubsection Acknowledgements + +The syntactic closures facility was invented by Alan Bawden and Jonathan +Rees. The use of aliases to implement @code{syntax-rules} was invented +by Alan Bawden (who prefers to call them @dfn{synthetic names}). Much +of this proposal is derived from an earlier proposal by Alan +Bawden.@refill + + + + + +@node Syntax-Case Macros, Fluid-Let, Syntactic Closures, Macros +@section Syntax-Case Macros + +@code{(require 'syntax-case)} + +@defun macro:expand expression +@defunx syncase:expand expression +Returns scheme code with the macros and derived expression types of +@var{expression} expanded to primitive expression types.@refill +@end defun + +@defun macro:eval expression +@defunx syncase:eval expression +@code{macro:eval} returns the value of @var{expression} in the current +top level environment. @var{expression} can contain macro definitions. +Side effects of @var{expression} will affect the top level +environment.@refill +@end defun + +@deffn Procedure macro:load filename +@deffnx Procedure syncase:load filename +@var{filename} should be a string. If filename names an existing file, +the @code{macro:load} procedure reads Scheme source code expressions and +definitions from the file and evaluates them sequentially. These +source code expressions and definitions may contain macro definitions. +The @code{macro:load} procedure does not affect the values returned by +@code{current-input-port} and @code{current-output-port}.@refill +@end deffn + +This is version 2.1 of @code{syntax-case}, the low-level macro facility +proposed and implemented by Robert Hieb and R. Kent Dybvig. + +This version is further adapted by Harald Hanche-Olsen +<hanche@@imf.unit.no> to make it compatible with, and easily usable +with, SLIB. Mainly, these adaptations consisted of: + +@itemize @bullet +@item +Removing white space from @file{expand.pp} to save space in the +distribution. This file is not meant for human readers anyway@dots{} + +@item +Removed a couple of Chez scheme dependencies. + +@item +Renamed global variables used to minimize the possibility of name +conflicts. + +@item +Adding an SLIB-specific initialization file. + +@item +Removing a couple extra files, most notably the documentation (but see +below). +@end itemize + +If you wish, you can see exactly what changes were done by reading the +shell script in the file @file{syncase.sh}. + +The two PostScript files were omitted in order to not burden the SLIB +distribution with them. If you do intend to use @code{syntax-case}, +however, you should get these files and print them out on a PostScript +printer. They are available with the original @code{syntax-case} +distribution by anonymous FTP in +@file{cs.indiana.edu:/pub/scheme/syntax-case}.@refill + +In order to use syntax-case from an interactive top level, execute: +@lisp +(require 'syntax-case) +(require 'repl) +(repl:top-level macro:eval) +@end lisp +See the section Repl (@xref{Repl}) for more information. + +To check operation of syntax-case get +@file{cs.indiana.edu:/pub/scheme/syntax-case}, and type +@lisp +(require 'syntax-case) +(syncase:sanity-check) +@end lisp + +Beware that @code{syntax-case} takes a long time to load -- about 20s on +a SPARCstation SLC (with SCM) and about 90s on a Macintosh SE/30 (with +Gambit). + +@subsection Notes + +All R4RS syntactic forms are defined, including @code{delay}. Along +with @code{delay} are simple definitions for @code{make-promise} (into +which @code{delay} expressions expand) and @code{force}.@refill + +@code{syntax-rules} and @code{with-syntax} (described in @cite{TR356}) +are defined.@refill + +@code{syntax-case} is actually defined as a macro that expands into +calls to the procedure @code{syntax-dispatch} and the core form +@code{syntax-lambda}; do not redefine these names.@refill + +Several other top-level bindings not documented in TR356 are created: +@itemize +@item the ``hooks'' in @file{hooks.ss} +@item the @code{build-} procedures in @file{output.ss} +@item @code{expand-syntax} (the expander) +@end itemize + +The syntax of define has been extended to allow @code{(define @var{id})}, +which assigns @var{id} to some unspecified value.@refill + +We have attempted to maintain R4RS compatibility where possible. The +incompatibilities should be confined to @file{hooks.ss}. Please let us +know if there is some incompatibility that is not flagged as such.@refill + +Send bug reports, comments, suggestions, and questions to Kent Dybvig +(dyb@@iuvax.cs.indiana.edu). + +@subsection Note from maintainer + +Included with the @code{syntax-case} files was @file{structure.scm} +which defines a macro @code{define-structure}. There is no +documentation for this macro and it is not used by any code in SLIB. + +@node Fluid-Let, Yasos, Syntax-Case Macros, Macros +@section Fluid-Let + +@code{(require 'fluid-let)} + +@deffn Syntax fluid-let @code{(@var{bindings} @dots{})} @var{forms}@dots{} +@end deffn +@lisp +(fluid-let ((@var{variable} @var{init}) @dots{}) + @var{expression} @var{expression} @dots{}) +@end lisp + +The @var{init}s are evaluated in the current environment (in some +unspecified order), the current values of the @var{variable}s are saved, +the results are assigned to the @var{variable}s, the @var{expression}s +are evaluated sequentially in the current environment, the +@var{variable}s are restored to their original values, and the value of +the last @var{expression} is returned.@refill + +The syntax of this special form is similar to that of @code{let}, but +@code{fluid-let} temporarily rebinds existing @var{variable}s. Unlike +@code{let}, @code{fluid-let} creates no new bindings; instead it +@emph{assigns} the values of each @var{init} to the binding (determined +by the rules of lexical scoping) of its corresponding +@var{variable}.@refill + + +@node Yasos, , Fluid-Let, Macros +@section Yasos + +@c Much of the documentation in this section was written by Dave Love +@c (d.love@dl.ac.uk) -- don't blame Ken Dickey for its faults. +@c but we can blame him for not writing it! + +@code{(require 'oop)} or @code{(require 'yasos)} + +`Yet Another Scheme Object System' is a simple object system for Scheme +based on the paper by Norman Adams and Jonathan Rees: @cite{Object +Oriented Programming in Scheme}, Proceedings of the 1988 ACM Conference +on LISP and Functional Programming, July 1988 [ACM #552880].@refill + +Another reference is: + +Ken Dickey. +@ifset html +<A HREF="ftp://ftp.cs.indiana.edu/pub/scheme-repository/doc/pubs/swob.txt"> +@end ifset +Scheming with Objects +@ifset html +</A> +@end ifset +@cite{AI Expert} Volume 7, Number 10 (October 1992), pp. 24-33. + +@menu +* Yasos terms:: Definitions and disclaimer. +* Yasos interface:: The Yasos macros and procedures. +* Setters:: Dylan-like setters in Yasos. +* Yasos examples:: Usage of Yasos and setters. +@end menu + +@node Yasos terms, Yasos interface, Yasos, Yasos +@subsection Terms + +@table @asis +@item @dfn{Object} +Any Scheme data object. + +@item @dfn{Instance} +An instance of the OO system; an @dfn{object}. + +@item @dfn{Operation} +A @var{method}. +@end table + +@table @emph +@item Notes: +The object system supports multiple inheritance. An instance can +inherit from 0 or more ancestors. In the case of multiple inherited +operations with the same identity, the operation used is that from the +first ancestor which contains it (in the ancestor @code{let}). An +operation may be applied to any Scheme data object---not just instances. +As code which creates instances is just code, there are no @dfn{classes} +and no meta-@var{anything}. Method dispatch is by a procedure call a la +CLOS rather than by @code{send} syntax a la Smalltalk.@refill + +@item Disclaimer: +There are a number of optimizations which can be made. This +implementation is expository (although performance should be quite +reasonable). See the L&FP paper for some suggestions.@refill +@end table + + + + + +@node Yasos interface, Setters, Yasos terms, Yasos +@subsection Interface + +@deffn Syntax define-operation @code{(}opname self arg @dots{}@code{)} @var{default-body} +Defines a default behavior for data objects which don't handle the +operation @var{opname}. The default default behavior (for an empty +@var{default-body}) is to generate an error.@refill +@end deffn + +@deffn Syntax define-predicate opname? +Defines a predicate @var{opname?}, usually used for determining the +@dfn{type} of an object, such that @code{(@var{opname?} @var{object})} +returns @code{#t} if @var{object} has an operation @var{opname?} and +@code{#f} otherwise.@refill +@end deffn + +@deffn Syntax object @code{((@var{name} @var{self} @var{arg} @dots{}) @var{body})} @dots{} +Returns an object (an instance of the object system) with operations. +Invoking @code{(@var{name} @var{object} @var{arg} @dots{}} executes the +@var{body} of the @var{object} with @var{self} bound to @var{object} and +with argument(s) @var{arg}@dots{}.@refill +@end deffn + +@deffn Syntax object-with-ancestors @code{((}ancestor1 init1@code{)} @dots{}@code{)} operation @dots{} +A @code{let}-like form of @code{object} for multiple inheritance. It +returns an object inheriting the behaviour of @var{ancestor1} etc. An +operation will be invoked in an ancestor if the object itself does not +provide such a method. In the case of multiple inherited operations +with the same identity, the operation used is the one found in the first +ancestor in the ancestor list. +@end deffn + +@deffn Syntax operate-as component operation self arg @dots{} +Used in an operation definition (of @var{self}) to invoke the +@var{operation} in an ancestor @var{component} but maintain the object's +identity. Also known as ``send-to-super''.@refill +@end deffn + +@deffn Procedure print obj port +A default @code{print} operation is provided which is just @code{(format +@var{port} @var{obj})} (@xref{Format}) for non-instances and prints +@var{obj} preceded by @samp{#<INSTANCE>} for instances. +@end deffn + +@defun size obj +The default method returns the number of elements in @var{obj} if it is +a vector, string or list, @code{2} for a pair, @code{1} for a character +and by default id an error otherwise. Objects such as collections +(@xref{Collections}) may override the default in an obvious way.@refill +@end defun + + + + + +@node Setters, Yasos examples, Yasos interface, Yasos +@subsection Setters + +@dfn{Setters} implement @dfn{generalized locations} for objects +associated with some sort of mutable state. A @dfn{getter} operation +retrieves a value from a generalized location and the corresponding +setter operation stores a value into the location. Only the getter is +named -- the setter is specified by a procedure call as below. (Dylan +uses special syntax.) Typically, but not necessarily, getters are +access operations to extract values from Yasos objects (@xref{Yasos}). +Several setters are predefined, corresponding to getters @code{car}, +@code{cdr}, @code{string-ref} and @code{vector-ref} e.g., @code{(setter +car)} is equivalent to @code{set-car!}. + +This implementation of setters is similar to that in Dylan(TM) +(@cite{Dylan: An object-oriented dynamic language}, Apple Computer +Eastern Research and Technology). Common LISP provides similar +facilities through @code{setf}. + +@defun setter getter +Returns the setter for the procedure @var{getter}. E.g., since +@code{string-ref} is the getter corresponding to a setter which is +actually @code{string-set!}: +@example +(define foo "foo") +((setter string-ref) foo 0 #\F) ; set element 0 of foo +foo @result{} "Foo" +@end example +@end defun + +@deffn Syntax set place new-value +If @var{place} is a variable name, @code{set} is equivalent to +@code{set!}. Otherwise, @var{place} must have the form of a procedure +call, where the procedure name refers to a getter and the call indicates +an accessible generalized location, i.e., the call would return a value. +The return value of @code{set} is usually unspecified unless used with a +setter whose definition guarantees to return a useful value. +@example +(set (string-ref foo 2) #\O) ; generalized location with getter +foo @result{} "FoO" +(set foo "foo") ; like set! +foo @result{} "foo" +@end example +@end deffn + +@deffn Procedure add-setter getter setter +Add procedures @var{getter} and @var{setter} to the (inaccessible) list +of valid setter/getter pairs. @var{setter} implements the store +operation corresponding to the @var{getter} access operation for the +relevant state. The return value is unspecified. +@end deffn + +@deffn Procedure remove-setter-for getter +Removes the setter corresponding to the specified @var{getter} from the +list of valid setters. The return value is unspecified. +@end deffn + +@deffn Syntax define-access-operation getter-name +Shorthand for a Yasos @code{define-operation} defining an operation +@var{getter-name} that objects may support to return the value of some +mutable state. The default operation is to signal an error. The return +value is unspecified. +@end deffn + + + + + +@node Yasos examples, , Setters, Yasos +@subsection Examples + +@lisp +(define-operation (print obj port) + (format port + (if (instance? obj) "#<instance>" "~s") + obj)) + +(define-operation (SIZE obj) + (cond + ((vector? obj) (vector-length obj)) + ((list? obj) (length obj)) + ((pair? obj) 2) + ((string? obj) (string-length obj)) + ((char? obj) 1) + (else + (error "Operation not supported: size" obj)))) + +(define-predicate cell?) +(define-operation (fetch obj)) +(define-operation (store! obj newValue)) + +(define (make-cell value) + (object + ((cell? self) #t) + ((fetch self) value) + ((store! self newValue) + (set! value newValue) + newValue) + ((size self) 1) + ((print self port) + (format port "#<Cell: ~s>" (fetch self))))) + +(define-operation (discard obj value) + (format #t "Discarding ~s~%" value)) + +(define (make-filtered-cell value filter) + (object-with-ancestors ((cell (make-cell value))) + ((store! self newValue) + (if (filter newValue) + (store! cell newValue) + (discard self newValue))))) + +(define-predicate array?) +(define-operation (array-ref array index)) +(define-operation (array-set! array index value)) + +(define (make-array num-slots) + (let ((anArray (make-vector num-slots))) + (object + ((array? self) #t) + ((size self) num-slots) + ((array-ref self index) (vector-ref anArray index)) + ((array-set! self index newValue) (vector-set! anArray index newValue)) + ((print self port) (format port "#<Array ~s>" (size self)))))) + +(define-operation (position obj)) +(define-operation (discarded-value obj)) + +(define (make-cell-with-history value filter size) + (let ((pos 0) (most-recent-discard #f)) + (object-with-ancestors + ((cell (make-filtered-call value filter)) + (sequence (make-array size))) + ((array? self) #f) + ((position self) pos) + ((store! self newValue) + (operate-as cell store! self newValue) + (array-set! self pos newValue) + (set! pos (+ pos 1))) + ((discard self value) + (set! most-recent-discard value)) + ((discarded-value self) most-recent-discard) + ((print self port) + (format port "#<Cell-with-history ~s>" (fetch self)))))) + +(define-access-operation fetch) +(add-setter fetch store!) +(define foo (make-cell 1)) +(print foo #f) +@result{} "#<Cell: 1>" +(set (fetch foo) 2) +@result{} +(print foo #f) +@result{} "#<Cell: 2>" +(fetch foo) +@result{} 2 +@end lisp + +@node Numerics, Procedures, Macros, Top +@chapter Numerics + +@menu +* Bit-Twiddling:: 'logical +* Modular Arithmetic:: 'modular +* Prime Testing and Generation:: 'primes +* Prime Factorization:: 'factor +* Random Numbers:: 'random +* Cyclic Checksum:: 'make-crc +* Plotting:: 'charplot +* Root Finding:: +@end menu + + +@node Bit-Twiddling, Modular Arithmetic, Numerics, Numerics +@section Bit-Twiddling + +@code{(require 'logical)} + +The bit-twiddling functions are made available through the use of the +@code{logical} package. @code{logical} is loaded by inserting +@code{(require 'logical)} before the code that uses these +functions.@refill + +@defun logand n1 n1 +Returns the integer which is the bit-wise AND of the two integer +arguments. + +Example: +@lisp +(number->string (logand #b1100 #b1010) 2) + @result{} "1000" +@end lisp +@end defun + +@defun logior n1 n2 +Returns the integer which is the bit-wise OR of the two integer +arguments. + +Example: +@lisp +(number->string (logior #b1100 #b1010) 2) + @result{} "1110" +@end lisp +@end defun + +@defun logxor n1 n2 +Returns the integer which is the bit-wise XOR of the two integer +arguments. + +Example: +@lisp +(number->string (logxor #b1100 #b1010) 2) + @result{} "110" +@end lisp +@end defun + +@defun lognot n +Returns the integer which is the 2s-complement of the integer argument. + +Example: +@lisp +(number->string (lognot #b10000000) 2) + @result{} "-10000001" +(number->string (lognot #b0) 2) + @result{} "-1" +@end lisp +@end defun + +@defun logtest j k +@example +(logtest j k) @equiv{} (not (zero? (logand j k))) + +(logtest #b0100 #b1011) @result{} #f +(logtest #b0100 #b0111) @result{} #t +@end example +@end defun + +@defun logbit? index j +@example +(logbit? index j) @equiv{} (logtest (integer-expt 2 index) j) + +(logbit? 0 #b1101) @result{} #t +(logbit? 1 #b1101) @result{} #f +(logbit? 2 #b1101) @result{} #t +(logbit? 3 #b1101) @result{} #t +(logbit? 4 #b1101) @result{} #f +@end example +@end defun + +@defun ash int count +Returns an integer equivalent to +@code{(inexact->exact (floor (* @var{int} (expt 2 @var{count}))))}.@refill + +Example: +@lisp +(number->string (ash #b1 3) 2) + @result{} "1000" +(number->string (ash #b1010 -1) 2) + @result{} "101" +@end lisp +@end defun + +@defun logcount n +Returns the number of bits in integer @var{n}. If integer is positive, +the 1-bits in its binary representation are counted. If negative, the +0-bits in its two's-complement binary representation are counted. If 0, +0 is returned. + +Example: +@lisp +(logcount #b10101010) + @result{} 4 +(logcount 0) + @result{} 0 +(logcount -2) + @result{} 1 +@end lisp +@end defun + +@defun integer-length n +Returns the number of bits neccessary to represent @var{n}. + +Example: +@lisp +(integer-length #b10101010) + @result{} 8 +(integer-length 0) + @result{} 0 +(integer-length #b1111) + @result{} 4 +@end lisp +@end defun + +@defun integer-expt n k +Returns @var{n} raised to the non-negative integer exponent @var{k}. + +Example: +@lisp +(integer-expt 2 5) + @result{} 32 +(integer-expt -3 3) + @result{} -27 +@end lisp +@end defun + +@defun bit-extract n start end +Returns the integer composed of the @var{start} (inclusive) through +@var{end} (exclusive) bits of @var{n}. The @var{start}th bit becomes +the 0-th bit in the result.@refill + +Example: +@lisp +(number->string (bit-extract #b1101101010 0 4) 2) + @result{} "1010" +(number->string (bit-extract #b1101101010 4 9) 2) + @result{} "10110" +@end lisp +@end defun + + +@node Modular Arithmetic, Prime Testing and Generation, Bit-Twiddling, Numerics +@section Modular Arithmetic + +@code{(require 'modular)} + +@defun extended-euclid n1 n2 +Returns a list of 3 integers @code{(d x y)} such that d = gcd(@var{n1}, +@var{n2}) = @var{n1} * x + @var{n2} * y.@refill +@end defun + +@defun symmetric:modulus n +Returns @code{(quotient (+ -1 n) -2)} for positive odd integer @var{n}. +@end defun + +@defun modulus->integer modulus +Returns the non-negative integer characteristic of the ring formed when +@var{modulus} is used with @code{modular:} procedures. +@end defun + +@defun modular:normalize modulus n +Returns the integer @code{(modulo @var{n} (modulus->integer +@var{modulus}))} in the representation specified by @var{modulus}. +@end defun + +@noindent +The rest of these functions assume normalized arguments; That is, the +arguments are constrained by the following table: + +@noindent +For all of these functions, if the first argument (@var{modulus}) is: +@table @code +@item positive? +Work as before. The result is between 0 and @var{modulus}. + +@item zero? +The arguments are treated as integers. An integer is returned. + +@item negative? +The arguments and result are treated as members of the integers modulo +@code{(+ 1 (* -2 @var{modulus}))}, but with @dfn{symmetric} +representation; i.e. @code{(<= (- @var{modulus}) @var{n} +@var{modulus})}. +@end table + +@noindent +If all the arguments are fixnums the computation will use only fixnums. + +@defun modular:invertable? modulus k +Returns @code{#t} if there exists an integer n such that @var{k} * n +@equiv{} 1 mod @var{modulus}, and @code{#f} otherwise. +@end defun + +@defun modular:invert modulus k2 +Returns an integer n such that 1 = (n * @var{k2}) mod @var{modulus}. If +@var{k2} has no inverse mod @var{modulus} an error is signaled. +@end defun + +@defun modular:negate modulus k2 +Returns (@minus{}@var{k2}) mod @var{modulus}. +@end defun + +@defun modular:+ modulus k2 k3 +Returns (@var{k2} + @var{k3}) mod @var{modulus}. +@end defun + +@defun modular:@minus{} modulus k2 k3 +Returns (@var{k2} @minus{} @var{k3}) mod @var{modulus}. +@end defun + +@defun modular:* modulus k2 k3 +Returns (@var{k2} * @var{k3}) mod @var{modulus}. + +The Scheme code for @code{modular:*} with negative @var{modulus} is not +completed for fixnum-only implementations. +@end defun + +@defun modular:expt modulus k2 k3 +Returns (@var{k2} ^ @var{k3}) mod @var{modulus}. +@end defun + + +@node Prime Testing and Generation, Prime Factorization, Modular Arithmetic, Numerics +@section Prime Testing and Generation + +@code{(require 'primes)} + +This package tests and generates prime numbers. The strategy used is +as follows: + +@itemize +@item +First, use trial division by small primes (primes less than 1000) to +quickly weed out composites with small factors. As a side benefit, this +makes the test precise for numbers up to one million. +@item +Second, apply the Miller-Rabin primality test to detect (with high +probability) any remaining composites. +@end itemize + +The Miller-Rabin test is a Monte-Carlo test---in other words, it's fast +and it gets the right answer with high probability. For a candidate +that @emph{is} prime, the Miller-Rabin test is certain to report +"prime"; it will never report "composite". However, for a candidate +that is composite, there is a (small) probability that the Miller-Rabin +test will erroneously report "prime". This probability can be made +arbitarily small by adjusting the number of iterations of the +Miller-Rabin test. + +@defun probably-prime? candidate +@defunx probably-prime? candidate iter +Returns @code{#t} if @code{candidate} is probably prime. The optional +parameter @code{iter} controls the number of iterations of the +Miller-Rabin test. The probability of a composite candidate being +mistaken for a prime is at most @code{(1/4)^iter}. The default value of +@code{iter} is 15, which makes the probability less than 1 in 10^9. + +@end defun + +@defun primes< start count +@defunx primes< start count iter +@defunx primes> start count +@defunx primes> start count iter +Returns a list of the first @code{count} odd probable primes less (more) +than or equal to @code{start}. The optional parameter @code{iter} +controls the number of iterations of the Miller-Rabin test for each +candidate. The probability of a composite candidate being mistaken for +a prime is at most @code{(1/4)^iter}. The default value of @code{iter} +is 15, which makes the probability less than 1 in 10^9. + +@end defun + +@menu +* The Miller-Rabin Test:: How the Miller-Rabin test works +@end menu + +@node The Miller-Rabin Test, , Prime Testing and Generation, Prime Testing and Generation +@subsection Theory + +Rabin and Miller's result can be summarized as follows. Let @code{p} +(the candidate prime) be any odd integer greater than 2. Let @code{b} +(the "base") be an integer in the range @code{2 ... p-1}. There is a +fairly simple Boolean function---call it @code{C}, for +"Composite"---with the following properties: +@itemize + +@item +If @code{p} is prime, @code{C(p, b)} is false for all @code{b} in the range +@code{2 ... p-1}. + +@item +If @code{p} is composite, @code{C(p, b)} is false for at most 1/4 of all +@code{b} in the range @code{ 2 ... p-1}. (If the test fails for base +@code{b}, @code{p} is called a @emph{strong pseudo-prime to base +@code{b}}.) + +@end itemize +For details of @code{C}, and why it fails for at most 1/4 of the +potential bases, please consult a book on number theory or cryptography +such as "A Course in Number Theory and Cryptography" by Neal Koblitz, +published by Springer-Verlag 1994. + +There is nothing probablistic about this result. It's true for all +@code{p}. If we had time to test @code{(1/4)p + 1} different bases, we +could definitively determine the primality of @code{p}. For large +candidates, that would take much too long---much longer than the simple +approach of dividing by all numbers up to @code{sqrt(p)}. This is +where probability enters the picture. + +Suppose we have some candidate prime @code{p}. Pick a random integer +@code{b} in the range @code{2 ... p-1}. Compute @code{C(p,b)}. If +@code{p} is prime, the result will certainly be false. If @code{p} is +composite, the probability is at most 1/4 that the result will be false +(demonstrating that @code{p} is a strong pseudoprime to base @code{b}). +The test can be repeated with other random bases. If @code{p} is prime, +each test is certain to return false. If @code{p} is composite, the +probability of @code{C(p,b)} returning false is at most 1/4 for each +test. Since the @code{b} are chosen at random, the tests outcomes are +independent. So if @code{p} is composite and the test is repeated, say, +15 times, the probability of it returning false all fifteen times is at +most (1/4)^15, or about 10^-9. If the test is repeated 30 times, the +probability of failure drops to at most 8.3e-25. + +Rabin and Miller's result holds for @emph{all} candidates @code{p}. +However, if the candidate @code{p} is picked at random, the probability +of the Miller-Rabin test failing is much less than the computed bound. +This is because, for @emph{most} composite numbers, the fraction of +bases that cause the test to fail is much less than 1/4. For example, +if you pick a random odd number less than 1000 and apply the +Miller-Rabin test with only 3 random bases, the computed failure bound +is (1/4)^3, or about 1.6e-2. However, the actual probability of failure +is much less---about 7.2e-5. If you accidentally pick 703 to test for +primality, the probability of failure is (161/703)^3, or about 1.2e-2, +which is almost as high as the computed bound. This is because 703 is a +strong pseudoprime to 161 bases. But if you pick at random there is +only a small chance of picking 703, and no other number less than 1000 +has that high a percentage of pseudoprime bases. + +The Miller-Rabin test is sometimes used in a slightly different fashion, +where it can, at least in principle, cause problems. The weaker version +uses small prime bases instead of random bases. If you are picking +candidates at random and testing for primality, this works well since +very few composites are strong pseudo-primes to small prime bases. (For +example, there is only one composite less than 2.5e10 that is a strong +pseudo-prime to the bases 2, 3, 5, and 7.) The problem with this +approach is that once a candidate has been picked, the test is +deterministic. This distinction is subtle, but real. With the +randomized test, for @emph{any} candidate you pick---even if your +candidate-picking procedure is strongly biased towards troublesome +numbers, the test will work with high probability. With the +deterministic version, for any particular candidate, the test will +either work (with probability 1), or fail (with probability 1). It +won't fail for very many candidates, but that won't be much consolation +if your candidate-picking procedure is somehow biased toward troublesome +numbers. + + +@node Prime Factorization, Random Numbers, Prime Testing and Generation, Numerics +@section Prime Factorization + +@code{(require 'factor)} + + +@defun factor k +Returns a list of the prime factors of @var{k}. The order of the +factors is unspecified. In order to obtain a sorted list do +@code{(sort! (factor k) <)}.@refill +@end defun + +@emph{Note:} The rest of these procedures implement the Solovay-Strassen +primality test. This test has been superseeded by the faster +@xref{Prime Testing and Generation, probably-prime?}. However these are +left here as they take up little space and may be of use to an +implementation without bignums. + +See Robert Solovay and Volker Strassen, @cite{A Fast Monte-Carlo Test +for Primality}, SIAM Journal on Computing, 1977, pp 84-85. + +@defun jacobi-symbol p q +Returns the value (+1, @minus{}1, or 0) of the Jacobi-Symbol of exact +non-negative integer @var{p} and exact positive odd integer +@var{q}.@refill +@end defun + +@defun prime? p +Returns @code{#f} if @var{p} is composite; @code{#t} if @var{p} is +prime. There is a slight chance @code{(expt 2 (- prime:trials))} that a +composite will return @code{#t}.@refill +@end defun + +@defun prime:trials +Is the maxinum number of iterations of Solovay-Strassen that will be +done to test a number for primality. +@end defun + + + +@node Random Numbers, Cyclic Checksum, Prime Factorization, Numerics +@section Random Numbers + +@code{(require 'random)} + + +@deffn Procedure random n +@deffnx Procedure random n state +Accepts a positive integer or real @var{n} and returns a number of the +same type between zero (inclusive) and @var{n} (exclusive). The values +returned have a uniform distribution.@refill + +The optional argument @var{state} must be of the type produced by +@code{(make-random-state)}. It defaults to the value of the variable +@code{*random-state*}. This object is used to maintain the state of the +pseudo-random-number generator and is altered as a side effect of the +@code{random} operation.@refill +@end deffn + +@defvar *random-state* +Holds a data structure that encodes the internal state of the +random-number generator that @code{random} uses by default. The nature +of this data structure is implementation-dependent. It may be printed +out and successfully read back in, but may or may not function correctly +as a random-number state object in another implementation.@refill +@end defvar + +@deffn Procedure make-random-state +@deffnx Procedure make-random-state state +Returns a new object of type suitable for use as the value of the +variable @code{*random-state*} and as a second argument to +@code{random}. If argument @var{state} is given, a copy of it is +returned. Otherwise a copy of @code{*random-state*} is returned.@refill +@end deffn + +If inexact numbers are support by the Scheme implementation, +@file{randinex.scm} will be loaded as well. @file{randinex.scm} +contains procedures for generating inexact distributions.@refill + +@deffn Procedure random:uniform state +Returns an uniformly distributed inexact real random number in the +range between 0 and 1. +@end deffn + +@deffn Procedure random:solid-sphere! vect +@deffnx Procedure random:solid-sphere! vect state +Fills @var{vect} with inexact real random numbers the sum of whose +squares is less than 1.0. Thinking of @var{vect} as coordinates in +space of dimension @var{n} = @code{(vector-length @var{vect})}, the +coordinates are uniformly distributed within the unit @var{n}-shere. +The sum of the squares of the numbers is returned.@refill +@end deffn + +@deffn Procedure random:hollow-sphere! vect +@deffnx Procedure random:hollow-sphere! vect state +Fills @var{vect} with inexact real random numbers the sum of whose +squares is equal to 1.0. Thinking of @var{vect} as coordinates in space +of dimension n = @code{(vector-length @var{vect})}, the coordinates are +uniformly distributed over the surface of the unit n-shere.@refill +@end deffn + +@deffn Procedure random:normal +@deffnx Procedure random:normal state +Returns an inexact real in a normal distribution with mean 0 and +standard deviation 1. For a normal distribution with mean @var{m} and +standard deviation @var{d} use @code{(+ @var{m} (* @var{d} +(random:normal)))}.@refill +@end deffn + +@deffn Procedure random:normal-vector! vect +@deffnx Procedure random:normal-vector! vect state +Fills @var{vect} with inexact real random numbers which are independent +and standard normally distributed (i.e., with mean 0 and variance 1). +@end deffn + +@deffn Procedure random:exp +@deffnx Procedure random:exp state +Returns an inexact real in an exponential distribution with mean 1. For +an exponential distribution with mean @var{u} use (* @var{u} +(random:exp)).@refill +@end deffn + + +@node Cyclic Checksum, Plotting, Random Numbers, Numerics +@section Cyclic Checksum + +@code{(require 'make-crc)} + +@defun make-port-crc +@defunx make-port-crc degree +@defunx make-port-crc degree generator +Returns an expression for a procedure of one argument, a port. This +procedure reads characters from the port until the end of file and +returns the integer checksum of the bytes read. + +The integer @var{degree}, if given, specifies the degree of the +polynomial being computed -- which is also the number of bits computed +in the checksums. The default value is 32. + +The integer @var{generator} specifies the polynomial being computed. +The power of 2 generating each 1 bit is the exponent of a term of the +polynomial. The bit at position @var{degree} is implicit and should not +be part of @var{generator}. This allows systems with numbers limited to +32 bits to calculate 32 bit checksums. The default value of +@var{generator} when @var{degree} is 32 (its default) is: + +@example +(make-port-crc 32 #b00000100110000010001110110110111) +@end example + +Creates a procedure to calculate the P1003.2/D11.2 (POSIX.2) 32-bit +checksum from the polynomial: + +@example + 32 26 23 22 16 12 11 + ( x + x + x + x + x + x + x + + + 10 8 7 5 4 2 1 + x + x + x + x + x + x + x + 1 ) mod 2 +@end example +@end defun + +@example +(require 'make-crc) +(define crc32 (slib:eval (make-port-crc))) +(define (file-check-sum file) (call-with-input-file file crc32)) +(file-check-sum (in-vicinity (library-vicinity) "ratize.scm")) + +@result{} 3553047446 +@end example + +@node Plotting, Root Finding, Cyclic Checksum, Numerics +@section Plotting on Character Devices + +@code{(require 'charplot)} + +The plotting procedure is made available through the use of the +@code{charplot} package. @code{charplot} is loaded by inserting +@code{(require 'charplot)} before the code that uses this +procedure.@refill + +@defvar charplot:height +The number of rows to make the plot vertically. +@end defvar + +@defvar charplot:width +The number of columns to make the plot horizontally. +@end defvar + +@deffn Procedure plot! coords x-label y-label +@var{coords} is a list of pairs of x and y coordinates. @var{x-label} +and @var{y-label} are strings with which to label the x and y +axes.@refill + +Example: +@example +(require 'charplot) +(set! charplot:height 19) +(set! charplot:width 45) + +(define (make-points n) + (if (zero? n) + '() + (cons (cons (/ n 6) (sin (/ n 6))) (make-points (1- n))))) + +(plot! (make-points 37) "x" "Sin(x)") +@print{} +@group + Sin(x) ______________________________________________ + 1.25|- | + | | + 1|- **** | + | ** ** | + 750.0e-3|- * * | + | * * | + 500.0e-3|- * * | + | * | + 250.0e-3|- * | + | * * | + 0|-------------------*--------------------------| + | * | + -250.0e-3|- * * | + | * * | + -500.0e-3|- * | + | * * | + -750.0e-3|- * * | + | ** ** | + -1|- **** | + |____________:_____._____:_____._____:_________| + x 2 4 +@end group +@end example +@end deffn + + +@node Root Finding, , Plotting, Numerics +@section Root Finding + +@code{(require 'root)} + +@defun newtown:find-integer-root f df/dx x0 +Given integer valued procedure @var{f}, its derivative (with respect to +its argument) @var{df/dx}, and initial integer value @var{x0} for which +@var{df/dx}(@var{x0}) is non-zero, returns an integer @var{x} for which +@var{f}(@var{x}) is closer to zero than either of the integers adjacent +to @var{x}; or returns @code{#f} if such an integer can't be found. + +To find the closest integer to a given integers square root: + +@example +(define (integer-sqrt y) + (newton:find-integer-root + (lambda (x) (- (* x x) y)) + (lambda (x) (* 2 x)) + (ash 1 (quotient (integer-length y) 2)))) + +(integer-sqrt 15) @result{} 4 +@end example +@end defun + +@defun integer-sqrt y +Given a non-negative integer @var{y}, returns the rounded square-root of +@var{y}. +@end defun + +@defun newton:find-root f df/dx x0 prec +Given real valued procedures @var{f}, @var{df/dx} of one (real) +argument, initial real value @var{x0} for which @var{df/dx}(@var{x0}) is +non-zero, and positive real number @var{prec}, returns a real @var{x} +for which @code{abs}(@var{f}(@var{x})) is less than @var{prec}; or +returns @code{#f} if such a real can't be found. + +If @code{prec} is instead a negative integer, @code{newton:find-root} +returns the result of -@var{prec} iterations. +@end defun + +@noindent +H. J. Orchard, @cite{The Laguerre Method for Finding the Zeros of +Polynomials}, IEEE Transactions on Circuits and Systems, Vol. 36, +No. 11, November 1989, pp 1377-1381. + +@quotation +There are 2 errors in Orchard's Table II. Line k=2 for starting +value of 1000+j0 should have Z_k of 1.0475 + j4.1036 and line k=2 +for starting value of 0+j1000 should have Z_k of 1.0988 + j4.0833. +@end quotation + + +@defun laguerre:find-root f df/dz ddf/dz^2 z0 prec +Given complex valued procedure @var{f} of one (complex) argument, its +derivative (with respect to its argument) @var{df/dx}, its second +derivative @var{ddf/dz^2}, initial complex value @var{z0}, and positive +real number @var{prec}, returns a complex number @var{z} for which +@code{magnitude}(@var{f}(@var{z})) is less than @var{prec}; or returns +@code{#f} if such a number can't be found. + +If @code{prec} is instead a negative integer, @code{laguerre:find-root} +returns the result of -@var{prec} iterations. +@end defun + +@defun laguerre:find-polynomial-root deg f df/dz ddf/dz^2 z0 prec +Given polynomial procedure @var{f} of integer degree @var{deg} of one +argument, its derivative (with respect to its argument) @var{df/dx}, its +second derivative @var{ddf/dz^2}, initial complex value @var{z0}, and +positive real number @var{prec}, returns a complex number @var{z} for +which @code{magnitude}(@var{f}(@var{z})) is less than @var{prec}; or +returns @code{#f} if such a number can't be found. + +If @code{prec} is instead a negative integer, +@code{laguerre:find-polynomial-root} returns the result of -@var{prec} +iterations. +@end defun + + +@node Procedures, Standards Support, Numerics, Top +@chapter Procedures + +Anything that doesn't fall neatly into any of the other categories winds +up here. + +@menu +* Batch:: 'batch +* Common List Functions:: 'common-list-functions +* Format:: 'format +* Generic-Write:: 'generic-write +* Line I/O:: 'line-i/o +* Multi-Processing:: 'process +* Object-To-String:: 'object->string +* Pretty-Print:: 'pretty-print, 'pprint-file +* Sorting:: 'sort +* Topological Sort:: +* Standard Formatted I/O:: 'printf, 'scanf +* String-Case:: 'string-case +* String Ports:: 'string-port +* String Search:: +* Tektronix Graphics Support:: +* Tree Operations:: 'tree +@end menu + +@node Batch, Common List Functions, Procedures, Procedures +@section Batch + +@code{(require 'batch)} + +@noindent +The batch procedures provide a way to write and execute portable scripts +for a variety of operating systems. Each @code{batch:} procedure takes +as its first argument a parameter-list (@pxref{Parameter lists}). This +parameter-list argument @var{parms} contains named associations. Batch +currently uses 2 of these: + +@table @code +@item batch-port +The port on which to write lines of the batch file. +@item batch-dialect +The syntax of batch file to generate. Currently supported are: +@itemize @bullet +@item +unix +@item +dos +@item +vms +@item +system +@item +*unknown* +@end itemize +@end table + +@noindent +@file{batch.scm} uses 2 enhanced relational tables (@pxref{Database +Utilities}) to store information linking the names of +@code{operating-system}s to @code{batch-dialect}es. + +@defun batch:initialize! database +Defines @code{operating-system} and @code{batch-dialect} tables and adds +the domain @code{operating-system} to the enhanced relational database +@var{database}. +@end defun + +@defvar batch:platform +Is batch's best guess as to which operating-system it is running under. +@code{batch:platform} is set to @code{(software-type)} +(@pxref{Configuration}) unless @code{(software-type)} is @code{unix}, +in which case finer distinctions are made. +@end defvar + +@defun batch:call-with-output-script parms file proc +@var{proc} should be a procedure of one argument. If @var{file} is an +output-port, @code{batch:call-with-output-script} writes an appropriate +header to @var{file} and then calls @var{proc} with @var{file} as the +only argument. If @var{file} is a string, +@code{batch:call-with-output-script} opens a output-file of name +@var{file}, writes an appropriate header to @var{file}, and then calls +@var{proc} with the newly opened port as the only argument. Otherwise, +@code{batch:call-with-output-script} acts as if it was called with the +result of @code{(current-output-port)} as its third argument. +@end defun + +@defun batch:apply-chop-to-fit proc arg1 arg2 @dots{} list +The procedure @var{proc} must accept at least one argument and return +@code{#t} if successful, @code{#f} if not. +@code{batch:apply-chop-to-fit} calls @var{proc} with @var{arg1}, +@var{arg2}, @dots{}, and @var{chunk}, where @var{chunk} is a subset of +@var{list}. @code{batch:apply-chop-to-fit} tries @var{proc} with +successively smaller subsets of @var{list} until either @var{proc} +returns non-false, or the @var{chunk}s become empty. +@end defun + +@noindent +The rest of the @code{batch:} procedures write (or execute if +@code{batch-dialect} is @code{system}) commands to the batch port which +has been added to @var{parms} or @code{(copy-tree @var{parms})} by the +code: + +@example +(adjoin-parameters! @var{parms} (list 'batch-port @var{port})) +@end example + +@defun batch:system parms string1 string2 @dots{} +Calls @code{batch:try-system} (below) with arguments, but signals an +error if @code{batch:try-system} returns @code{#f}. +@end defun + +@noindent +These functions return a non-false value if the command was successfully +translated into the batch dialect and @code{#f} if not. In the case of +the @code{system} dialect, the value is non-false if the operation +suceeded. + +@defun batch:try-system parms string1 string2 @dots{} +Writes a command to the @code{batch-port} in @var{parms} which executes +the program named @var{string1} with arguments @var{string2} @dots{}. +@end defun + +@defun batch:run-script parms string1 string2 @dots{} +Writes a command to the @code{batch-port} in @var{parms} which executes +the batch script named @var{string1} with arguments @var{string2} +@dots{}. + +@emph{Note:} @code{batch:run-script} and @code{batch:try-system} are not the +same for some operating systems (VMS). +@end defun + +@defun batch:comment parms line1 @dots{} +Writes comment lines @var{line1} @dots{} to the @code{batch-port} in +@var{parms}. +@end defun + +@defun batch:lines->file parms file line1 @dots{} +Writes commands to the @code{batch-port} in @var{parms} which create a +file named @var{file} with contents @var{line1} @dots{}. +@end defun + +@defun batch:delete-file parms file +Writes a command to the @code{batch-port} in @var{parms} which deletes +the file named @var{file}. +@end defun + +@defun batch:rename-file parms old-name new-name +Writes a command to the @code{batch-port} in @var{parms} which renames +the file @var{old-name} to @var{new-name}. +@end defun + +@noindent +In addition, batch provides some small utilities very useful for writing +scripts: + +@defun replace-suffix str old new +Returns a new string similar to @code{str} but with the suffix string +@var{old} removed and the suffix string @var{new} appended. If the end +of @var{str} does not match @var{old}, an error is signaled. +@end defun + +@defun string-join joiner string1 @dots{} +Returns a new string consisting of all the strings @var{string1} @dots{} +in order appended together with the string @var{joiner} between each +adjacent pair. +@end defun + +@defun must-be-first list1 list2 +Returns a new list consisting of the elements of @var{list2} ordered so +that if some elements of @var{list1} are @code{equal?} to elements of +@var{list2}, then those elements will appear first and in the order of +@var{list1}. +@end defun + +@defun must-be-last list1 list2 +Returns a new list consisting of the elements of @var{list1} ordered so +that if some elements of @var{list2} are @code{equal?} to elements of +@var{list1}, then those elements will appear last and in the order of +@var{list2}. +@end defun + +@defun os->batch-dialect osname +Returns its best guess for the @code{batch-dialect} to be used for the +operating-system named @var{osname}. @code{os->batch-dialect} uses the +tables added to @var{database} by @code{batch:initialize!}. +@end defun + +@noindent +Here is an example of the use of most of batch's procedures: + +@example +(require 'database-utilities) +(require 'parameters) +(require 'batch) + +(define batch (create-database #f 'alist-table)) +(batch:initialize! batch) + +(define my-parameters + (list (list 'batch-dialect (os->batch-dialect batch:platform)) + (list 'platform batch:platform) + (list 'batch-port (current-output-port)))) ;gets filled in later + +(batch:call-with-output-script + my-parameters + "my-batch" + (lambda (batch-port) + (adjoin-parameters! my-parameters (list 'batch-port batch-port)) + (and + (batch:comment my-parameters + "================ Write file with C program.") + (batch:rename-file my-parameters "hello.c" "hello.c~") + (batch:lines->file my-parameters "hello.c" + "#include <stdio.h>" + "int main(int argc, char **argv)" + "@{" + " printf(\"hello world\\n\");" + " return 0;" + "@}" ) + (batch:system my-parameters "cc" "-c" "hello.c") + (batch:system my-parameters "cc" "-o" "hello" + (replace-suffix "hello.c" ".c" ".o")) + (batch:system my-parameters "hello") + (batch:delete-file my-parameters "hello") + (batch:delete-file my-parameters "hello.c") + (batch:delete-file my-parameters "hello.o") + (batch:delete-file my-parameters "my-batch") + ))) +@end example + +@noindent +Produces the file @file{my-batch}: + +@example +#!/bin/sh +# "my-batch" build script created Sat Jun 10 21:20:37 1995 +# ================ Write file with C program. +mv -f hello.c hello.c~ +rm -f hello.c +echo '#include <stdio.h>'>>hello.c +echo 'int main(int argc, char **argv)'>>hello.c +echo '@{'>>hello.c +echo ' printf("hello world\n");'>>hello.c +echo ' return 0;'>>hello.c +echo '@}'>>hello.c +cc -c hello.c +cc -o hello hello.o +hello +rm -f hello +rm -f hello.c +rm -f hello.o +rm -f my-batch +@end example + +@noindent +When run, @file{my-batch} prints: + +@example +bash$ my-batch +mv: hello.c: No such file or directory +hello world +@end example + + +@node Common List Functions, Format, Batch, Procedures +@section Common List Functions + +@code{(require 'common-list-functions)} + +The procedures below follow the Common LISP equivalents apart from +optional arguments in some cases. + +@menu +* List construction:: +* Lists as sets:: +* Lists as sequences:: +* Destructive list operations:: +* Non-List functions:: +@end menu + + +@node List construction, Lists as sets, Common List Functions, Common List Functions +@subsection List construction + +@defun make-list k . init +@code{make-list} creates and returns a list of @var{k} elements. If +@var{init} is included, all elements in the list are initialized to +@var{init}.@refill + +Example: +@lisp +(make-list 3) + @result{} (#<unspecified> #<unspecified> #<unspecified>) +(make-list 5 'foo) + @result{} (foo foo foo foo foo) +@end lisp +@end defun + + +@defun list* x . y +Works like @code{list} except that the cdr of the last pair is the last +argument unless there is only one argument, when the result is just that +argument. Sometimes called @code{cons*}. E.g.:@refill +@lisp +(list* 1) + @result{} 1 +(list* 1 2 3) + @result{} (1 2 . 3) +(list* 1 2 '(3 4)) + @result{} (1 2 3 4) +(list* @var{args} '()) + @equiv{} (list @var{args}) +@end lisp +@end defun + +@defun copy-list lst +@code{copy-list} makes a copy of @var{lst} using new pairs and returns +it. Only the top level of the list is copied, i.e., pairs forming +elements of the copied list remain @code{eq?} to the corresponding +elements of the original; the copy is, however, not @code{eq?} to the +original, but is @code{equal?} to it.@refill + +Example: +@lisp +(copy-list '(foo foo foo)) + @result{} (foo foo foo) +(define q '(foo bar baz bang)) +(define p q) +(eq? p q) + @result{} #t +(define r (copy-list q)) +(eq? q r) + @result{} #f +(equal? q r) + @result{} #t +(define bar '(bar)) +(eq? bar (car (copy-list (list bar 'foo)))) +@result{} #t + @end lisp +@end defun + + + + + + +@node Lists as sets, Lists as sequences, List construction, Common List Functions +@subsection Lists as sets + +@code{eq?} is used to test for membership by all the procedures below +which treat lists as sets.@refill + +@defun adjoin e l +@code{adjoin} returns the adjoint of the element @var{e} and the list +@var{l}. That is, if @var{e} is in @var{l}, @code{adjoin} returns +@var{l}, otherwise, it returns @code{(cons @var{e} @var{l})}.@refill + +Example: +@lisp +(adjoin 'baz '(bar baz bang)) + @result{} (bar baz bang) +(adjoin 'foo '(bar baz bang)) + @result{} (foo bar baz bang) +@end lisp +@end defun + +@defun union l1 l2 +@code{union} returns the combination of @var{l1} and @var{l2}. +Duplicates between @var{l1} and @var{l2} are culled. Duplicates within +@var{l1} or within @var{l2} may or may not be removed.@refill + +Example: +@lisp +(union '(1 2 3 4) '(5 6 7 8)) + @result{} (4 3 2 1 5 6 7 8) +(union '(1 2 3 4) '(3 4 5 6)) + @result{} (2 1 3 4 5 6) +@end lisp +@end defun + +@defun intersection l1 l2 +@code{intersection} returns all elements that are in both @var{l1} and +@var{l2}.@refill + +Example: +@lisp +(intersection '(1 2 3 4) '(3 4 5 6)) + @result{} (3 4) +(intersection '(1 2 3 4) '(5 6 7 8)) + @result{} () +@end lisp +@end defun + +@defun set-difference l1 l2 +@code{set-difference} returns the union of all elements that are in +@var{l1} but not in @var{l2}.@refill + +Example: +@lisp +(set-difference '(1 2 3 4) '(3 4 5 6)) + @result{} (1 2) +(set-difference '(1 2 3 4) '(1 2 3 4 5 6)) + @result{} () +@end lisp +@end defun + +@defun member-if pred lst +@code{member-if} returns @var{lst} if @code{(@var{pred} @var{element})} +is @code{#t} for any @var{element} in @var{lst}. Returns @code{#f} if +@var{pred} does not apply to any @var{element} in @var{lst}.@refill + +Example: +@lisp +(member-if vector? '(1 2 3 4)) + @result{} #f +(member-if number? '(1 2 3 4)) + @result{} (1 2 3 4) +@end lisp +@end defun + +@defun some pred lst . more-lsts +@var{pred} is a boolean function of as many arguments as there are list +arguments to @code{some} i.e., @var{lst} plus any optional arguments. +@var{pred} is applied to successive elements of the list arguments in +order. @code{some} returns @code{#t} as soon as one of these +applications returns @code{#t}, and is @code{#f} if none returns +@code{#t}. All the lists should have the same length.@refill + + +Example: +@lisp +(some odd? '(1 2 3 4)) + @result{} #t + +(some odd? '(2 4 6 8)) + @result{} #f + +(some > '(2 3) '(1 4)) + @result{} #f +@end lisp +@end defun + +@defun every pred lst . more-lsts +@code{every} is analogous to @code{some} except it returns @code{#t} if +every application of @var{pred} is @code{#t} and @code{#f} +otherwise.@refill + +Example: +@lisp +(every even? '(1 2 3 4)) + @result{} #f + +(every even? '(2 4 6 8)) + @result{} #t + +(every > '(2 3) '(1 4)) + @result{} #f +@end lisp +@end defun + +@defun notany pred . lst +@code{notany} is analogous to @code{some} but returns @code{#t} if no +application of @var{pred} returns @code{#t} or @code{#f} as soon as any +one does.@refill +@end defun + +@defun notevery pred . lst +@code{notevery} is analogous to @code{some} but returns @code{#t} as soon +as an application of @var{pred} returns @code{#f}, and @code{#f} +otherwise.@refill + +Example: +@lisp +(notevery even? '(1 2 3 4)) + @result{} #t + +(notevery even? '(2 4 6 8)) + @result{} #f +@end lisp +@end defun + +@defun find-if pred lst +@code{find-if} searches for the first @var{element} in @var{lst} such +that @code{(@var{pred} @var{element})} returns @code{#t}. If it finds +any such @var{element} in @var{lst}, @var{element} is returned. +Otherwise, @code{#f} is returned.@refill + +Example: +@lisp +(find-if number? '(foo 1 bar 2)) + @result{} 1 + +(find-if number? '(foo bar baz bang)) + @result{} #f + +(find-if symbol? '(1 2 foo bar)) + @result{} foo +@end lisp +@end defun + +@defun remove elt lst +@code{remove} removes all occurrences of @var{elt} from @var{lst} using +@code{eqv?} to test for equality and returns everything that's left. +N.B.: other implementations (Chez, Scheme->C and T, at least) use +@code{equal?} as the equality test.@refill + +Example: +@lisp +(remove 1 '(1 2 1 3 1 4 1 5)) + @result{} (2 3 4 5) + +(remove 'foo '(bar baz bang)) + @result{} (bar baz bang) +@end lisp +@end defun + +@defun remove-if pred lst +@code{remove-if} removes all @var{element}s from @var{lst} where +@code{(@var{pred} @var{element})} is @code{#t} and returns everything +that's left.@refill + +Example: +@lisp +(remove-if number? '(1 2 3 4)) + @result{} () + +(remove-if even? '(1 2 3 4 5 6 7 8)) + @result{} (1 3 5 7) +@end lisp +@end defun + +@defun remove-if-not pred lst +@code{remove-if-not} removes all @var{element}s from @var{lst} for which +@code{(@var{pred} @var{element})} is @code{#f} and returns everything that's +left.@refill + +Example: +@lisp +(remove-if-not number? '(foo bar baz)) + @result{} () +(remove-if-not odd? '(1 2 3 4 5 6 7 8)) + @result{} (1 3 5 7) +@end lisp +@end defun + +@defun has-duplicates? lst +returns @code{#t} if 2 members of @var{lst} are @code{equal?}, @code{#f} +otherwise. +Example: +@lisp +(has-duplicates? '(1 2 3 4)) + @result{} #f + +(has-duplicates? '(2 4 3 4)) + @result{} #t +@end lisp +@end defun + + +@node Lists as sequences, Destructive list operations, Lists as sets, Common List Functions +@subsection Lists as sequences + +@defun position obj lst +@code{position} returns the 0-based position of @var{obj} in @var{lst}, +or @code{#f} if @var{obj} does not occur in @var{lst}.@refill + +Example: +@lisp +(position 'foo '(foo bar baz bang)) + @result{} 0 +(position 'baz '(foo bar baz bang)) + @result{} 2 +(position 'oops '(foo bar baz bang)) + @result{} #f +@end lisp +@end defun + +@defun reduce p lst +@code{reduce} combines all the elements of a sequence using a binary +operation (the combination is left-associative). For example, using +@code{+}, one can add up all the elements. @code{reduce} allows you to +apply a function which accepts only two arguments to more than 2 +objects. Functional programmers usually refer to this as @dfn{foldl}. +@code{collect:reduce} (@xref{Collections}) provides a version of +@code{collect} generalized to collections.@refill + +Example: +@lisp +(reduce + '(1 2 3 4)) + @result{} 10 +(define (bad-sum . l) (reduce + l)) +(bad-sum 1 2 3 4) + @equiv{} (reduce + (1 2 3 4)) + @equiv{} (+ (+ (+ 1 2) 3) 4) +@result{} 10 +(bad-sum) + @equiv{} (reduce + ()) + @result{} () +(reduce string-append '("hello" "cruel" "world")) + @equiv{} (string-append (string-append "hello" "cruel") "world") + @result{} "hellocruelworld" +(reduce anything '()) + @result{} () +(reduce anything '(x)) + @result{} x +@end lisp + +What follows is a rather non-standard implementation of @code{reverse} +in terms of @code{reduce} and a combinator elsewhere called +@dfn{C}.@refill + +@lisp +;;; Contributed by Jussi Piitulainen (jpiitula@@ling.helsinki.fi) + +(define commute + (lambda (f) + (lambda (x y) + (f y x)))) + +(define reverse + (lambda (args) + (reduce-init (commute cons) args))) +@end lisp +@end defun + +@defun reduce-init p init lst +@code{reduce-init} is the same as reduce, except that it implicitly +inserts @var{init} at the start of the list. @code{reduce-init} is +preferred if you want to handle the null list, the one-element, and +lists with two or more elements consistently. It is common to use the +operator's idempotent as the initializer. Functional programmers +usually call this @dfn{foldl}.@refill + +Example: +@lisp +(define (sum . l) (reduce-init + 0 l)) +(sum 1 2 3 4) + @equiv{} (reduce-init + 0 (1 2 3 4)) + @equiv{} (+ (+ (+ (+ 0 1) 2) 3) 4) + @result{} 10 +(sum) + @equiv{} (reduce-init + 0 '()) + @result{} 0 + +(reduce-init string-append "@@" '("hello" "cruel" "world")) +@equiv{} +(string-append (string-append (string-append "@@" "hello") + "cruel") + "world") +@result{} "@@hellocruelworld" +@end lisp + +Given a differentiation of 2 arguments, @code{diff}, the following will +differentiate by any number of variables. +@lisp +(define (diff* exp . vars) + (reduce-init diff exp vars)) +@end lisp + +Example: +@lisp +;;; Real-world example: Insertion sort using reduce-init. + +(define (insert l item) + (if (null? l) + (list item) + (if (< (car l) item) + (cons (car l) (insert (cdr l) item)) + (cons item l)))) +(define (insertion-sort l) (reduce-init insert '() l)) + +(insertion-sort '(3 1 4 1 5) + @equiv{} (reduce-init insert () (3 1 4 1 5)) + @equiv{} (insert (insert (insert (insert (insert () 3) 1) 4) 1) 5) + @equiv{} (insert (insert (insert (insert (3)) 1) 4) 1) 5) + @equiv{} (insert (insert (insert (1 3) 4) 1) 5) + @equiv{} (insert (insert (1 3 4) 1) 5) + @equiv{} (insert (1 1 3 4) 5) + @result{} (1 1 3 4 5) + @end lisp +@end defun + +@defun butlast lst n +@code{butlast} returns all but the last @var{n} elements of +@var{lst}.@refill + +Example: +@lisp +(butlast '(1 2 3 4) 3) + @result{} (1) +(butlast '(1 2 3 4) 4) + @result{} () +@end lisp +@end defun + +@defun nthcdr n lst +@code{nthcdr} takes @var{n} @code{cdr}s of @var{lst} and returns the +result. Thus @code{(nthcdr 3 @var{lst})} @equiv{} @code{(cdddr +@var{lst})} + +Example: +@lisp +(nthcdr 2 '(1 2 3 4)) + @result{} (3 4) +(nthcdr 0 '(1 2 3 4)) + @result{} (1 2 3 4) +@end lisp +@end defun + +@defun last lst n +@code{last} returns the last @var{n} elements of @var{lst}. @var{n} +must be a non-negative integer. + +Example: +@lisp +(last '(foo bar baz bang) 2) + @result{} (baz bang) +(last '(1 2 3) 0) + @result{} 0 +@end lisp +@end defun + + + + + + +@node Destructive list operations, Non-List functions, Lists as sequences, Common List Functions +@subsection Destructive list operations + +These procedures may mutate the list they operate on, but any such +mutation is undefined. + +@deffn Procedure nconc args +@code{nconc} destructively concatenates its arguments. (Compare this +with @code{append}, which copies arguments rather than destroying them.) +Sometimes called @code{append!} (@xref{Rev2 Procedures}).@refill + +Example: You want to find the subsets of a set. Here's the obvious way: + +@lisp +(define (subsets set) + (if (null? set) + '(()) + (append (mapcar (lambda (sub) (cons (car set) sub)) + (subsets (cdr set))) + (subsets (cdr set))))) +@end lisp +But that does way more consing than you need. Instead, you could +replace the @code{append} with @code{nconc}, since you don't have any +need for all the intermediate results.@refill + +Example: +@lisp +(define x '(a b c)) +(define y '(d e f)) +(nconc x y) + @result{} (a b c d e f) +x + @result{} (a b c d e f) +@end lisp + +@code{nconc} is the same as @code{append!} in @file{sc2.scm}. +@end deffn + +@deffn Procedure nreverse lst +@code{nreverse} reverses the order of elements in @var{lst} by mutating +@code{cdr}s of the list. Sometimes called @code{reverse!}.@refill + +Example: +@lisp +(define foo '(a b c)) +(nreverse foo) + @result{} (c b a) +foo + @result{} (a) +@end lisp + +Some people have been confused about how to use @code{nreverse}, +thinking that it doesn't return a value. It needs to be pointed out +that@refill +@lisp +(set! lst (nreverse lst)) +@end lisp +@noindent +is the proper usage, not +@lisp +(nreverse lst) +@end lisp +The example should suffice to show why this is the case. +@end deffn + +@deffn Procedure delete elt lst +@deffnx Procedure delete-if pred lst +@deffnx Procedure delete-if-not pred lst +Destructive versions of @code{remove} @code{remove-if}, and +@code{remove-if-not}.@refill + +Example: +@lisp +(define lst '(foo bar baz bang)) +(delete 'foo lst) + @result{} (bar baz bang) +lst + @result{} (foo bar baz bang) + +(define lst '(1 2 3 4 5 6 7 8 9)) +(delete-if odd? lst) + @result{} (2 4 6 8) +lst + @result{} (1 2 4 6 8) +@end lisp + +Some people have been confused about how to use @code{delete}, +@code{delete-if}, and @code{delete-if}, thinking that they dont' return +a value. It needs to be pointed out that@refill +@lisp +(set! lst (delete el lst)) +@end lisp +@noindent +is the proper usage, not +@lisp +(delete el lst) +@end lisp +The examples should suffice to show why this is the case. +@end deffn + + + +@node Non-List functions, , Destructive list operations, Common List Functions +@subsection Non-List functions + +@defun and? . args +@code{and?} checks to see if all its arguments are true. If they are, +@code{and?} returns @code{#t}, otherwise, @code{#f}. (In contrast to +@code{and}, this is a function, so all arguments are always evaluated +and in an unspecified order.)@refill + +Example: +@lisp +(and? 1 2 3) + @result{} #t +(and #f 1 2) + @result{} #f +@end lisp +@end defun + +@defun or? . args +@code{or?} checks to see if any of its arguments are true. If any is +true, @code{or?} returns @code{#t}, and @code{#f} otherwise. (To +@code{or} as @code{and?} is to @code{and}.)@refill + +Example: +@lisp +(or? 1 2 #f) + @result{} #t +(or? #f #f #f) + @result{} #f +@end lisp +@end defun + +@defun atom? object +Returns @code{#t} if @var{object} is not a pair and @code{#f} if it is +pair. (Called @code{atom} in Common LISP.) +@lisp +(atom? 1) + @result{} #t +(atom? '(1 2)) + @result{} #f +(atom? #(1 2)) ; dubious! + @result{} #t +@end lisp +@end defun + +@defun type-of object +Returns a symbol name for the type of @var{object}. +@end defun + +@defun coerce object result-type +Converts and returns @var{object} of type @code{char}, @code{number}, +@code{string}, @code{symbol}, @code{list}, or @code{vector} to +@var{result-type} (which must be one of these symbols). +@end defun + +@node Format, Generic-Write, Common List Functions, Procedures +@section Format + +@code{(require 'format)} + +@menu +* Format Interface:: +* Format Specification:: +@end menu + +@node Format Interface, Format Specification, Format, Format +@subsection Format Interface + +@defun format destination format-string . arguments +An almost complete implementation of Common LISP format description +according to the CL reference book @cite{Common LISP} from Guy L. +Steele, Digital Press. Backward compatible to most of the available +Scheme format implementations. + +Returns @code{#t}, @code{#f} or a string; has side effect of printing +according to @var{format-string}. If @var{destination} is @code{#t}, +the output is to the current output port and @code{#t} is returned. If +@var{destination} is @code{#f}, a formatted string is returned as the +result of the call. NEW: If @var{destination} is a string, +@var{destination} is regarded as the format string; @var{format-string} is +then the first argument and the output is returned as a string. If +@var{destination} is a number, the output is to the current error port +if available by the implementation. Otherwise @var{destination} must be +an output port and @code{#t} is returned.@refill + +@var{format-string} must be a string. In case of a formatting error +format returns @code{#f} and prints a message on the current output or +error port. Characters are output as if the string were output by the +@code{display} function with the exception of those prefixed by a tilde +(~). For a detailed description of the @var{format-string} syntax +please consult a Common LISP format reference manual. For a test suite +to verify this format implementation load @file{formatst.scm}. Please +send bug reports to @code{lutzeb@@cs.tu-berlin.de}. + +Note: @code{format} is not reentrant, i.e. only one @code{format}-call +may be executed at a time. + +@end defun + +@node Format Specification, , Format Interface, Format +@subsection Format Specification (Format version 3.0) + +Please consult a Common LISP format reference manual for a detailed +description of the format string syntax. For a demonstration of the +implemented directives see @file{formatst.scm}.@refill + +This implementation supports directive parameters and modifiers +(@code{:} and @code{@@} characters). Multiple parameters must be +separated by a comma (@code{,}). Parameters can be numerical parameters +(positive or negative), character parameters (prefixed by a quote +character (@code{'}), variable parameters (@code{v}), number of rest +arguments parameter (@code{#}), empty and default parameters. Directive +characters are case independent. The general form of a directive +is:@refill + +@noindent +@var{directive} ::= ~@{@var{directive-parameter},@}[:][@@]@var{directive-character} + +@noindent +@var{directive-parameter} ::= [ [-|+]@{0-9@}+ | '@var{character} | v | # ] + + +@subsubsection Implemented CL Format Control Directives + +Documentation syntax: Uppercase characters represent the corresponding +control directive characters. Lowercase characters represent control +directive parameter descriptions. + +@table @asis +@item @code{~A} +Any (print as @code{display} does). +@table @asis +@item @code{~@@A} +left pad. +@item @code{~@var{mincol},@var{colinc},@var{minpad},@var{padchar}A} +full padding. +@end table +@item @code{~S} +S-expression (print as @code{write} does). +@table @asis +@item @code{~@@S} +left pad. +@item @code{~@var{mincol},@var{colinc},@var{minpad},@var{padchar}S} +full padding. +@end table +@item @code{~D} +Decimal. +@table @asis +@item @code{~@@D} +print number sign always. +@item @code{~:D} +print comma separated. +@item @code{~@var{mincol},@var{padchar},@var{commachar}D} +padding. +@end table +@item @code{~X} +Hexadecimal. +@table @asis +@item @code{~@@X} +print number sign always. +@item @code{~:X} +print comma separated. +@item @code{~@var{mincol},@var{padchar},@var{commachar}X} +padding. +@end table +@item @code{~O} +Octal. +@table @asis +@item @code{~@@O} +print number sign always. +@item @code{~:O} +print comma separated. +@item @code{~@var{mincol},@var{padchar},@var{commachar}O} +padding. +@end table +@item @code{~B} +Binary. +@table @asis +@item @code{~@@B} +print number sign always. +@item @code{~:B} +print comma separated. +@item @code{~@var{mincol},@var{padchar},@var{commachar}B} +padding. +@end table +@item @code{~@var{n}R} +Radix @var{n}. +@table @asis +@item @code{~@var{n},@var{mincol},@var{padchar},@var{commachar}R} +padding. +@end table +@item @code{~@@R} +print a number as a Roman numeral. +@item @code{~:R} +print a number as an ordinal English number. +@item @code{~:@@R} +print a number as a cardinal English number. +@item @code{~P} +Plural. +@table @asis +@item @code{~@@P} +prints @code{y} and @code{ies}. +@item @code{~:P} +as @code{~P but jumps 1 argument backward.} +@item @code{~:@@P} +as @code{~@@P but jumps 1 argument backward.} +@end table +@item @code{~C} +Character. +@table @asis +@item @code{~@@C} +prints a character as the reader can understand it (i.e. @code{#\} prefixing). +@item @code{~:C} +prints a character as emacs does (eg. @code{^C} for ASCII 03). +@end table +@item @code{~F} +Fixed-format floating-point (prints a flonum like @var{mmm.nnn}). +@table @asis +@item @code{~@var{width},@var{digits},@var{scale},@var{overflowchar},@var{padchar}F} +@item @code{~@@F} +If the number is positive a plus sign is printed. +@end table +@item @code{~E} +Exponential floating-point (prints a flonum like @var{mmm.nnn@code{E}ee}). +@table @asis +@item @code{~@var{width},@var{digits},@var{exponentdigits},@var{scale},@var{overflowchar},@var{padchar},@var{exponentchar}E} +@item @code{~@@E} +If the number is positive a plus sign is printed. +@end table +@item @code{~G} +General floating-point (prints a flonum either fixed or exponential). +@table @asis +@item @code{~@var{width},@var{digits},@var{exponentdigits},@var{scale},@var{overflowchar},@var{padchar},@var{exponentchar}G} +@item @code{~@@G} +If the number is positive a plus sign is printed. +@end table +@item @code{~$} +Dollars floating-point (prints a flonum in fixed with signs separated). +@table @asis +@item @code{~@var{digits},@var{scale},@var{width},@var{padchar}$} +@item @code{~@@$} +If the number is positive a plus sign is printed. +@item @code{~:@@$} +A sign is always printed and appears before the padding. +@item @code{~:$} +The sign appears before the padding. +@end table +@item @code{~%} +Newline. +@table @asis +@item @code{~@var{n}%} +print @var{n} newlines. +@end table +@item @code{~&} +print newline if not at the beginning of the output line. +@table @asis +@item @code{~@var{n}&} +prints @code{~&} and then @var{n-1} newlines. +@end table +@item @code{~|} +Page Separator. +@table @asis +@item @code{~@var{n}|} +print @var{n} page separators. +@end table +@item @code{~~} +Tilde. +@table @asis +@item @code{~@var{n}~} +print @var{n} tildes. +@end table +@item @code{~}<newline> +Continuation Line. +@table @asis +@item @code{~:}<newline> +newline is ignored, white space left. +@item @code{~@@}<newline> +newline is left, white space ignored. +@end table +@item @code{~T} +Tabulation. +@table @asis +@item @code{~@@T} +relative tabulation. +@item @code{~@var{colnum,colinc}T} +full tabulation. +@end table +@item @code{~?} +Indirection (expects indirect arguments as a list). +@table @asis +@item @code{~@@?} +extracts indirect arguments from format arguments. +@end table +@item @code{~(@var{str}~)} +Case conversion (converts by @code{string-downcase}). +@table @asis +@item @code{~:(@var{str}~)} +converts by @code{string-capitalize}. +@item @code{~@@(@var{str}~)} +converts by @code{string-capitalize-first}. +@item @code{~:@@(@var{str}~)} +converts by @code{string-upcase}. +@end table +@item @code{~*} +Argument Jumping (jumps 1 argument forward). +@table @asis +@item @code{~@var{n}*} +jumps @var{n} arguments forward. +@item @code{~:*} +jumps 1 argument backward. +@item @code{~@var{n}:*} +jumps @var{n} arguments backward. +@item @code{~@@*} +jumps to the 0th argument. +@item @code{~@var{n}@@*} +jumps to the @var{n}th argument (beginning from 0) +@end table +@item @code{~[@var{str0}~;@var{str1}~;...~;@var{strn}~]} +Conditional Expression (numerical clause conditional). +@table @asis +@item @code{~@var{n}[} +take argument from @var{n}. +@item @code{~@@[} +true test conditional. +@item @code{~:[} +if-else-then conditional. +@item @code{~;} +clause separator. +@item @code{~:;} +default clause follows. +@end table +@item @code{~@{@var{str}~@}} +Iteration (args come from the next argument (a list)). +@table @asis +@item @code{~@var{n}@{} +at most @var{n} iterations. +@item @code{~:@{} +args from next arg (a list of lists). +@item @code{~@@@{} +args from the rest of arguments. +@item @code{~:@@@{} +args from the rest args (lists). +@end table +@item @code{~^} +Up and out. +@table @asis +@item @code{~@var{n}^} +aborts if @var{n} = 0 +@item @code{~@var{n},@var{m}^} +aborts if @var{n} = @var{m} +@item @code{~@var{n},@var{m},@var{k}^} +aborts if @var{n} <= @var{m} <= @var{k} +@end table +@end table + + +@subsubsection Not Implemented CL Format Control Directives + +@table @asis +@item @code{~:A} +print @code{#f} as an empty list (see below). +@item @code{~:S} +print @code{#f} as an empty list (see below). +@item @code{~<~>} +Justification. +@item @code{~:^} +(sorry I don't understand its semantics completely) +@end table + + +@subsubsection Extended, Replaced and Additional Control Directives + +@table @asis +@item @code{~@var{mincol},@var{padchar},@var{commachar},@var{commawidth}D} +@item @code{~@var{mincol},@var{padchar},@var{commachar},@var{commawidth}X} +@item @code{~@var{mincol},@var{padchar},@var{commachar},@var{commawidth}O} +@item @code{~@var{mincol},@var{padchar},@var{commachar},@var{commawidth}B} +@item @code{~@var{n},@var{mincol},@var{padchar},@var{commachar},@var{commawidth}R} +@var{commawidth} is the number of characters between two comma characters. +@end table + +@table @asis +@item @code{~I} +print a R4RS complex number as @code{~F~@@Fi} with passed parameters for +@code{~F}. +@item @code{~Y} +Pretty print formatting of an argument for scheme code lists. +@item @code{~K} +Same as @code{~?.} +@item @code{~!} +Flushes the output if format @var{destination} is a port. +@item @code{~_} +Print a @code{#\space} character +@table @asis +@item @code{~@var{n}_} +print @var{n} @code{#\space} characters. +@end table +@item @code{~/} +Print a @code{#\tab} character +@table @asis +@item @code{~@var{n}/} +print @var{n} @code{#\tab} characters. +@end table +@item @code{~@var{n}C} +Takes @var{n} as an integer representation for a character. No arguments +are consumed. @var{n} is converted to a character by +@code{integer->char}. @var{n} must be a positive decimal number.@refill +@item @code{~:S} +Print out readproof. Prints out internal objects represented as +@code{#<...>} as strings @code{"#<...>"} so that the format output can always +be processed by @code{read}. +@refill +@item @code{~:A} +Print out readproof. Prints out internal objects represented as +@code{#<...>} as strings @code{"#<...>"} so that the format output can always +be processed by @code{read}. +@item @code{~Q} +Prints information and a copyright notice on the format implementation. +@table @asis +@item @code{~:Q} +prints format version. +@end table +@refill +@item @code{~F, ~E, ~G, ~$} +may also print number strings, i.e. passing a number as a string and +format it accordingly. +@end table + +@subsubsection Configuration Variables + +Format has some configuration variables at the beginning of +@file{format.scm} to suit the systems and users needs. There should be +no modification necessary for the configuration that comes with SLIB. +If modification is desired the variable should be set after the format +code is loaded. Format detects automatically if the running scheme +system implements floating point numbers and complex numbers. + +@table @asis + +@item @var{format:symbol-case-conv} +Symbols are converted by @code{symbol->string} so the case type of the +printed symbols is implementation dependent. +@code{format:symbol-case-conv} is a one arg closure which is either +@code{#f} (no conversion), @code{string-upcase}, @code{string-downcase} +or @code{string-capitalize}. (default @code{#f}) + +@item @var{format:iobj-case-conv} +As @var{format:symbol-case-conv} but applies for the representation of +implementation internal objects. (default @code{#f}) + +@item @var{format:expch} +The character prefixing the exponent value in @code{~E} printing. (default +@code{#\E}) + +@end table + +@subsubsection Compatibility With Other Format Implementations + +@table @asis +@item SLIB format 2.x: +See @file{format.doc}. + +@item SLIB format 1.4: +Downward compatible except for padding support and @code{~A}, @code{~S}, +@code{~P}, @code{~X} uppercase printing. SLIB format 1.4 uses C-style +@code{printf} padding support which is completely replaced by the CL +@code{format} padding style. + +@item MIT C-Scheme 7.1: +Downward compatible except for @code{~}, which is not documented +(ignores all characters inside the format string up to a newline +character). (7.1 implements @code{~a}, @code{~s}, +~@var{newline}, @code{~~}, @code{~%}, numerical and variable +parameters and @code{:/@@} modifiers in the CL sense).@refill + +@item Elk 1.5/2.0: +Downward compatible except for @code{~A} and @code{~S} which print in +uppercase. (Elk implements @code{~a}, @code{~s}, @code{~~}, and +@code{~%} (no directive parameters or modifiers)).@refill + +@item Scheme->C 01nov91: +Downward compatible except for an optional destination parameter: S2C +accepts a format call without a destination which returns a formatted +string. This is equivalent to a #f destination in S2C. (S2C implements +@code{~a}, @code{~s}, @code{~c}, @code{~%}, and @code{~~} (no directive +parameters or modifiers)).@refill + +@end table + +This implementation of format is solely useful in the SLIB context +because it requires other components provided by SLIB.@refill + + +@node Generic-Write, Line I/O, Format, Procedures +@section Generic-Write + +@code{(require 'generic-write)} + +@code{generic-write} is a procedure that transforms a Scheme data value +(or Scheme program expression) into its textual representation and +prints it. The interface to the procedure is sufficiently general to +easily implement other useful formatting procedures such as pretty +printing, output to a string and truncated output.@refill + +@deffn Procedure generic-write obj display? width output +@table @var +@item obj +Scheme data value to transform. +@item display? +Boolean, controls whether characters and strings are quoted. +@item width +Extended boolean, selects format: +@table @asis +@item #f +single line format +@item integer > 0 +pretty-print (value = max nb of chars per line) +@end table +@item output +Procedure of 1 argument of string type, called repeatedly with +successive substrings of the textual representation. This procedure can +return @code{#f} to stop the transformation. +@end table + +The value returned by @code{generic-write} is undefined. + +Examples: +@lisp +(write obj) @equiv{} (generic-write obj #f #f @var{display-string}) +(display obj) @equiv{} (generic-write obj #t #f @var{display-string}) +@end lisp +@noindent +where +@lisp +@var{display-string} @equiv{} +(lambda (s) (for-each write-char (string->list s)) #t) +@end lisp +@end deffn + + + + + +@node Line I/O, Multi-Processing, Generic-Write, Procedures +@section Line I/O + +@code{(require 'line-i/o)} + +@defun read-line +@defunx read-line port +Returns a string of the characters up to, but not including a newline or +end of file, updating @var{port} to point to the character following the +newline. If no characters are available, an end of file object is +returned. @var{port} may be omitted, in which case it defaults to the +value returned by @code{current-input-port}.@refill +@end defun + +@defun read-line! string +@defunx read-line! string port +Fills @var{string} with characters up to, but not including a newline or +end of file, updating the port to point to the last character read or +following the newline if it was read. If no characters are available, +an end of file object is returned. If a newline or end of file was +found, the number of characters read is returned. Otherwise, @code{#f} +is returned. @var{port} may be omitted, in which case it defaults to +the value returned by @code{current-input-port}.@refill +@end defun + +@defun write-line string +@defunx write-line string port +Writes @var{string} followed by a newline to the given port and returns +an unspecified value. Port may be omited, in which case it defaults to +the value returned by @code{current-input-port}.@refill +@end defun + + + + +@node Multi-Processing, Object-To-String, Line I/O, Procedures +@section Multi-Processing + +@code{(require 'process)} + +@deffn Procedure add-process! proc +Adds proc, which must be a procedure (or continuation) capable of +accepting accepting one argument, to the @code{process:queue}. The +value returned is unspecified. The argument to @var{proc} should be +ignored. If @var{proc} returns, the process is killed.@refill +@end deffn + +@deffn Procedure process:schedule! +Saves the current process on @code{process:queue} and runs the next +process from @code{process:queue}. The value returned is +unspecified.@refill +@end deffn + + +@deffn Procedure kill-process! +Kills the current process and runs the next process from +@code{process:queue}. If there are no more processes on +@code{process:queue}, @code{(slib:exit)} is called (@xref{System}). +@end deffn + + + + + +@node Object-To-String, Pretty-Print, Multi-Processing, Procedures +@section Object-To-String + +@code{(require 'object->string)} + +@defun object->string obj +Returns the textual representation of @var{obj} as a string. +@end defun + + + + +@node Pretty-Print, Sorting, Object-To-String, Procedures +@section Pretty-Print + +@code{(require 'pretty-print)} + +@deffn Procedure pretty-print obj +@deffnx Procedure pretty-print obj port + +@code{pretty-print}s @var{obj} on @var{port}. If @var{port} is not +specified, @code{current-output-port} is used. + +Example: +@example +@group +(pretty-print '((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))) + @print{} ((1 2 3 4 5) + @print{} (6 7 8 9 10) + @print{} (11 12 13 14 15) + @print{} (16 17 18 19 20) + @print{} (21 22 23 24 25)) +@end group +@end example +@end deffn + + +@code{(require 'pprint-file)} + +@deffn Procedure pprint-file infile +@deffnx Procedure pprint-file infile outfile +Pretty-prints all the code in @var{infile}. If @var{outfile} is +specified, the output goes to @var{outfile}, otherwise it goes to +@code{(current-output-port)}.@refill +@end deffn + +@defun pprint-filter-file infile proc outfile +@defunx pprint-filter-file infile proc +@var{infile} is a port or a string naming an existing file. Scheme +source code expressions and definitions are read from the port (or file) +and @var{proc} is applied to them sequentially. + +@var{outfile} is a port or a string. If no @var{outfile} is specified +then @code{current-output-port} is assumed. These expanded expressions +are then @code{pretty-print}ed to this port. + +Whitepsace and comments (introduced by @code{;}) which are not part of +scheme expressions are reproduced in the output. This procedure does +not affect the values returned by @code{current-input-port} and +@code{current-output-port}.@refill +@end defun + +@code{pprint-filter-file} can be used to pre-compile macro-expansion and +thus can reduce loading time. The following will write into +@file{exp-code.scm} the result of expanding all defmacros in +@file{code.scm}. +@lisp +(require 'pprint-file) +(require 'defmacroexpand) +(defmacro:load "my-macros.scm") +(pprint-filter-file "code.scm" defmacro:expand* "exp-code.scm") +@end lisp + + +@node Sorting, Topological Sort, Pretty-Print, Procedures +@section Sorting + +@code{(require 'sort)} + +Many Scheme systems provide some kind of sorting functions. They do +not, however, always provide the @emph{same} sorting functions, and +those that I have had the opportunity to test provided inefficient ones +(a common blunder is to use quicksort which does not perform well). + +Because @code{sort} and @code{sort!} are not in the standard, there is +very little agreement about what these functions look like. For +example, Dybvig says that Chez Scheme provides +@lisp +(merge predicate list1 list2) +(merge! predicate list1 list2) +(sort predicate list) +(sort! predicate list) +@end lisp +@noindent +while MIT Scheme 7.1, following Common LISP, offers unstable +@lisp +(sort list predicate) +@end lisp +@noindent +TI PC Scheme offers +@lisp +(sort! list/vector predicate?) +@end lisp +@noindent +and Elk offers +@lisp +(sort list/vector predicate?) +(sort! list/vector predicate?) +@end lisp + +Here is a comprehensive catalogue of the variations I have found. + +@enumerate +@item +Both @code{sort} and @code{sort!} may be provided. +@item +@code{sort} may be provided without @code{sort!}. +@item +@code{sort!} may be provided without @code{sort}. +@item +Neither may be provided. +@item +The sequence argument may be either a list or a vector. +@item +The sequence argument may only be a list. +@item +The sequence argument may only be a vector. +@item +The comparison function may be expected to behave like @code{<}. +@item +The comparison function may be expected to behave like @code{<=}. +@item +The interface may be @code{(sort predicate? sequence)}. +@item +The interface may be @code{(sort sequence predicate?)}. +@item +The interface may be @code{(sort sequence &optional (predicate? <))}. +@item +The sort may be stable. +@item +The sort may be unstable. +@end enumerate + +All of this variation really does not help anybody. A nice simple merge +sort is both stable and fast (quite a lot faster than @emph{quick} sort). + +I am providing this source code with no restrictions at all on its use +(but please retain D.H.D.Warren's credit for the original idea). You +may have to rename some of these functions in order to use them in a +system which already provides incompatible or inferior sorts. For each +of the functions, only the top-level define needs to be edited to do +that. + +I could have given these functions names which would not clash with any +Scheme that I know of, but I would like to encourage implementors to +converge on a single interface, and this may serve as a hint. The +argument order for all functions has been chosen to be as close to +Common LISP as made sense, in order to avoid NIH-itis. + +Each of the five functions has a required @emph{last} parameter which is +a comparison function. A comparison function @code{f} is a function of +2 arguments which acts like @code{<}. For example,@refill + +@lisp +(not (f x x)) +(and (f x y) (f y z)) @equiv{} (f x z) +@end lisp + +The standard functions @code{<}, @code{>}, @code{char<?}, @code{char>?}, +@code{char-ci<?}, @code{char-ci>?}, @code{string<?}, @code{string>?}, +@code{string-ci<?}, and @code{string-ci>?} are suitable for use as +comparison functions. Think of @code{(less? x y)} as saying when +@code{x} must @emph{not} precede @code{y}.@refill + +@defun sorted? sequence less? +Returns @code{#t} when the sequence argument is in non-decreasing order +according to @var{less?} (that is, there is no adjacent pair @code{@dots{} x +y @dots{}} for which @code{(less? y x)}).@refill + +Returns @code{#f} when the sequence contains at least one out-of-order +pair. It is an error if the sequence is neither a list nor a vector. +@end defun + +@defun merge list1 list2 less? +This merges two lists, producing a completely new list as result. I +gave serious consideration to producing a Common-LISP-compatible +version. However, Common LISP's @code{sort} is our @code{sort!} (well, +in fact Common LISP's @code{stable-sort} is our @code{sort!}, merge sort +is @emph{fast} as well as stable!) so adapting CL code to Scheme takes a +bit of work anyway. I did, however, appeal to CL to determine the +@emph{order} of the arguments. +@end defun + +@deffn Procedure merge! list1 list2 less? +Merges two lists, re-using the pairs of @var{list1} and @var{list2} to +build the result. If the code is compiled, and @var{less?} constructs +no new pairs, no pairs at all will be allocated. The first pair of the +result will be either the first pair of @var{list1} or the first pair of +@var{list2}, but you can't predict which. + +The code of @code{merge} and @code{merge!} could have been quite a bit +simpler, but they have been coded to reduce the amount of work done per +iteration. (For example, we only have one @code{null?} test per +iteration.)@refill +@end deffn + +@defun sort sequence less? +Accepts either a list or a vector, and returns a new sequence which is +sorted. The new sequence is the same type as the input. Always +@code{(sorted? (sort sequence less?) less?)}. The original sequence is +not altered in any way. The new sequence shares its @emph{elements} +with the old one; no elements are copied.@refill +@end defun + +@deffn Procedure sort! sequence less? +Returns its sorted result in the original boxes. If the original +sequence is a list, no new storage is allocated at all. If the original +sequence is a vector, the sorted elements are put back in the same +vector. + +Some people have been confused about how to use @code{sort!}, thinking +that it doesn't return a value. It needs to be pointed out that +@lisp +(set! slist (sort! slist <)) +@end lisp +@noindent +is the proper usage, not +@lisp +(sort! slist <) +@end lisp +@end deffn + +Note that these functions do @emph{not} accept a CL-style @samp{:key} +argument. A simple device for obtaining the same expressiveness is to +define@refill +@lisp +(define (keyed less? key) + (lambda (x y) (less? (key x) (key y)))) +@end lisp +@noindent +and then, when you would have written +@lisp +(sort a-sequence #'my-less :key #'my-key) +@end lisp +@noindent +in Common LISP, just write +@lisp +(sort! a-sequence (keyed my-less? my-key)) +@end lisp +@noindent +in Scheme. + +@node Topological Sort, Standard Formatted I/O, Sorting, Procedures +@section Topological Sort + +@code{(require 'topological-sort)} or @code{(require 'tsort)} + +@noindent +The algorithm is inspired by Cormen, Leiserson and Rivest (1990) +@cite{Introduction to Algorithms}, chapter 23. + +@defun tsort dag pred +@defunx topological-sort dag pred +where +@table @var +@item dag +is a list of sublists. The car of each sublist is a vertex. The cdr is +the adjacency list of that vertex, i.e. a list of all vertices to which +there exists an edge from the car vertex. +@item pred +is one of @code{eq?}, @code{eqv?}, @code{equal?}, @code{=}, +@code{char=?}, @code{char-ci=?}, @code{string=?}, or @code{string-ci=?}. +@end table + +Sort the directed acyclic graph @var{dag} so that for every edge from +vertex @var{u} to @var{v}, @var{u} will come before @var{v} in the +resulting list of vertices. + +Time complexity: O (|V| + |E|) + +Example (from Cormen): +@quotation +Prof. Bumstead topologically sorts his clothing when getting +dressed. The first argument to `tsort' describes which +garments he needs to put on before others. (For example, +Prof Bumstead needs to put on his shirt before he puts on his +tie or his belt.) `tsort' gives the correct order of dressing: +@end quotation + +@example +(require 'tsort) +(tsort '((shirt tie belt) + (tie jacket) + (belt jacket) + (watch) + (pants shoes belt) + (undershorts pants shoes) + (socks shoes)) + eq?) +@result{} +(socks undershorts pants shoes watch shirt belt tie jacket) +@end example +@end defun + +@node Standard Formatted I/O, String-Case, Topological Sort, Procedures +@section Standard Formatted I/O + +@menu +* Standard Formatted Output:: +* Standard Formatted Input:: +@end menu + +@subsection stdio + +@code{(require 'stdio)} + +@code{require}s @code{printf} and @code{scanf} and additionally defines +the symbols: + +@defvar stdin +Defined to be @code{(current-input-port)}. +@end defvar +@defvar stdout +Defined to be @code{(current-output-port)}. +@end defvar +@defvar stderr +Defined to be @code{(current-error-port)}. +@end defvar + + +@node Standard Formatted Output, Standard Formatted Input, Standard Formatted I/O, Standard Formatted I/O +@subsection Standard Formatted Output + +@code{(require 'printf)} + +@deffn Procedure printf format arg1 @dots{} +@deffnx Procedure fprintf port format arg1 @dots{} +@deffnx Procedure sprintf str format arg1 @dots{} + +Each function converts, formats, and outputs its @var{arg1} @dots{} +arguments according to the control string @var{format} argument and +returns the number of characters output. + +@code{printf} sends its output to the port @code{(current-output-port)}. +@code{fprintf} sends its output to the port @var{port}. @code{sprintf} +@code{string-set!}s locations of the non-constant string argument +@var{str} to the output characters. + +@quotation +@emph{Note:} sprintf should be changed to a macro so a @code{substring} +expression could be used for the @var{str} argument. +@end quotation + +The string @var{format} contains plain characters which are copied to +the output stream, and conversion specifications, each of which results +in fetching zero or more of the arguments @var{arg1} @dots{}. The +results are undefined if there are an insufficient number of arguments +for the format. If @var{format} is exhausted while some of the +@var{arg1} @dots{} arguments remain unused, the excess @var{arg1} +@dots{} arguments are ignored. + +The conversion specifications in a format string have the form: + +@example +% @r{[} @var{flags} @r{]} @r{[} @var{width} @r{]} @r{[} . @var{precision} @r{]} @r{[} @var{type} @r{]} @var{conversion} +@end example + +An output conversion specifications consist of an initial @samp{%} +character followed in sequence by: + +@itemize @bullet +@item +Zero or more @dfn{flag characters} that modify the normal behavior of +the conversion specification. + +@table @asis +@item @samp{-} +Left-justify the result in the field. Normally the result is +right-justified. + +@item @samp{+} +For the signed @samp{%d} and @samp{%i} conversions and all inexact +conversions, prefix a plus sign if the value is positive. + +@item @samp{ } +For the signed @samp{%d} and @samp{%i} conversions, if the result +doesn't start with a plus or minus sign, prefix it with a space +character instead. Since the @samp{+} flag ensures that the result +includes a sign, this flag is ignored if both are specified. + +@item @samp{#} +For inexact conversions, @samp{#} specifies that the result should +always include a decimal point, even if no digits follow it. For the +@samp{%g} and @samp{%G} conversions, this also forces trailing zeros +after the decimal point to be printed where they would otherwise be +elided. + +For the @samp{%o} conversion, force the leading digit to be @samp{0}, as +if by increasing the precision. For @samp{%x} or @samp{%X}, prefix a +leading @samp{0x} or @samp{0X} (respectively) to the result. This +doesn't do anything useful for the @samp{%d}, @samp{%i}, or @samp{%u} +conversions. Using this flag produces output which can be parsed by the +@code{scanf} functions with the @samp{%i} conversion (@pxref{Standard +Formatted Input}). + + +@item @samp{0} +Pad the field with zeros instead of spaces. The zeros are placed after +any indication of sign or base. This flag is ignored if the @samp{-} +flag is also specified, or if a precision is specified for an exact +converson. +@end table + +@item +An optional decimal integer specifying the @dfn{minimum field width}. +If the normal conversion produces fewer characters than this, the field +is padded (with spaces or zeros per the @samp{0} flag) to the specified +width. This is a @emph{minimum} width; if the normal conversion +produces more characters than this, the field is @emph{not} truncated. +@cindex minimum field width (@code{printf}) + +Alternatively, if the field width is @samp{*}, the next argument in the +argument list (before the actual value to be printed) is used as the +field width. The width value must be an integer. If the value is +negative it is as though the @samp{-} flag is set (see above) and the +absolute value is used as the field width. + +@item +An optional @dfn{precision} to specify the number of digits to be +written for numeric conversions and the maximum field width for string +conversions. The precision is specified by a period (@samp{.}) followed +optionally by a decimal integer (which defaults to zero if omitted). +@cindex precision (@code{printf}) + +Alternatively, if the precision is @samp{.*}, the next argument in the +argument list (before the actual value to be printed) is used as the +precision. The value must be an integer, and is ignored if negative. +If you specify @samp{*} for both the field width and precision, the +field width argument precedes the precision argument. The @samp{.*} +precision is an enhancement. C library versions may not accept this +syntax. + +For the @samp{%f}, @samp{%e}, and @samp{%E} conversions, the precision +specifies how many digits follow the decimal-point character. The +default precision is @code{6}. If the precision is explicitly @code{0}, +the decimal point character is suppressed. + +For the @samp{%g} and @samp{%G} conversions, the precision specifies how +many significant digits to print. Significant digits are the first +digit before the decimal point, and all the digits after it. If the +precision is @code{0} or not specified for @samp{%g} or @samp{%G}, it is +treated like a value of @code{1}. If the value being printed cannot be +expressed accurately in the specified number of digits, the value is +rounded to the nearest number that fits. + +For exact conversions, if a precision is supplied it specifies the +minimum number of digits to appear; leading zeros are produced if +necessary. If a precision is not supplied, the number is printed with +as many digits as necessary. Converting an exact @samp{0} with an +explicit precision of zero produces no characters. + +@item +An optional one of @samp{l}, @samp{h} or @samp{L}, which is ignored for +numeric conversions. It is an error to specify these modifiers for +non-numeric conversions. + +@item +A character that specifies the conversion to be applied. +@end itemize + +@subsubsection Exact Conversions + +@table @asis +@item @samp{d}, @samp{i} +Print an integer as a signed decimal number. @samp{%d} and @samp{%i} +are synonymous for output, but are different when used with @code{scanf} +for input (@pxref{Standard Formatted Input}). + +@item @samp{o} +Print an integer as an unsigned octal number. + +@item @samp{u} +Print an integer as an unsigned decimal number. + +@item @samp{x}, @samp{X} +Print an integer as an unsigned hexadecimal number. @samp{%x} prints +using the digits @samp{0123456789abcdef}. @samp{%X} prints using the +digits @samp{0123456789ABCDEF}. +@end table + +@subsubsection Inexact Conversions +@emph{Note:} Inexact conversions are not supported yet. + +@table @asis +@item @samp{f} +Print a floating-point number in fixed-point notation. + +@item @samp{e}, @samp{E} +Print a floating-point number in exponential notation. @samp{%e} prints +@samp{e} between mantissa and exponont. @samp{%E} prints @samp{E} +between mantissa and exponont. + +@item @samp{g}, @samp{G} +Print a floating-point number in either normal or exponential notation, +whichever is more appropriate for its magnitude. @samp{%g} prints +@samp{e} between mantissa and exponont. @samp{%G} prints @samp{E} +between mantissa and exponont. +@end table + +@subsubsection Other Conversions +@table @asis +@item @samp{c} +Print a single character. The @samp{-} flag is the only one which can +be specified. It is an error to specify a precision. + +@item @samp{s} +Print a string. The @samp{-} flag is the only one which can be +specified. A precision specifies the maximum number of characters to +output; otherwise all characters in the string are output. + +@item @samp{a}, @samp{A} +Print a scheme expression. The @samp{-} flag left-justifies the output. +The @samp{#} flag specifies that strings and characters should be quoted +as by @code{write} (which can be read using @code{read}); otherwise, +output is as @code{display} prints. A precision specifies the maximum +number of characters to output; otherwise as many characters as needed +are output. + +@emph{Note:} @samp{%a} and @samp{%A} are SLIB extensions. + +@c @item @samp{p} +@c Print the value of a pointer. + +@c @item @samp{n} +@c Get the number of characters printed so far. @xref{Other Output Conversions}. +@c Note that this conversion specification never produces any output. + +@c @item @samp{m} +@c Print the string corresponding to the value of @code{errno}. +@c (This is a GNU extension.) +@c @xref{Other Output Conversions}. + +@item @samp{%} +Print a literal @samp{%} character. No argument is consumed. It is an +error to specifiy flags, field width, precision, or type modifiers with +@samp{%%}. +@end table +@end deffn + + +@node Standard Formatted Input, , Standard Formatted Output, Standard Formatted I/O +@subsection Standard Formatted Input + +@code{(require 'scanf)} + +@deffn Function scanf-read-list format +@deffnx Function scanf-read-list format port +@deffnx Function scanf-read-list format string +@end deffn + +@defmac scanf format arg1 @dots{} +@defmacx fscanf port format arg1 @dots{} +@defmacx sscanf str format arg1 @dots{} + +Each function reads characters, interpreting them according to the +control string @var{format} argument. + +@code{scanf-read-list} returns a list of the items specified as far as +the input matches @var{format}. @code{scanf}, @code{fscanf}, and +@code{sscanf} return the number of items successfully matched and +stored. @code{scanf}, @code{fscanf}, and @code{sscanf} also set the +location corresponding to @var{arg1} @dots{} using the methods: + +@table @asis +@item symbol +@code{set!} +@item car expression +@code{set-car!} +@item cdr expression +@code{set-cdr!} +@item vector-ref expression +@code{vector-set!} +@item substring expression +@code{substring-move-left!} +@end table + +The argument to a @code{substring} expression in @var{arg1} @dots{} must +be a non-constant string. Characters will be stored starting at the +position specified by the second argument to @code{substring}. The +number of characters stored will be limited by either the position +specified by the third argument to @code{substring} or the length of the +matched string, whichever is less. + +The control string, @var{format}, contains conversion specifications and +other characters used to direct interpretation of input sequences. The +control string contains: + +@itemize @bullet +@item White-space characters (blanks, tabs, newlines, or formfeeds) +that cause input to be read (and discarded) up to the next +non-white-space character. + +@item An ordinary character (not @samp{%}) that must match the next +character of the input stream. + +@item Conversion specifications, consisting of the character @samp{%}, an +optional assignment suppressing character @samp{*}, an optional +numerical maximum-field width, an optional @samp{l}, @samp{h} or +@samp{L} which is ignored, and a conversion code. + +@c @item The conversion specification can alternatively be prefixed by +@c the character sequence @samp{%n$} instead of the character @samp{%}, +@c where @var{n} is a decimal integer in the range. The @samp{%n$} +@c construction indicates that the value of the next input field should be +@c placed in the @var{n}th place in the return list, rather than to the next +@c unused one. The two forms of introducing a conversion specification, +@c @samp{%} and @samp{%n$}, must not be mixed within a single format string +@c with the following exception: Skip fields (see below) can be designated +@c as @samp{%*} or @samp{%n$*}. In the latter case, @var{n} is ignored. + +@end itemize + +Unless the specification contains the @samp{n} conversion character +(described below), a conversion specification directs the conversion of +the next input field. The result of a conversion specification is +returned in the position of the corresponding argument points, unless +@samp{*} indicates assignment suppression. Assignment suppression +provides a way to describe an input field to be skipped. An input field +is defined as a string of characters; it extends to the next +inappropriate character or until the field width, if specified, is +exhausted. + +@quotation +@emph{Note:} This specification of format strings differs from the +@cite{ANSI C} and @cite{POSIX} specifications. In SLIB, white space +before an input field is not skipped unless white space appears before +the conversion specification in the format string. In order to write +format strings which work identically with @cite{ANSI C} and SLIB, +prepend whitespace to all conversion specifications except @samp{[} and +@samp{c}. +@end quotation + +The conversion code indicates the interpretation of the input field; For +a suppressed field, no value is returned. The following conversion +codes are legal: + +@table @asis + +@item @samp{%} +A single % is expected in the input at this point; no value is returned. + +@item @samp{d}, @samp{D} +A decimal integer is expected. + +@item @samp{u}, @samp{U} +An unsigned decimal integer is expected. + +@item @samp{o}, @samp{O} +An octal integer is expected. + +@item @samp{x}, @samp{X} +A hexadecimal integer is expected. + +@item @samp{i} +An integer is expected. Returns the value of the next input item, +interpreted according to C conventions; a leading @samp{0} implies +octal, a leading @samp{0x} implies hexadecimal; otherwise, decimal is +assumed. + +@item @samp{n} +Returns the total number of bytes (including white space) read by +@code{scanf}. No input is consumed by @code{%n}. + +@item @samp{f}, @samp{F}, @samp{e}, @samp{E}, @samp{g}, @samp{G} +A floating-point number is expected. The input format for +floating-point numbers is an optionally signed string of digits, +possibly containing a radix character @samp{.}, followed by an optional +exponent field consisting of an @samp{E} or an @samp{e}, followed by an +optional @samp{+}, @samp{-}, or space, followed by an integer. + +@item @samp{c}, @samp{C} +@var{Width} characters are expected. The normal skip-over-white-space +is suppressed in this case; to read the next non-space character, use +@samp{%1s}. If a field width is given, a string is returned; up to the +indicated number of characters is read. + +@item @samp{s}, @samp{S} +A character string is expected The input field is terminated by a +white-space character. @code{scanf} cannot read a null string. + +@item @samp{[} +Indicates string data and the normal skip-over-leading-white-space is +suppressed. The left bracket is followed by a set of characters, called +the scanset, and a right bracket; the input field is the maximal +sequence of input characters consisting entirely of characters in the +scanset. @samp{^}, when it appears as the first character in the +scanset, serves as a complement operator and redefines the scanset as +the set of all characters not contained in the remainder of the scanset +string. Construction of the scanset follows certain conventions. A +range of characters may be represented by the construct first-last, +enabling @samp{[0123456789]} to be expressed @samp{[0-9]}. Using this +convention, first must be lexically less than or equal to last; +otherwise, the dash stands for itself. The dash also stands for itself +when it is the first or the last character in the scanset. To include +the right square bracket as an element of the scanset, it must appear as +the first character (possibly preceded by a @samp{^}) of the scanset, in +which case it will not be interpreted syntactically as the closing +bracket. At least one character must match for this conversion to +succeed. +@end table + +The @code{scanf} functions terminate their conversions at end-of-file, +at the end of the control string, or when an input character conflicts +with the control string. In the latter case, the offending character is +left unread in the input stream. +@end defmac + +@node String-Case, String Ports, Standard Formatted I/O, Procedures +@section String-Case + +@code{(require 'string-case)} + +@deffn Procedure string-upcase str +@deffnx Procedure string-downcase str +@deffnx Procedure string-capitalize str +The obvious string conversion routines. These are non-destructive. +@end deffn + +@defun string-upcase! str +@defunx string-downcase! str +@defunx string-captialize! str +The destructive versions of the functions above. +@end defun + + + + + +@node String Ports, String Search, String-Case, Procedures +@section String Ports + +@code{(require 'string-port)} + +@deffn Procedure call-with-output-string proc +@var{proc} must be a procedure of one argument. This procedure calls +@var{proc} with one argument: a (newly created) output port. When the +function returns, the string composed of the characters written into the +port is returned.@refill +@end deffn + +@deffn Procedure call-with-input-string string proc +@var{proc} must be a procedure of one argument. This procedure calls +@var{proc} with one argument: an (newly created) input port from which +@var{string}'s contents may be read. When @var{proc} returns, the port +is closed and the value yielded by the procedure @var{proc} is +returned.@refill +@end deffn + + +@node String Search, Tektronix Graphics Support, String Ports, Procedures +@section String Search + +@code{(require 'string-search)} + +@deffn Procedure string-index string char +Returns the index of the first occurence of @var{char} within +@var{string}, or @code{#f} if the @var{string} does not contain a +character @var{char}. +@end deffn + +@deffn procedure substring? pattern string +Searches @var{string} to see if some substring of @var{string} is equal +to @var{pattern}. @code{substring?} returns the index of the first +character of the first substring of @var{string} that is equal to +@var{pattern}; or @code{#f} if @var{string} does not contain +@var{pattern}. + +@example +(substring? "rat" "pirate") @result{} 2 +(substring? "rat" "outrage") @result{} #f +(substring? "" any-string) @result{} 0 +@end example +@end deffn + +@deffn Procedure find-string-from-port? str in-port max-no-chars +@deffnx Procedure find-string-from-port? str in-port +Looks for a string @var{str} within the first @var{max-no-chars} chars +of the input port @var{in-port}. @var{max-no-chars} may be omitted: in +that case, the search span is limited by the end of the input stream. +When the @var{str} is found, the function returns the number of +characters it has read from the port, and the port is set to read the +first char after that (that is, after the @var{str}) The function +returns @code{#f} when the @var{str} isn't found. + +@code{find-string-from-port?} reads the port @emph{strictly} +sequentially, and does not perform any buffering. So +@code{find-string-from-port?} can be used even if the @var{in-port} is +open to a pipe or other communication channel. +@end deffn + + +@node Tektronix Graphics Support, Tree Operations, String Search, Procedures +@section Tektronix Graphics Support + +@emph{Note:} The Tektronix graphics support files need more work, and +are not complete. + +@subsection Tektronix 4000 Series Graphics + +The Tektronix 4000 series graphics protocol gives the user a 1024 by +1024 square drawing area. The origin is in the lower left corner of the +screen. Increasing y is up and increasing x is to the right. + +The graphics control codes are sent over the current-output-port and can +be mixed with regular text and ANSI or other terminal control sequences. + +@deffn Procedure tek40:init +@end deffn + +@deffn Procedure tek40:graphics +@end deffn + +@deffn Procedure tek40:text +@end deffn + +@deffn Procedure tek40:linetype linetype +@end deffn + +@deffn Procedure tek40:move x y +@end deffn + +@deffn Procedure tek40:draw x y +@end deffn + +@deffn Procedure tek40:put-text x y str +@end deffn + +@deffn Procedure tek40:reset +@end deffn + + +@subsection Tektronix 4100 Series Graphics + +The graphics control codes are sent over the current-output-port and can +be mixed with regular text and ANSI or other terminal control sequences. + +@deffn Procedure tek41:init +@end deffn + +@deffn Procedure tek41:reset +@end deffn + +@deffn Procedure tek41:graphics +@end deffn + +@deffn Procedure tek41:move x y +@end deffn + +@deffn Procedure tek41:draw x y +@end deffn + +@deffn Procedure tek41:point x y number +@end deffn + +@deffn Procedure tek41:encode-x-y x y +@end deffn + +@deffn Procedure tek41:encode-int number +@end deffn + + + +@node Tree Operations, , Tektronix Graphics Support, Procedures +@section Tree operations + +@code{(require 'tree)} + +These are operations that treat lists a representations of trees. + +@defun subst new old tree +@defunx substq new old tree +@defunx substv new old tree +@code{subst} makes a copy of @var{tree}, substituting @var{new} for +every subtree or leaf of @var{tree} which is @code{equal?} to @var{old} +and returns a modified tree. The original @var{tree} is unchanged, but +may share parts with the result.@refill + +@code{substq} and @code{substv} are similar, but test against @var{old} +using @code{eq?} and @code{eqv?} respectively.@refill + +Examples: +@lisp +(substq 'tempest 'hurricane '(shakespeare wrote (the hurricane))) + @result{} (shakespeare wrote (the tempest)) +(substq 'foo '() '(shakespeare wrote (twelfth night))) + @result{} (shakespeare wrote (twelfth night . foo) . foo) +(subst '(a . cons) '(old . pair) + '((old . spice) ((old . shoes) old . pair) (old . pair))) + @result{} ((old . spice) ((old . shoes) a . cons) (a . cons)) +@end lisp +@end defun + +@defun copy-tree tree +Makes a copy of the nested list structure @var{tree} using new pairs and +returns it. All levels are copied, so that none of the pairs in the +tree are @code{eq?} to the original ones -- only the leaves are.@refill + +Example: +@lisp +(define bar '(bar)) +(copy-tree (list bar 'foo)) + @result{} ((bar) foo) +(eq? bar (car (copy-tree (list bar 'foo)))) + @result{} #f +@end lisp +@end defun + + + + + +@node Standards Support, Session Support, Procedures, Top +@chapter Standards Support + + + +@menu +* With-File:: 'with-file +* Transcripts:: 'transcript +* Rev2 Procedures:: 'rev2-procedures +* Rev4 Optional Procedures:: 'rev4-optional-procedures +* Multi-argument / and -:: 'multiarg/and- +* Multi-argument Apply:: 'multiarg-apply +* Rationalize:: 'rationalize +* Promises:: 'promise +* Dynamic-Wind:: 'dynamic-wind +* Values:: 'values +* Time:: 'time +* CLTime:: 'common-lisp-time +@end menu + +@node With-File, Transcripts, Standards Support, Standards Support +@section With-File + +@code{(require 'with-file)} + +@defun with-input-from-file file thunk +@defunx with-output-to-file file thunk +Description found in R4RS. +@end defun + +@node Transcripts, Rev2 Procedures, With-File, Standards Support +@section Transcripts + +@code{(require 'transcript)} + +@defun transcript-on filename +@defunx transcript-off filename +Redefines @code{read-char}, @code{read}, @code{write-char}, +@code{write}, @code{display}, and @code{newline}.@refill +@end defun + + + + + +@node Rev2 Procedures, Rev4 Optional Procedures, Transcripts, Standards Support +@section Rev2 Procedures + +@code{(require 'rev2-procedures)} + +The procedures below were specified in the @cite{Revised^2 Report on +Scheme}. @strong{N.B.}: The symbols @code{1+} and @code{-1+} are not +@cite{R4RS} syntax. Scheme->C, for instance, barfs on this +module.@refill + +@deffn Procedure substring-move-left! string1 start1 end1 string2 start2 +@deffnx Procedure substring-move-right! string1 start1 end1 string2 start2 +@var{string1} and @var{string2} must be a strings, and @var{start1}, +@var{start2} and @var{end1} must be exact integers satisfying@refill + +@display +0 <= @var{start1} <= @var{end1} <= (string-length @var{string1}) +0 <= @var{start2} <= @var{end1} - @var{start1} + @var{start2} <= (string-length @var{string2}) +@end display + +@code{substring-move-left!} and @code{substring-move-right!} store +characters of @var{string1} beginning with index @var{start1} +(inclusive) and ending with index @var{end1} (exclusive) into +@var{string2} beginning with index @var{start2} (inclusive).@refill + +@code{substring-move-left!} stores characters in time order of +increasing indices. @code{substring-move-right!} stores characters in +time order of increasing indeces.@refill +@end deffn + +@deffn Procedure substring-fill! string start end char +Fills the elements @var{start}--@var{end} of @var{string} with the +character @var{char}.@refill +@end deffn + +@defun string-null? str +@equiv{} @code{(= 0 (string-length @var{str}))} +@end defun + +@deffn Procedure append! . pairs +Destructively appends its arguments. Equivalent to @code{nconc}. +@end deffn + +@defun 1+ n +Adds 1 to @var{n}. +@end defun + +@defun -1+ n +Subtracts 1 from @var{n}. +@end defun + +@defun <? +@defunx <=? +@defunx =? +@defunx >? +@defunx >=? +These are equivalent to the procedures of the same name but without the +trailing @samp{?}. +@end defun + + + +@node Rev4 Optional Procedures, Multi-argument / and -, Rev2 Procedures, Standards Support +@section Rev4 Optional Procedures + +@code{(require 'rev4-optional-procedures)} + +For the specification of these optional procedures, +@xref{Standard procedures, , ,r4rs, Revised(4) Scheme}. + +@defun list-tail l p +@end defun + +@defun string->list s +@end defun + +@defun list->string l +@end defun + +@defun string-copy +@end defun + +@deffn Procedure string-fill! s obj +@end deffn + +@defun list->vector l +@end defun + +@defun vector->list s +@end defun + +@deffn Procedure vector-fill! s obj +@end deffn + + + + + +@node Multi-argument / and -, Multi-argument Apply, Rev4 Optional Procedures, Standards Support +@section Multi-argument / and - + +@code{(require 'mutliarg/and-)} + +For the specification of these optional forms, @xref{Numerical +operations, , ,r4rs, Revised(4) Scheme}. The @code{two-arg:}* forms are +only defined if the implementation does not support the many-argument +forms.@refill + +@defun two-arg:/ n1 n2 +The original two-argument version of @code{/}. +@end defun + +@defun / divident . divisors +@end defun + +@defun two-arg:- n1 n2 +The original two-argument version of @code{-}. +@end defun + +@defun - minuend . subtrahends +@end defun + + + + + +@node Multi-argument Apply, Rationalize, Multi-argument / and -, Standards Support +@section Multi-argument Apply + +@code{(require 'multiarg-apply)} + +@noindent +For the specification of this optional form, +@xref{Control features, , ,r4rs, Revised(4) Scheme}. + +@defun two-arg:apply proc l +The implementation's native @code{apply}. Only defined for +implementations which don't support the many-argument version. +@end defun + +@defun apply proc . args +@end defun + + + + + +@node Rationalize, Promises, Multi-argument Apply, Standards Support +@section Rationalize + +@code{(require 'rationalize)} + +The procedure rationalize is interesting because most programming +languages do not provide anything analogous to it. For simplicity, we +present an algorithm which computes the correct result for exact +arguments (provided the implementation supports exact rational numbers +of unlimited precision), and produces a reasonable answer for inexact +arguments when inexact arithmetic is implemented using floating-point. +We thank Alan Bawden for contributing this algorithm. + +@defun rationalize x e +@end defun + + + + + +@node Promises, Dynamic-Wind, Rationalize, Standards Support +@section Promises + +@code{(require 'promise)} + +@defun make-promise proc +@end defun + +Change occurrences of @code{(delay @var{expression})} to +@code{(make-promise (lambda () @var{expression}))} and @code{(define +force promise:force)} to implement promises if your implementation +doesn't support them +(@pxref{Control features, , ,r4rs, Revised(4) Scheme}). + + + + +@node Dynamic-Wind, Values, Promises, Standards Support +@section Dynamic-Wind + +@code{(require 'dynamic-wind)} + +This facility is a generalization of Common LISP @code{unwind-protect}, +designed to take into account the fact that continuations produced by +@code{call-with-current-continuation} may be reentered.@refill + +@deffn Procedure dynamic-wind thunk1 thunk2 thunk3 +The arguments @var{thunk1}, @var{thunk2}, and @var{thunk3} must all be +procedures of no arguments (thunks).@refill + +@code{dynamic-wind} calls @var{thunk1}, @var{thunk2}, and then +@var{thunk3}. The value returned by @var{thunk2} is returned as the +result of @code{dynamic-wind}. @var{thunk3} is also called just before +control leaves the dynamic context of @var{thunk2} by calling a +continuation created outside that context. Furthermore, @var{thunk1} is +called before reentering the dynamic context of @var{thunk2} by calling +a continuation created inside that context. (Control is inside the +context of @var{thunk2} if @var{thunk2} is on the current return stack). + +@strong{Warning:} There is no provision for dealing with errors or +interrupts. If an error or interrupt occurs while using +@code{dynamic-wind}, the dynamic environment will be that in effect at +the time of the error or interrupt.@refill +@end deffn + + + + +@node Values, Time, Dynamic-Wind, Standards Support +@section Values + +@code{(require 'values)} + +@defun values obj @dots{} +@code{values} takes any number of arguments, and passes (returns) them +to its continuation.@refill +@end defun + + +@defun call-with-values thunk proc +@var{thunk} must be a procedure of no arguments, and @var{proc} must be +a procedure. @code{call-with-values} calls @var{thunk} with a +continuation that, when passed some values, calls @var{proc} with those +values as arguments.@refill + +Except for continuations created by the @code{call-with-values} +procedure, all continuations take exactly one value, as now; the effect +of passing no value or more than one value to continuations that were +not created by the @code{call-with-values} procedure is +unspecified.@refill +@end defun + +@node Time, CLTime, Values, Standards Support +@section Time + +The procedures @code{current-time}, @code{difftime}, and +@code{offset-time} are supported by all implementations (SLIB provides +them if feature @code{('current-time)} is missing. @code{current-time} +returns a @dfn{calendar time} (caltime) which can be a number or other +type. + +@defun current-time +Returns the time since 00:00:00 GMT, January 1, 1970, measured in +seconds. Note that the reference time is different from the reference +time for @code{get-universal-time} in @ref{CLTime}. On implementations +which cannot support actual times, @code{current-time} will increment a +counter and return its value when called. +@end defun + +@defun difftime caltime1 caltime0 +Returns the difference (number of seconds) between twe calendar times: +@var{caltime1} - @var{caltime0}. @var{caltime0} can also be a number. +@end defun + +@defun offset-time caltime offset +Returns the calendar time of @var{caltime} offset by @var{offset} number +of seconds @code{(+ caltime offset)}. +@end defun + +@example +(require 'posix-time) +@end example + +These procedures are intended to be compatible with Posix time +conversion functions. + +@defvar *timezone* +contains the difference, in seconds, between UTC and local standard time +(for example, in the U.S. Eastern time zone (EST), timezone is +5*60*60). @code{*timezone*} is initialized by @code{tzset}. +@end defvar + +@defun tzset +initializes the @var{*timezone*} variable from the TZ environment +variable. This function is automatically called by the other time +conversion functions that depend on the time zone. +@end defun + +@defun gmtime caltime +converts the calendar time @var{caltime} to a vector of integers +representing the time expressed as Coordinated Universal Time (UTC). + +@defunx localtime caltime +converts the calendar time @var{caltime} to a vector of integers expressed +relative to the user's time zone. @code{localtime} sets the variable +@var{*timezone*} with the difference between Coordinated Universal Time +(UTC) and local standard time in seconds by calling @code{tzset}. +The elements of the returned vector are as follows: + +@enumerate 0 +@item + seconds (0 - 61) +@item + minutes (0 - 59) +@item + hours since midnight +@item + day of month +@item + month (0 - 11). Note difference from @code{decode-universal-time}. +@item + year (A.D.) +@item + day of week (0 - 6) +@item + day of year (0 - 365) +@item + 1 for daylight savings, 0 for regular time +@end enumerate +@end defun + +@defun mktime univtime +Converts a vector of integers in Coordinated Universal Time (UTC) format +to calendar time (caltime) format. +@end defun + +@defun asctime univtime +Converts the vector of integers @var{caltime} in Coordinated +Universal Time (UTC) format into a string of the form +@code{"Wed Jun 30 21:49:08 1993"}. +@end defun + +@defun ctime caltime +Equivalent to @code{(time:asctime (time:localtime @var{caltime}))}. +@end defun + +@node CLTime, , Time, Standards Support +@section CLTime + +@defun get-decoded-time +Equivalent to @code{(decode-universal-time (get-universal-time))}. +@end defun + +@defun get-universal-time +Returns the current time as @dfn{Universal Time}, number of seconds +since 00:00:00 Jan 1, 1900 GMT. Note that the reference time is +different from @code{current-time}. +@end defun + +@defun decode-universal-time univtime +Converts @var{univtime} to @dfn{Decoded Time} format. +Nine values are returned: +@enumerate 0 +@item + seconds (0 - 61) +@item + minutes (0 - 59) +@item + hours since midnight +@item + day of month +@item + month (1 - 12). Note difference from @code{gmtime} and @code{localtime}. +@item + year (A.D.) +@item + day of week (0 - 6) +@item + #t for daylight savings, #f otherwise +@item + hours west of GMT (-24 - +24) +@end enumerate + +Notice that the values returned by @code{decode-universal-time} do not +match the arguments to @code{encode-universal-time}. +@end defun + +@defun encode-universal-time second minute hour date month year +@defunx encode-universal-time second minute hour date month year time-zone +Converts the arguments in Decoded Time format to Universal Time format. +If @var{time-zone} is not specified, the returned time is adjusted for +daylight saving time. Otherwise, no adjustment is performed. + +Notice that the values returned by @code{decode-universal-time} do not +match the arguments to @code{encode-universal-time}. +@end defun + + +@node Session Support, Optional SLIB Packages, Standards Support, Top +@chapter Session Support + +@menu +* Repl:: Macros at top-level +* Quick Print:: Loop-safe Output +* Debug:: To err is human ... +* Breakpoints:: Pause execution +* Trace:: 'trace +* Getopt:: Command Line option parsing +* Command Line:: A command line reader for Scheme shells +* System Interface:: 'system and 'getenv + +Certain features are so simple, system-dependent, or widely subcribed +that they are supported by all implementations as part of the +@samp{*.init} files. + +The features described in the following sections are provided by all +implementations. + +* Require:: Module Management +* Vicinity:: Pathname Management +* Configuration:: Characteristics of Scheme Implementation +* Input/Output:: Things not provided by the Scheme specs. +* Legacy:: +* System:: LOADing, EVALing, ERRORing, and EXITing +@end menu + + + +@node Repl, Quick Print, Session Support, Session Support +@section Repl + +@code{(require 'repl)} + +Here is a read-eval-print-loop which, given an eval, evaluates forms. + +@deffn Procedure repl:top-level repl:eval +@code{read}s, @code{repl:eval}s and @code{write}s expressions from +@code{(current-input-port)} to @code{(current-output-port)} until an +end-of-file is encountered. @code{load}, @code{slib:eval}, +@code{slib:error}, and @code{repl:quit} dynamically bound during +@code{repl:top-level}.@refill +@end deffn + +@deffn Procedure repl:quit +Exits from the invocation of @code{repl:top-level}. +@end deffn + +The @code{repl:} procedures establish, as much as is possible to do +portably, a top level environment supporting macros. +@code{repl:top-level} uses @code{dynamic-wind} to catch error conditions +and interrupts. If your implementation supports this you are all set. + +Otherwise, if there is some way your implementation can catch error +conditions and interrupts, then have them call @code{slib:error}. It +will display its arguments and reenter @code{repl:top-level}. +@code{slib:error} dynamically bound by @code{repl:top-level}.@refill + +To have your top level loop always use macros, add any interrupt +catching lines and the following lines to your Scheme init file: +@lisp +(require 'macro) +(require 'repl) +(repl:top-level macro:eval) +@end lisp + +@node Quick Print, Debug, Repl, Session Support +@section Quick Print + +@code{(require 'qp)} + +@noindent +When displaying error messages and warnings, it is paramount that the +output generated for circular lists and large data structures be +limited. This section supplies a procedure to do this. It could be +much improved. + +@quotation +Notice that the neccessity for truncating output eliminates +Common-Lisp's @xref{Format} from consideration; even when variables +@code{*print-level*} and @code{*print-level*} are set, huge strings and +bit-vectors are @emph{not} limited. +@end quotation + +@deffn Procedure qp arg1 @dots{} +@deffnx Procedure qpn arg1 @dots{} +@deffnx Procedure qpr arg1 @dots{} +@code{qp} writes its arguments, separated by spaces, to +@code{(current-output-port)}. @code{qp} compresses printing by +substituting @samp{...} for substructure it does not have sufficient +room to print. @code{qpn} is like @code{qp} but outputs a newline +before returning. @code{qpr} is like @code{qpn} except that it returns +its last argument.@refill +@end deffn + +@defvar *qp-width* +@code{*qp-width*} is the largest number of characters that @code{qp} +should use.@refill +@end defvar + +@node Debug, Breakpoints, Quick Print, Session Support +@section Debug + +@code{(require 'debug)} + +@noindent +Requiring @code{debug} automatically requires @code{trace} and +@code{break}. + +@noindent +An application with its own datatypes may want to substitute its own +printer for @code{qp}. This example shows how to do this: + +@example +(define qpn (lambda args) @dots{}) +(provide 'qp) +(require 'debug) +@end example + +@deffn Procedure trace-all file +Traces (@pxref{Trace}) all procedures @code{define}d at top-level in +file @file{file}. +@end deffn + +@deffn Procedure break-all file +Breakpoints (@pxref{Breakpoints}) all procedures @code{define}d at +top-level in file @file{file}. +@end deffn + +@node Breakpoints, Trace, Debug, Session Support +@section Breakpoints + +@code{(require 'break)} + +@defun init-debug +If your Scheme implementation does not support @code{break} or +@code{abort}, a message will appear when you @code{(require 'break)} or +@code{(require 'debug)} telling you to type @code{(init-debug)}. This +is in order to establish a top-level continuation. Typing +@code{(init-debug)} at top level sets up a continuation for +@code{break}. +@end defun + +@defun breakpoint arg1 @dots{} +Returns from the top level continuation and pushes the continuation from +which it was called on a continuation stack. +@end defun + +@defun continue +Pops the topmost continuation off of the continuation stack and returns +an unspecified value to it. +@defunx continue arg1 @dots{} +Pops the topmost continuation off of the continuation stack and returns +@var{arg1} @dots{} to it. +@end defun + +@defmac break proc1 @dots{} +Redefines the top-level named procedures given as arguments so that +@code{breakpoint} is called before calling @var{proc1} @dots{}. +@defmacx break +With no arguments, makes sure that all the currently broken identifiers +are broken (even if those identifiers have been redefined) and returns a +list of the broken identifiers. +@end defmac + +@defmac unbreak proc1 @dots{} +Turns breakpoints off for its arguments. +@defmacx unbreak +With no arguments, unbreaks all currently broken identifiers and returns +a list of these formerly broken identifiers. +@end defmac + +The following routines are the procedures which actually do the tracing +when this module is supplied by SLIB, rather than natively. If +defmacros are not natively supported by your implementation, these might +be more convenient to use. + +@defun breakf proc +@defunx breakf proc name +@defunx debug:breakf proc +@defunx debug:breakf proc name +To break, type +@lisp +(set! @var{symbol} (breakf @var{symbol})) +@end lisp +@noindent +or +@lisp +(set! @var{symbol} (breakf @var{symbol} '@var{symbol})) +@end lisp +@noindent +or +@lisp +(define @var{symbol} (breakf @var{function})) +@end lisp +@noindent +or +@lisp +(define @var{symbol} (breakf @var{function} '@var{symbol})) +@end lisp +@end defun + +@defun unbreakf proc +@defunx debug:unbreakf proc +To unbreak, type +@lisp +(set! @var{symbol} (unbreakf @var{symbol})) +@end lisp +@end defun + +@node Trace, Getopt, Breakpoints, Session Support +@section Tracing + +@code{(require 'trace)} + +@defmac trace proc1 @dots{} +Traces the top-level named procedures given as arguments. +@defmacx trace +With no arguments, makes sure that all the currently traced identifiers +are traced (even if those identifiers have been redefined) and returns a +list of the traced identifiers. +@end defmac + +@defmac untrace proc1 @dots{} +Turns tracing off for its arguments. +@defmacx untrace +With no arguments, untraces all currently traced identifiers and returns +a list of these formerly traced identifiers. +@end defmac + +The following routines are the procedures which actually do the tracing +when this module is supplied by SLIB, rather than natively. If +defmacros are not natively supported by your implementation, these might +be more convenient to use. + +@defun tracef proc +@defunx tracef proc name +@defunx debug:tracef proc +@defunx debug:tracef proc name +To trace, type +@lisp +(set! @var{symbol} (tracef @var{symbol})) +@end lisp +@noindent +or +@lisp +(set! @var{symbol} (tracef @var{symbol} '@var{symbol})) +@end lisp +@noindent +or +@lisp +(define @var{symbol} (tracef @var{function})) +@end lisp +@noindent +or +@lisp +(define @var{symbol} (tracef @var{function} '@var{symbol})) +@end lisp +@end defun + +@defun untracef proc +@defunx debug:untracef proc +To untrace, type +@lisp +(set! @var{symbol} (untracef @var{symbol})) +@end lisp +@end defun + + +@node Getopt, Command Line, Trace, Session Support +@section Getopt + +@code{(require 'getopt)} + +This routine implements Posix command line argument parsing. Notice +that returning values through global variables means that @code{getopt} +is @emph{not} reentrant. + +@defvar *optind* +Is the index of the current element of the command line. It is +initially one. In order to parse a new command line or reparse an old +one, @var{*opting*} must be reset. +@end defvar + +@defvar *optarg* +Is set by getopt to the (string) option-argument of the current option. +@end defvar + +@deffn Procedure getopt argc argv optstring +Returns the next option letter in @var{argv} (starting from +@code{(vector-ref argv *optind*)}) that matches a letter in +@var{optstring}. @var{argv} is a vector or list of strings, the 0th of +which getopt usually ignores. @var{argc} is the argument count, usually +the length of @var{argv}. @var{optstring} is a string of recognized +option characters; if a character is followed by a colon, the option +takes an argument which may be immediately following it in the string or +in the next element of @var{argv}. + +@var{*optind*} is the index of the next element of the @var{argv} vector +to be processed. It is initialized to 1 by @file{getopt.scm}, and +@code{getopt} updates it when it finishes with each element of +@var{argv}. + +@code{getopt} returns the next option character from @var{argv} that +matches a character in @var{optstring}, if there is one that matches. +If the option takes an argument, @code{getopt} sets the variable +@var{*optarg*} to the option-argument as follows: + +@itemize @bullet +@item +If the option was the last character in the string pointed to by an +element of @var{argv}, then @var{*optarg*} contains the next element of +@var{argv}, and @var{*optind*} is incremented by 2. If the resulting +value of @var{*optind*} is greater than or equal to @var{argc}, this +indicates a missing option argument, and @code{getopt} returns an error +indication. + +@item +Otherwise, @var{*optarg*} is set to the string following the option +character in that element of @var{argv}, and @var{*optind*} is +incremented by 1. +@end itemize + +If, when @code{getopt} is called, the string @code{(vector-ref argv +*optind*)} either does not begin with the character @code{#\-} or is +just @code{"-"}, @code{getopt} returns @code{#f} without changing +@var{*optind*}. If @code{(vector-ref argv *optind*)} is the string +@code{"--"}, @code{getopt} returns @code{#f} after incrementing +@var{*optind*}. + +If @code{getopt} encounters an option character that is not contained in +@var{optstring}, it returns the question-mark @code{#\?} character. If +it detects a missing option argument, it returns the colon character +@code{#\:} if the first character of @var{optstring} was a colon, or a +question-mark character otherwise. In either case, @code{getopt} sets +the variable @var{getopt:opt} to the option character that caused the +error. + +The special option @code{"--"} can be used to delimit the end of the +options; @code{#f} is returned, and @code{"--"} is skipped. + +RETURN VALUE + +@code{getopt} returns the next option character specified on the command +line. A colon @code{#\:} is returned if @code{getopt} detects a missing argument +and the first character of @var{optstring} was a colon @code{#\:}. + +A question-mark @code{#\?} is returned if @code{getopt} encounters an option +character not in @var{optstring} or detects a missing argument and the first +character of @var{optstring} was not a colon @code{#\:}. + +Otherwise, @code{getopt} returns @code{#f} when all command line options have been +parsed. + +Example: +@lisp +#! /usr/local/bin/scm +;;;This code is SCM specific. +(define argv (program-arguments)) +(require 'getopt) + +(define opts ":a:b:cd") +(let loop ((opt (getopt (length argv) argv opts))) + (case opt + ((#\a) (print "option a: " *optarg*)) + ((#\b) (print "option b: " *optarg*)) + ((#\c) (print "option c")) + ((#\d) (print "option d")) + ((#\?) (print "error" getopt:opt)) + ((#\:) (print "missing arg" getopt:opt)) + ((#f) (if (< *optind* (length argv)) + (print "argv[" *optind* "]=" + (list-ref argv *optind*))) + (set! *optind* (+ *optind* 1)))) + (if (< *optind* (length argv)) + (loop (getopt (length argv) argv opts)))) + +(slib:exit) +@end lisp +@end deffn + +@section Getopt-- + +@defun getopt-- argc argv optstring +The procedure @code{getopt--} is an extended version of @code{getopt} +which parses @dfn{long option names} of the form +@samp{--hold-the-onions} and @samp{--verbosity-level=extreme}. +@w{@code{Getopt--}} behaves as @code{getopt} except for non-empty +options beginning with @samp{--}. + +Options beginning with @samp{--} are returned as strings rather than +characters. If a value is assigned (using @samp{=}) to a long option, +@code{*optarg*} is set to the value. The @samp{=} and value are +not returned as part of the option string. + +No information is passed to @code{getopt--} concerning which long +options should be accepted or whether such options can take arguments. +If a long option did not have an argument, @code{*optarg} will be set to +@code{#f}. The caller is responsible for detecting and reporting +errors. + +@example +(define opts ":-:b:") +(define argc 5) +(define argv '("foo" "-b9" "--f1" "--2=" "--g3=35234.342" "--")) +(define *optind* 1) +(define *optarg* #f) +(require 'qp) +(do ((i 5 (+ -1 i))) + ((zero? i)) + (define opt (getopt-- argc argv opts)) + (print *optind* opt *optarg*))) +@print{} +2 #\b "9" +3 "f1" #f +4 "2" "" +5 "g3" "35234.342" +5 #f "35234.342" +@end example +@end defun + +@node Command Line, System Interface, Getopt, Session Support +@section Command Line + +@code{(require 'read-command)} + +@defun read-command port +@defunx read-command +@code{read-command} converts a @dfn{command line} into a list of strings +suitable for parsing by @code{getopt}. The syntax of command lines +supported resembles that of popular @dfn{shell}s. @code{read-command} +updates @var{port} to point to the first character past the command +delimiter. + +If an end of file is encountered in the input before any characters are +found that can begin an object or comment, then an end of file object is +returned. + +The @var{port} argument may be omitted, in which case it defaults to the +value returned by @code{current-input-port}. + +The fields into which the command line is split are delimited by +whitespace as defined by @code{char-whitespace?}. The end of a command +is delimited by end-of-file or unescaped semicolon (@key{;}) or +@key{newline}. Any character can be literally included in a field by +escaping it with a backslach (@key{\}). + +The initial character and types of fields recognized are: +@table @asis +@item @samp{\} +The next character has is taken literally and not interpreted as a field +delimiter. If @key{\} is the last character before a @key{newline}, +that @key{newline} is just ignored. Processing continues from the +characters after the @key{newline} as though the backslash and +@key{newline} were not there. +@item @samp{"} +The characters up to the next unescaped @key{"} are taken literally, +according to [R4RS] rules for literal strings (@pxref{Strings, , ,r4rs, +Revised(4) Scheme}). +@item @samp{(}, @samp{%'} +One scheme expression is @code{read} starting with this character. The +@code{read} expression is evaluated, converted to a string +(using @code{display}), and replaces the expression in the returned +field. +@item @samp{;} +Semicolon delimits a command. Using semicolons more than one command +can appear on a line. Escaped semicolons and semicolons inside strings +do not delimit commands. +@end table + +@noindent +The comment field differs from the previous fields in that it must be +the first character of a command or appear after whitespace in order to +be recognized. @key{#} can be part of fields if these conditions are +not met. For instance, @code{ab#c} is just the field ab#c. + +@table @samp +@item # +Introduces a comment. The comment continues to the end of the line on +which the semicolon appears. Comments are treated as whitespace by +@code{read-dommand-line} and backslashes before @key{newline}s in +comments are also ignored. +@end table +@end defun + +@node System Interface, Require, Command Line, Session Support +@section System Interface + +If @code{(provided? 'getenv)}: + +@defun getenv name +Looks up @var{name}, a string, in the program environment. If @var{name} is +found a string of its value is returned. Otherwise, @code{#f} is returned. +@end defun + +If @code{(provided? 'system)}: + +@defun system command-string +Executes the @var{command-string} on the computer and returns the +integer status code. +@end defun + + +@node Require, Vicinity, System Interface, Session Support +@section Require + +These variables and procedures are provided by all implementations. + +@defvar *features* +Is a list of symbols denoting features supported in this implementation. +@end defvar + +@defvar *modules* +Is a list of pathnames denoting files which have been loaded. +@end defvar + +@defvar *catalog* +Is an association list of features (symbols) and pathnames which will +supply those features. The pathname can be either a string or a pair. +If pathname is a pair then the first element should be a macro feature +symbol, @code{source}, or @code{compiled}. The cdr of the pathname +should be either a string or a list. +@end defvar + +In the following three functions if @var{feature} is not a symbol it is +assumed to be a pathname.@refill + +@defun provided? feature +Returns @code{#t} if @var{feature} is a member of @code{*features*} or +@code{*modules*} or if @var{feature} is supported by a file already +loaded and @code{#f} otherwise.@refill +@end defun + +@deffn Procedure require feature +If @code{(not (provided? @var{feature}))} it is loaded if @var{feature} +is a pathname or if @code{(assq @var{feature} *catalog*)}. Otherwise an +error is signaled.@refill +@end deffn + +@deffn Procedure provide feature +Assures that @var{feature} is contained in @code{*features*} if +@var{feature} is a symbol and @code{*modules*} otherwise.@refill +@end deffn + +@defun require:feature->path feature +Returns @code{#t} if @var{feature} is a member of @code{*features*} or +@code{*modules*} or if @var{feature} is supported by a file already +loaded. Returns a path if one was found in @code{*catalog*} under the +feature name, and @code{#f} otherwise. The path can either be a string +suitable as an argument to load or a pair as described above for +*catalog*. +@end defun + +Below is a list of features that are automatically determined by +@code{require}. For each item, @code{(provided? '@var{feature})} will +return @code{#t} if that feature is available, and @code{#f} if +not.@refill + +@itemize @bullet +@item +'inexact +@item +'rational +@item +'real +@item +'complex +@item +'bignum +@end itemize + + + + + +@node Vicinity, Configuration, Require, Session Support +@section Vicinity + +A vicinity is a descriptor for a place in the file system. Vicinities +hide from the programmer the concepts of host, volume, directory, and +version. Vicinities express only the concept of a file environment +where a file name can be resolved to a file in a system independent +manner. Vicinities can even be used on @dfn{flat} file systems (which +have no directory structure) by having the vicinity express constraints +on the file name. On most systems a vicinity would be a string. All of +these procedures are file system dependent. + +These procedures are provided by all implementations. + +@defun make-vicinity filename +Returns the vicinity of @var{filename} for use by @code{in-vicinity}. +@end defun + +@defun program-vicinity +Returns the vicinity of the currently loading Scheme code. For an +interpreter this would be the directory containing source code. For a +compiled system (with multiple files) this would be the directory where +the object or executable files are. If no file is currently loading it +the result is undefined. @strong{Warning:} @code{program-vicinity} can +return incorrectl values if your program escapes back into a +@code{load}.@refill +@end defun + +@defun library-vicinity +Returns the vicinity of the shared Scheme library. +@end defun + +@defun implementation-vicinity +Returns the vicinity of the underlying Scheme implementation. This +vicinity will likely contain startup code and messages and a compiler. +@end defun + +@defun user-vicinity +Returns the vicinity of the current directory of the user. On most +systems this is @file{""} (the empty string). +@end defun + +@c @defun scheme-file-suffix +@c Returns the default filename suffix for scheme source files. On most +@c systems this is @samp{.scm}.@refill +@c @end defun + +@defun in-vicinity vicinity filename +Returns a filename suitable for use by @code{slib:load}, +@code{slib:load-source}, @code{slib:load-compiled}, +@code{open-input-file}, @code{open-output-file}, etc. The returned +filename is @var{filename} in @var{vicinity}. @code{in-vicinity} should +allow @var{filename} to override @var{vicinity} when @var{filename} is +an absolute pathname and @var{vicinity} is equal to the value of +@code{(user-vicinity)}. The behavior of @code{in-vicinity} when +@var{filename} is absolute and @var{vicinity} is not equal to the value +of @code{(user-vicinity)} is unspecified. For most systems +@code{in-vicinity} can be @code{string-append}.@refill +@end defun + +@defun sub-vicinity vicinity name +Returns the vicinity of @var{vicinity} restricted to @var{name}. This +is used for large systems where names of files in subsystems could +conflict. On systems with directory structure @code{sub-vicinity} will +return a pathname of the subdirectory @var{name} of +@var{vicinity}.@refill +@end defun + + + +@node Configuration, Input/Output, Vicinity, Session Support +@section Configuration + +These constants and procedures describe characteristics of the Scheme +and underlying operating system. They are provided by all +implementations. + +@defvr Constant char-code-limit +An integer 1 larger that the largest value which can be returned by +@code{char->integer}.@refill +@end defvr + +@defvr Constant most-positive-fixnum +The immediate integer closest to positive infinity. +@end defvr + +@defvr Constant slib:tab +The tab character. +@end defvr + +@defvr Constant slib:form-feed +The form-feed character. +@end defvr + +@defun software-type +Returns a symbol denoting the generic operating system type. For +instance, @code{unix}, @code{vms}, @code{macos}, @code{amiga}, or +@code{ms-dos}. +@end defun + +@defun slib:report-version +Displays the versions of SLIB and the underlying Scheme implementation +and the name of the operating system. An unspecified value is returned. + +@example +(slib:report-version) @result{} slib "2a3" on scm "4e1" on unix +@end example +@end defun + +@defun slib:report +Displays the information of @code{(slib:report-version)} followed by +almost all the information neccessary for submitting a problem report. +An unspecified value is returned. + +@defunx slib:report #t +provides a more verbose listing. + +@defunx slib:report filename +Writes the report to file @file{filename}. + +@example +(slib:report) +@result{} +slib "2a3" on scm "4e1" on unix +(implementation-vicinity) is "/usr/local/src/scm/" +(library-vicinity) is "/usr/local/lib/slib/" +(scheme-file-suffix) is ".scm" +implementation *features* : + bignum complex real rational + inexact vicinity ed getenv + tmpnam system abort transcript + with-file ieee-p1178 rev4-report rev4-optional-procedures + hash object-hash delay eval + dynamic-wind multiarg-apply multiarg/and- logical + defmacro string-port source array-for-each + array full-continuation char-ready? line-i/o + i/o-extensions pipe +implementation *catalog* : + (rev4-optional-procedures . "/usr/local/lib/slib/sc4opt") + ... +@end example +@end defun + +@node Input/Output, Legacy, Configuration, Session Support +@section Input/Output + +These procedures are provided by all implementations. + +@deffn Procedure file-exists? filename +Returns @code{#t} if the specified file exists. Otherwise, returns +@code{#f}. If the underlying implementation does not support this +feature then @code{#f} is always returned. +@end deffn + +@deffn Procedure delete-file filename +Deletes the file specified by @var{filename}. If @var{filename} can not +be deleted, @code{#f} is returned. Otherwise, @code{#t} is +returned.@refill +@end deffn + +@deffn Procedure tmpnam +Returns a pathname for a file which will likely not be used by any other +process. Successive calls to @code{(tmpnam)} will return different +pathnames.@refill +@end deffn + +@deffn Procedure current-error-port +Returns the current port to which diagnostic and error output is +directed. +@end deffn + +@deffn Procedure force-output +@deffnx Procedure force-output port +Forces any pending output on @var{port} to be delivered to the output +device and returns an unspecified value. The @var{port} argument may be +omitted, in which case it defaults to the value returned by +@code{(current-output-port)}.@refill +@end deffn + +@deffn Procedure output-port-width +@deffnx Procedure output-port-width port + +Returns the width of @var{port}, which defaults to +@code{(current-output-port)} if absent. If the width cannot be +determined 79 is returned.@refill +@end deffn + +@deffn Procedure output-port-height +@deffnx Procedure output-port-height port + +Returns the height of @var{port}, which defaults to +@code{(current-output-port)} if absent. If the height cannot be +determined 24 is returned.@refill +@end deffn + +@node Legacy, System, Input/Output, Session Support +@section Legacy + +@defun identity x +@var{identity} returns its argument. + +Example: +@lisp +(identity 3) + @result{} 3 +(identity '(foo bar)) + @result{} (foo bar) +(map identity @var{lst}) + @equiv{} (copy-list @var{lst}) +@end lisp +@end defun + +These were present in Scheme until R4RS (@pxref{Notes, , Language +changes ,r4rs, Revised(4) Scheme}). + +@defvr Constant t +Derfined as @code{#t}. +@end defvr + +@defvr Constant nil +Defined as @code{#f}. +@end defvr + +@defun last-pair l +Returns the last pair in the list @var{l}. Example: +@lisp +(last-pair (cons 1 2)) + @result{} (1 . 2) +(last-pair '(1 2)) + @result{} (2) + @equiv{} (cons 2 '()) +@end lisp +@end defun + +@node System, , Legacy, Session Support +@section System + +These procedures are provided by all implementations. + +@deffn Procedure slib:load-source name +Loads a file of Scheme source code from @var{name} with the default +filename extension used in SLIB. For instance if the filename extension +used in SLIB is @file{.scm} then @code{(slib:load-source "foo")} will +load from file @file{foo.scm}. +@end deffn + +@deffn Procedure slib:load-compiled name +On implementations which support separtely loadable compiled modules, +loads a file of compiled code from @var{name} with the implementation's +filename extension for compiled code appended. +@end deffn + +@deffn Procedure slib:load name +Loads a file of Scheme source or compiled code from @var{name} with the +appropriate suffixes appended. If both source and compiled code are +present with the appropriate names then the implementation will load +just one. It is up to the implementation to choose which one will be +loaded. + +If an implementation does not support compiled code then +@code{slib:load} will be identical to @code{slib:load-source}. +@end deffn + +@deffn Procedure slib:eval obj +@code{eval} returns the value of @var{obj} evaluated in the current top +level environment.@refill +@end deffn + +@deffn Procedure slib:eval-load filename eval +@var{filename} should be a string. If filename names an existing file, +the Scheme source code expressions and definitions are read from the +file and @var{eval} called with them sequentially. The +@code{slib:eval-load} procedure does not affect the values returned by +@code{current-input-port} and @code{current-output-port}.@refill +@end deffn + +@deffn Procedure slib:error arg1 arg2 @dots{} +Outputs an error message containing the arguments, aborts evaluation of +the current form and responds in a system dependent way to the error. +Typical responses are to abort the program or to enter a read-eval-print +loop.@refill +@end deffn + +@deffn Procedure slib:exit n +@deffnx Procedure slib:exit +Exits from the Scheme session returning status @var{n} to the system. +If @var{n} is omitted or @code{#t}, a success status is returned to the +system (if possible). If @var{n} is @code{#f} a failure is returned to +the system (if possible). If @var{n} is an integer, then @var{n} is +returned to the system (if possible). If the Scheme session cannot exit +an unspecified value is returned from @code{slib:exit}. +@end deffn + + +@node Optional SLIB Packages, Procedure and Macro Index, Session Support, Top +@chapter Optional SLIB Packages + +Several Scheme packages have been written using SLIB. There are several +reasons why a package might not be included in the SLIB distribution: +@itemize @bullet +@item +Because it requires special hardware or software which is not universal. +@item +Because it is large and of limited interest to most Scheme users. +@item +Because it has copying terms different enough from the other SLIB +packages that its inclusion would cause confusion. +@item +Because it is an application program, rather than a library module. +@item +Because I have been too busy to integrate it. +@end itemize + +Once an optional package is installed (and an entry added to +@code{*catalog*}, the @code{require} mechanism allows it to be called up +and used as easily as any other SLIB package. Some optional packages +(for which @code{*catalog*} already has entries) available from SLIB +sites are: + +@table @asis +@item SLIB-PSD is a portable debugger for Scheme (requires emacs editor). +@lisp +ftp-swiss.ai.mit.edu:pub/scm/slib-psd1-3.tar.gz +prep.ai.mit.edu:pub/gnu/jacal/slib-psd1-3.tar.gz +ftp.maths.tcd.ie:pub/bosullvn/jacal/slib-psd1-3.tar.gz +ftp.cs.indiana.edu:/pub/scheme-repository/utl/slib-psd1-3.tar.gz +@end lisp + +With PSD, you can run a Scheme program in an Emacs buffer, set +breakpoints, single step evaluation and access and modify the program's +variables. It works by instrumenting the original source code, so it +should run with any R4RS compliant Scheme. It has been tested with SCM, +Elk 1.5, and the sci interpreter in the Scheme->C system, but should +work with other Schemes with a minimal amount of porting, if at +all. Includes documentation and user's manual. Written by Pertti +Kellom\"aki, pk@@cs.tut.fi. The Lisp Pointers article describing PSD +(Lisp Pointers VI(1):15-23, January-March 1993) is available as +@lisp +http://www.cs.tut.fi/staff/pk/scheme/psd/article/article.html +@end lisp +@item SLIB-SCHELOG is an embedding of Prolog in Scheme. +@lisp +ftp-swiss.ai.mit.edu:pub/scm/slib-schelog.tar.gz +prep.ai.mit.edu:pub/gnu/jacal/slib-schelog.tar.gz +ftp.maths.tcd.ie:pub/bosullvn/jacal/slib-schelog.tar.gz +ftp.cs.indiana.edu:/pub/scheme-repository/utl/slib-schelog.tar.gz +@end lisp +@end table + +@node Procedure and Macro Index, Variable Index, Optional SLIB Packages, Top +@unnumbered Procedure and Macro Index + +This is an alphabetical list of all the procedures and macros in SLIB. + +@printindex fn + +@node Variable Index, , Procedure and Macro Index, Top +@unnumbered Variable Index + +This is an alphabetical list of all the global variables in SLIB. + +@printindex vr + +@contents +@bye diff --git a/sort.scm b/sort.scm new file mode 100644 index 0000000..ab9b938 --- /dev/null +++ b/sort.scm @@ -0,0 +1,154 @@ +;;; "sort.scm" Defines: sorted?, merge, merge!, sort, sort! +;;; Author : Richard A. O'Keefe (based on Prolog code by D.H.D.Warren) +;;; +;;; This code is in the public domain. + +;;; Updated: 11 June 1991 +;;; Modified for scheme library: Aubrey Jaffer 19 Sept. 1991 +;;; Updated: 19 June 1995 + +;;; (sorted? sequence less?) +;;; is true when sequence is a list (x0 x1 ... xm) or a vector #(x0 ... xm) +;;; such that for all 1 <= i <= m, +;;; (not (less? (list-ref list i) (list-ref list (- i 1)))). + +(define (sort:sorted? seq less?) + (cond + ((null? seq) + #t) + ((vector? seq) + (let ((n (vector-length seq))) + (if (<= n 1) + #t + (do ((i 1 (+ i 1))) + ((or (= i n) + (less? (vector-ref seq (- i 1)) + (vector-ref seq i))) + (= i n)) )) )) + (else + (let loop ((last (car seq)) (next (cdr seq))) + (or (null? next) + (and (not (less? (car next) last)) + (loop (car next) (cdr next)) )) )) )) + + +;;; (merge a b less?) +;;; takes two lists a and b such that (sorted? a less?) and (sorted? b less?) +;;; and returns a new list in which the elements of a and b have been stably +;;; interleaved so that (sorted? (merge a b less?) less?). +;;; Note: this does _not_ accept vectors. See below. + +(define (sort:merge a b less?) + (cond + ((null? a) b) + ((null? b) a) + (else (let loop ((x (car a)) (a (cdr a)) (y (car b)) (b (cdr b))) + ;; The loop handles the merging of non-empty lists. It has + ;; been written this way to save testing and car/cdring. + (if (less? y x) + (if (null? b) + (cons y (cons x a)) + (cons y (loop x a (car b) (cdr b)) )) + ;; x <= y + (if (null? a) + (cons x (cons y b)) + (cons x (loop (car a) (cdr a) y b)) )) )) )) + + +;;; (merge! a b less?) +;;; takes two sorted lists a and b and smashes their cdr fields to form a +;;; single sorted list including the elements of both. +;;; Note: this does _not_ accept vectors. + +(define (sort:merge! a b less?) + (define (loop r a b) + (if (less? (car b) (car a)) + (begin + (set-cdr! r b) + (if (null? (cdr b)) + (set-cdr! b a) + (loop b a (cdr b)) )) + ;; (car a) <= (car b) + (begin + (set-cdr! r a) + (if (null? (cdr a)) + (set-cdr! a b) + (loop a (cdr a) b)) )) ) + (cond + ((null? a) b) + ((null? b) a) + ((less? (car b) (car a)) + (if (null? (cdr b)) + (set-cdr! b a) + (loop b a (cdr b))) + b) + (else ; (car a) <= (car b) + (if (null? (cdr a)) + (set-cdr! a b) + (loop a (cdr a) b)) + a))) + + + +;;; (sort! sequence less?) +;;; sorts the list or vector sequence destructively. It uses a version +;;; of merge-sort invented, to the best of my knowledge, by David H. D. +;;; Warren, and first used in the DEC-10 Prolog system. R. A. O'Keefe +;;; adapted it to work destructively in Scheme. + +(define (sort:sort! seq less?) + (define (step n) + (cond + ((> n 2) + (let* ((j (quotient n 2)) + (a (step j)) + (k (- n j)) + (b (step k))) + (sort:merge! a b less?))) + ((= n 2) + (let ((x (car seq)) + (y (cadr seq)) + (p seq)) + (set! seq (cddr seq)) + (if (less? y x) (begin + (set-car! p y) + (set-car! (cdr p) x))) + (set-cdr! (cdr p) '()) + p)) + ((= n 1) + (let ((p seq)) + (set! seq (cdr seq)) + (set-cdr! p '()) + p)) + (else + '()) )) + (if (vector? seq) + (let ((n (vector-length seq)) + (vec seq)) + (set! seq (vector->list seq)) + (do ((p (step n) (cdr p)) + (i 0 (+ i 1))) + ((null? p) vec) + (vector-set! vec i (car p)) )) + ;; otherwise, assume it is a list + (step (length seq)) )) + +;;; (sort sequence less?) +;;; sorts a vector or list non-destructively. It does this by sorting a +;;; copy of the sequence. My understanding is that the Standard says +;;; that the result of append is always "newly allocated" except for +;;; sharing structure with "the last argument", so (append x '()) ought +;;; to be a standard way of copying a list x. + +(define (sort:sort seq less?) + (if (vector? seq) + (list->vector (sort:sort! (vector->list seq) less?)) + (sort:sort! (append seq '()) less?))) + +;;; eof + +(define sorted? sort:sorted?) +(define merge sort:merge) +(define merge! sort:merge!) +(define sort sort:sort) +(define sort! sort:sort!) diff --git a/soundex.scm b/soundex.scm new file mode 100644 index 0000000..eb3a542 --- /dev/null +++ b/soundex.scm @@ -0,0 +1,82 @@ +;"soundex.scm" Original SOUNDEX algorithm. +;From jjb@isye.gatech.edu Mon May 2 22:29:43 1994 +; +; This code is in the public domain. + +;Date: Mon, 2 May 94 13:45:39 -0500 + +; Taken from Knuth, Vol. 3 "Sorting and searching", pp 391--2 + +(require 'common-list-functions) + +(define SOUNDEX + (let* ((letters-to-omit + (list #\A #\E #\H #\I #\O #\U #\W #\Y)) + (codes + (list (list #\B #\1) + (list #\F #\1) + (list #\P #\1) + (list #\V #\1) + ; + (list #\C #\2) + (list #\G #\2) + (list #\J #\2) + (list #\K #\2) + (list #\Q #\2) + (list #\S #\2) + (list #\X #\2) + (list #\Z #\2) + ; + (list #\D #\3) + (list #\T #\3) + ; + (list #\L #\4) + ; + (list #\M #\5) + (list #\N #\5) + ; + (list #\R #\6))) + (xform + (lambda (c) + (let ((code (assq c codes))) + (if code + (cadr code) + c))))) + (lambda (name) + (let ((char-list + (map char-upcase + (remove-if (lambda (c) + (not (char-alphabetic? c))) + (string->list name))))) + (if (null? char-list) + name + (let* (; Replace letters except first with codes: + (n1 (cons (car char-list) (map xform char-list))) + ; If 2 or more letter with same code are adjacent + ; in the original name, omit all but the first: + (n2 (let loop ((chars n1)) + (cond ((null? (cdr chars)) + chars) + (else + (if (char=? (xform (car chars)) + (cadr chars)) + (loop (cdr chars)) + (cons (car chars) (loop (cdr chars)))))))) + ; Omit vowels and similar letters, except first: + (n3 (cons (car char-list) + (remove-if + (lambda (c) + (memq c letters-to-omit)) + (cdr n2))))) + ; + ; pad with 0's or drop rightmost digits until of form "annn": + (let loop ((rev-chars (reverse n3))) + (let ((len (length rev-chars))) + (cond ((= 4 len) + (list->string (reverse rev-chars))) + ((> 4 len) + (loop (cons #\0 rev-chars))) + ((< 4 len) + (loop (cdr rev-chars)))))))))))) + + diff --git a/stdio.scm b/stdio.scm new file mode 100644 index 0000000..bc4e914 --- /dev/null +++ b/stdio.scm @@ -0,0 +1,7 @@ + +(require 'scanf) +(require 'printf) + +(define stdin (current-input-port)) +(define stdout (current-output-port)) +(define stderr (current-error-port)) diff --git a/strcase.scm b/strcase.scm new file mode 100644 index 0000000..f223527 --- /dev/null +++ b/strcase.scm @@ -0,0 +1,45 @@ +;;; "strcase.scm" String casing functions. +; Written 1992 by Dirk Lutzebaeck (lutzeb@cs.tu-berlin.de) +; +; This code is in the public domain. + +; Modified by Aubrey Jaffer Nov 1992. +; Authors of the original version were Ken Dickey and Aubrey Jaffer. + +;string-upcase, string-downcase, string-capitalize +; are obvious string conversion procedures and are non destructive. +;string-upcase!, string-downcase!, string-capitalize! +; are destructive versions. + +(define (string-upcase! str) + (do ((i (- (string-length str) 1) (- i 1))) + ((< i 0) str) + (string-set! str i (char-upcase (string-ref str i))))) + +(define (string-upcase str) + (string-upcase! (string-copy str))) + +(define (string-downcase! str) + (do ((i (- (string-length str) 1) (- i 1))) + ((< i 0) str) + (string-set! str i (char-downcase (string-ref str i))))) + +(define (string-downcase str) + (string-downcase! (string-copy str))) + +(define (string-capitalize! str) ; "hello" -> "Hello" + (let ((non-first-alpha #f) ; "hELLO" -> "Hello" + (str-len (string-length str))) ; "*hello" -> "*Hello" + (do ((i 0 (+ i 1))) ; "hello you" -> "Hello You" + ((= i str-len) str) + (let ((c (string-ref str i))) + (if (char-alphabetic? c) + (if non-first-alpha + (string-set! str i (char-downcase c)) + (begin + (set! non-first-alpha #t) + (string-set! str i (char-upcase c)))) + (set! non-first-alpha #f)))))) + +(define (string-capitalize str) + (string-capitalize! (string-copy str))) diff --git a/strport.scm b/strport.scm new file mode 100644 index 0000000..54d8d39 --- /dev/null +++ b/strport.scm @@ -0,0 +1,51 @@ +;;;;"strport.scm" Portable string ports for Scheme +;;;Copyright 1993 Dorai Sitaram and Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;N.B.: This implementation assumes you have tmpnam and +;delete-file defined in your .init file. tmpnam generates +;temp file names. delete-file may be defined to be a dummy +;procedure that does nothing. + +(define (call-with-output-string f) + (let ((tmpf (tmpnam))) + (call-with-output-file tmpf f) + (let ((s "") (buf (make-string 512))) + (call-with-input-file tmpf + (lambda (inp) + (let loop ((i 0)) + (let ((c (read-char inp))) + (cond ((eof-object? c) + (set! s (string-append s (substring buf 0 i)))) + ((>= i 512) + (set! s (string-append s buf)) + (loop 0)) + (else + (string-set! buf i c) + (loop (+ i 1)))))))) + (delete-file tmpf) + s))) + +(define (call-with-input-string s f) + (let ((tmpf (tmpnam))) + (call-with-output-file tmpf + (lambda (outp) + (display s outp))) + (let ((x (call-with-input-file tmpf f))) + (delete-file tmpf) + x))) diff --git a/strsrch.scm b/strsrch.scm new file mode 100644 index 0000000..a08510e --- /dev/null +++ b/strsrch.scm @@ -0,0 +1,95 @@ +;;; "MISCIO" Search for string from port. +; Written 1995, 1996 by Oleg Kiselyov (oleg@ponder.csci.unt.edu) +; Modified 1996 by A. Jaffer (jaffer@ai.mit.edu) +; +; This code is in the public domain. + +;;; Return the index of the first occurence of a-char in str, or #f +(define (string-index str a-char) + (let loop ((pos 0)) + (cond + ;; whole string has been searched, in vain + ((>= pos (string-length str)) #f) + ((char=? a-char (string-ref str pos)) pos) + (else (loop (+ 1 pos)))))) + +(define (substring? pattern str) + (let* ((pat-len (string-length pattern)) + (search-span (- (string-length str) pat-len)) + (c1 (if (zero? pat-len) #f (string-ref pattern 0))) + (c2 (if (<= pat-len 1) #f (string-ref pattern 1)))) + (cond + ((not c1) 0) ; empty pattern, matches upfront + ((not c2) (string-index str c1)) ; one-char pattern + (else ; matching pattern of > two chars + (let outer ((pos 0)) + (cond + ((> pos search-span) #f) ; nothing was found thru the whole str + ((not (char=? c1 (string-ref str pos))) + (outer (+ 1 pos))) ; keep looking for the right beginning + ((not (char=? c2 (string-ref str (+ 1 pos)))) + (outer (+ 1 pos))) ; could've done pos+2 if c1 == c2.... + (else ; two char matched: high probability + ; the rest will match too + (let inner ((i-pat 2) (i-str (+ 2 pos))) + (if (>= i-pat pat-len) pos ; the whole pattern matched + (if (char=? (string-ref pattern i-pat) + (string-ref str i-str)) + (inner (+ 1 i-pat) (+ 1 i-str)) + ;; mismatch after partial match + (outer (+ 1 pos)))))))))))) + +(define (find-string-from-port? str <input-port> . max-no-char) + (set! max-no-char (if (null? max-no-char) #f (car max-no-char))) + (letrec + ((no-chars-read 0) + (my-peek-char ; Return a peeked char or #f + (lambda () (and (or (not max-no-char) (< no-chars-read max-no-char)) + (let ((c (peek-char <input-port>))) + (if (eof-object? c) #f c))))) + (next-char (lambda () (read-char <input-port>) + (set! no-chars-read (+ 1 no-chars-read)))) + (match-1st-char ; of the string str + (lambda () + (let ((c (my-peek-char))) + (if (not c) #f + (begin (next-char) + (if (char=? c (string-ref str 0)) + (match-other-chars 1) + (match-1st-char))))))) + ;; There has been a partial match, up to the point pos-to-match + ;; (for example, str[0] has been found in the stream) + ;; Now look to see if str[pos-to-match] for would be found, too + (match-other-chars + (lambda (pos-to-match) + (if (>= pos-to-match (string-length str)) + no-chars-read ; the entire string has matched + (let ((c (my-peek-char))) + (and c + (if (not (char=? c (string-ref str pos-to-match))) + (backtrack 1 pos-to-match) + (begin (next-char) + (match-other-chars (+ 1 pos-to-match))))))))) + + ;; There had been a partial match, but then a wrong char showed up. + ;; Before discarding previously read (and matched) characters, we check + ;; to see if there was some smaller partial match. Note, characters read + ;; so far (which matter) are those of str[0..matched-substr-len - 1] + ;; In other words, we will check to see if there is such i>0 that + ;; substr(str,0,j) = substr(str,i,matched-substr-len) + ;; where j=matched-substr-len - i + (backtrack + (lambda (i matched-substr-len) + (let ((j (- matched-substr-len i))) + (if (<= j 0) + ;; backed off completely to the begining of str + (match-1st-char) + (let loop ((k 0)) + (if (>= k j) + (match-other-chars j) ; there was indeed a shorter match + (if (char=? (string-ref str k) + (string-ref str (+ i k))) + (loop (+ 1 k)) + (backtrack (+ 1 i) matched-substr-len)))))))) + ) + (match-1st-char))) diff --git a/struct.scm b/struct.scm new file mode 100644 index 0000000..8c5c423 --- /dev/null +++ b/struct.scm @@ -0,0 +1,165 @@ +;;; "struct.scm": defmacros for RECORDS +;;; Copyright 1992 Jeff Alexander, Shinnder Lee, and Lewis Patterson + +;;; Defmacros which implement RECORDS from the book: +;;; "Essentials of Programming Languages" by Daniel P. Friedman, +;;; M. Wand and C.T. Haynes. + +;;; jaffer@ai.mit.edu, Feb 1993 ported to SLIB. + +;;; Date: Sun, 20 Aug 1995 19:20:35 -0500 +;;; From: Gary Leavens <leavens@cs.iastate.edu> +;;; I thought you might want to know that, for using the file +;;; struct.scm with the EOPL book, one has to make 2 corrections. To +;;; correct it, there are two places where "-" has to be replaced by +;;; "->" as in the code below. + +(require 'common-list-functions) + +(defmacro define-record args + (check-define-record-syntax args + (lambda (name make-name name? field-accessors field-setters) + (letrec + ((make-fields + (lambda (field-accessors i) + (if (null? field-accessors) + '() + (cons + `(define ,(car field-accessors) + (lambda (obj) + (if (,name? obj) + (vector-ref obj ,i) + (slib:error ',(car field-accessors) + ": bad record" obj)))) + (make-fields (cdr field-accessors) (+ i 1)))))) + (make-setters + (lambda (field-accessors i) + (if (null? field-accessors) + '() + (cons + `(define ,(car field-accessors) + (lambda (obj val) + (if (,name? obj) + (vector-set! obj ,i val) + (slib:error ',(car field-accessors) + ": bad record" obj)))) + (make-setters (cdr field-accessors) (+ i 1))))))) + `(begin + ,@(make-fields field-accessors 1) + ,@(make-setters field-setters 1) + (define ,name? + (lambda (obj) + (and (vector? obj) + (= (vector-length obj) ,(+ 1 (length field-accessors))) + (eq? (vector-ref obj 0) ',name)))) + (define ,make-name + (lambda ,field-accessors + (vector ',name ,@field-accessors)))))))) + +(defmacro variant-case args + (check-variant-case-syntax args + (lambda (exp clauses) + (let ((var (gentemp))) + (let + ((make-clause + (lambda (clause) + (if (eq? (car clause) 'else) + `(#t ,@(cdr clause)) + `((,(car clause) ,var) + (let ,(map (lambda (field) + `(,(car field) (,(cdr field) ,var))) + (cadr clause)) + ,@(cddr clause))))))) + `(let ((,var ,exp)) + (cond ,@(map make-clause clauses)))))))) + +;;; syntax checkers + +;;; name make-name name? field-accessors + +(define check-define-record-syntax + (lambda (x k) + (cond + ((and (list? x) + (= (length x) 2) + (symbol? (car x)) + (list? (cadr x)) + (comlist:every symbol? (cadr x)) + (not (struct:duplicate-fields? (cadr x)))) + (let ((name (symbol->string (car x)))) + (let ((make-name (string->symbol + (string-append (symbol->string 'make-) name))) + (name? (string->symbol (string-append name "?"))) + (field-accessors + (map + (lambda (field) + (string->symbol + (string-append name "->" (symbol->string field)))) + (cadr x))) + (field-setters + (map + (lambda (field) + (string->symbol + (string-append + "set-" name "-" (symbol->string field) "!"))) + (cadr x)))) + (k (car x) make-name name? field-accessors field-setters)))) + (else (slib:error "define-record: invalid syntax" x))))) + +(define check-variant-case-syntax + (let + ((make-clause + (lambda (clause) + (if (eq? (car clause) 'else) + clause + (let ((name (symbol->string (car clause)))) + (let ((name? (string->symbol (string-append name "?"))) + (fields + (map + (lambda (field) + (cons field + (string->symbol + (string-append name "->" + (symbol->string field))))) + (cadr clause)))) + (cons name? (cons fields (cddr clause))))))))) + (lambda (args k) + (if (and (list? args) + (<= 2 (length args)) + (struct:clauses? (cdr args))) + (k (car args) (map make-clause (cdr args))) + (slib:error "variant-case: invalid syntax" args))))) + +(define struct:duplicate-fields? + (lambda (fields) + (cond + ((null? fields) #f) + ((memq (car fields) (cdr fields)) #t) + (else (struct:duplicate-fields? (cdr fields)))))) + +(define struct:clauses? + (let + ((clause? + (lambda (clause) + (and (list? clause) + (not (null? clause)) + (cond + ((eq? (car clause) 'else) + (not (null? (cdr clause)))) + (else (and (symbol? (car clause)) + (not (null? (cdr clause))) + (list? (cadr clause)) + (comlist:every symbol? (cadr clause)) + (not (struct:duplicate-fields? (cadr clause))) + (not (null? (cddr clause)))))))))) + (letrec + ((struct:duplicate-tags? + (lambda (tags) + (cond + ((null? tags) #f) + ((eq? (car tags) 'else) (not (null? (cdr tags)))) + ((memq (car tags) (cdr tags)) #t) + (else (struct:duplicate-tags? (cdr tags))))))) + (lambda (clauses) + (and (comlist:every clause? clauses) + (not (struct:duplicate-tags? (map car clauses)))))))) diff --git a/structst.scm b/structst.scm new file mode 100644 index 0000000..ea298e0 --- /dev/null +++ b/structst.scm @@ -0,0 +1,37 @@ +;"structst.scm" test "struct.scm" +;Copyright (C) 1993 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'struct) + +(define-record foo (a b c)) +(define-record goo (xx yy)) + +(define a-foo (make-foo 1 2 3)) +(define a-goo (make-goo 4 5)) + +(define (struct:test) + (define (t1 x) + (variant-case x + (foo (a b c) (list a b c)) + (goo (xx yy) (list xx yy)) + (else (list 7 8)))) + (write (append (t1 a-foo) (t1 a-goo) (t1 9))) + (newline)) + +(struct:test) diff --git a/structure.scm b/structure.scm new file mode 100644 index 0000000..0d379b9 --- /dev/null +++ b/structure.scm @@ -0,0 +1,80 @@ +;;; "structure.scm" syntax-case structure macros +;;; Copyright (C) 1992 R. Kent Dybvig +;;; +;;; Permission to copy this software, in whole or in part, to use this +;;; software for any lawful purpose, and to redistribute this software +;;; is granted subject to the restriction that all copies made of this +;;; software must include this copyright notice in full. This software +;;; is provided AS IS, with NO WARRANTY, EITHER EXPRESS OR IMPLIED, +;;; INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY +;;; OR FITNESS FOR ANY PARTICULAR PURPOSE. IN NO EVENT SHALL THE +;;; AUTHORS BE LIABLE FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES OF ANY +;;; NATURE WHATSOEVER. + +;;; Written by Robert Hieb & Kent Dybvig + +;;; This file was munged by a simple minded sed script since it left +;;; its original authors' hands. See syncase.sh for the horrid details. + +;;; structure.ss +;;; Robert Hieb & Kent Dybvig +;;; 92/06/18 + +(define-syntax define-structure + (lambda (x) + (define construct-name + (lambda (template-identifier . args) + (implicit-identifier + template-identifier + (string->symbol + (apply string-append + (map (lambda (x) + (if (string? x) + x + (symbol->string (syntax-object->datum x)))) + args)))))) + (syntax-case x () + ((_ (name id1 ...)) + (syntax (define-structure (name id1 ...) ()))) + ((_ (name id1 ...) ((id2 init) ...)) + (with-syntax + ((constructor (construct-name (syntax name) "make-" (syntax name))) + (predicate (construct-name (syntax name) (syntax name) "?")) + ((access ...) + (map (lambda (x) (construct-name x (syntax name) "-" x)) + (syntax (id1 ... id2 ...)))) + ((assign ...) + (map (lambda (x) + (construct-name x "set-" (syntax name) "-" x "!")) + (syntax (id1 ... id2 ...)))) + (structure-length + (+ (length (syntax (id1 ... id2 ...))) 1)) + ((index ...) + (let f ((i 1) (ids (syntax (id1 ... id2 ...)))) + (if (null? ids) + '() + (cons i (f (+ i 1) (cdr ids))))))) + (syntax (begin + (define constructor + (lambda (id1 ...) + (let* ((id2 init) ...) + (vector 'name id1 ... id2 ...)))) + (define predicate + (lambda (x) + (and (vector? x) + (= (vector-length x) structure-length) + (eq? (vector-ref x 0) 'name)))) + (define access + (lambda (x) + (vector-ref x index))) + ... + ;; define macro accessors this way: + ;; (define-syntax access + ;; (syntax-case x () + ;; ((_ x) + ;; (syntax (vector-ref x index))))) + ;; ... + (define assign + (lambda (x update) + (vector-set! x index update))) + ...))))))) diff --git a/syncase.sh b/syncase.sh new file mode 100644 index 0000000..4ae4db4 --- /dev/null +++ b/syncase.sh @@ -0,0 +1,146 @@ +#! /bin/sh -e + +echo Cleaning up old version and unpacking original ... +rm -fr syntax-case +gzip --decompress --stdout syntax-case.tar.z | tar xf - + +cd syntax-case + +echo Removing some files ... +rm *.ps loadpp.ss hooks* + +# Remove enormous amount (about 200k) of white space in expand.pp +echo Slimming expand.pp ... +sed -e '/^ */s///' expand.pp > tt; mv tt expand.pp + +echo Patching ... +patch -s -b .ORIG << 'PATCH' +--- ./expand.pp.ORIG Wed Mar 24 19:54:52 1993 ++++ ./expand.pp Wed Mar 24 19:54:52 1993 +@@ -337,9 +337,10 @@ + '() + (lambda (e maps) (regen e))))) + (ellipsis? (lambda (x) +-(if (if (top-level-bound? 'dp) dp #f) +-(break) +-(void)) ++;; I dont know what this is supposed to do, and removing it seemed harmless. ++;; (if (if (top-level-bound? 'dp) dp #f) ++;; (break) ++;; (void)) + (if (identifier? x) + (free-id=? x '...) + #f))) +@@ -1674,7 +1675,7 @@ + (set! generate-temporaries + (lambda (ls) + (arg-check list? ls 'generate-temporaries) +-(map (lambda (x) (wrap (gensym) top-wrap)) ls))) ++(map (lambda (x) (wrap (new-symbol-hook "g") top-wrap)) ls))) + (set! free-identifier=? + (lambda (x y) + (arg-check id? x 'free-identifier=?) +--- ./expand.ss.ORIG Thu Jul 2 13:56:19 1992 ++++ ./expand.ss Wed Mar 24 19:54:53 1993 +@@ -564,7 +564,8 @@ + + (define ellipsis? + (lambda (x) +- (when (and (top-level-bound? 'dp) dp) (break)) ++ ;; I dont know what this is supposed to do, and removing it seemed harmless. ++ ;; (when (and (top-level-bound? 'dp) dp) (break)) + (and (identifier? x) + (free-id=? x (syntax (... ...)))))) + +@@ -887,7 +888,7 @@ + ;; gensym + (lambda (ls) + (arg-check list? ls 'generate-temporaries) +- (map (lambda (x) (wrap (gensym) top-wrap)) ls))) ++ (map (lambda (x) (wrap (new-symbol-hook "g") top-wrap)) ls))) + + (set! free-identifier=? + (lambda (x y) +--- ./macro-defs.ss.ORIG Thu Jul 2 12:28:49 1992 ++++ ./macro-defs.ss Wed Mar 24 19:55:31 1993 +@@ -161,26 +161,3 @@ + (syntax-case x () + ((- e) (gen (syntax e) 0)))))) + +-;;; simple delay and force; also defines make-promise +- +-(define-syntax delay +- (lambda (x) +- (syntax-case x () +- ((delay exp) +- (syntax (make-promise (lambda () exp))))))) +- +-(define make-promise +- (lambda (thunk) +- (let ([value (void)] [set? #f]) +- (lambda () +- (unless set? +- (let ([v (thunk)]) +- (unless set? +- (set! value v) +- (set! set? #t)))) +- value)))) +- +-(define force +- (lambda (promise) +- (promise))) +- +PATCH +test $# -gt 0 && exit 0 +rm *.ORIG +############################################################################### + +echo Renaming globals ... + +CR=' +' +SEDCMD='s/list\*/syncase:list*/g' +for x in \ + build- void andmap install-global-transformer eval-hook error-hook \ + new-symbol-hook put-global-definition-hook get-global-definition-hook \ + expand-install-hook; +do SEDCMD=$SEDCMD$CR"s/$x/syncase:$x/g"; done + +WARN=";;; This file was munged by a simple minded sed script since it left +;;; its original authors' hands. See syncase.doc for the horrid details. +" + +for f in *.pp *.ss; do + mv $f tt; (echo "$WARN"; sed -e "$SEDCMD" tt) >$f; rm tt; done + +echo Making the doc file ... +DOC=syncase.doc +cp ../$DOC . +for f in Notes ReadMe; do +echo " +******************************************************************************* +The file named $f in the original distribution: +" +cat $f +rm $f +done >>$DOC + +echo " +******************************************************************************* +The shell script that created these files out of the original distribution: +" >>$DOC +cat ../fixit >>$DOC + +echo Renaming files ... +mv compat.ss sca-comp.scm +mv output.ss scaoutp.scm +mv init.ss scaglob.scm +mv expand.pp scaexpp.scm +mv expand.ss sca-exp.scm +mv macro-defs.ss scamacr.scm +mv structure.ss structure.scm + +echo Adding new pieces ... +cp ../sca-init.scm scainit.scm + +echo Done. diff --git a/synchk.scm b/synchk.scm new file mode 100644 index 0000000..7e45a73 --- /dev/null +++ b/synchk.scm @@ -0,0 +1,104 @@ +;;; "synchk.scm" Syntax Checking -*-Scheme-*- +;;; Copyright (c) 1989-91 Massachusetts Institute of Technology +;;; +;;; This material was developed by the Scheme project at the +;;; Massachusetts Institute of Technology, Department of Electrical +;;; Engineering and Computer Science. Permission to copy this +;;; software, to redistribute it, and to use it for any purpose is +;;; granted, subject to the following restrictions and understandings. +;;; +;;; 1. Any copy made of this software must include this copyright +;;; notice in full. +;;; +;;; 2. Users of this software agree to make their best efforts (a) to +;;; return to the MIT Scheme project any improvements or extensions +;;; that they make, so that these may be included in future releases; +;;; and (b) to inform MIT of noteworthy uses of this software. +;;; +;;; 3. All materials developed as a consequence of the use of this +;;; software shall duly acknowledge such use, in accordance with the +;;; usual standards of acknowledging credit in academic research. +;;; +;;; 4. MIT has made no warrantee or representation that the operation +;;; of this software will be error-free, and MIT is under no +;;; obligation to provide any services, by way of maintenance, update, +;;; or otherwise. +;;; +;;; 5. In conjunction with products arising from the use of this +;;; material, there shall be no use of the name of the Massachusetts +;;; Institute of Technology nor of any adaptation thereof in any +;;; advertising, promotional, or sales literature without prior +;;; written consent from MIT in each case. + +;;;; Syntax Checking +;;; written by Alan Bawden +;;; modified by Chris Hanson + +(define (syntax-check pattern form) + (if (not (syntax-match? (cdr pattern) (cdr form))) + (syntax-error "ill-formed special form" form))) + +(define (ill-formed-syntax form) + (syntax-error "ill-formed special form" form)) + +(define (syntax-match? pattern object) + (let ((match-error + (lambda () + (impl-error "ill-formed pattern" pattern)))) + (cond ((symbol? pattern) + (case pattern + ((IDENTIFIER) (identifier? object)) + ((DATUM EXPRESSION FORM) #t) + ((R4RS-BVL) + (let loop ((seen '()) (object object)) + (or (null? object) + (if (identifier? object) + (not (memq object seen)) + (and (pair? object) + (identifier? (car object)) + (not (memq (car object) seen)) + (loop (cons (car object) seen) (cdr object))))))) + ((MIT-BVL) (lambda-list? object)) + (else (match-error)))) + ((pair? pattern) + (case (car pattern) + ((*) + (if (pair? (cdr pattern)) + (let ((head (cadr pattern)) + (tail (cddr pattern))) + (let loop ((object object)) + (or (and (pair? object) + (syntax-match? head (car object)) + (loop (cdr object))) + (syntax-match? tail object)))) + (match-error))) + ((+) + (if (pair? (cdr pattern)) + (let ((head (cadr pattern)) + (tail (cddr pattern))) + (and (pair? object) + (syntax-match? head (car object)) + (let loop ((object (cdr object))) + (or (and (pair? object) + (syntax-match? head (car object)) + (loop (cdr object))) + (syntax-match? tail object))))) + (match-error))) + ((?) + (if (pair? (cdr pattern)) + (or (and (pair? object) + (syntax-match? (cadr pattern) (car object)) + (syntax-match? (cddr pattern) (cdr object))) + (syntax-match? (cddr pattern) object)) + (match-error))) + ((QUOTE) + (if (and (pair? (cdr pattern)) + (null? (cddr pattern))) + (eqv? (cadr pattern) object) + (match-error))) + (else + (and (pair? object) + (syntax-match? (car pattern) (car object)) + (syntax-match? (cdr pattern) (cdr object)))))) + (else + (eqv? pattern object))))) diff --git a/synclo.scm b/synclo.scm new file mode 100644 index 0000000..3c61de3 --- /dev/null +++ b/synclo.scm @@ -0,0 +1,748 @@ +;;; "synclo.scm" Syntactic Closures -*-Scheme-*- +;;; Copyright (c) 1989-91 Massachusetts Institute of Technology +;;; +;;; This material was developed by the Scheme project at the +;;; Massachusetts Institute of Technology, Department of Electrical +;;; Engineering and Computer Science. Permission to copy this +;;; software, to redistribute it, and to use it for any purpose is +;;; granted, subject to the following restrictions and understandings. +;;; +;;; 1. Any copy made of this software must include this copyright +;;; notice in full. +;;; +;;; 2. Users of this software agree to make their best efforts (a) to +;;; return to the MIT Scheme project any improvements or extensions +;;; that they make, so that these may be included in future releases; +;;; and (b) to inform MIT of noteworthy uses of this software. +;;; +;;; 3. All materials developed as a consequence of the use of this +;;; software shall duly acknowledge such use, in accordance with the +;;; usual standards of acknowledging credit in academic research. +;;; +;;; 4. MIT has made no warrantee or representation that the operation +;;; of this software will be error-free, and MIT is under no +;;; obligation to provide any services, by way of maintenance, update, +;;; or otherwise. +;;; +;;; 5. In conjunction with products arising from the use of this +;;; material, there shall be no use of the name of the Massachusetts +;;; Institute of Technology nor of any adaptation thereof in any +;;; advertising, promotional, or sales literature without prior +;;; written consent from MIT in each case. + +;;;; Syntactic Closures +;;; written by Alan Bawden +;;; extensively modified by Chris Hanson + +;;; See "Syntactic Closures", by Alan Bawden and Jonathan Rees, in +;;; Proceedings of the 1988 ACM Conference on Lisp and Functional +;;; Programming, page 86. + +;;;; Classifier +;;; The classifier maps forms into items. In addition to locating +;;; definitions so that they can be properly processed, it also +;;; identifies keywords and variables, which allows a powerful form +;;; of syntactic binding to be implemented. + +(define (classify/form form environment definition-environment) + (cond ((identifier? form) + (syntactic-environment/lookup environment form)) + ((syntactic-closure? form) + (let ((form (syntactic-closure/form form)) + (environment + (filter-syntactic-environment + (syntactic-closure/free-names form) + environment + (syntactic-closure/environment form)))) + (classify/form form + environment + definition-environment))) + ((pair? form) + (let ((item + (classify/subexpression (car form) environment))) + (cond ((keyword-item? item) + ((keyword-item/classifier item) form + environment + definition-environment)) + ((list? (cdr form)) + (let ((items + (classify/subexpressions (cdr form) + environment))) + (make-expression-item + (lambda () + (output/combination + (compile-item/expression item) + (map compile-item/expression items))) + form))) + (else + (syntax-error "combination must be a proper list" + form))))) + (else + (make-expression-item ;don't quote literals evaluating to themselves + (if (or (boolean? form) (char? form) (number? form) (string? form)) + (lambda () (output/literal-unquoted form)) + (lambda () (output/literal-quoted form))) form)))) + +(define (classify/subform form environment definition-environment) + (classify/form form + environment + definition-environment)) + +(define (classify/subforms forms environment definition-environment) + (map (lambda (form) + (classify/subform form environment definition-environment)) + forms)) + +(define (classify/subexpression expression environment) + (classify/subform expression environment environment)) + +(define (classify/subexpressions expressions environment) + (classify/subforms expressions environment environment)) + +;;;; Compiler +;;; The compiler maps items into the output language. + +(define (compile-item/expression item) + (let ((illegal + (lambda (item name) + (let ((decompiled (decompile-item item))) (newline) + (slib:error (string-append name + " may not be used as an expression") + decompiled))))) + (cond ((variable-item? item) + (output/variable (variable-item/name item))) + ((expression-item? item) + ((expression-item/compiler item))) + ((body-item? item) + (let ((items (flatten-body-items (body-item/components item)))) + (if (null? items) + (illegal item "empty sequence") + (output/sequence (map compile-item/expression items))))) + ((definition-item? item) + (let ((binding ;allows later scheme errors, but allows top-level + (bind-definition-item! ;(if (not (defined? x)) define it) + scheme-syntactic-environment item))) ;as in Init.scm + (output/top-level-definition + (car binding) + (compile-item/expression (cdr binding))))) + ((keyword-item? item) + (illegal item "keyword")) + (else + (impl-error "unknown item" item))))) + +(define (compile/subexpression expression environment) + (compile-item/expression + (classify/subexpression expression environment))) + +(define (compile/top-level forms environment) + ;; Top-level syntactic definitions affect all forms that appear + ;; after them. + (output/top-level-sequence + (let forms-loop ((forms forms)) + (if (null? forms) + '() + (let items-loop + ((items + (item->list + (classify/subform (car forms) + environment + environment)))) + (cond ((null? items) + (forms-loop (cdr forms))) + ((definition-item? (car items)) + (let ((binding + (bind-definition-item! environment (car items)))) + (if binding + (cons (output/top-level-definition + (car binding) + (compile-item/expression (cdr binding))) + (items-loop (cdr items))) + (items-loop (cdr items))))) + (else + (cons (compile-item/expression (car items)) + (items-loop (cdr items)))))))))) + +;;;; De-Compiler +;;; The de-compiler maps partly-compiled things back to the input language, +;;; as far as possible. Used to display more meaningful macro error messages. + +(define (decompile-item item) + (display " ") + (cond ((variable-item? item) (variable-item/name item)) + ((expression-item? item) + (decompile-item (expression-item/annotation item))) + ((body-item? item) + (let ((items (flatten-body-items (body-item/components item)))) + (display "sequence") + (if (null? items) + "empty sequence" + "non-empty sequence"))) + ((definition-item? item) "definition") + ((keyword-item? item) + (decompile-item (keyword-item/name item)));in case expression + ((syntactic-closure? item); (display "syntactic-closure;") + (decompile-item (syntactic-closure/form item))) + ((list? item) (display "(") + (map decompile-item item) (display ")") "see list above") + ((string? item) item);explicit name-string for keyword-item + ((symbol? item) (display item) item) ;symbol for syntactic-closures + ((boolean? item) (display item) item) ;symbol for syntactic-closures + (else (write item) (impl-error "unknown item" item)))) + +;;;; Syntactic Closures + +(define syntactic-closure-type + (make-record-type "syntactic-closure" '(ENVIRONMENT FREE-NAMES FORM))) + +(define make-syntactic-closure + (record-constructor syntactic-closure-type '(ENVIRONMENT FREE-NAMES FORM))) + +(define syntactic-closure? + (record-predicate syntactic-closure-type)) + +(define syntactic-closure/environment + (record-accessor syntactic-closure-type 'ENVIRONMENT)) + +(define syntactic-closure/free-names + (record-accessor syntactic-closure-type 'FREE-NAMES)) + +(define syntactic-closure/form + (record-accessor syntactic-closure-type 'FORM)) + +(define (make-syntactic-closure-list environment free-names forms) + (map (lambda (form) (make-syntactic-closure environment free-names form)) + forms)) + +(define (strip-syntactic-closures object) + (cond ((syntactic-closure? object) + (strip-syntactic-closures (syntactic-closure/form object))) + ((pair? object) + (cons (strip-syntactic-closures (car object)) + (strip-syntactic-closures (cdr object)))) + ((vector? object) + (let ((length (vector-length object))) + (let ((result (make-vector length))) + (do ((i 0 (+ i 1))) + ((= i length)) + (vector-set! result i + (strip-syntactic-closures (vector-ref object i)))) + result))) + (else + object))) + +(define (identifier? object) + (or (symbol? object) + (synthetic-identifier? object))) + +(define (synthetic-identifier? object) + (and (syntactic-closure? object) + (identifier? (syntactic-closure/form object)))) + +(define (identifier->symbol identifier) + (cond ((symbol? identifier) + identifier) + ((synthetic-identifier? identifier) + (identifier->symbol (syntactic-closure/form identifier))) + (else + (impl-error "not an identifier" identifier)))) + +(define (identifier=? environment-1 identifier-1 environment-2 identifier-2) + (let ((item-1 (syntactic-environment/lookup environment-1 identifier-1)) + (item-2 (syntactic-environment/lookup environment-2 identifier-2))) + (or (eq? item-1 item-2) + ;; This is necessary because an identifier that is not + ;; explicitly bound by an environment is mapped to a variable + ;; item, and the variable items are not cached. Therefore + ;; two references to the same variable result in two + ;; different variable items. + (and (variable-item? item-1) + (variable-item? item-2) + (eq? (variable-item/name item-1) + (variable-item/name item-2)))))) + +;;;; Syntactic Environments + +(define syntactic-environment-type + (make-record-type + "syntactic-environment" + '(PARENT + LOOKUP-OPERATION + RENAME-OPERATION + DEFINE-OPERATION + BINDINGS-OPERATION))) + +(define make-syntactic-environment + (record-constructor syntactic-environment-type + '(PARENT + LOOKUP-OPERATION + RENAME-OPERATION + DEFINE-OPERATION + BINDINGS-OPERATION))) + +(define syntactic-environment? + (record-predicate syntactic-environment-type)) + +(define syntactic-environment/parent + (record-accessor syntactic-environment-type 'PARENT)) + +(define syntactic-environment/lookup-operation + (record-accessor syntactic-environment-type 'LOOKUP-OPERATION)) + +(define (syntactic-environment/assign! environment name item) + (let ((binding + ((syntactic-environment/lookup-operation environment) name))) + (if binding + (set-cdr! binding item) + (impl-error "can't assign unbound identifier" name)))) + +(define syntactic-environment/rename-operation + (record-accessor syntactic-environment-type 'RENAME-OPERATION)) + +(define (syntactic-environment/rename environment name) + ((syntactic-environment/rename-operation environment) name)) + +(define syntactic-environment/define! + (let ((accessor + (record-accessor syntactic-environment-type 'DEFINE-OPERATION))) + (lambda (environment name item) + ((accessor environment) name item)))) + +(define syntactic-environment/bindings + (let ((accessor + (record-accessor syntactic-environment-type 'BINDINGS-OPERATION))) + (lambda (environment) + ((accessor environment))))) + +(define (syntactic-environment/lookup environment name) + (let ((binding + ((syntactic-environment/lookup-operation environment) name))) + (cond (binding + (let ((item (cdr binding))) + (if (reserved-name-item? item) + (syntax-error "premature reference to reserved name" + name) + item))) + ((symbol? name) + (make-variable-item name)) + ((synthetic-identifier? name) + (syntactic-environment/lookup (syntactic-closure/environment name) + (syntactic-closure/form name))) + (else + (impl-error "not an identifier" name))))) + +(define root-syntactic-environment + (make-syntactic-environment + #f + (lambda (name) + name + #f) + (lambda (name) + name) + (lambda (name item) + (impl-error "can't bind name in root syntactic environment" name item)) + (lambda () + '()))) + +(define null-syntactic-environment + (make-syntactic-environment + #f + (lambda (name) + (impl-error "can't lookup name in null syntactic environment" name)) + (lambda (name) + (impl-error "can't rename name in null syntactic environment" name)) + (lambda (name item) + (impl-error "can't bind name in null syntactic environment" name item)) + (lambda () + '()))) + +(define (top-level-syntactic-environment parent) + (let ((bound '())) + (make-syntactic-environment + parent + (let ((parent-lookup (syntactic-environment/lookup-operation parent))) + (lambda (name) + (or (assq name bound) + (parent-lookup name)))) + (lambda (name) + name) + (lambda (name item) + (let ((binding (assq name bound))) + (if binding + (set-cdr! binding item) + (set! bound (cons (cons name item) bound))))) + (lambda () + (map (lambda (pair) (cons (car pair) (cdr pair))) bound))))) + +(define (internal-syntactic-environment parent) + (let ((bound '()) + (free '())) + (make-syntactic-environment + parent + (let ((parent-lookup (syntactic-environment/lookup-operation parent))) + (lambda (name) + (or (assq name bound) + (assq name free) + (let ((binding (parent-lookup name))) + (if binding (set! free (cons binding free))) + binding)))) + (make-name-generator) + (lambda (name item) + (cond ((assq name bound) + => + (lambda (association) + (if (and (reserved-name-item? (cdr association)) + (not (reserved-name-item? item))) + (set-cdr! association item) + (impl-error "can't redefine name; already bound" name)))) + ((assq name free) + (if (reserved-name-item? item) + (syntax-error "premature reference to reserved name" + name) + (impl-error "can't define name; already free" name))) + (else + (set! bound (cons (cons name item) bound))))) + (lambda () + (map (lambda (pair) (cons (car pair) (cdr pair))) bound))))) + +(define (filter-syntactic-environment names names-env else-env) + (if (or (null? names) + (eq? names-env else-env)) + else-env + (let ((make-operation + (lambda (get-operation) + (let ((names-operation (get-operation names-env)) + (else-operation (get-operation else-env))) + (lambda (name) + ((if (memq name names) names-operation else-operation) + name)))))) + (make-syntactic-environment + else-env + (make-operation syntactic-environment/lookup-operation) + (make-operation syntactic-environment/rename-operation) + (lambda (name item) + (impl-error "can't bind name in filtered syntactic environment" + name item)) + (lambda () + (map (lambda (name) + (cons name + (syntactic-environment/lookup names-env name))) + names)))))) + +;;;; Items + +;;; Reserved name items do not represent any form, but instead are +;;; used to reserve a particular name in a syntactic environment. If +;;; the classifier refers to a reserved name, a syntax error is +;;; signalled. This is used in the implementation of LETREC-SYNTAX +;;; to signal a meaningful error when one of the <init>s refers to +;;; one of the names being bound. + +(define reserved-name-item-type + (make-record-type "reserved-name-item" '())) + +(define make-reserved-name-item + (record-constructor reserved-name-item-type)) ; '() + +(define reserved-name-item? + (record-predicate reserved-name-item-type)) + +;;; Keyword items represent macro keywords. + +(define keyword-item-type + (make-record-type "keyword-item" '(CLASSIFIER NAME))) +; (make-record-type "keyword-item" '(CLASSIFIER))) + +(define make-keyword-item +; (lambda (cl) (display "make-keyword-item:") (write cl) (newline) +; ((record-constructor keyword-item-type '(CLASSIFIER)) cl))) + (record-constructor keyword-item-type '(CLASSIFIER NAME))) +; (record-constructor keyword-item-type '(CLASSIFIER))) + +(define keyword-item? + (record-predicate keyword-item-type)) + +(define keyword-item/classifier + (record-accessor keyword-item-type 'CLASSIFIER)) + +(define keyword-item/name + (record-accessor keyword-item-type 'NAME)) + +;;; Variable items represent run-time variables. + +(define variable-item-type + (make-record-type "variable-item" '(NAME))) + +(define make-variable-item + (record-constructor variable-item-type '(NAME))) + +(define variable-item? + (record-predicate variable-item-type)) + +(define variable-item/name + (record-accessor variable-item-type 'NAME)) + +;;; Expression items represent any kind of expression other than a +;;; run-time variable or a sequence. The ANNOTATION field is used to +;;; make expression items that can appear in non-expression contexts +;;; (for example, this could be used in the implementation of SETF). + +(define expression-item-type + (make-record-type "expression-item" '(COMPILER ANNOTATION))) + +(define make-expression-item + (record-constructor expression-item-type '(COMPILER ANNOTATION))) + +(define expression-item? + (record-predicate expression-item-type)) + +(define expression-item/compiler + (record-accessor expression-item-type 'COMPILER)) + +(define expression-item/annotation + (record-accessor expression-item-type 'ANNOTATION)) + +;;; Body items represent sequences (e.g. BEGIN). + +(define body-item-type + (make-record-type "body-item" '(COMPONENTS))) + +(define make-body-item + (record-constructor body-item-type '(COMPONENTS))) + +(define body-item? + (record-predicate body-item-type)) + +(define body-item/components + (record-accessor body-item-type 'COMPONENTS)) + +;;; Definition items represent definitions, whether top-level or +;;; internal, keyword or variable. + +(define definition-item-type + (make-record-type "definition-item" '(BINDING-THEORY NAME VALUE))) + +(define make-definition-item + (record-constructor definition-item-type '(BINDING-THEORY NAME VALUE))) + +(define definition-item? + (record-predicate definition-item-type)) + +(define definition-item/binding-theory + (record-accessor definition-item-type 'BINDING-THEORY)) + +(define definition-item/name + (record-accessor definition-item-type 'NAME)) + +(define definition-item/value + (record-accessor definition-item-type 'VALUE)) + +(define (bind-definition-item! environment item) + ((definition-item/binding-theory item) + environment + (definition-item/name item) + (promise:force (definition-item/value item)))) + +(define (syntactic-binding-theory environment name item) + (if (or (keyword-item? item) + (variable-item? item)) + (begin + (syntactic-environment/define! environment name item) + #f) + (syntax-error "syntactic binding value must be a keyword or a variable" + item))) + +(define (variable-binding-theory environment name item) + ;; If ITEM isn't a valid expression, an error will be signalled by + ;; COMPILE-ITEM/EXPRESSION later. + (cons (bind-variable! environment name) item)) + +(define (overloaded-binding-theory environment name item) + (if (keyword-item? item) + (begin + (syntactic-environment/define! environment name item) + #f) + (cons (bind-variable! environment name) item))) + +;;;; Classifiers, Compilers, Expanders + +(define (sc-expander->classifier expander keyword-environment) + (lambda (form environment definition-environment) + (classify/form (expander form environment) + keyword-environment + definition-environment))) + +(define (er-expander->classifier expander keyword-environment) + (sc-expander->classifier (er->sc-expander expander) keyword-environment)) + +(define (er->sc-expander expander) + (lambda (form environment) + (capture-syntactic-environment + (lambda (keyword-environment) + (make-syntactic-closure + environment '() + (expander form + (let ((renames '())) + (lambda (identifier) + (let ((association (assq identifier renames))) + (if association + (cdr association) + (let ((rename + (make-syntactic-closure + keyword-environment + '() + identifier))) + (set! renames + (cons (cons identifier rename) + renames)) + rename))))) + (lambda (x y) + (identifier=? environment x + environment y)))))))) + +(define (classifier->keyword classifier) + (make-syntactic-closure + (let ((environment + (internal-syntactic-environment null-syntactic-environment))) + (syntactic-environment/define! environment + 'KEYWORD + (make-keyword-item classifier "c->k")) + environment) + '() + 'KEYWORD)) + +(define (compiler->keyword compiler) + (classifier->keyword (compiler->classifier compiler))) + +(define (classifier->form classifier) + `(,(classifier->keyword classifier))) + +(define (compiler->form compiler) + (classifier->form (compiler->classifier compiler))) + +(define (compiler->classifier compiler) + (lambda (form environment definition-environment) + definition-environment ;ignore + (make-expression-item + (lambda () (compiler form environment)) form))) + +;;;; Macrologies +;;; A macrology is a procedure that accepts a syntactic environment +;;; as an argument, producing a new syntactic environment that is an +;;; extension of the argument. + +(define (make-primitive-macrology generate-definitions) + (lambda (base-environment) + (let ((environment (top-level-syntactic-environment base-environment))) + (let ((define-classifier + (lambda (keyword classifier) + (syntactic-environment/define! + environment + keyword + (make-keyword-item classifier keyword))))) + (generate-definitions + define-classifier + (lambda (keyword compiler) + (define-classifier keyword (compiler->classifier compiler))))) + environment))) + +(define (make-expander-macrology object->classifier generate-definitions) + (lambda (base-environment) + (let ((environment (top-level-syntactic-environment base-environment))) + (generate-definitions + (lambda (keyword object) + (syntactic-environment/define! + environment + keyword + (make-keyword-item (object->classifier object environment) keyword))) + base-environment) + environment))) + +(define (make-sc-expander-macrology generate-definitions) + (make-expander-macrology sc-expander->classifier generate-definitions)) + +(define (make-er-expander-macrology generate-definitions) + (make-expander-macrology er-expander->classifier generate-definitions)) + +(define (compose-macrologies . macrologies) + (lambda (environment) + (do ((macrologies macrologies (cdr macrologies)) + (environment environment ((car macrologies) environment))) + ((null? macrologies) environment)))) + +;;;; Utilities + +(define (bind-variable! environment name) + (let ((rename (syntactic-environment/rename environment name))) + (syntactic-environment/define! environment + name + (make-variable-item rename)) + rename)) + +(define (reserve-names! names environment) + (let ((item (make-reserved-name-item))) + (for-each (lambda (name) + (syntactic-environment/define! environment name item)) + names))) + +(define (capture-syntactic-environment expander) + (classifier->form + (lambda (form environment definition-environment) + form ;ignore + (classify/form (expander environment) + environment + definition-environment)))) + +(define (unspecific-expression) + (compiler->form + (lambda (form environment) + form environment ;ignore + (output/unspecific)))) + +(define (unassigned-expression) + (compiler->form + (lambda (form environment) + form environment ;ignore + (output/unassigned)))) + +(define (syntax-quote expression) + `(,(compiler->keyword + (lambda (form environment) + environment ;ignore + (syntax-check '(KEYWORD DATUM) form) + (output/literal-quoted (cadr form)))) + ,expression)) + +(define (flatten-body-items items) + (append-map item->list items)) + +(define (item->list item) + (if (body-item? item) + (flatten-body-items (body-item/components item)) + (list item))) + +(define (output/let names values body) + (if (null? names) + body + (output/combination (output/lambda names body) values))) + +(define (output/letrec names values body) + (if (null? names) + body + (output/let + names + (map (lambda (name) name (output/unassigned)) names) + (output/sequence + (list (if (null? (cdr names)) + (output/assignment (car names) (car values)) + (let ((temps (map (make-name-generator) names))) + (output/let + temps + values + (output/sequence + (map output/assignment names temps))))) + body))))) + +(define (output/top-level-sequence expressions) + (if (null? expressions) + (output/unspecific) + (output/sequence expressions))) diff --git a/synrul.scm b/synrul.scm new file mode 100644 index 0000000..c23275f --- /dev/null +++ b/synrul.scm @@ -0,0 +1,327 @@ +;;; "synrul.scm" Rule-based Syntactic Expanders -*-Scheme-*- +;;; Copyright (c) 1989-91 Massachusetts Institute of Technology +;;; +;;; This material was developed by the Scheme project at the +;;; Massachusetts Institute of Technology, Department of Electrical +;;; Engineering and Computer Science. Permission to copy this +;;; software, to redistribute it, and to use it for any purpose is +;;; granted, subject to the following restrictions and understandings. +;;; +;;; 1. Any copy made of this software must include this copyright +;;; notice in full. +;;; +;;; 2. Users of this software agree to make their best efforts (a) to +;;; return to the MIT Scheme project any improvements or extensions +;;; that they make, so that these may be included in future releases; +;;; and (b) to inform MIT of noteworthy uses of this software. +;;; +;;; 3. All materials developed as a consequence of the use of this +;;; software shall duly acknowledge such use, in accordance with the +;;; usual standards of acknowledging credit in academic research. +;;; +;;; 4. MIT has made no warrantee or representation that the operation +;;; of this software will be error-free, and MIT is under no +;;; obligation to provide any services, by way of maintenance, update, +;;; or otherwise. +;;; +;;; 5. In conjunction with products arising from the use of this +;;; material, there shall be no use of the name of the Massachusetts +;;; Institute of Technology nor of any adaptation thereof in any +;;; advertising, promotional, or sales literature without prior +;;; written consent from MIT in each case. + +;;;; Rule-based Syntactic Expanders + +;;; See "Syntactic Extensions in the Programming Language Lisp", by +;;; Eugene Kohlbecker, Ph.D. dissertation, Indiana University, 1986. +;;; See also "Macros That Work", by William Clinger and Jonathan Rees +;;; (reference? POPL?). This implementation is derived from an +;;; implementation by Kent Dybvig, and includes some ideas from +;;; another implementation by Jonathan Rees. + +;;; The expansion of SYNTAX-RULES references the following keywords: +;;; ER-TRANSFORMER LAMBDA IF BEGIN SET! QUOTE +;;; and the following procedures: +;;; CAR CDR NULL? PAIR? EQUAL? MAP LIST CONS APPEND +;;; ILL-FORMED-SYNTAX +;;; it also uses the anonymous keyword SYNTAX-QUOTE. + +;;; For testing. +;;;(define (run-sr form) +;;; (expand/syntax-rules form (lambda (x) x) eq?)) + +(define (make-syntax-rules-macrology) + (make-er-expander-macrology + (lambda (define-classifier base-environment) + base-environment ;ignore + (define-classifier 'SYNTAX-RULES expand/syntax-rules)))) + +(define (expand/syntax-rules form rename compare) + (if (syntax-match? '((* IDENTIFIER) + ((IDENTIFIER . DATUM) EXPRESSION)) + (cdr form)) + (let ((keywords (cadr form)) + (clauses (cddr form))) + (if (let loop ((keywords keywords)) + (and (pair? keywords) + (or (memq (car keywords) (cdr keywords)) + (loop (cdr keywords))))) + (syntax-error "keywords list contains duplicates" keywords) + (let ((r-form (rename 'FORM)) + (r-rename (rename 'RENAME)) + (r-compare (rename 'COMPARE))) + `(,(rename 'ER-TRANSFORMER) + (,(rename 'LAMBDA) + (,r-form ,r-rename ,r-compare) + ,(let loop ((clauses clauses)) + (if (null? clauses) + `(,(rename 'ILL-FORMED-SYNTAX) ,r-form) + (let ((pattern (caar clauses))) + (let ((sids + (parse-pattern rename compare keywords + pattern r-form))) + `(,(rename 'IF) + ,(generate-match rename compare keywords + r-rename r-compare + pattern r-form) + ,(generate-output rename compare r-rename + sids (cadar clauses) + syntax-error) + ,(loop (cdr clauses)))))))))))) + (ill-formed-syntax form))) + +(define (parse-pattern rename compare keywords pattern expression) + (let loop + ((pattern pattern) + (expression expression) + (sids '()) + (control #f)) + (cond ((identifier? pattern) + (if (memq pattern keywords) + sids + (cons (make-sid pattern expression control) sids))) + ((and (or (zero-or-more? pattern rename compare) + (at-least-one? pattern rename compare)) + (null? (cddr pattern))) + (let ((variable ((make-name-generator) 'CONTROL))) + (loop (car pattern) + variable + sids + (make-sid variable expression control)))) + ((pair? pattern) + (loop (car pattern) + `(,(rename 'CAR) ,expression) + (loop (cdr pattern) + `(,(rename 'CDR) ,expression) + sids + control) + control)) + (else sids)))) + +(define (generate-match rename compare keywords r-rename r-compare + pattern expression) + (letrec + ((loop + (lambda (pattern expression) + (cond ((identifier? pattern) + (if (memq pattern keywords) + (let ((temp (rename 'TEMP))) + `((,(rename 'LAMBDA) + (,temp) + (,(rename 'IF) + (,(rename 'IDENTIFIER?) ,temp) + (,r-compare ,temp + (,r-rename ,(syntax-quote pattern))) + #f)) + ,expression)) + `#t)) + ((and (zero-or-more? pattern rename compare) + (null? (cddr pattern))) + (do-list (car pattern) expression)) + ((and (at-least-one? pattern rename compare) + (null? (cddr pattern))) + `(,(rename 'IF) (,(rename 'NULL?) ,expression) + #F + ,(do-list (car pattern) expression))) + ((pair? pattern) + (let ((generate-pair + (lambda (expression) + (conjunction + `(,(rename 'PAIR?) ,expression) + (conjunction + (loop (car pattern) + `(,(rename 'CAR) ,expression)) + (loop (cdr pattern) + `(,(rename 'CDR) ,expression))))))) + (if (identifier? expression) + (generate-pair expression) + (let ((temp (rename 'TEMP))) + `((,(rename 'LAMBDA) (,temp) ,(generate-pair temp)) + ,expression))))) + ((null? pattern) + `(,(rename 'NULL?) ,expression)) + (else + `(,(rename 'EQUAL?) ,expression + (,(rename 'QUOTE) ,pattern)))))) + (do-list + (lambda (pattern expression) + (let ((r-loop (rename 'LOOP)) + (r-l (rename 'L)) + (r-lambda (rename 'LAMBDA))) + `(((,r-lambda + (,r-loop) + (,(rename 'BEGIN) + (,(rename 'SET!) + ,r-loop + (,r-lambda + (,r-l) + (,(rename 'IF) + (,(rename 'NULL?) ,r-l) + #T + ,(conjunction + `(,(rename 'PAIR?) ,r-l) + (conjunction (loop pattern `(,(rename 'CAR) ,r-l)) + `(,r-loop (,(rename 'CDR) ,r-l))))))) + ,r-loop)) + #F) + ,expression)))) + (conjunction + (lambda (predicate consequent) + (cond ((eq? predicate #T) consequent) + ((eq? consequent #T) predicate) + (else `(,(rename 'IF) ,predicate ,consequent #F)))))) + (loop pattern expression))) + +(define (generate-output rename compare r-rename sids template syntax-error) + (let loop ((template template) (ellipses '())) + (cond ((identifier? template) + (let ((sid + (let loop ((sids sids)) + (and (not (null? sids)) + (if (eq? (sid-name (car sids)) template) + (car sids) + (loop (cdr sids))))))) + (if sid + (begin + (add-control! sid ellipses syntax-error) + (sid-expression sid)) + `(,r-rename ,(syntax-quote template))))) + ((or (zero-or-more? template rename compare) + (at-least-one? template rename compare)) + (optimized-append rename compare + (let ((ellipsis (make-ellipsis '()))) + (generate-ellipsis rename + ellipsis + (loop (car template) + (cons ellipsis + ellipses)))) + (loop (cddr template) ellipses))) + ((pair? template) + (optimized-cons rename compare + (loop (car template) ellipses) + (loop (cdr template) ellipses))) + (else + `(,(rename 'QUOTE) ,template))))) + +(define (add-control! sid ellipses syntax-error) + (let loop ((sid sid) (ellipses ellipses)) + (let ((control (sid-control sid))) + (cond (control + (if (null? ellipses) + (syntax-error "missing ellipsis in expansion" #f) + (let ((sids (ellipsis-sids (car ellipses)))) + (cond ((not (memq control sids)) + (set-ellipsis-sids! (car ellipses) + (cons control sids))) + ((not (eq? control (car sids))) + (syntax-error "illegal control/ellipsis combination" + control sids))))) + (loop control (cdr ellipses))) + ((not (null? ellipses)) + (syntax-error "extra ellipsis in expansion" #f)))))) + +(define (generate-ellipsis rename ellipsis body) + (let ((sids (ellipsis-sids ellipsis))) + (let ((name (sid-name (car sids))) + (expression (sid-expression (car sids)))) + (cond ((and (null? (cdr sids)) + (eq? body name)) + expression) + ((and (null? (cdr sids)) + (pair? body) + (pair? (cdr body)) + (eq? (cadr body) name) + (null? (cddr body))) + `(,(rename 'MAP) ,(car body) ,expression)) + (else + `(,(rename 'MAP) (,(rename 'LAMBDA) ,(map sid-name sids) ,body) + ,@(map sid-expression sids))))))) + +(define (zero-or-more? pattern rename compare) + (and (pair? pattern) + (pair? (cdr pattern)) + (identifier? (cadr pattern)) + (compare (cadr pattern) (rename '...)))) + +(define (at-least-one? pattern rename compare) +;;; (and (pair? pattern) +;;; (pair? (cdr pattern)) +;;; (identifier? (cadr pattern)) +;;; (compare (cadr pattern) (rename '+))) + pattern rename compare ;ignore + #f) + +(define (optimized-cons rename compare a d) + (cond ((and (pair? d) + (compare (car d) (rename 'QUOTE)) + (pair? (cdr d)) + (null? (cadr d)) + (null? (cddr d))) + `(,(rename 'LIST) ,a)) + ((and (pair? d) + (compare (car d) (rename 'LIST)) + (list? (cdr d))) + `(,(car d) ,a ,@(cdr d))) + (else + `(,(rename 'CONS) ,a ,d)))) + +(define (optimized-append rename compare x y) + (if (and (pair? y) + (compare (car y) (rename 'QUOTE)) + (pair? (cdr y)) + (null? (cadr y)) + (null? (cddr y))) + x + `(,(rename 'APPEND) ,x ,y))) + +(define sid-type + (make-record-type "sid" '(NAME EXPRESSION CONTROL OUTPUT-EXPRESSION))) + +(define make-sid + (record-constructor sid-type '(NAME EXPRESSION CONTROL))) + +(define sid-name + (record-accessor sid-type 'NAME)) + +(define sid-expression + (record-accessor sid-type 'EXPRESSION)) + +(define sid-control + (record-accessor sid-type 'CONTROL)) + +(define sid-output-expression + (record-accessor sid-type 'OUTPUT-EXPRESSION)) + +(define set-sid-output-expression! + (record-modifier sid-type 'OUTPUT-EXPRESSION)) + +(define ellipsis-type + (make-record-type "ellipsis" '(SIDS))) + +(define make-ellipsis + (record-constructor ellipsis-type '(SIDS))) + +(define ellipsis-sids + (record-accessor ellipsis-type 'SIDS)) + +(define set-ellipsis-sids! + (record-modifier ellipsis-type 'SIDS)) @@ -0,0 +1,425 @@ +;"t3.init" Initialization file for SLIB for T3.1. -*-scheme-*- +;Copyright (C) 1991, 1992 David Carlton & Stephen Bevan +;Copyright 1993 F. Javier Thayer. +;Copyright (C) 1991, 1992, 1993 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;; File has T syntax, and should be compiled in standard-env. +;;; Compiled file has .so suffix. +;;; File (or compiled version) should be loaded into scheme-env. + +;;; This is provided with ABSOLUTELY NO GUARANTEE. +(herald t3) + +(define (software-type) 'UNIX) + +(define (scheme-implementation-type) 'T) + +(define (scheme-implementation-version) "3.1") + +;;; (implementation-vicinity) should be defined to be the pathname of +;;; the directory where any auxillary files to your Scheme +;;; implementation reside. It is settable. + +(define implementation-vicinity + (make-simple-switch 'implementation-vicinity + (lambda (x) (or (string? x) (false? x))) + '#f)) +(set (implementation-vicinity) "/usr/local/lib/tsystem/") + +;;; (library-vicinity) should be defined to be the pathname of the +;;; directory where files of Scheme library functions reside. It is settable. + +(define library-vicinity + (make-simple-switch 'library-vicinity + (lambda (x) (or (string? x) (false? x))) + '#f)) +(set (library-vicinity) "/usr/local/lib/slib/") +;;Obviously put your value here. + +;;; *FEATURES* should be set to a list of symbols describing features +;;; of this implementation. See Template.scm for the list of feature +;;; names. + +(define *features* + '( + source ;can load scheme source files + ;(slib:load-source "filename") + compiled ;can load compiled files + ;(slib:load-compiled "filename") + rev3-report + rev4-optional-procedures + rev3-procedures + rev2-procedures + multiarg/and- + multiarg-apply + rationalize + object-hash + delay + i/o-redirection + char-ready? + with-file + transcript + full-continuation + pretty-print + format + trace ;has macros: TRACE and UNTRACE + program-arguments + )) + +(define substring + (let ((primitive-substring (*value standard-env 'substring))) + (lambda (string start end) + (primitive-substring string start (max 0 (- end 1)))))) + +; Modify substring as T's substring takes (start,count) instead of +; (start,end) + +(set (syntax-table-entry (env-syntax-table scheme-env) 'require) '#f) + +; Turn off the macro REQUIRE so that it can be rebound as a function +; later. + +; extend <, >, <= and >= so that they take more than two arguments. + +(define < + (let ((primitive< (*value standard-env '<))) + (labels ((v (lambda (a b . rest) + (if (null? rest) + (primitive< a b) + (and (primitive< a b) + (apply v b (car rest) (cdr rest))))))) + v))) + +(define > + (let ((primitive> (*value standard-env '>))) + (labels ((v (lambda (a b . rest) + (if (null? rest) + (primitive> a b) + (and (primitive> a b) + (apply v b (car rest) (cdr rest))))))) + v))) + +(define <= + (let ((primitive<= (*value standard-env '<=))) + (labels ((v (lambda (a b . rest) + (if (null? rest) + (primitive<= a b) + (and (primitive<= a b) + (apply v b (car rest) (cdr rest))))))) + v))) + +(define >= + (let ((primitive>= (*value standard-env '>=))) + (labels ((v (lambda (a b . rest) + (if (null? rest) + (primitive>= a b) + (and (primitive>= a b) + (apply v b (car rest) (cdr rest))))))) + v))) + +(define = + (let ((primitive= (*value standard-env '=))) + (labels ((v (lambda (a b . rest) + (if (null? rest) + (primitive= a b) + (and (primitive= a b) + (apply v b (car rest) (cdr rest))))))) + v))) + +(define gcd + (let ((prim (*value standard-env 'gcd))) + (labels ((v (lambda x + (cond ((null? x) 0) + ((= (length x) 1) (car x)) + ('#t (prim (car x) (apply v (cdr x)))))))) + v))) + +(define list? (*value standard-env 'proper-list?)) + +(define program-arguments command-line) + +;;; (OUTPUT-PORT-WIDTH <port>) +(define output-port-width + (lambda x + (if (null? x) (line-length (standard-input)) + (line-length (car x))))) + +;;; (OUTPUT-PORT-HEIGHT <port>) +(define (output-port-height . arg) 24) + +;;; (CURRENT-ERROR-PORT) +(define current-error-port + (let ((port (current-output-port))) + (lambda () port))) + +;;; (TMPNAM) makes a temporary file name. +(define tmpnam + (let ((cntr 100)) + (lambda () (set! cntr (+ 1 cntr)) + (let ((tmp (string-append "slib_" (number->string cntr)))) + (if (file-exists? tmp) (tmpnam) tmp))))) + +(define delete-file file-delete) + +;;; CHAR-CODE-LIMIT is one greater than the largest integer which can +;;; be returned by CHAR->INTEGER. +(define char-code-limit 256) + +;;; MOST-POSITIVE-FIXNUM is used in modular.scm +;;; T already has it. + +;;; Return argument +(define (identity x) x) + +;;; If your implementation provides eval, SLIB:EVAL is single argument +;;; eval using the top-level (user) environment. +(define (slib:eval form) (eval form scheme-env)) + +;;; If your implementation provides R4RS macros: +;(define macro:eval slib:eval) +;(define macro:load load) + +(define *defmacros* + (list (cons 'defmacro + (lambda (name parms . body) + `(set! *defmacros* (cons (cons ',name (lambda ,parms ,@body)) + *defmacros*)))))) +(define (defmacro? m) (and (assq m *defmacros*) #t)) + +(define (macroexpand-1 e) + (if (pair? e) (let ((a (car e))) + (cond ((symbol? a) (set! a (assq a *defmacros*)) + (if a (apply (cdr a) (cdr e)) e)) + (else e))) + e)) + +(define (macroexpand e) + (if (pair? e) (let ((a (car e))) + (cond ((symbol? a) + (set! a (assq a *defmacros*)) + (if a (macroexpand (apply (cdr a) (cdr e))) e)) + (else e))) + e)) + +(define gentemp + (let ((*gensym-counter* -1)) + (lambda () + (set! *gensym-counter* (+ *gensym-counter* 1)) + (string->symbol + (string-append "slib:G" (number->string *gensym-counter*)))))) + +(define base:eval slib:eval) +(define (defmacro:eval x) (base:eval (defmacro:expand* x))) +(define (defmacro:expand* x) + (require 'defmacroexpand) (apply defmacro:expand* x '())) + +(define (defmacro:load <pathname>) + (slib:eval-load <pathname> defmacro:eval)) + +(define (slib:eval-load <pathname> evl) + (if (not (file-exists? <pathname>)) + (set! <pathname> (string-append <pathname> (scheme-file-suffix)))) + (call-with-input-file <pathname> + (lambda (port) + (let ((old-load-pathname *load-pathname*)) + (set! *load-pathname* <pathname>) + (do ((o (read port) (read port))) + ((eof-object? o)) + (evl o)) + (set! *load-pathname* old-load-pathname))))) + +;;; define an error procedure for the library +(define slib:error error) + +;;; define these as appropriate for your system. +(define slib:tab #\tab) +(define slib:form-feed #\form) + +;;; Define these if your implementation's syntax can support it and if +;;; they are not already defined. + +;(define (1+ n) (+ n 1)) +(define (1- n) (+ n -1)) +;(define (-1+ n) (+ n -1)) + +(define program-vicinity + (make-simple-switch 'program-vicinity + (lambda (x) (or (string? x) (false? x))) + '#f)) + +(define in-vicinity string-append) + +;;; Define SLIB:EXIT to be the implementation procedure to exit or +;;; return if exitting not supported. +(define slib:exit (lambda args (exit)) + +(define (string . args) (apply string-append (map char->string args))) + +(define make-string + (let ((t:make-string (*value standard-env 'make-string))) + (lambda (a . b) + (let ((str (t:make-string a))) + (if b (map-string! (lambda (x) (ignore x) (car b)) str) str))))) + +(define (string>? a b) + (labels ((aux + (lambda (n a b) + ;;start off with n<=(string-length b) and n<=(string-length a) + ;;a,b coincide for chars <n + (cond ((= (string-length a) n) (< n (string-length b))) + ;;now (< n (string-length a)) + ((= (string-length b) n) '#f) + ;;now (< n (string-length a)) + ((char=? (nthchar a n) (nthchar b n) ) (aux (+ 1 n) a b)) + ('#t (char<? (nthchar b n) (nthchar a n))))))) + (aux 0 a b))) + +(define (string<? a b) (string>? b a)) +(define (string<=? a b) (not (string>? a b))) +(define (string>=? a b) (not (string<? a b))) + +(define (string-ci<? a b) + (string<? (string-upcase a) (string-upcase b))) + +(define (string-ci>? a b) + (string>? (string-upcase a) (string-upcase b))) + +(define (string-ci<=? a b) + (string<=? (string-upcase a) (string-upcase b))) + +(define (string-ci>=? a b) + (string>=? (string-upcase a) (string-upcase b))) + +;;; FORCE-OUTPUT flushes any pending output on optional arg output port +;;; use this definition if your system doesn't have such a procedure. +;;; T already has it, but requires 1 argument. + +(define force-output + (let ((t:force-output (*value standard-env 'force-output))) + (lambda x + (if x + (t:force-output (car x)) + (t:force-output (current-output-port)))))) + +;;; CALL-WITH-INPUT-STRING and CALL-WITH-OUTPUT-STRING are the string +;;; port versions of CALL-WITH-*PUT-FILE. +(define (call-with-output-string proc) + (with-output-to-string var (proc var))) + +(define (call-with-input-string string proc) + (with-input-from-string (variable string) (proc variable))) + +(define (string->number s . x) + (let ((base (if x (car x) 10)) + (s (string-upcase s))) + (or (mem? = base '(8 10 16)) + (error (format (current-error-port) "Bad radix ~A" base))) + (if (= (string-length s) 0) '() + (let ((char->number + (lambda (ch) + (cdr (ass char=? ch + '((#\0 . 0) + (#\1 . 1) (#\2 . 2) (#\3 . 3) (#\4 . 4) + (#\5 . 5) (#\6 . 6) (#\7 . 7) (#\8 . 8) + (#\9 . 9) (#\A . 10) (#\B . 11) (#\C . 12) + (#\D . 13) (#\E . 14) (#\F . 15))))))) + (catch not-num + (iterate loop ((pos (- (string-length s) 1)) + (power 1) (accum 0)) + (if (< pos 0) accum + (let ((num (char->number (string-ref s pos)))) + (or num (not-num '())) + (or (< num base) (not-num '())) + (loop (- pos 1) + (* power base) + (+ accum (* num power))))))))))) + +(define (number->string n . x) + (let ((rad (if (car x) (car x) 10))) + (format nil + (case rad + ((8) "~O") + ((10) "~D") + ((16) "~X") + (else (error (format (current-error-port) + "Bad radix ~A" (car x))))) + n))) + +(define (inexact? f) + (float? f)) + +(define (exact? f) + (not (inexact? f))) + +(define exact->inexact ->float) + +(define peek-char + (let ((t:peek-char (*value standard-env 'peek-char))) + (lambda p + (let ((port (if p (car p) (current-input-port)))) + (t:peek-char port))))) + +;;;(set ((*value scheme-env 'standard-early-binding-env) 'load) '#f) +;;;(set ((*value scheme-env 'standard-early-binding-env) 'substring) '#f) +(set ((*value scheme-env 'standard-early-binding-env) 'less?) '#f) +(set ((*value scheme-env 'standard-early-binding-env) 'greater?) '#f) +(set ((*value scheme-env 'standard-early-binding-env) 'not-less?) '#f) +(set ((*value scheme-env 'standard-early-binding-env) 'not-greater?) '#f) +(set ((*value scheme-env 'standard-early-binding-env) 'number-equal?) '#f) +(set ((*value scheme-internal-env 'standard-early-binding-env) 'list?) '#f) + +(set ((*value t-implementation-env 'SOURCE-FILE-EXTENSION)) 'scm) + +;;; Here for backward compatability +(define (scheme-file-suffix) "") + +(define load + (let ((t:load (*value standard-env 'load))) + (lambda (filespec . x) + (apply t:load (->filename filespec) x)))) + +;;; (SLIB:LOAD-SOURCE "foo") should load "foo.scm" or with whatever +;;; suffix all the module files in SLIB have. See feature 'SOURCE. + +(define slib:load-source load) + +;;; (SLIB:LOAD-COMPILED "foo") should load the file that was produced +;;; by compiling "foo.scm" if this implementation can compile files. +;;; See feature 'COMPILED. + +(define slib:load-compiled load) + +;;; At this point SLIB:LOAD must be able to load SLIB files. + +(define slib:load slib:load-source) + +(slib:load (in-vicinity (library-vicinity) "require") scheme-env) + +;;;(define scheme-read-table +;;; (make-read-table standard-read-table 'modified-read-table)) +;;; +;;;(set (read-table-entry scheme-read-table '#\#) +;;; (lambda (p ch rtable) +;;; (ignore ch) (ignore rtable) +;;; ((*value scheme-env 'string->number) +;;; (symbol->string (read-refusing-eof p)) 16))) +;;; +;;;(set (port-read-table (standard-input)) scheme-read-table) + +; eof diff --git a/tek40.scm b/tek40.scm new file mode 100644 index 0000000..f45a1fa --- /dev/null +++ b/tek40.scm @@ -0,0 +1,92 @@ +;"tek40.scm", Tektronix 4000 series graphics support in Scheme. +;Copyright (C) 1992, 1994 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;THIS FILE NEEDS MORE WORK. + +;The Tektronix 4000 series graphics protocol gives the user a 1024 by +;1024 square drawing area. The origin is in the lower left corner of +;the screen. Increasing y is up and increasing x is to the right. + +;The graphics control codes are sent over the current-output-port and +;can be mixed with regular text and ANSI or other terminal control +;sequences. + +; (tek40:init) procedure + +(define (tek40:init) 'noop) + +(define esc-string (string (integer->char #o33))) + +(define tek40:graphics-str + (string-append + (string slib:form-feed) + esc-string (string (integer->char #o14)) + ;; clear the screen + )) + +(define (tek40:graphics) (display tek40:graphics-str) (force-output)) + +(define (tek40:text) + (tek40:move 0 12) + (write-char (integer->char #o37))) + +(define (tek40:linetype linetype) + (cond ((or (negative? linetype) (> linetype 15)) + (slib:error "bad linetype" linetype)) + (else + (display esc-string) + (write-char (integer->char (+ (char->integer #\`) linetype)))))) + +(define (tek40:move x y) + (write-char (integer->char #o35)) + (tek40:draw x y)) + +(define (tek40:draw x y) + (display (string + (integer->char (+ #x20 (quotient y 32))) + (integer->char (+ #x60 (remainder y 32))) + (integer->char (+ #x20 (quotient x 32))) + (integer->char (+ #x40 (remainder x 32)))))) + +(define (tek40:put-text x y str) + (tek40:move x (+ y -11)) + (write-char (integer->char #o37)) + (display str)) + +(define (tek40:reset) (display tek40:graphics-str) (force-output)) + +(define (tek40:test) + (tek40:init) +; (tek40:reset) + (tek40:graphics) + (tek40:linetype 0) + (tek40:move 100 100) + (tek40:draw 200 100) + (tek40:draw 200 200) + (tek40:draw 100 200) + (tek40:draw 100 100) + (do ((i 0 (+ 1 i))) + ((> i 15)) + (tek40:linetype i) + (tek40:move (+ (* 50 i) 100) 100) + (tek40:put-text (+ (* 50 i) 100) 100 (number->string i)) + (tek40:move (+ (* 50 i) 100) 100) + (tek40:draw (+ (* 50 i) 200) 200)) + (tek40:linetype 0) + (tek40:text)) diff --git a/tek41.scm b/tek41.scm new file mode 100644 index 0000000..988f8ea --- /dev/null +++ b/tek41.scm @@ -0,0 +1,147 @@ +;"tek41.scm", Tektronix 4100 series graphics support in Scheme. +;Copyright (C) 1992, 1994 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;THIS FILE NEEDS MORE WORK. Let me know if you test or fix it. + +;The graphics control codes are sent over the current-output-port and +;can be mixed with regular text and ANSI or other terminal control +;sequences. + +(define esc-string (string (integer->char #o33))) + +(define tek41:init + (string-append + esc-string "%!0" + ;;1. set tek mode + esc-string "MN0" + ;;2. set character path to 0 (characters placed equal to rotation) + esc-string "MCB7C;" + ;;3. set character size to 59 height + esc-string "MQ1" + ;;4. set character precision to string + esc-string "MT1" + ;;5. set character text index to 1 + esc-string "MG1" + ;;6. set character write mode to overstrike + esc-string "RK!" + ;;7. clear the view + esc-string "SK!" + ;;8. clear the segments + esc-string "LZ" + ;;9. clear the dialog buffer + esc-string "%!1" + ;;10. set ansi mode + )) + +(define (tek41:init) (display tek41:init-str) (force-output)) + +(define (tek41:reset) + (string-append + esc-string "%!0" + ;;1. set tek mode + esc-string "LZ" + ;;2. clear the dialog buffer + esc-string "%!1" + ;;3. set ansi mode + )) + +(define (tek41:reset) (display tek41:reset-str) (force-output)) + +(define tek41:graphics-str + (string-append + esc-string "%!0" + ;;1. set tek mode + esc-string (string (integer->char #o14)) + ;;2. clear the screen + esc-string "LV0" + ;;3. set dialog area invisible + )) + +(define (tek41:graphics) (display tek41:graphics-str) (force-output)) + +(define tek41:text-str + (string-append + esc-string "LV1" + ;;1. set dialog area visible + esc-string "%!1" + ;;2. set ansi mode + )) + +(define (tek41:text) (display tek41:text-str) (force-output)) + +(define tek41:move-str + (string-append esc-string "LF")) + +(define (tek41:move x y) + (display tek41:move-str) + (tek41:encode-x-y x y) + (force-output)) + +(define tek41:draw-str + (string-append esc-string "LG")) + +(define (tek41:draw x y) + (display tek41:draw-str) + (tek41:encode-x-y x y) + (force-output)) + +(define tek41:set-marker-str (string-append esc-string "MM")) +(define tek41:draw-marker-str (string-append esc-string "LH")) + +(define (tek41:point x y number) + (display tek41:set-marker-str) + (tek41:encode-int (remainder (max number 0) 11)) + (display tek41:draw-marker-str) + (tek41:encode-x-y x y) + (force-output)) + +(define (tek41:encode-x-y x y) + (let ((hix (+ (quotient x 128) 32)) + (lox (+ (modulo (quotient x 4) 32) 64)) + (hiy (+ (quotient y 128) 32)) + (loy (+ (modulo (quotient y 4) 32) 96)) + (eb (+ (* (modulo y 4) 4) (modulo x 4) 96))) + (if (positive? hiy) (write-char (integer->char hiy))) + (if (positive? eb) (write-char (integer->char eb))) + (if (positive? (+ loy eb hix)) (write-char (integer->char loy))) + (if (positive? hix) (write-char (integer->char hix))) + (write-char (integer->char lox)))) + +(define (tek41:encode-int number) + (let* ((mag (abs number)) + (hi1 (+ (quotient mag 1024) 64)) + (hi2 (+ (modulo (quotient mag 16) 64) 64)) + (lo (+ (modulo mag 16) 32))) + (if (>= number 0) (set! lo (+ lo 16))) + (if (not (= hi1 64)) (write-char (integer->char hi1))) + (if (or (not (= hi2 64)) + (not (= hi1 64))) + (write-char (integer->char hi2))) + (write-char (integer->char lo)))) + +(define (test) + (tek41:init) + (tek41:reset) + (tek41:graphics) + (do ((i 0 (+ 1 i))) + ((> i 15)) + (tek41:linetype i) + (tek41:move (+ (* 200 i) 1000) 1000) + (tek41:draw (+ (* 200 i) 2000) 2000)) + (tek41:text)) diff --git a/time.scm b/time.scm new file mode 100644 index 0000000..7ddf524 --- /dev/null +++ b/time.scm @@ -0,0 +1,158 @@ +;;;; "time.scm" Posix time conversion routines +;;; Copyright (C) 1994 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(define time:daylight 0) +(define *timezone* 0) +(define time:tzname #("GMT" "GDT")) + +(define (time:tzset) + (set! time:daylight 1) + (set! *timezone* (* 5 60 60)) + (set! time:tzname #("EST" "EDT"))) + +;;; No, it doesn't do leap seconds. If you want to add it, go ahead. + +;;; Returns the `struct tm' representation of T, +;;; offset TM_GMTOFF seconds east of UCT. +(define (time:split t tm_isdst tm_gmtoff tm_zone) + (set! t (difftime t tm_gmtoff)) + (let* ((days-in-year (lambda (year) + (if (and (zero? (remainder year 4)) + (or (not (zero? (remainder year 100))) + (zero? (remainder year 400)))) + 366 365))) + (days/month #(#(31 28 31 30 31 30 31 31 30 31 30 31) ; Normal years. + #(31 29 31 30 31 30 31 31 30 31 30 31))) ; Leap years. + (SECS/HOUR (* 60 60)) + (SECS/DAY (* SECS/HOUR 24)) + (secs (modulo t SECS/DAY)) + (days (+ (quotient t SECS/DAY) + (if (and (negative? t) (positive? secs)) -1 0)))) + (let ((tm_hour (quotient secs SECS/HOUR)) + (secs (remainder secs SECS/HOUR)) + (tm_wday (modulo (+ 4 days) 7))) ; January 1, 1970 was a Thursday. + (let loop ((tm_year 1970) + (tm_yday days)) + (let ((diy (days-in-year tm_year))) + (cond + ((negative? tm_yday) (loop (+ -1 tm_year) (+ tm_yday diy))) + ((>= tm_yday diy) (loop (+ 1 tm_year) (- tm_yday diy))) + (else + (let* ((mv (vector-ref days/month (- diy 365)))) + (do ((tm_mon 0 (+ 1 tm_mon)) + (tm_mday tm_yday (- tm_mday (vector-ref mv tm_mon)))) + ((< tm_mday (vector-ref mv tm_mon)) + (vector + (remainder secs 60) ; Seconds. [0-61] (2 leap seconds) + (quotient secs 60) ; Minutes. [0-59] + tm_hour ; Hours. [0-23] + (+ tm_mday 1) ; Day. [1-31] + tm_mon ; Month. [0-11] + (- tm_year 1900) ; Year - 1900. + tm_wday ; Day of week. [0-6] + tm_yday ; Days in year. [0-365] + tm_isdst ; DST. [-1/0/1] + tm_gmtoff ; Seconds west of UTC. + tm_zone ; Timezone abbreviation. + ))))))))))) + +(define (time:gmtime t) + (time:split t 0 0 "GMT")) + +(define (time:localtime t) + (time:tzset) + (time:split t time:daylight *timezone* + (vector-ref time:tzname time:daylight))) + +(define time:year-70 + (let* ((t (current-time))) + (offset-time (offset-time t (- (difftime t 0))) (* -70 32140800)))) + +(define (time:invert decoder target) + (let* ((times #(1 60 3600 86400 2678400 32140800)) + (trough ; rough time for target + (do ((i 5 (+ i -1)) + (trough time:year-70 + (offset-time trough (* (vector-ref target i) + (vector-ref times i))))) + ((negative? i) trough)))) +;;; (print 'trough trough 'target target) + (let loop ((guess trough) + (j 0) + (guess-tm (decoder trough))) +;;; (print 'guess guess 'guess-tm guess-tm) + (do ((i 5 (+ i -1)) + (rough time:year-70 + (offset-time rough (* (vector-ref guess-tm i) + (vector-ref times i)))) + (sign (let ((d (- (vector-ref target 5) + (vector-ref guess-tm 5)))) + (and (not (zero? d)) d)) + (or sign + (let ((d (- (vector-ref target i) + (vector-ref guess-tm i)))) + (and (not (zero? d)) d))))) + ((negative? i) + (let* ((distance (abs (- trough rough)))) + (cond ((and (zero? distance) sign) +;;; (print "trying to jump") + (set! distance (if (negative? sign) -86400 86400))) + ((and sign (negative? sign)) (set! distance (- distance)))) + (set! guess (offset-time guess distance)) +;;; (print 'distance distance 'sign sign) + (cond ((zero? distance) guess) + ((> j 5) #f) ;to prevent inf loops. + (else + (loop guess + (+ 1 j) + (decoder guess)))))))))) + +(define (time:mktime time) + (time:tzset) + (time:invert localtime time)) + +(define (time:asctime decoded) + (let ((days #("Sun" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat")) + (months #("Jan" "Feb" "Mar" "Apr" "May" "Jun" + "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")) + (number->2digits + (lambda (n ch) + (set! n (number->string n)) + (if (= 1 (string-length n)) + (string-append ch n) + n)))) + (string-append + (vector-ref days (vector-ref decoded 6)) " " + (vector-ref months (vector-ref decoded 4)) " " + (number->2digits (vector-ref decoded 3) " ") " " + (number->2digits (vector-ref decoded 2) "0") ":" + (number->2digits (vector-ref decoded 1) "0") ":" + (number->2digits (vector-ref decoded 0) "0") " " + (number->string (+ 1900 (vector-ref decoded 5))) + (string #\newline)))) + +(define (time:ctime time) + (time:asctime (time:localtime time))) + +(define tzset time:tzset) +(define gmtime time:gmtime) +(define localtime time:localtime) +(define mktime time:mktime) +(define asctime time:asctime) +(define ctime time:ctime) diff --git a/trace.scm b/trace.scm new file mode 100644 index 0000000..d595277 --- /dev/null +++ b/trace.scm @@ -0,0 +1,106 @@ +;;;; "trace.scm" Utility macros for tracing in Scheme. +;;; Copyright (C) 1991, 1992, 1993, 1994, 1995 Aubrey Jaffer. +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'qp) ;for the qp printer. +(define debug:indent 0) + +(define debug:tracef + (let ((null? null?) ;These bindings are so that + (not not) ;tracef will not trace parts + (car car) (cdr cdr) ;of itself. + (eq? eq?) (+ +) (zero? zero?) (modulo modulo) + (apply apply) (display display) (qpn qpn)) + (lambda (function . optname) + (set! debug:indent 0) + (let ((name (if (null? optname) function (car optname)))) + (lambda args + (cond ((and (not (null? args)) + (eq? (car args) 'debug:untrace-object) + (null? (cdr args))) + function) + (else + (do ((i debug:indent (+ -1 i))) ((zero? i)) (display #\ )) + (apply qpn "CALLED" name args) + (set! debug:indent (modulo (+ 1 debug:indent) 8)) + (let ((ans (apply function args))) + (set! debug:indent (modulo (+ -1 debug:indent) 8)) + (do ((i debug:indent (+ -1 i))) ((zero? i)) (display #\ )) + (qpn "RETURNED" name ans) + ans)))))))) + +;;; the reason I use a symbol for debug:untrace-object is so +;;; that functions can still be untraced if this file is read in twice. + +(define (debug:untracef function) + (set! debug:indent 0) + (function 'debug:untrace-object)) + +;;;;The trace: functions wrap around the debug: functions to provide +;;; niceties like keeping track of traced functions and dealing with +;;; redefinition. + +(require 'alist) +(define trace:adder (alist-associator eq?)) +(define trace:deler (alist-remover eq?)) + +(define *traced-procedures* '()) +(define (trace:tracef fun sym) + (cond ((not (procedure? fun)) + (display "WARNING: not a procedure " (current-error-port)) + (display sym (current-error-port)) + (newline (current-error-port)) + (set! *traced-procedures* (trace:deler *traced-procedures* sym)) + fun) + (else + (let ((p (assq sym *traced-procedures*))) + (cond ((and p (eq? (cdr p) fun)) + fun) + (else + (let ((tfun (debug:tracef fun sym))) + (set! *traced-procedures* + (trace:adder *traced-procedures* sym tfun)) + tfun))))))) + +(define (trace:untracef fun sym) + (let ((p (assq sym *traced-procedures*))) + (set! *traced-procedures* (trace:deler *traced-procedures* sym)) + (cond ((not (procedure? fun)) fun) + ((not p) fun) + ((eq? (cdr p) fun) + (debug:untracef fun)) + (else fun)))) + +(define tracef debug:tracef) +(define untracef debug:untracef) + +;;;; Finally, the macros trace and untrace + +(defmacro trace xs + (if (null? xs) + `(begin ,@(map (lambda (x) `(set! ,x (trace:tracef ,x ',x))) + (map car *traced-procedures*)) + (map car *traced-procedures*)) + `(begin ,@(map (lambda (x) `(set! ,x (trace:tracef ,x ',x))) xs)))) +(defmacro untrace xs + (if (null? xs) + (slib:eval + `(begin ,@(map (lambda (x) `(set! ,x (trace:untracef ,x ',x))) + (map car *traced-procedures*)) + '',(map car *traced-procedures*))) + `(begin ,@(map (lambda (x) `(set! ,x (trace:untracef ,x ',x))) xs)))) diff --git a/tree.scm b/tree.scm new file mode 100644 index 0000000..f400d1b --- /dev/null +++ b/tree.scm @@ -0,0 +1,62 @@ +;;"tree.scm" Implementation of COMMON LISP tree functions for Scheme +; Copyright 1993, 1994 David Love (d.love@dl.ac.uk) +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;; Deep copy of the tree -- new one has all new pairs. (Called +;; tree-copy in Dybvig.) +(define (tree:copy-tree tree) + (if (pair? tree) + (cons (tree:copy-tree (car tree)) + (tree:copy-tree (cdr tree))) + tree)) + +;; Substitute occurrences of old equal? to new in tree. +;; Similar to tree walks in SICP without the internal define. +(define (tree:subst new old tree) + (let walk ((tree tree)) + (cond ((equal? old tree) + new) + ((pair? tree) + (cons (walk (car tree)) + (walk (cdr tree)))) + (else tree)))) + +;; The next 2 aren't in CL. (Names from Dybvig) + +(define (tree:substq new old tree) + (let walk ((tree tree)) + (cond ((eq? old tree) + new) + ((pair? tree) + (cons (walk (car tree)) + (walk (cdr tree)))) + (else tree)))) + +(define (tree:substv new old tree) + (let walk ((tree tree)) + (cond ((eqv? old tree) + new) + ((pair? tree) + (cons (walk (car tree)) + (walk (cdr tree)))) + (else tree)))) + +(define copy-tree tree:copy-tree) +(define subst tree:subst) +(define substq tree:substq) +(define substv tree:substv) diff --git a/trnscrpt.scm b/trnscrpt.scm new file mode 100644 index 0000000..45d884e --- /dev/null +++ b/trnscrpt.scm @@ -0,0 +1,76 @@ +; "trnscrpt.scm", transcript functions for Scheme. +; Copyright (c) 1992, 1993, 1995 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(define transcript:port #f) + +(define (transcript-on filename) + (set! transcript:port (open-output-file filename))) + +(define (transcript-off) + (if (output-port? transcript:port) + (close-output-port transcript:port)) + (set! transcript:port #f)) + +(define read-char + (let ((read-char read-char) (write-char write-char)) + (lambda opt + (let ((ans (apply read-char opt))) + (cond ((eof-object? ans)) + ((output-port? transcript:port) + (write-char ans transcript:port))) + ans)))) + +(define read + (let ((read read) (write write) (newline newline)) + (lambda opt + (let ((ans (apply read opt))) + (cond ((eof-object? ans)) + ((output-port? transcript:port) + (write ans transcript:port) + (if (eqv? #\newline (apply peek-char opt)) + (newline transcript:port)))) + ans)))) + +(define write-char + (let ((write-char write-char)) + (lambda (obj . opt) + (apply write-char obj opt) + (if (output-port? transcript:port) + (write-char obj transcript:port))))) + +(define write + (let ((write write)) + (lambda (obj . opt) + (apply write obj opt) + (if (output-port? transcript:port) + (write obj transcript:port))))) + +(define display + (let ((display display)) + (lambda (obj . opt) + (apply display obj opt) + (if (output-port? transcript:port) + (display obj transcript:port))))) + +(define newline + (let ((newline newline)) + (lambda opt + (apply newline opt) + (if (output-port? transcript:port) + (newline transcript:port))))) diff --git a/tsort.scm b/tsort.scm new file mode 100644 index 0000000..9371f3c --- /dev/null +++ b/tsort.scm @@ -0,0 +1,46 @@ +;;; "tsort.scm" Topological sort +;;; Copyright (C) 1995 Mikael Djurfeldt +; +; This code is in the public domain. + +;;; The algorithm is inspired by Cormen, Leiserson and Rivest (1990) +;;; "Introduction to Algorithms", chapter 23 + +(require 'hash-table) +(require 'primes) + +(define (topological-sort dag pred) + (if (null? dag) + '() + (let* ((adj-table (make-hash-table + (car (primes> (length dag) 1)))) + (insert (hash-associator pred)) + (lookup (hash-inquirer pred)) + (sorted '())) + (letrec ((visit + (lambda (u adj-list) + ;; Color vertex u + (insert adj-table u 'colored) + ;; Visit uncolored vertices which u connects to + (for-each (lambda (v) + (let ((val (lookup adj-table v))) + (if (not (eq? val 'colored)) + (visit v (or val '()))))) + adj-list) + ;; Since all vertices downstream u are visited + ;; by now, we can safely put u on the output list + (set! sorted (cons u sorted))))) + ;; Hash adjacency lists + (for-each (lambda (def) + (insert adj-table (car def) (cdr def))) + (cdr dag)) + ;; Visit vertices + (visit (caar dag) (cdar dag)) + (for-each (lambda (def) + (let ((val (lookup adj-table (car def)))) + (if (not (eq? val 'colored)) + (visit (car def) (cdr def))))) + (cdr dag))) + sorted))) + +(define tsort topological-sort) diff --git a/values.scm b/values.scm new file mode 100644 index 0000000..b47e0f8 --- /dev/null +++ b/values.scm @@ -0,0 +1,27 @@ +;"values.scm" multiple values +;By david carlton, carlton@husc.harvard.edu. +; +;This code is in the public domain. + +(require 'record) + +(define values:*values-rtd* + (make-record-type "values" + '(values))) + +(define values + (let ((make-values (record-constructor values:*values-rtd*))) + (lambda x + (if (and (not (null? x)) + (null? (cdr x))) + (car x) + (make-values x))))) + +(define call-with-values + (let ((access-values (record-accessor values:*values-rtd* 'values)) + (values-predicate? (record-predicate values:*values-rtd*))) + (lambda (producer consumer) + (let ((result (producer))) + (if (values-predicate? result) + (apply consumer (access-values result)) + (consumer result)))))) diff --git a/vscm.init b/vscm.init new file mode 100644 index 0000000..7d4661b --- /dev/null +++ b/vscm.init @@ -0,0 +1,306 @@ +;;;"vscm.init" Configuration of *features* for VSCM -*-scheme-*- +;Copyright (C) 1994 Aubrey Jaffer +; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;;; From: Matthias Blume <blume@cs.Princeton.EDU> +;;; Date: Tue, 1 Mar 1994 11:42:31 -0500 +;;; Disclaimer: The code below is only a quick hack. If I find some +;;; time to spare I might get around to make some more things work. +;;; In particular, string ports could be made available without too +;;; much trouble. + +;;; You have to provide ``vscm.init'' as an explicit command line +;;; argument. Since this is not very nice I would recommend the +;;; following installation procedure: + +;1. run scheme +;2. (load "vscm.init") +;3. (slib:dump "dumpfile") +;3. mv dumpfile place-where-vscm-standard-bootfile-resides, e.g. +; mv dumpfile /usr/local/vscm/lib/scheme-boot +; (In this case vscm should have been compiled with flag +; -DDEFAULT_BOOTFILE='"/usr/local/vscm/lib/scheme-boot"'. See +; Makefile (definition of DDP) for details.) + +(define (slib:dump dump-to-file) + (let ((args (dump dump-to-file))) + (if args + (begin + (display "[SLIB available]") + (newline) + (((mcm) 'toplevel) args)) + (quit)))) + +;;; Caveat: While playing with this code I discovered a nasty bug. +;;; (Something is wrong with my ``restore'' code -- it seems to break +;;; on 64 bit machines (not always, though).) It works on MIPS, etc. + +;;; (software-type) should be set to the generic operating system type. +;;; UNIX, VMS, MACOS, AMIGA and MS-DOS are supported. + +(define (software-type) 'UNIX) + +;;; (scheme-implementation-type) should return the name of the scheme +;;; implementation loading this file. + +(define (scheme-implementation-type) 'Vscm) + +;;; (scheme-implementation-version) should return a string describing the +;;; version the scheme implementation loading this file. + +(define (scheme-implementation-version) "?") + +;;; (implementation-vicinity) should be defined to be the pathname of +;;; the directory where any auxillary files to your Scheme +;;; implementation reside. + +(define (implementation-vicinity) + (case (software-type) + ((UNIX) "/usr/local/src/scheme/") + ((VMS) "scheme$src:") + ((MS-DOS) "C:\\scheme\\"))) + +;;; (library-vicinity) should be defined to be the pathname of the +;;; directory where files of Scheme library functions reside. + +(define library-vicinity + (let ((library-path + (or (getenv "SCHEME_LIBRARY_PATH") + ;; Uses this path if SCHEME_LIBRARY_PATH is not set. + (case (software-type) + ((UNIX) "/usr/local/lib/slib/") + ((VMS) "lib$scheme:") + ((MS-DOS) "C:\\SLIB\\") + (else ""))))) + (lambda () library-path))) + +;;; *FEATURES* should be set to a list of symbols describing features +;;; of this implementation. Suggestions for features are: + +(define *features* + '( + source ;can load scheme source files + ;(slib:load-source "filename") +; compiled ;can load compiled files + ;(slib:load-compiled "filename") + rev4-report ;conforms to +; rev3-report ;conforms to + ieee-p1178 ;conforms to +; sicp ;runs code from Structure and + ;Interpretation of Computer + ;Programs by Abelson and Sussman. + rev4-optional-procedures ;LIST-TAIL, STRING->LIST, + ;LIST->STRING, STRING-COPY, + ;STRING-FILL!, LIST->VECTOR, + ;VECTOR->LIST, and VECTOR-FILL! + rev3-procedures ;LAST-PAIR, T, and NIL +; rev2-procedures ;SUBSTRING-MOVE-LEFT!, + ;SUBSTRING-MOVE-RIGHT!, + ;SUBSTRING-FILL!, + ;STRING-NULL?, APPEND!, 1+, + ;-1+, <?, <=?, =?, >?, >=? + multiarg/and- ;/ and - can take more than 2 args. + multiarg-apply ;APPLY can take more than 2 args. + rationalize + delay ;has DELAY and FORCE + with-file ;has WITH-INPUT-FROM-FILE and + ;WITH-OUTPUT-FROM-FILE +; string-port ;has CALL-WITH-INPUT-STRING and + ;CALL-WITH-OUTPUT-STRING +; transcript ;TRANSCRIPT-ON and TRANSCRIPT-OFF +; char-ready? +; macro ;has R4RS high level macros +; defmacro ;has Common Lisp DEFMACRO + eval ;SLIB:EVAL is single argument eval +; record ;has user defined data structures + values ;proposed multiple values +; dynamic-wind ;proposed dynamic-wind + ieee-floating-point ;conforms to + full-continuation ;can return multiple times +; object-hash ;has OBJECT-HASH + +; sort +; queue ;queues +; pretty-print + object->string +; format +; trace ;has macros: TRACE and UNTRACE +; compiler ;has (COMPILER) +; ed ;(ED) is editor + system ;posix (system <string>) + getenv ;posix (getenv <string>) + program-arguments ;returns list of strings (argv) +; Xwindows ;X support +; curses ;screen management package +; termcap ;terminal description package +; terminfo ;sysV terminal description + )) + +;;; (OBJECT->STRING obj) -- analogous to WRITE +(define object->string string-write) + +;;; (PROGRAM-ARGUMENTS) +;;; +(define (program-arguments) command-line-arguments) + +;;; (OUTPUT-PORT-WIDTH <port>) +(define (output-port-width . arg) 79) + +;;; (CURRENT-ERROR-PORT) +(define current-error-port + (standard-port 2)) + +;;; (TMPNAM) makes a temporary file name. +(define tmpnam (let ((cntr 100)) + (lambda () (set! cntr (+ 1 cntr)) + (string-append "slib_" (number->string cntr))))) + +;;; (FILE-EXISTS? <string>) +(define (file-exists? f) + (system (string-append "test -f " f))) + +;;; (DELETE-FILE <string>) +(define (delete-file f) + (remove-file f)) + +;;; FORCE-OUTPUT flushes any pending output on optional arg output port +(define force-output flush) + +;;; CALL-WITH-INPUT-STRING and CALL-WITH-OUTPUT-STRING are the string +;;; port versions of CALL-WITH-*PUT-FILE. + +;;; CHAR-CODE-LIMIT is one greater than the largest integer which can +;;; be returned by CHAR->INTEGER. +(define char-code-limit 256) + +;;; MOST-POSITIVE-FIXNUM is used in modular.scm +(define most-positive-fixnum #x0fffffff) + +;;; Return argument +(define (identity x) x) + +;;; If your implementation provides eval SLIB:EVAL is single argument +;;; eval using the top-level (user) environment. +(define slib:eval eval) + +;;; If your implementation provides R4RS macros: +(define macro:eval slib:eval) +(define macro:load load) + +(define *defmacros* + (list (cons 'defmacro + (lambda (name parms . body) + `(set! *defmacros* (cons (cons ',name (lambda ,parms ,@body)) + *defmacros*)))))) +(define (defmacro? m) (and (assq m *defmacros*) #t)) + +(define (macroexpand-1 e) + (if (pair? e) (let ((a (car e))) + (cond ((symbol? a) (set! a (assq a *defmacros*)) + (if a (apply (cdr a) (cdr e)) e)) + (else e))) + e)) + +(define (macroexpand e) + (if (pair? e) (let ((a (car e))) + (cond ((symbol? a) + (set! a (assq a *defmacros*)) + (if a (macroexpand (apply (cdr a) (cdr e))) e)) + (else e))) + e)) + +(define gentemp + (let ((*gensym-counter* -1)) + (lambda () + (set! *gensym-counter* (+ *gensym-counter* 1)) + (string->symbol + (string-append "slib:G" (number->string *gensym-counter*)))))) + +(define base:eval slib:eval) +(define (defmacro:eval x) (base:eval (defmacro:expand* x))) +(define (defmacro:expand* x) + (require 'defmacroexpand) (apply defmacro:expand* x '())) + +(define (defmacro:load <pathname>) + (slib:eval-load <pathname> defmacro:eval)) + +(define (slib:eval-load <pathname> evl) + (if (not (file-exists? <pathname>)) + (set! <pathname> (string-append <pathname> (scheme-file-suffix)))) + (call-with-input-file <pathname> + (lambda (port) + (let ((old-load-pathname *load-pathname*)) + (set! *load-pathname* <pathname>) + (do ((o (read port) (read port))) + ((eof-object? o)) + (evl o)) + (set! *load-pathname* old-load-pathname))))) + +;;; define an error procedure for the library +(define slib:error error) + +;;; define these as appropriate for your system. +(define slib:tab #\Tab) +(define slib:form-feed #\d12) + +;;; Support for older versions of Scheme. Not enough code for its own file. +(define (last-pair l) (if (pair? (cdr l)) (last-pair (cdr l)) l)) +(define t #t) +(define nil #f) + +;;; Define these if your implementation's syntax can support it and if +;;; they are not already defined. + +(define (1+ n) (+ n 1)) +(define (-1+ n) (+ n -1)) +(define 1- -1+) + +(define in-vicinity string-append) + +;;; Define SLIB:EXIT to be the implementation procedure to exit or +;;; return if exitting not supported. +(define slib:exit + (lambda args + (cond ((null? args) (quit)) + ((eqv? #t (car args)) (quit)) + ((eqv? #f (car args)) (quit 1)) + (else (quit (car args)))))) + +;;; Here for backward compatability +(define scheme-file-suffix + (let ((suffix (case (software-type) + ((NOSVE) "_scm") + (else ".scm")))) + (lambda () suffix))) + +;;; (SLIB:LOAD-SOURCE "foo") should load "foo.scm" or with whatever +;;; suffix all the module files in SLIB have. See feature 'SOURCE. + +(define (slib:load-source f) (load (string-append f (scheme-file-suffix)))) + +;;; (SLIB:LOAD-COMPILED "foo") should load the file that was produced +;;; by compiling "foo.scm" if this implementation can compile files. +;;; See feature 'COMPILED. + +(define slib:load-compiled load) + +;;; At this point SLIB:LOAD must be able to load SLIB files. + +(define slib:load slib:load-source) + +(slib:load (in-vicinity (library-vicinity) "require")) diff --git a/withfile.scm b/withfile.scm new file mode 100644 index 0000000..fc13510 --- /dev/null +++ b/withfile.scm @@ -0,0 +1,82 @@ +; "withfile.scm", with-input-from-file and with-output-to-file for Scheme +; Copyright (c) 1992, 1993 Aubrey Jaffer +;; +;Permission to copy this software, to redistribute it, and to use it +;for any purpose is granted, subject to the following restrictions and +;understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +(require 'dynamic-wind) + +(define withfile:current-input (current-input-port)) +(define withfile:current-output (current-output-port)) + +(define (current-input-port) withfile:current-input) +(define (current-output-port) withfile:current-output) + +(define (with-input-from-file file thunk) + (define oport withfile:current-input) + (define port (open-input-file file)) + (dynamic-wind (lambda () (set! oport withfile:current-input) + (set! withfile:current-input port)) + (lambda() (let ((ans (thunk))) (close-input-port port) ans)) + (lambda() (set! withfile:current-input oport)))) + +(define (with-output-from-file file thunk) + (define oport withfile:current-output) + (define port (open-output-file file)) + (dynamic-wind (lambda() (set! oport withfile:current-output) + (set! withfile:current-output port)) + (lambda() (let ((ans (thunk))) (close-output-port port) ans)) + (lambda() (set! withfile:current-output oport)))) + +(define peek-char + (let ((peek-char peek-char)) + (lambda opt + (peek-char (if (null? opt) withfile:current-input (car opt)))))) + +(define read-char + (let ((read-char read-char)) + (lambda opt + (read-char (if (null? opt) withfile:current-input (car opt)))))) + +(define read + (let ((read read)) + (lambda opt + (read (if (null? opt) withfile:current-input (car opt)))))) + +(define write-char + (let ((write-char write-char)) + (lambda (obj . opt) + (write-char obj (if (null? opt) withfile:current-output (car opt)))))) + +(define write + (let ((write write)) + (lambda (obj . opt) + (write obj (if (null? opt) withfile:current-output (car opt)))))) + +(define display + (let ((display display)) + (lambda (obj . opt) + (display obj (if (null? opt) withfile:current-output (car opt)))))) + +(define newline + (let ((newline newline)) + (lambda opt + (newline (if (null? opt) withfile:current-output (car opt)))))) + +(define force-output + (let ((force-output force-output)) + (lambda opt + (force-output (if (null? opt) withfile:current-output (car opt)))))) diff --git a/wttest.scm b/wttest.scm new file mode 100644 index 0000000..cc8b5e3 --- /dev/null +++ b/wttest.scm @@ -0,0 +1,134 @@ +;; "wttrtst.scm" Test Weight balanced trees -*-Scheme-*- +;; Copyright (c) 1993-1994 Stephen Adams +;; +;; Copyright (c) 1993-94 Massachusetts Institute of Technology +;; +;; This material was developed by the Scheme project at the Massachusetts +;; Institute of Technology, Department of Electrical Engineering and +;; Computer Science. Permission to copy this software, to redistribute +;; it, and to use it for any purpose is granted, subject to the following +;; restrictions and understandings. +;; +;; 1. Any copy made of this software must include this copyright notice +;; in full. +;; +;; 2. Users of this software agree to make their best efforts (a) to +;; return to the MIT Scheme project any improvements or extensions that +;; they make, so that these may be included in future releases; and (b) +;; to inform MIT of noteworthy uses of this software. +;; +;; 3. All materials developed as a consequence of the use of this +;; software shall duly acknowledge such use, in accordance with the usual +;; standards of acknowledging credit in academic research. +;; +;; 4. MIT has made no warrantee or representation that the operation of +;; this software will be error-free, and MIT is under no obligation to +;; provide any services, by way of maintenance, update, or otherwise. +;; +;; 5. In conjunction with products arising from the use of this material, +;; there shall be no use of the name of the Massachusetts Institute of +;; Technology nor of any adaptation thereof in any advertising, +;; promotional, or sales literature without prior written consent from +;; MIT in each case. + +(require 'wt-tree) + +;; Test code, using maps from digit strings to the numbers they represent. + +(define (wt-test) + + (define (make-map lo hi step) + (let loop ((i lo) (map (make-wt-tree string-wt-type))) + (if (> i hi) + map + (loop (+ i step) (wt-tree/add map (number->string i) i))))) + + (define (wt-tree->alist t) + (wt-tree/fold (lambda (key datum rest) (cons (cons key datum) rest)) '() t)) + + (define (try-all operation trees) + (map (lambda (t1) + (map (lambda (t2) + (operation t1 t2)) + trees)) + trees)) + + (define (chunk tree) + (let ((size (wt-tree/size tree))) + (if (< size 8) + size + (let* ((midpoint (if (even? size) + (/ size 2) + (/ (+ size 1) 2))) + (fulcrum (wt-tree/index tree midpoint))) + (list (chunk (wt-tree/split< tree fulcrum)) + (list fulcrum) + (chunk (wt-tree/split> tree fulcrum))))))) + + (define (verify name result expected) + (newline) + (display "Test ") (display name) + (if (equal? result expected) + (begin + (display " passed")) + (begin + (display " unexpected result") + (newline) + (display "Expected: " expected) + (newline) + (display "Got: " result)))) + + (let ((t1 (make-map 0 99 2)) ; 0,2,4,...,98 + (t2 (make-map 1 100 2)) ; 1,3,5,...,99 + (t3 (make-map 0 100 3))) ; 0,3,6,...,99 + + + (verify 'alist (wt-tree->alist t3) ; + '(("0" . 0) ("12" . 12) ("15" . 15) ("18" . 18) ("21" . 21) + ("24" . 24) ("27" . 27) ("3" . 3) ("30" . 30) ("33" . 33) + ("36" . 36) ("39" . 39) ("42" . 42) ("45" . 45) ("48" . 48) + ("51" . 51) ("54" . 54) ("57" . 57) ("6" . 6) ("60" . 60) + ("63" . 63) ("66" . 66) ("69" . 69) ("72" . 72) ("75" . 75) + ("78" . 78) ("81" . 81) ("84" . 84) ("87" . 87) ("9" . 9) + ("90" . 90) ("93" . 93) ("96" . 96) ("99" . 99))) + + + (verify 'union-sizes + (try-all (lambda (t1 t2) (wt-tree/size (wt-tree/union t1 t2))) + (list t1 t2 t3)) + '((50 100 67) (100 50 67) (67 67 34))) + + (verify 'difference-sizes + (try-all (lambda (t1 t2) + (wt-tree/size (wt-tree/difference t1 t2))) + (list t1 t2 t3)) + '((0 50 33) (50 0 33) (17 17 0))) + + (verify 'intersection-sizes + (try-all (lambda (t1 t2) + (wt-tree/size (wt-tree/intersection t1 t2))) + (list t1 t2 t3)) + '((50 0 17) (0 50 17) (17 17 34))) + + (verify 'equalities + (try-all (lambda (t1 t2) + (wt-tree/set-equal? (wt-tree/difference t1 t2) + (wt-tree/difference t2 t1))) + (list t1 t2 t3)) + '((#t #f #f) (#f #t #f) (#f #f #t))) + + (verify 'indexing + (chunk (make-map 0 99 1)) + '((((7 ("15") 5) ("20") (6 ("27") 4)) ("31") + ((6 ("38") 5) ("43") (6 ("5") 4))) + ("54") + (((7 ("61") 5) ("67") (6 ("73") 4)) ("78") + ((6 ("84") 5) ("9") (5 ("95") 4))))) + (newline))) + +(wt-test) + +;;; Local Variables: +;;; eval: (put 'with-n-node 'scheme-indent-function 1) +;;; eval: (put 'with-n-node 'scheme-indent-hook 1) +;;; End: diff --git a/wttree.scm b/wttree.scm new file mode 100644 index 0000000..467aa86 --- /dev/null +++ b/wttree.scm @@ -0,0 +1,784 @@ +;; "wttree.scm" Weight balanced trees -*-Scheme-*- +;; Copyright (c) 1993-1994 Stephen Adams +;; +;; $Id: wttree.scm,v 1.1 1994/11/28 21:58:48 adams Exp adams $ +;; +;; References: +;; +;; Stephen Adams, Implemeting Sets Efficiently in a Functional +;; Language, CSTR 92-10, Department of Electronics and Computer +;; Science, University of Southampton, 1992 +;; +;; +;; Copyright (c) 1993-94 Massachusetts Institute of Technology +;; +;; This material was developed by the Scheme project at the Massachusetts +;; Institute of Technology, Department of Electrical Engineering and +;; Computer Science. Permission to copy this software, to redistribute +;; it, and to use it for any purpose is granted, subject to the following +;; restrictions and understandings. +;; +;; 1. Any copy made of this software must include this copyright notice +;; in full. +;; +;; 2. Users of this software agree to make their best efforts (a) to +;; return to the MIT Scheme project any improvements or extensions that +;; they make, so that these may be included in future releases; and (b) +;; to inform MIT of noteworthy uses of this software. +;; +;; 3. All materials developed as a consequence of the use of this +;; software shall duly acknowledge such use, in accordance with the usual +;; standards of acknowledging credit in academic research. +;; +;; 4. MIT has made no warrantee or representation that the operation of +;; this software will be error-free, and MIT is under no obligation to +;; provide any services, by way of maintenance, update, or otherwise. +;; +;; 5. In conjunction with products arising from the use of this material, +;; there shall be no use of the name of the Massachusetts Institute of +;; Technology nor of any adaptation thereof in any advertising, +;; promotional, or sales literature without prior written consent from +;; MIT in each case. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Weight Balanced Binary Trees +;; +;; +;; +;; This file has been modified from the MIT-Scheme library version to +;; make it more standard. The main changes are +;; +;; . The whole thing has been put in a LET as R4RS Scheme has no module +;; system. +;; . The MIT-Scheme define structure operations have been written out by +;; hand. +;; +;; It has been tested on MIT-Scheme, scheme48 and scm4e1 +;; +;; Non-standard procedures: +;; error +;; error:wrong-type-argument +;; error:band-range-argument +;; These are only called when there is an error so it is not critical to +;; have them defined :-) +;; +;; +;; If your system has a compiler and you want this code to run fast, you +;; should do whatever is necessary to inline all of the structure accessors. +;; +;; This is MIT-Scheme's way of saying that +, car etc should all be inlined. +;; +;;(declare (usual-integrations)) + + +;;; +;;; Interface to this package. +;;; +;;; ONLY these procedures (and TEST at the end of the file) will be +;;; (re)defined in your system. +;;; + +(define make-wt-tree-type #f) +(define number-wt-type #f) +(define string-wt-type #f) + +(define make-wt-tree #f) +(define singleton-wt-tree #f) +(define alist->wt-tree #f) +(define wt-tree/empty? #f) +(define wt-tree/size #f) +(define wt-tree/add #f) +(define wt-tree/delete #f) +(define wt-tree/add! #f) +(define wt-tree/delete! #f) +(define wt-tree/member? #f) +(define wt-tree/lookup #f) +(define wt-tree/split< #f) +(define wt-tree/split> #f) +(define wt-tree/union #f) +(define wt-tree/intersection #f) +(define wt-tree/difference #f) +(define wt-tree/subset? #f) +(define wt-tree/set-equal? #f) +(define wt-tree/fold #f) +(define wt-tree/for-each #f) +(define wt-tree/index #f) +(define wt-tree/index-datum #f) +(define wt-tree/index-pair #f) +(define wt-tree/rank #f) +(define wt-tree/min #f) +(define wt-tree/min-datum #f) +(define wt-tree/min-pair #f) +(define wt-tree/delete-min #f) +(define wt-tree/delete-min! #f) + + +;; This LET sets all of the above variables. + +(let () + + ;; We use the folowing MIT-Scheme operation on fixnums (small + ;; integers). R4RS compatible (but less efficient) definitions. + ;; You should replace these with something that is efficient in your + ;; system. + + (define fix:fixnum? (lambda (x) (and (exact? x) (integer? x)))) + (define fix:+ +) + (define fix:- -) + (define fix:< <) + (define fix:<= <) + (define fix:> >) + (define fix:* *) + + ;; A TREE-TYPE is a collection of those procedures that depend on the + ;; ordering relation. + + ;; MIT-Scheme structure definition + ;;(define-structure + ;; (tree-type + ;; (conc-name tree-type/) + ;; (constructor %make-tree-type)) + ;; (key<? #F read-only true) + ;; (alist->tree #F read-only true) + ;; (add #F read-only true) + ;; (insert! #F read-only true) + ;; (delete #F read-only true) + ;; (delete! #F read-only true) + ;; (member? #F read-only true) + ;; (lookup #F read-only true) + ;; (split-lt #F read-only true) + ;; (split-gt #F read-only true) + ;; (union #F read-only true) + ;; (intersection #F read-only true) + ;; (difference #F read-only true) + ;; (subset? #F read-only true) + ;; (rank #F read-only true) + ;;) + + ;; Written out by hand, using vectors: + ;; + ;; If possible, you should teach your system to print out something + ;; like #[tree-type <] instread of the whole vector. + + (define tag:tree-type (string->symbol "#[(runtime wttree)tree-type]")) + + (define (%make-tree-type key<? alist->tree + add insert! + delete delete! + member? lookup + split-lt split-gt + union intersection + difference subset? + rank ) + (vector tag:tree-type + key<? alist->tree add insert! + delete delete! member? lookup + split-lt split-gt union intersection + difference subset? rank )) + + (define (tree-type? tt) + (and (vector? tt) + (eq? (vector-ref tt 0) tag:tree-type))) + + (define (tree-type/key<? tt) (vector-ref tt 1)) + (define (tree-type/alist->tree tt) (vector-ref tt 2)) + (define (tree-type/add tt) (vector-ref tt 3)) + (define (tree-type/insert! tt) (vector-ref tt 4)) + (define (tree-type/delete tt) (vector-ref tt 5)) + (define (tree-type/delete! tt) (vector-ref tt 6)) + (define (tree-type/member? tt) (vector-ref tt 7)) + (define (tree-type/lookup tt) (vector-ref tt 8)) + (define (tree-type/split-lt tt) (vector-ref tt 9)) + (define (tree-type/split-gt tt) (vector-ref tt 10)) + (define (tree-type/union tt) (vector-ref tt 11)) + (define (tree-type/intersection tt) (vector-ref tt 12)) + (define (tree-type/difference tt) (vector-ref tt 13)) + (define (tree-type/subset? tt) (vector-ref tt 14)) + (define (tree-type/rank tt) (vector-ref tt 15)) + + ;; User level tree representation. + ;; + ;; WT-TREE is a wrapper for trees of nodes. + ;; + ;;MIT-Scheme: + ;;(define-structure + ;; (wt-tree + ;; (conc-name tree/) + ;; (constructor %make-wt-tree)) + ;; (type #F read-only true) + ;; (root #F read-only false)) + + ;; If possible, you should teach your system to print out something + ;; like #[wt-tree] instread of the whole vector. + + (define tag:wt-tree (string->symbol "#[(runtime wttree)wt-tree]")) + + (define (%make-wt-tree type root) + (vector tag:wt-tree type root)) + + (define (wt-tree? t) + (and (vector? t) + (eq? (vector-ref t 0) tag:wt-tree))) + + (define (tree/type t) (vector-ref t 1)) + (define (tree/root t) (vector-ref t 2)) + (define (set-tree/root! t v) (vector-set! t 2 v)) + + ;; Nodes are the thing from which the real trees are built. There are + ;; lots of these and the uninquisitibe user will never see them, so + ;; they are represented as untagged to save the slot that would be + ;; used for tagging structures. + ;; In MIT-Scheme these were all DEFINE-INTEGRABLE + + (define (make-node k v l r w) (vector w l k r v)) + (define (node/k node) (vector-ref node 2)) + (define (node/v node) (vector-ref node 4)) + (define (node/l node) (vector-ref node 1)) + (define (node/r node) (vector-ref node 3)) + (define (node/w node) (vector-ref node 0)) + + (define empty 'empty) + (define (empty? x) (eq? x 'empty)) + + (define (node/size node) + (if (empty? node) 0 (node/w node))) + + (define (node/singleton k v) (make-node k v empty empty 1)) + + (define (with-n-node node receiver) + (receiver (node/k node) (node/v node) (node/l node) (node/r node))) + + ;; + ;; Constructors for building node trees of various complexity + ;; + + (define (n-join k v l r) + (make-node k v l r (fix:+ 1 (fix:+ (node/size l) (node/size r))))) + + (define (single-l a.k a.v x r) + (with-n-node r + (lambda (b.k b.v y z) (n-join b.k b.v (n-join a.k a.v x y) z)))) + + (define (double-l a.k a.v x r) + (with-n-node r + (lambda (c.k c.v r.l z) + (with-n-node r.l + (lambda (b.k b.v y1 y2) + (n-join b.k b.v + (n-join a.k a.v x y1) + (n-join c.k c.v y2 z))))))) + + (define (single-r b.k b.v l z) + (with-n-node l + (lambda (a.k a.v x y) (n-join a.k a.v x (n-join b.k b.v y z))))) + + (define (double-r c.k c.v l z) + (with-n-node l + (lambda (a.k a.v x l.r) + (with-n-node l.r + (lambda (b.k b.v y1 y2) + (n-join b.k b.v + (n-join a.k a.v x y1) + (n-join c.k c.v y2 z))))))) + + ;; (define-integrable wt-tree-ratio 5) + (define wt-tree-ratio 5) + + (define (t-join k v l r) + (define (simple-join) (n-join k v l r)) + (let ((l.n (node/size l)) + (r.n (node/size r))) + (cond ((fix:< (fix:+ l.n r.n) 2) (simple-join)) + ((fix:> r.n (fix:* wt-tree-ratio l.n)) + ;; right is too big + (let ((r.l.n (node/size (node/l r))) + (r.r.n (node/size (node/r r)))) + (if (fix:< r.l.n r.r.n) + (single-l k v l r) + (double-l k v l r)))) + ((fix:> l.n (fix:* wt-tree-ratio r.n)) + ;; left is too big + (let ((l.l.n (node/size (node/l l))) + (l.r.n (node/size (node/r l)))) + (if (fix:< l.r.n l.l.n) + (single-r k v l r) + (double-r k v l r)))) + (else + (simple-join))))) + ;; + ;; Node tree procedures that are independent of key<? + ;; + + (define (node/min node) + (cond ((empty? node) (error:empty 'min)) + ((empty? (node/l node)) node) + (else (node/min (node/l node))))) + + (define (node/delmin node) + (cond ((empty? node) (error:empty 'delmin)) + ((empty? (node/l node)) (node/r node)) + (else (t-join (node/k node) (node/v node) + (node/delmin (node/l node)) (node/r node))))) + + (define (node/concat2 node1 node2) + (cond ((empty? node1) node2) + ((empty? node2) node1) + (else + (let ((min-node (node/min node2))) + (t-join (node/k min-node) (node/v min-node) + node1 (node/delmin node2)))))) + + (define (node/inorder-fold procedure base node) + (define (fold base node) + (if (empty? node) + base + (with-n-node node + (lambda (k v l r) + (fold (procedure k v (fold base r)) l))))) + (fold base node)) + + (define (node/for-each procedure node) + (if (not (empty? node)) + (with-n-node node + (lambda (k v l r) + (node/for-each procedure l) + (procedure k v) + (node/for-each procedure r))))) + + (define (node/height node) + (if (empty? node) + 0 + (+ 1 (max (node/height (node/l node)) + (node/height (node/r node)))))) + + (define (node/index node index) + (define (loop node index) + (let ((size.l (node/size (node/l node)))) + (cond ((fix:< index size.l) (loop (node/l node) index)) + ((fix:> index size.l) (loop (node/r node) + (fix:- index (fix:+ 1 size.l)))) + (else node)))) + (let ((bound (node/size node))) + (if (or (< index 0) + (>= index bound) + (not (fix:fixnum? index))) + (error:bad-range-argument index 'node/index) + (loop node index)))) + + (define (error:empty owner) + (error "Operation requires non-empty tree:" owner)) + + + (define (local:make-wt-tree-type key<?) + + ;; MIT-Scheme definitions: + ;;(declare (integrate key<?)) + ;;(define-integrable (key>? x y) (key<? y x)) + + (define (key>? x y) (key<? y x)) + + (define (node/find k node) + ;; Returns either the node or #f. + ;; Loop takes D comparisons where D is the depth of the tree + ;; rather than the traditional compare-low, compare-high which + ;; takes on average 1.5(D-1) comparisons + (define (loop this best) + (cond ((empty? this) best) + ((key<? k (node/k this)) (loop (node/l this) best)) + (else (loop (node/r this) this)))) + (let ((best (loop node #f))) + (cond ((not best) #f) + ((key<? (node/k best) k) #f) + (else best)))) + + (define (node/rank k node rank) + (cond ((empty? node) #f) + ((key<? k (node/k node)) (node/rank k (node/l node) rank)) + ((key>? k (node/k node)) + (node/rank k (node/r node) + (fix:+ 1 (fix:+ rank (node/size (node/l node)))))) + (else (fix:+ rank (node/size (node/l node)))))) + + (define (node/add node k v) + (if (empty? node) + (node/singleton k v) + (with-n-node node + (lambda (key val l r) + (cond ((key<? k key) (t-join key val (node/add l k v) r)) + ((key<? key k) (t-join key val l (node/add r k v))) + (else (n-join key v l r))))))) + + (define (node/delete x node) + (if (empty? node) + empty + (with-n-node node + (lambda (key val l r) + (cond ((key<? x key) (t-join key val (node/delete x l) r)) + ((key<? key x) (t-join key val l (node/delete x r))) + (else (node/concat2 l r))))))) + + (define (node/concat tree1 tree2) + (cond ((empty? tree1) tree2) + ((empty? tree2) tree1) + (else + (let ((min-node (node/min tree2))) + (node/concat3 (node/k min-node) (node/v min-node) tree1 + (node/delmin tree2)))))) + + (define (node/concat3 k v l r) + (cond ((empty? l) (node/add r k v)) + ((empty? r) (node/add l k v)) + (else + (let ((n1 (node/size l)) + (n2 (node/size r))) + (cond ((fix:< (fix:* wt-tree-ratio n1) n2) + (with-n-node r + (lambda (k2 v2 l2 r2) + (t-join k2 v2 (node/concat3 k v l l2) r2)))) + ((fix:< (fix:* wt-tree-ratio n2) n1) + (with-n-node l + (lambda (k1 v1 l1 r1) + (t-join k1 v1 l1 (node/concat3 k v r1 r))))) + (else + (n-join k v l r))))))) + + (define (node/split-lt node x) + (cond ((empty? node) empty) + ((key<? x (node/k node)) + (node/split-lt (node/l node) x)) + ((key<? (node/k node) x) + (node/concat3 (node/k node) (node/v node) (node/l node) + (node/split-lt (node/r node) x))) + (else (node/l node)))) + + (define (node/split-gt node x) + (cond ((empty? node) empty) + ((key<? (node/k node) x) + (node/split-gt (node/r node) x)) + ((key<? x (node/k node)) + (node/concat3 (node/k node) (node/v node) + (node/split-gt (node/l node) x) (node/r node))) + (else (node/r node)))) + + (define (node/union tree1 tree2) + (cond ((empty? tree1) tree2) + ((empty? tree2) tree1) + (else + (with-n-node tree2 + (lambda (ak av l r) + (let ((l1 (node/split-lt tree1 ak)) + (r1 (node/split-gt tree1 ak))) + (node/concat3 ak av (node/union l1 l) (node/union r1 r)))))))) + + (define (node/difference tree1 tree2) + (cond ((empty? tree1) empty) + ((empty? tree2) tree1) + (else + (with-n-node tree2 + (lambda (ak av l r) + (let ((l1 (node/split-lt tree1 ak)) + (r1 (node/split-gt tree1 ak))) + av + (node/concat (node/difference l1 l) + (node/difference r1 r)))))))) + + (define (node/intersection tree1 tree2) + (cond ((empty? tree1) empty) + ((empty? tree2) empty) + (else + (with-n-node tree2 + (lambda (ak av l r) + (let ((l1 (node/split-lt tree1 ak)) + (r1 (node/split-gt tree1 ak))) + (if (node/find ak tree1) + (node/concat3 ak av (node/intersection l1 l) + (node/intersection r1 r)) + (node/concat (node/intersection l1 l) + (node/intersection r1 r))))))))) + + (define (node/subset? tree1 tree2) + (or (empty? tree1) + (and (fix:<= (node/size tree1) (node/size tree2)) + (with-n-node tree1 + (lambda (k v l r) + v + (cond ((key<? k (node/k tree2)) + (and (node/subset? l (node/l tree2)) + (node/find k tree2) + (node/subset? r tree2))) + ((key>? k (node/k tree2)) + (and (node/subset? r (node/r tree2)) + (node/find k tree2) + (node/subset? l tree2))) + (else + (and (node/subset? l (node/l tree2)) + (node/subset? r (node/r tree2)))))))))) + + + ;;; Tree interface: stripping off or injecting the tree types + + (define (tree/map-add tree k v) + (%make-wt-tree (tree/type tree) + (node/add (tree/root tree) k v))) + + (define (tree/insert! tree k v) + (set-tree/root! tree (node/add (tree/root tree) k v))) + + (define (tree/delete tree k) + (%make-wt-tree (tree/type tree) + (node/delete k (tree/root tree)))) + + (define (tree/delete! tree k) + (set-tree/root! tree (node/delete k (tree/root tree)))) + + (define (tree/split-lt tree key) + (%make-wt-tree (tree/type tree) + (node/split-lt (tree/root tree) key))) + + (define (tree/split-gt tree key) + (%make-wt-tree (tree/type tree) + (node/split-gt (tree/root tree) key))) + + (define (tree/union tree1 tree2) + (%make-wt-tree (tree/type tree1) + (node/union (tree/root tree1) (tree/root tree2)))) + + (define (tree/intersection tree1 tree2) + (%make-wt-tree (tree/type tree1) + (node/intersection (tree/root tree1) (tree/root tree2)))) + + (define (tree/difference tree1 tree2) + (%make-wt-tree (tree/type tree1) + (node/difference (tree/root tree1) (tree/root tree2)))) + + (define (tree/subset? tree1 tree2) + (node/subset? (tree/root tree1) (tree/root tree2))) + + (define (alist->tree alist) + (define (loop alist node) + (cond ((null? alist) node) + ((pair? alist) (loop (cdr alist) + (node/add node (caar alist) (cdar alist)))) + (else + (error:wrong-type-argument alist "alist" 'alist->tree)))) + (%make-wt-tree my-type (loop alist empty))) + + (define (tree/get tree key default) + (let ((node (node/find key (tree/root tree)))) + (if node + (node/v node) + default))) + + (define (tree/rank tree key) (node/rank key (tree/root tree) 0)) + + (define (tree/member? key tree) + (and (node/find key (tree/root tree)) + #t)) + + (define my-type #F) + + (set! my-type + (%make-tree-type + key<? ; key<? + alist->tree ; alist->tree + tree/map-add ; add + tree/insert! ; insert! + tree/delete ; delete + tree/delete! ; delete! + tree/member? ; member? + tree/get ; lookup + tree/split-lt ; split-lt + tree/split-gt ; split-gt + tree/union ; union + tree/intersection ; intersection + tree/difference ; difference + tree/subset? ; subset? + tree/rank ; rank + )) + + my-type) + + (define (guarantee-tree tree procedure) + (if (not (wt-tree? tree)) + (error:wrong-type-argument tree "weight-balanced tree" procedure))) + + (define (guarantee-tree-type type procedure) + (if (not (tree-type? type)) + (error:wrong-type-argument type "weight-balanced tree type" procedure))) + + (define (guarantee-compatible-trees tree1 tree2 procedure) + (guarantee-tree tree1 procedure) + (guarantee-tree tree2 procedure) + (if (not (eq? (tree/type tree1) (tree/type tree2))) + (error "The trees" tree1 'and tree2 'have 'incompatible 'types + (tree/type tree1) 'and (tree/type tree2)))) + +;;;______________________________________________________________________ +;;; +;;; Export interface +;;; + (set! make-wt-tree-type local:make-wt-tree-type) + + (set! make-wt-tree + (lambda (tree-type) + (%make-wt-tree tree-type empty))) + + (set! singleton-wt-tree + (lambda (type key value) + (guarantee-tree-type type 'singleton-wt-tree) + (%make-wt-tree type (node/singleton key value)))) + + (set! alist->wt-tree + (lambda (type alist) + (guarantee-tree-type type 'alist->wt-tree) + ((tree-type/alist->tree type) alist))) + + (set! wt-tree/empty? + (lambda (tree) + (guarantee-tree tree 'wt-tree/empty?) + (empty? (tree/root tree)))) + + (set! wt-tree/size + (lambda (tree) + (guarantee-tree tree 'wt-tree/size) + (node/size (tree/root tree)))) + + (set! wt-tree/add + (lambda (tree key datum) + (guarantee-tree tree 'wt-tree/add) + ((tree-type/add (tree/type tree)) tree key datum))) + + (set! wt-tree/delete + (lambda (tree key) + (guarantee-tree tree 'wt-tree/delete) + ((tree-type/delete (tree/type tree)) tree key))) + + (set! wt-tree/add! + (lambda (tree key datum) + (guarantee-tree tree 'wt-tree/add!) + ((tree-type/insert! (tree/type tree)) tree key datum))) + + (set! wt-tree/delete! + (lambda (tree key) + (guarantee-tree tree 'wt-tree/delete!) + ((tree-type/delete! (tree/type tree)) tree key))) + + (set! wt-tree/member? + (lambda (key tree) + (guarantee-tree tree 'wt-tree/member?) + ((tree-type/member? (tree/type tree)) key tree))) + + (set! wt-tree/lookup + (lambda (tree key default) + (guarantee-tree tree 'wt-tree/lookup) + ((tree-type/lookup (tree/type tree)) tree key default))) + + (set! wt-tree/split< + (lambda (tree key) + (guarantee-tree tree 'wt-tree/split<) + ((tree-type/split-lt (tree/type tree)) tree key))) + + (set! wt-tree/split> + (lambda (tree key) + (guarantee-tree tree 'wt-tree/split>) + ((tree-type/split-gt (tree/type tree)) tree key))) + + (set! wt-tree/union + (lambda (tree1 tree2) + (guarantee-compatible-trees tree1 tree2 'wt-tree/union) + ((tree-type/union (tree/type tree1)) tree1 tree2))) + + (set! wt-tree/intersection + (lambda (tree1 tree2) + (guarantee-compatible-trees tree1 tree2 'wt-tree/intersection) + ((tree-type/intersection (tree/type tree1)) tree1 tree2))) + + (set! wt-tree/difference + (lambda (tree1 tree2) + (guarantee-compatible-trees tree1 tree2 'wt-tree/difference) + ((tree-type/difference (tree/type tree1)) tree1 tree2))) + + (set! wt-tree/subset? + (lambda (tree1 tree2) + (guarantee-compatible-trees tree1 tree2 'wt-tree/subset?) + ((tree-type/subset? (tree/type tree1)) tree1 tree2))) + + (set! wt-tree/set-equal? + (lambda (tree1 tree2) + (and (wt-tree/subset? tree1 tree2) + (wt-tree/subset? tree2 tree1)))) + + (set! wt-tree/fold + (lambda (combiner-key-datum-result init tree) + (guarantee-tree tree 'wt-tree/fold) + (node/inorder-fold combiner-key-datum-result + init + (tree/root tree)))) + + (set! wt-tree/for-each + (lambda (action-key-datum tree) + (guarantee-tree tree 'wt-tree/for-each) + (node/for-each action-key-datum (tree/root tree)))) + + (set! wt-tree/index + (lambda (tree index) + (guarantee-tree tree 'wt-tree/index) + (let ((node (node/index (tree/root tree) index))) + (and node (node/k node))))) + + (set! wt-tree/index-datum + (lambda (tree index) + (guarantee-tree tree 'wt-tree/index-datum) + (let ((node (node/index (tree/root tree) index))) + (and node (node/v node))))) + + (set! wt-tree/index-pair + (lambda (tree index) + (guarantee-tree tree 'wt-tree/index-pair) + (let ((node (node/index (tree/root tree) index))) + (and node (cons (node/k node) (node/v node)))))) + + (set! wt-tree/rank + (lambda (tree key) + (guarantee-tree tree 'wt-tree/rank) + ((tree-type/rank (tree/type tree)) tree key))) + + (set! wt-tree/min + (lambda (tree) + (guarantee-tree tree 'wt-tree/min) + (node/k (node/min (tree/root tree))))) + + (set! wt-tree/min-datum + (lambda (tree) + (guarantee-tree tree 'wt-tree/min-datum) + (node/v (node/min (tree/root tree))))) + + (set! wt-tree/min-pair + (lambda (tree) + (guarantee-tree tree 'wt-tree/min-pair) + (let ((node (node/min (tree/root tree)))) + (cons (node/k node) (node/v node))))) + + (set! wt-tree/delete-min + (lambda (tree) + (guarantee-tree tree 'wt-tree/delete-min) + (%make-wt-tree (tree/type tree) + (node/delmin (tree/root tree))))) + + (set! wt-tree/delete-min! + (lambda (tree) + (guarantee-tree tree 'wt-tree/delete-min!) + (set-tree/root! tree (node/delmin (tree/root tree))))) + + ;; < is a lexpr. Many compilers can open-code < so the lambda is faster + ;; than passing <. + (set! number-wt-type (local:make-wt-tree-type (lambda (u v) (< u v)))) + (set! string-wt-type (local:make-wt-tree-type string<?)) + + 'done) + +;;; Local Variables: +;;; eval: (put 'with-n-node 'scheme-indent-function 1) +;;; eval: (put 'with-n-node 'scheme-indent-hook 1) +;;; End: diff --git a/yasyn.scm b/yasyn.scm new file mode 100644 index 0000000..12228f4 --- /dev/null +++ b/yasyn.scm @@ -0,0 +1,201 @@ +;;"yasyn.scm" YASOS in terms of "object.scm" +;;;From: whumeniu@datap.ca (Wade Humeniuk) + +(require 'object) + +(define yasos:instance? object?) +;; Removed (define yasos:make-instance 'bogus) ;; +;; Removed (define-syntax YASOS:INSTANCE-DISPATCHER ;; alias so compiler can inline for speed +;; (syntax-rules () ((yasos:instance-dispatcher inst) (cdr inst)))) +;; DEFINE-OPERATION + +(define-syntax define-operation + (syntax-rules () + ((define-operation (<name> <inst> <arg> ...) <exp1> <exp2> ...) + ;;=> + (define <name> (make-generic-method + (lambda (<inst> <arg> ...) <exp1> <exp2> ...)))) + + ((define-operation (<name> <inst> <arg> ...) ) ;; no body + ;;=> + (define-operation (<name> <inst> <arg> ...) + (slib:error "Operation not handled" + '<name> + (format #f (if (yasos:instance? <inst>) "#<INSTANCE>" "~s") + <inst>)))))) + +;; DEFINE-PREDICATE + +(define-syntax define-predicate + (syntax-rules () + ((define-predicate <name>) + ;;=> + (define <name> (make-generic-predicate))))) + +;; OBJECT + +(define-syntax object + (syntax-rules () + ((object ((<name> <self> <arg> ...) <exp1> <exp2> ...) ...) + ;;=> + (let ((self (make-object))) + (make-method! self <name> (lambda (<self> <arg> ...) <exp1> <exp2> ...)) + ... + self)))) + +;; OBJECT with MULTIPLE INHERITANCE {First Found Rule} + +(define-syntax object-with-ancestors + (syntax-rules () + ((object-with-ancestors ( (<ancestor1> <init1>) ... ) + ((<name> <self> <arg> ...) <exp1> <exp2> ...) ...) + ;;=> + (let* ((<ancestor1> <init1>) + ... + (self (make-object <ancestor1> ...))) + (make-method! self <name> (lambda (<self> <arg> ...) <exp1> <exp2> ...)) + ... + self)))) + +;; OPERATE-AS {a.k.a. send-to-super} + +; used in operations/methods + +(define-syntax operate-as + (syntax-rules () + ((operate-as <component> <op> <composit> <arg> ...) ;; What is <composit> ??? + ;;=> + ((get-method <component> <op>) <composit> <arg> ...)))) + + + +;; SET & SETTER + + +(define-syntax set + (syntax-rules () + ((set (<access> <index> ...) <newval>) + ((yasos:setter <access>) <index> ... <newval>) + ) + ((set <var> <newval>) + (set! <var> <newval>) + ) +) ) + + +(define yasos:add-setter 'bogus) +(define yasos:remove-setter-for 'bogus) + +(define yasos:setter + (let ( (known-setters (list (cons car set-car!) + (cons cdr set-cdr!) + (cons vector-ref vector-set!) + (cons string-ref string-set!)) + ) + (added-setters '()) + ) + + (set! YASOS:ADD-SETTER + (lambda (getter setter) + (set! added-setters (cons (cons getter setter) added-setters))) + ) + (set! YASOS:REMOVE-SETTER-FOR + (lambda (getter) + (cond + ((null? added-setters) + (slib:error "REMOVE-SETTER-FOR: Unknown getter" getter) + ) + ((eq? getter (caar added-setters)) + (set! added-setters (cdr added-setters)) + ) + (else + (let loop ((x added-setters) (y (cdr added-setters))) + (cond + ((null? y) (slib:error "REMOVE-SETTER-FOR: Unknown getter" + getter)) + ((eq? getter (caar y)) (set-cdr! x (cdr y))) + (else (loop (cdr x) (cdr y))) + ) ) ) + ) ) ) + + (letrec ( (self + (lambda (proc-or-operation) + (cond ((assq proc-or-operation known-setters) => cdr) + ((assq proc-or-operation added-setters) => cdr) + (else (proc-or-operation self))) ) + ) ) + self) +) ) + + + +(define (yasos:make-access-operation <name>) + (letrec ( (setter-dispatch + (lambda (inst . args) + (cond + ((and (yasos:instance? inst) + (get-method inst setter-dispatch)) + => (lambda (method) (apply method (cons inst args))) + ) + (else #f))) + ) + (self + (lambda (inst . args) + (cond + ((eq? inst yasos:setter) setter-dispatch) ; for (setter self) + ((and (yasos:instance? inst) + (get-method inst self)) + => (lambda (method) (apply method (cons inst args))) + ) + (else (slib:error "Operation not handled" <name> inst)) + ) ) + ) + ) + + self +) ) + +(define-syntax define-access-operation + (syntax-rules () + ((define-access-operation <name>) + ;=> + (define <name> (yasos:make-access-operation '<name>)) +) ) ) + + + +;;--------------------- +;; general operations +;;--------------------- + +(define-operation (yasos:print obj port) + (format port + ;; if an instance does not have a PRINT operation.. + (if (yasos:instance? obj) "#<INSTANCE>" "~s") + obj +) ) + +(define-operation (yasos:size obj) + ;; default behavior + (cond + ((vector? obj) (vector-length obj)) + ((list? obj) (length obj)) + ((pair? obj) 2) + ((string? obj) (string-length obj)) + ((char? obj) 1) + (else + (slib:error "Operation not supported: size" obj)) +) ) + +(require 'format) + +;;; exports: + +(define print yasos:print) ; print also in debug.scm +(define size yasos:size) +(define add-setter yasos:add-setter) +(define remove-setter-for yasos:remove-setter-for) +(define setter yasos:setter) + +(provide 'oop) ;in case we were loaded this way. +(provide 'yasos) |