summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTaylan Kammer <taylan.kammer@gmail.com>2025-04-05 17:11:29 +0200
committerTaylan Kammer <taylan.kammer@gmail.com>2025-04-05 17:11:29 +0200
commit70089dacfa6bab5a1e1d0d5aa257e2d671493beb (patch)
tree913b19c94792e2d41fdc800d728ad0bdabf0fada /src
parentcf934006c650d3d008a4408bedbd95597f906e43 (diff)
uhhhh buncha changes
Diffstat (limited to 'src')
-rw-r--r--src/test/data/parser-torture.scm132358
-rw-r--r--src/test/parse.zig70
-rw-r--r--src/test/strings.zig14
-rw-r--r--src/test/values.zig49
-rw-r--r--src/zisp.zig3
-rw-r--r--src/zisp/gc.zig60
-rw-r--r--src/zisp/io/Parser.zig165
-rw-r--r--src/zisp/io/unparser.zig98
-rw-r--r--src/zisp/lib/list.zig8
-rw-r--r--src/zisp/value.zig432
-rw-r--r--src/zisp/value/boole.zig4
-rw-r--r--src/zisp/value/char.zig4
-rw-r--r--src/zisp/value/fixnum.zig6
-rw-r--r--src/zisp/value/istr.zig51
-rw-r--r--src/zisp/value/pair.zig36
-rw-r--r--src/zisp/value/ptr.zig134
-rw-r--r--src/zisp/value/rune.zig35
-rw-r--r--src/zisp/value/seq.zig3
-rw-r--r--src/zisp/value/sstr.zig39
19 files changed, 594 insertions, 132975 deletions
diff --git a/src/test/data/parser-torture.scm b/src/test/data/parser-torture.scm
deleted file mode 100644
index d475379..0000000
--- a/src/test/data/parser-torture.scm
+++ /dev/null
@@ -1,132358 +0,0 @@
-;;; rnrs exceptions (6) --- R6RS exceptions
-
-;; Copyright (C) 2013 Taylan Ulrich Bayırlı/Kammer
-
-;; Author: Taylan Ulrich Bayırlı/Kammer <taylanbayirli@gmail.com>
-;; Keywords: ffi struct bytevector bytestructure
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; A clean implementation of (rnrs exceptions (6)). The dynamic environment
-;; capturing operations are noteworthy.
-
-
-;;; Code:
-
-(library
- (rnrs exceptions (6))
- (export with-exception-handler raise raise-continuable guard)
- (import (rnrs base (6))
- (srfi 39))
-
-;;; Helpers
-
-;;; Ignores any extra `else' clauses.
-;;; Helps to generate cond clauses with a default `else' clause.
- (define-syntax cond+
- (syntax-rules (else)
- ((cond+ clause ... (else else1) (else else2))
- (cond+ clause ... (else else1)))
- ((cond+ clause ...)
- (cond clause ...))))
-
-;;; Captures the current dynamic environment. It is reified as a procedure that
-;;; accepts a thunk and executes it in the captured dynenv.
- (define (capture-dynenv)
- ((call/cc
- (lambda (captured-env)
- (lambda ()
- (lambda (proc)
- (call/cc
- (lambda (go-back)
- (captured-env
- (lambda ()
- (call-with-values proc go-back)))))))))))
-
-;;; Captures the current dynamic environment and returns a procedure that
-;;; accepts as many arguments as PROC and applies PROC to them in that dynenv.
-;;; In other words, returns a version of PROC that's tied to the current dynenv.
- (define (dynenv-proc proc)
- (let ((env (capture-dynenv)))
- (lambda args
- (env (lambda () (apply proc args))))))
-
-;;; Returns a procedure that's always executed in the current dynamic
-;;; environment and not the one from which it's called.
- (define-syntax dynenv-lambda
- (syntax-rules ()
- ((_ args body body* ...)
- (dynenv-proc (lambda args body body* ...)))))
-
-
-;;; Main code:
-
- (define handlers (make-parameter '()))
-
- (define &non-continuable '&non-continuable)
-
- (define (with-exception-handler handler thunk)
- (parameterize ((handlers (cons handler (handlers))))
- (thunk)))
-
- (define (%raise condition continuable?)
- (if (null? (handlers))
- (error "unhandled exception" condition)
- (let ((handler (car (handlers))))
- (parameterize ((handlers (cdr (handlers))))
- (if continuable?
- (handler condition)
- (begin
- (handler condition)
- (%raise &non-continuable #f)))))))
-
- (define (raise-continuable condition)
- (%raise condition #t))
-
- (define (raise condition)
- (%raise condition #f))
-
- (define-syntax guard
- (syntax-rules ()
- ((guard (var clause clause* ...)
- body body* ...)
- (call/cc
- (lambda (return)
- (let ((handler (dynenv-lambda (var re-raise)
- (return
- (cond+ clause
- clause*
- ...
- (else (re-raise)))))))
- (with-exception-handler
- (lambda (condition)
- (let ((re-raise (dynenv-lambda ()
- (raise condition))))
- (handler condition re-raise)))
- (lambda ()
- body body* ...))))))))
- )
-(define-module (test)
- #\use-module (bytestructures guile))
-
-(display cstring-pointer)
-(newline)
-;;; align.scm --- Alignment calculation helpers.
-
-;; Copyright © 2018 Taylan Kammer <taylan.kammer@gmail.com>
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-
-;;; Code:
-
-;;; Either remains at 'position' or rounds up to the next multiple of
-;;; 'alignment' depending on whether 'size' (if not greater than 'alignment')
-;;; would fit. Returns three values: the chosen position, the start of the
-;;; alignment boundary of the chosen position, and the bit offset of the chosen
-;;; position from the start of the alignment boundary. A bit is represented by
-;;; the value 1/8.
-(define (align position size alignment)
- (let* ((integer (floor position))
- (fraction (- position integer)))
- (let-values (((prev-boundary-index offset) (floor/ integer alignment)))
- (let* ((prev-boundary (* prev-boundary-index alignment))
- (next-boundary (+ prev-boundary alignment)))
- (if (< next-boundary (+ position (min size alignment)))
- (values next-boundary next-boundary 0)
- (values position prev-boundary (* 8 (+ offset fraction))))))))
-
-;;; Returns 'position' if it's already a multiple of 'alignment'; otherwise
-;;; returns the next multiple.
-(define (next-boundary position alignment)
- (align position +inf.0 alignment))
-
-;;; align.scm ends here
-;;; bytestructures --- Structured access to bytevector contents.
-
-;; Copyright © 2015, 2016 Taylan Kammer <taylan.kammer@gmail.com>
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This is the base of the module, defining the data types and procedures that
-;; make up the bytestructures framework.
-
-
-;;; Code:
-
-;;; Descriptors
-
-(define-record-type <bytestructure-descriptor>
- (%make-bytestructure-descriptor size alignment unwrapper getter setter meta)
- bytestructure-descriptor?
- (size bd-size)
- (alignment bd-alignment)
- (unwrapper bd-unwrapper)
- (getter bd-getter)
- (setter bd-setter)
- (meta bd-meta))
-
-(define make-bytestructure-descriptor
- (case-lambda
- ((size alignment unwrapper getter setter)
- (%make-bytestructure-descriptor
- size alignment unwrapper getter setter #f))
- ((size alignment unwrapper getter setter meta)
- (%make-bytestructure-descriptor
- size alignment unwrapper getter setter meta))))
-
-(define bytestructure-descriptor-size
- (case-lambda
- ((descriptor) (bytestructure-descriptor-size descriptor #f #f))
- ((descriptor bytevector offset)
- (let ((size (bd-size descriptor)))
- (if (procedure? size)
- (size #f bytevector offset)
- size)))))
-
-(define (bytestructure-descriptor-size/syntax bytevector offset descriptor)
- (let ((size (bd-size descriptor)))
- (if (procedure? size)
- (size #t bytevector offset)
- size)))
-
-(define bytestructure-descriptor-alignment bd-alignment)
-(define bytestructure-descriptor-unwrapper bd-unwrapper)
-(define bytestructure-descriptor-getter bd-getter)
-(define bytestructure-descriptor-setter bd-setter)
-(define bytestructure-descriptor-metadata bd-meta)
-
-
-;;; Bytestructures
-
-(define-record-type <bytestructure>
- (make-bytestructure bytevector offset descriptor)
- bytestructure?
- (bytevector bytestructure-bytevector)
- (offset bytestructure-offset)
- (descriptor bytestructure-descriptor))
-
-(define bytestructure
- (case-lambda ((descriptor) (%bytestructure descriptor #f #f))
- ((descriptor values) (%bytestructure descriptor #t values))))
-
-(define (%bytestructure descriptor init? values)
- (let ((bytevector (make-bytevector
- (bytestructure-descriptor-size descriptor))))
- (when init?
- (bytestructure-primitive-set! bytevector 0 descriptor values))
- (make-bytestructure bytevector 0 descriptor)))
-
-(define (bytestructure-size bytestructure)
- (bytestructure-descriptor-size (bytestructure-descriptor bytestructure)
- (bytestructure-bytevector bytestructure)
- (bytestructure-offset bytestructure)))
-
-(define-syntax-rule (bytestructure-unwrap <bytestructure> <index> ...)
- (let ((bytestructure <bytestructure>))
- (let ((bytevector (bytestructure-bytevector bytestructure))
- (offset (bytestructure-offset bytestructure))
- (descriptor (bytestructure-descriptor bytestructure)))
- (bytestructure-unwrap* bytevector offset descriptor <index> ...))))
-
-(define-syntax bytestructure-unwrap*
- (syntax-rules ()
- ((_ <bytevector> <offset> <descriptor>)
- (values <bytevector> <offset> <descriptor>))
- ((_ <bytevector> <offset> <descriptor> <index> <indices> ...)
- (let ((bytevector <bytevector>)
- (offset <offset>)
- (descriptor <descriptor>))
- (let ((unwrapper (bd-unwrapper descriptor)))
- (when (not unwrapper)
- (error "Cannot index through this descriptor." descriptor))
- (let-values (((bytevector* offset* descriptor*)
- (unwrapper #f bytevector offset <index>)))
- (bytestructure-unwrap*
- bytevector* offset* descriptor* <indices> ...)))))))
-
-(define-syntax-rule (bytestructure-ref <bytestructure> <index> ...)
- (let-values (((bytevector offset descriptor)
- (bytestructure-unwrap <bytestructure> <index> ...)))
- (bytestructure-primitive-ref bytevector offset descriptor)))
-
-(define-syntax-rule (bytestructure-ref*
- <bytevector> <offset> <descriptor> <index> ...)
- (let-values (((bytevector offset descriptor)
- (bytestructure-unwrap*
- <bytevector> <offset> <descriptor> <index> ...)))
- (bytestructure-primitive-ref bytevector offset descriptor)))
-
-(define (bytestructure-primitive-ref bytevector offset descriptor)
- (let ((getter (bd-getter descriptor)))
- (if getter
- (getter #f bytevector offset)
- (make-bytestructure bytevector offset descriptor))))
-
-(define-syntax-rule (bytestructure-set! <bytestructure> <index> ... <value>)
- (let-values (((bytevector offset descriptor)
- (bytestructure-unwrap <bytestructure> <index> ...)))
- (bytestructure-primitive-set! bytevector offset descriptor <value>)))
-
-(define-syntax-rule (bytestructure-set!*
- <bytevector> <offset> <descriptor> <index> ... <value>)
- (let-values (((bytevector offset descriptor)
- (bytestructure-unwrap*
- <bytevector> <offset> <descriptor> <index> ...)))
- (bytestructure-primitive-set! bytevector offset descriptor <value>)))
-
-(define (bytestructure-primitive-set! bytevector offset descriptor value)
- (let ((setter (bd-setter descriptor)))
- (if setter
- (setter #f bytevector offset value)
- (if (bytevector? value)
- (bytevector-copy! bytevector offset value 0
- (bytestructure-descriptor-size
- descriptor bytevector offset))
- (error "Cannot write value with this bytestructure descriptor."
- value descriptor)))))
-
-(define (bytestructure-ref/dynamic bytestructure . indices)
- (let-values (((bytevector offset descriptor)
- (bytestructure-unwrap bytestructure)))
- (let loop ((bytevector bytevector)
- (offset offset)
- (descriptor descriptor)
- (indices indices))
- (if (null? indices)
- (bytestructure-primitive-ref bytevector offset descriptor)
- (let-values (((bytevector* offset* descriptor*)
- (bytestructure-unwrap*
- bytevector offset descriptor (car indices))))
- (loop bytevector*
- offset*
- descriptor*
- (cdr indices)))))))
-
-(define (bytestructure-set!/dynamic bytestructure . args)
- (let-values (((bytevector offset descriptor)
- (bytestructure-unwrap bytestructure)))
- (let loop ((bytevector bytevector)
- (offset offset)
- (descriptor descriptor)
- (args args))
- (if (null? (cdr args))
- (bytestructure-primitive-set! bytevector offset descriptor (car args))
- (let-values (((bytevector* offset* descriptor*)
- (bytestructure-unwrap*
- bytevector offset descriptor (car args))))
- (loop bytevector*
- offset*
- descriptor*
- (cdr args)))))))
-
-(define-syntax-case-stubs
- bytestructure-unwrap/syntax
- bytestructure-ref/syntax
- bytestructure-set!/syntax
- define-bytestructure-accessors)
-
-(cond-expand
- (guile (include-from-path "bytestructures/body/base.syntactic.scm"))
- (syntax-case (include "base.syntactic.scm"))
- (else))
-
-;;; base.scm ends here
-;;; bytestructures --- Structured access to bytevector contents.
-
-;; Copyright © 2015 Taylan Kammer <taylan.kammer@gmail.com>
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This is an extension to the base of the module which allows using the API
-;; purely in the macro-expand phase, which puts some limitations on its use but
-;; reduces run-time overhead to zero or nearly zero.
-
-
-;;; Code:
-
-(define-syntax-rule (syntax-case-lambda <pattern> <body>)
- (lambda (stx)
- (syntax-case stx ()
- (<pattern> <body>))))
-
-(define syntax-car (syntax-case-lambda (car . cdr) #'car))
-(define syntax-cdr (syntax-case-lambda (car . cdr) #'cdr))
-(define syntax-null? (syntax-case-lambda stx (null? (syntax->datum #'stx))))
-
-(define (syntactic-unwrap bytevector offset descriptor indices)
- (let loop ((bytevector bytevector)
- (offset offset)
- (descriptor descriptor)
- (indices indices))
- (if (not (syntax-null? indices))
- (let ((unwrapper (bd-unwrapper descriptor)))
- (when (not unwrapper)
- (error "Cannot index through this descriptor." descriptor))
- (let-values (((bytevector* offset* descriptor*)
- (unwrapper #t bytevector offset (syntax-car indices))))
- (loop bytevector* offset* descriptor* (syntax-cdr indices))))
- (let ((getter (bd-getter descriptor))
- (setter (bd-setter descriptor)))
- (values bytevector offset descriptor getter setter)))))
-
-(define (bytestructure-unwrap/syntax bytevector offset descriptor indices)
- (let-values (((bytevector* offset* _descriptor _getter _setter)
- (syntactic-unwrap bytevector offset descriptor indices)))
- #`(values #,bytevector* #,offset*)))
-
-(define (bytestructure-ref/syntax bytevector offset descriptor indices)
- (let-values (((bytevector* offset* descriptor* getter _setter)
- (syntactic-unwrap bytevector offset descriptor indices)))
- (if getter
- (getter #t bytevector* offset*)
- (error "The indices given to bytestructure-ref/syntax do not lead to a
-bytestructure descriptor that can decode values. You must have used the wrong
-getter macro, forgot to provide some of the indices, or meant to use the
-unwrapper instead of the getter. The given indices follow." indices))))
-
-(define (bytestructure-set!/syntax bytevector offset descriptor indices value)
- (let-values (((bytevector* offset* descriptor* _getter setter)
- (syntactic-unwrap bytevector offset descriptor indices)))
- (if setter
- (setter #t bytevector* offset* value)
- (error "The indices given to bytestructure-set!/syntax do not lead to a
-bytestructure descriptor that can encode values. You must have used the wrong
-setter macro, or forgot to provide some of the indices. The given indices
-follow." indices))))
-
-(define-syntax-rule (define-bytestructure-unwrapper <name> <descriptor>)
- (define-syntax <name>
- (let ((descriptor <descriptor>))
- (syntax-case-lambda (_ <bytevector> <offset> . <indices>)
- (bytestructure-unwrap/syntax
- #'<bytevector> #'<offset> descriptor #'<indices>)))))
-
-(define-syntax-rule (define-bytestructure-getter* <name> <descriptor>)
- (define-syntax <name>
- (let ((descriptor <descriptor>))
- (syntax-case-lambda (_ <bytevector> <offset> . <indices>)
- (bytestructure-ref/syntax
- #'<bytevector> #'<offset> descriptor #'<indices>)))))
-
-(define-syntax-rule (define-bytestructure-setter* <name> <descriptor>)
- (define-syntax <name>
- (let ((descriptor <descriptor>))
- (syntax-case-lambda (_ <bytevector> <offset> <index> (... ...) <value>)
- (bytestructure-set!/syntax
- #'<bytevector> #'<offset> descriptor #'(<index> (... ...)) #'<value>)))))
-
-(define-syntax-rule (define-bytestructure-getter <name> <descriptor>)
- (define-syntax <name>
- (let ((descriptor <descriptor>))
- (syntax-case-lambda (_ <bytevector> . <indices>)
- (bytestructure-ref/syntax #'<bytevector> 0 descriptor #'<indices>)))))
-
-(define-syntax-rule (define-bytestructure-setter <name> <descriptor>)
- (define-syntax <name>
- (let ((descriptor <descriptor>))
- (syntax-case-lambda (_ <bytevector> <index> (... ...) <value>)
- (bytestructure-set!/syntax
- #'<bytevector> 0 descriptor #'(<index> (... ...)) #'<value>)))))
-
-(define-syntax define-bytestructure-accessors
- (syntax-rules ()
- ((_ <descriptor> <unwrapper> <getter> <setter>)
- (begin
- (define-bytestructure-unwrapper <unwrapper> <descriptor>)
- (define-bytestructure-getter <getter> <descriptor>)
- (define-bytestructure-setter <setter> <descriptor>)))
- ((_ <descriptor> <unwrapper> <getter> <setter> <getter*> <setter*>)
- (begin
- (define-bytestructure-unwrapper <unwrapper> <descriptor>)
- (define-bytestructure-getter <getter> <descriptor>)
- (define-bytestructure-setter <setter> <descriptor>)
- (define-bytestructure-getter* <getter*> <descriptor>)
- (define-bytestructure-setter* <setter*> <descriptor>)))))
-
-;; Local Variables:
-;; eval: (put (quote syntax-case-lambda) (quote scheme-indent-function) 1)
-;; End:
-
-;;; base.syntactic.scm ends here
-;;; bitfields.scm --- Struct bitfield constructor.
-
-;; Copyright © 2015, 2016 Taylan Kammer <taylan.kammer@gmail.com>
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This module is complementary to the struct module. It isn't used on its own.
-
-;; This code partly uses rational numbers for byte counts and offsets, to
-;; represent granularity down to bits. I.e. 1/8 is a size or offset of one bit.
-
-
-;;; Code:
-
-;;; Only a macro for efficiency reasons.
-(define-syntax bit-field/signed
- (syntax-rules ()
- ((_ <num> <width> <start> <end> <signed?>)
- (let ((unsigned-value (bit-field <num> <start> <end>)))
- (if (not <signed?>)
- unsigned-value
- (let ((sign (bit-set? (- <width> 1) unsigned-value)))
- (if sign
- (- unsigned-value (expt 2 <width>))
- unsigned-value)))))))
-
-(define (validate-integer-descriptor descriptor)
- (when (not (assq descriptor integer-descriptors))
- (error "Invalid descriptor for bitfield." descriptor)))
-
-(define (integer-descriptor-signed? descriptor)
- (assq descriptor signed-integer-descriptors))
-
-(define integer-descriptor-signed->unsigned-mapping
- (map cons
- (map car signed-integer-descriptors)
- (map car unsigned-integer-descriptors)))
-
-(define (integer-descriptor-signed->unsigned descriptor)
- (cdr (assq descriptor integer-descriptor-signed->unsigned-mapping)))
-
-(define (unsigned-integer-descriptor integer-descriptor)
- (if (integer-descriptor-signed? integer-descriptor)
- (integer-descriptor-signed->unsigned integer-descriptor)
- integer-descriptor))
-
-(define-record-type <bitfield-metadata>
- (make-bitfield-metadata int-descriptor width)
- bitfield-metadata?
- (int-descriptor bitfield-metadata-int-descriptor)
- (width bitfield-metadata-width))
-
-(define (bitfield-descriptor int-descriptor bit-offset width)
- (validate-integer-descriptor int-descriptor)
- (let ((signed? (integer-descriptor-signed? int-descriptor))
- (uint-descriptor (unsigned-integer-descriptor int-descriptor)))
- (let ((num-getter (bytestructure-descriptor-getter uint-descriptor))
- (num-setter (bytestructure-descriptor-setter uint-descriptor)))
- (define start bit-offset)
- (define end (+ start width))
- (define (getter syntax? bytevector offset)
- (let ((num (num-getter syntax? bytevector offset)))
- (if syntax?
- (quasisyntax
- (bit-field/signed (unsyntax num) (unsyntax width)
- (unsyntax start) (unsyntax end)
- (unsyntax signed?)))
- (bit-field/signed num width start end signed?))))
- (define (setter syntax? bytevector offset value)
- (let* ((oldnum (num-getter syntax? bytevector offset))
- (newnum (if syntax?
- (quasisyntax
- (copy-bit-field (unsyntax oldnum) (unsyntax value)
- (unsyntax start) (unsyntax end)))
- (copy-bit-field oldnum value start end))))
- (num-setter syntax? bytevector offset newnum)))
- (define meta (make-bitfield-metadata int-descriptor width))
- (make-bytestructure-descriptor #f #f #f getter setter meta))))
-
-;;; bitfields.scm ends here
-;;; explicit-endianness.scm --- Auxiliary bytevector operations.
-
-;; Copyright © 2015 Taylan Kammer <taylan.kammer@gmail.com>
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; The numeric module requires top-level bindings to bytevector procedures with
-;; an explicit endianness, instead of the ones that take an endianness
-;; argument. This library provides them.
-
-
-;;; Code:
-
-(define-syntax define-explicit-endianness-getters
- (syntax-rules ()
- ((_ (original le-name be-name) ...)
- (begin
- (begin
- (define (le-name bytevector index)
- (original bytevector index (endianness little)))
- (define (be-name bytevector index)
- (original bytevector index (endianness big))))
- ...))))
-
-(define-explicit-endianness-getters
- (bytevector-ieee-single-ref bytevector-ieee-single-le-ref
- bytevector-ieee-single-be-ref)
- (bytevector-ieee-double-ref bytevector-ieee-double-le-ref
- bytevector-ieee-double-be-ref)
- (bytevector-s16-ref bytevector-s16le-ref
- bytevector-s16be-ref)
- (bytevector-u16-ref bytevector-u16le-ref
- bytevector-u16be-ref)
- (bytevector-s32-ref bytevector-s32le-ref
- bytevector-s32be-ref)
- (bytevector-u32-ref bytevector-u32le-ref
- bytevector-u32be-ref)
- (bytevector-s64-ref bytevector-s64le-ref
- bytevector-s64be-ref)
- (bytevector-u64-ref bytevector-u64le-ref
- bytevector-u64be-ref))
-
-(define-syntax define-explicit-endianness-setters
- (syntax-rules ()
- ((_ (original le-name be-name) ...)
- (begin
- (begin
- (define (le-name bytevector index value)
- (original bytevector index value (endianness little)))
- (define (be-name bytevector index value)
- (original bytevector index value (endianness big))))
- ...))))
-
-(define-explicit-endianness-setters
- (bytevector-ieee-single-set! bytevector-ieee-single-le-set!
- bytevector-ieee-single-be-set!)
- (bytevector-ieee-double-set! bytevector-ieee-double-le-set!
- bytevector-ieee-double-be-set!)
- (bytevector-s16-set! bytevector-s16le-set!
- bytevector-s16be-set!)
- (bytevector-u16-set! bytevector-u16le-set!
- bytevector-u16be-set!)
- (bytevector-s32-set! bytevector-s32le-set!
- bytevector-s32be-set!)
- (bytevector-u32-set! bytevector-u32le-set!
- bytevector-u32be-set!)
- (bytevector-s64-set! bytevector-s64le-set!
- bytevector-s64be-set!)
- (bytevector-u64-set! bytevector-u64le-set!
- bytevector-u64be-set!))
-
-;;; explicit-endianness.scm ends here
-;;; numeric.scm --- Numeric types as supported by (rnrs bytevectors).
-
-;; Copyright © 2015, 2016 Taylan Kammer <taylan.kammer@gmail.com>
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This module defines descriptors for numeric types of specific size, and
-;; native or specific endianness, as made possible by the bytevector referencing
-;; and assigning procedures in the (rnrs bytevectors) module.
-
-;; We use the strange cond-expand/runtime macro to make sure that certain checks
-;; for CPU architecture and data model are done at library-load-time and not
-;; compile time, since one might cross-compile the library.
-
-
-;;; Code:
-
-(define base-environment
- (cond-expand
- (guile-2
- (current-module))
- (else
- (environment '(scheme base)))))
-
-(define-syntax cond-expand/runtime
- (syntax-rules ()
- ((_ (<cond> <expr>) ...)
- (let ((const (eval '(cond-expand (<cond> '<expr>) ...)
- base-environment)))
- (cond
- ((equal? const '<expr>) <expr>)
- ...)))))
-
-(define i8align 1)
-
-(define i16align 2)
-
-(define i32align 4)
-
-(define i64align
- (cond-expand/runtime
- (i386 4)
- (else 8)))
-
-(define f32align 4)
-
-(define f64align
- (cond-expand/runtime
- (i386 4)
- (else 8)))
-
-(define-syntax-rule (make-numeric-descriptor <size> <align> <getter> <setter>)
- (let ()
- (define size <size>)
- (define alignment <align>)
- (define (getter syntax? bytevector offset)
- (if syntax?
- (quasisyntax
- (<getter> (unsyntax bytevector) (unsyntax offset)))
- (<getter> bytevector offset)))
- (define (setter syntax? bytevector offset value)
- (if syntax?
- (quasisyntax
- (<setter> (unsyntax bytevector) (unsyntax offset) (unsyntax value)))
- (<setter> bytevector offset value)))
- (make-bytestructure-descriptor size alignment #f getter setter)))
-
-(define-syntax-rule (define-numeric-descriptors <list>
- (<name> <size> <align> <getter> <setter>)
- ...)
- (begin
- (define <name>
- (make-numeric-descriptor <size> <align> <getter> <setter>))
- ...
- (define <list> (list (list <name> '<name> <getter> <setter>) ...))))
-
-(define-numeric-descriptors
- signed-integer-native-descriptors
- (int8 1 i8align bytevector-s8-ref bytevector-s8-set!)
- (int16 2 i16align bytevector-s16-native-ref bytevector-s16-native-set!)
- (int32 4 i32align bytevector-s32-native-ref bytevector-s32-native-set!)
- (int64 8 i64align bytevector-s64-native-ref bytevector-s64-native-set!))
-
-(define-numeric-descriptors
- unsigned-integer-native-descriptors
- (uint8 1 i8align bytevector-u8-ref bytevector-u8-set!)
- (uint16 2 i16align bytevector-u16-native-ref bytevector-u16-native-set!)
- (uint32 4 i32align bytevector-u32-native-ref bytevector-u32-native-set!)
- (uint64 8 i64align bytevector-u64-native-ref bytevector-u64-native-set!))
-
-(define-numeric-descriptors
- float-native-descriptors
- (float32 4 f32align
- bytevector-ieee-single-native-ref
- bytevector-ieee-single-native-set!)
- (float64 8 f64align
- bytevector-ieee-double-native-ref
- bytevector-ieee-double-native-set!))
-
-(define-syntax-rule (define-with-endianness <list> <endianness>
- (<name> <size> <align> <native-name> <getter> <setter>)
- ...)
- (begin
- (define <name>
- (if (equal? <endianness> (native-endianness))
- <native-name>
- (make-numeric-descriptor <size> <align> <getter> <setter>)))
- ...
- (define <list> (list (list <name> '<name> <getter> <setter>) ...))))
-
-(define-with-endianness
- signed-integer-le-descriptors (endianness little)
- (int16le 2 i16align int16 bytevector-s16le-ref bytevector-s16le-set!)
- (int32le 4 i32align int32 bytevector-s32le-ref bytevector-s32le-set!)
- (int64le 8 i64align int64 bytevector-s64le-ref bytevector-s64le-set!))
-
-(define-with-endianness
- signed-integer-be-descriptors (endianness big)
- (int16be 2 i16align int16 bytevector-s16be-ref bytevector-s16be-set!)
- (int32be 4 i32align int32 bytevector-s32be-ref bytevector-s32be-set!)
- (int64be 8 i64align int64 bytevector-s64be-ref bytevector-s64be-set!))
-
-(define-with-endianness
- unsigned-integer-le-descriptors (endianness little)
- (uint16le 2 i16align uint16 bytevector-u16le-ref bytevector-u16le-set!)
- (uint32le 4 i32align uint32 bytevector-u32le-ref bytevector-u32le-set!)
- (uint64le 8 i64align uint64 bytevector-u64le-ref bytevector-u64le-set!))
-
-(define-with-endianness
- unsigned-integer-be-descriptors (endianness big)
- (uint16be 2 i16align uint16 bytevector-u16be-ref bytevector-u16be-set!)
- (uint32be 4 i32align uint32 bytevector-u32be-ref bytevector-u32be-set!)
- (uint64be 8 i64align uint64 bytevector-u64be-ref bytevector-u64be-set!))
-
-(define-with-endianness
- float-le-descriptors (endianness little)
- (float32le 4 f32align float32
- bytevector-ieee-single-le-ref
- bytevector-ieee-single-le-set!)
- (float64le 8 f64align float64
- bytevector-ieee-double-le-ref
- bytevector-ieee-double-le-set!))
-
-(define-with-endianness
- float-be-descriptors (endianness big)
- (float32be 4 f32align float32
- bytevector-ieee-single-be-ref
- bytevector-ieee-single-be-set!)
- (float64be 8 f64align float64
- bytevector-ieee-double-be-ref
- bytevector-ieee-double-be-set!))
-
-(define-syntax-rule (make-complex-descriptor
- <float-size> <float-align> <float-getter> <float-setter>)
- (let ()
- (define size (* 2 <float-size>))
- (define alignment <float-align>)
- (define (getter syntax? bytevector offset)
- (if syntax?
- (quasisyntax
- (let ((real (<float-getter> (unsyntax bytevector)
- (unsyntax offset)))
- (imag (<float-getter> (unsyntax bytevector)
- (+ (unsyntax offset) <float-size>))))
- (make-rectangular real imag)))
- (let ((real (<float-getter> bytevector offset))
- (imag (<float-getter> bytevector (+ offset <float-size>))))
- (make-rectangular real imag))))
- (define (setter syntax? bytevector offset value)
- (if syntax?
- (quasisyntax
- (let ((val (unsyntax value)))
- (let ((real (real-part val))
- (imag (imag-part val)))
- (<float-setter> (unsyntax bytevector)
- (unsyntax offset)
- real)
- (<float-setter> (unsyntax bytevector)
- (+ (unsyntax offset) <float-size>)
- imag))))
- (let ((real (real-part value))
- (imag (imag-part value)))
- (<float-setter> bytevector offset real)
- (<float-setter> bytevector (+ offset <float-size>) imag))))
- (make-bytestructure-descriptor size alignment #f getter setter)))
-
-(define-syntax-rule (define-complex-descriptors <list>
- (<name> <float-size> <float-align>
- <float-getter> <float-setter>)
- ...)
- (begin
- (define <name>
- (make-complex-descriptor <float-size> <float-align>
- <float-getter> <float-setter>))
- ...
- (define <list> (list (list <name> '<name> <float-getter> <float-setter>)
- ...))))
-
-(define-complex-descriptors
- complex-native-descriptors
- (complex64 4 f32align
- bytevector-ieee-single-native-ref
- bytevector-ieee-single-native-set!)
- (complex128 8 f64align
- bytevector-ieee-double-native-ref
- bytevector-ieee-double-native-set!))
-
-(define-syntax-rule (define-complex-with-endianness <list> <endianness>
- (<name> <float-size> <float-align> <native-name>
- <float-getter> <float-setter>)
- ...)
- (begin
- (define <name>
- (if (equal? <endianness> (native-endianness))
- <native-name>
- (make-complex-descriptor <float-size> <float-align>
- <float-getter> <float-setter>)))
- ...
- (define <list> (list (list <name> '<name> <float-getter> <float-setter>)
- ...))))
-
-(define-complex-with-endianness
- complex-le-descriptors (endianness little)
- (complex64le 4 f32align complex64
- bytevector-ieee-single-le-ref
- bytevector-ieee-single-le-set!)
- (complex128le 8 f64align complex128
- bytevector-ieee-double-le-ref
- bytevector-ieee-double-le-set!))
-
-(define-complex-with-endianness
- complex-be-descriptors (endianness big)
- (complex64be 4 f32align complex64
- bytevector-ieee-single-be-ref
- bytevector-ieee-single-be-set!)
- (complex128be 8 f64align complex128
- bytevector-ieee-double-be-ref
- bytevector-ieee-double-be-set!))
-
-(define signed-integer-descriptors
- (append signed-integer-native-descriptors
- signed-integer-le-descriptors
- signed-integer-be-descriptors))
-
-(define unsigned-integer-descriptors
- (append unsigned-integer-native-descriptors
- unsigned-integer-le-descriptors
- unsigned-integer-be-descriptors))
-
-(define integer-descriptors
- (append signed-integer-descriptors unsigned-integer-descriptors))
-
-(define float-descriptors
- (append float-native-descriptors
- float-le-descriptors
- float-be-descriptors))
-
-(define complex-descriptors
- (append complex-native-descriptors
- complex-le-descriptors
- complex-be-descriptors))
-
-(define numeric-descriptors
- (append integer-descriptors float-descriptors complex-descriptors))
-
-(define short int16)
-(define unsigned-short uint16)
-
-(define int (cond-expand/runtime
- (lp32 int16)
- (ilp64 int64)
- (else int32)))
-
-(define unsigned-int (cond-expand/runtime
- (lp32 uint16)
- (ilp64 uint64)
- (else uint32)))
-
-(define long (cond-expand/runtime
- (ilp64 int64)
- (lp64 int64)
- (else int32)))
-
-(define unsigned-long (cond-expand/runtime
- (ilp64 uint64)
- (lp64 uint64)
- (else uint32)))
-
-(define long-long int64)
-(define unsigned-long-long uint64)
-
-(define arch32bit? (cond-expand/runtime
- (lp32 #t)
- (ilp32 #t)
- (else #f)))
-
-(define intptr_t (if arch32bit?
- int32
- int64))
-
-(define uintptr_t (if arch32bit?
- uint32
- uint64))
-
-(define size_t uintptr_t)
-
-(define ssize_t intptr_t)
-
-(define ptrdiff_t intptr_t)
-
-(define float float32)
-(define double float64)
-
-;;; numeric.scm ends here
-;;; string.scm --- Strings in encodings supported by (rnrs bytevectors).
-
-;; Copyright © 2017 Taylan Kammer <taylan.kammer@gmail.com>
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This module defines descriptors for strings encoded in various encodings, as
-;; supported by (rnrs bytevectors).
-
-
-;;; Code:
-
-(define (ascii->string bytevector start end)
- (let ((string (utf8->string bytevector start end)))
- (when (not (= (string-length string) (bytevector-length bytevector)))
- (error "Bytevector contains non-ASCII characters." bytevector))
- string))
-
-(define (string->ascii string)
- (let ((bytevector (string->utf8 string)))
- (when (not (= (string-length string) (bytevector-length bytevector)))
- (error "String contains non-ASCII characters." string))
- bytevector))
-
-(define (bytevector->string bytevector offset size encoding)
- (case encoding
- ((ascii) (ascii->string bytevector offset (+ offset size)))
- ((utf8) (utf8->string bytevector offset (+ offset size)))
- (else
- (let ((bytevector (bytevector-copy bytevector offset (+ offset size))))
- (case encoding
- ((utf16le) (utf16->string bytevector 'little #t))
- ((utf16be) (utf16->string bytevector 'big #t))
- ((utf32le) (utf32->string bytevector 'little #t))
- ((utf32be) (utf32->string bytevector 'big #t))
- (else (error "Unknown string encoding." encoding)))))))
-
-(define (string->bytevector string encoding)
- (case encoding
- ((ascii) (string->ascii string))
- ((utf8) (string->utf8 string))
- ((utf16le) (string->utf16 string 'little))
- ((utf16be) (string->utf16 string 'big))
- ((utf32le) (string->utf32 string 'little))
- ((utf32be) (string->utf32 string 'big))))
-
-;;; Note: because macro output may not contain raw symbols, we cannot output
-;;; (quote foo) for raw symbol foo either, so there's no way to inject symbol
-;;; literals into macro output. Hence we inject references to the following
-;;; variables instead.
-
-(define ascii 'ascii)
-(define utf8 'utf8)
-(define utf16le 'utf16le)
-(define utf16be 'utf16be)
-(define utf32le 'utf32le)
-(define utf32be 'utf32be)
-
-;;; Make sure this returns a boolean and not any other type of value, as the
-;;; output will be part of macro output.
-(define (fixed-width-encoding? encoding)
- (not (not (memq encoding '(ascii utf32le utf32be)))))
-
-(define (bytevector-zero! bv start end)
- (do ((i start (+ i 1)))
- ((= i end))
- (bytevector-u8-set! bv i #x00)))
-
-(define (bs:string size encoding)
- (define alignment 1)
- (define (getter syntax? bytevector offset)
- (if syntax?
- (quasisyntax
- (bytevector->string (unsyntax bytevector)
- (unsyntax offset)
- (unsyntax size)
- (unsyntax
- (datum->syntax (syntax utf8) encoding))))
- (bytevector->string bytevector offset size encoding)))
- (define (setter syntax? bytevector offset string)
- (if syntax?
- (quasisyntax
- (let* ((bv (string->bytevector
- (unsyntax string)
- (unsyntax
- (datum->syntax (syntax utf8) encoding))))
- (length (bytevector-length bv)))
- (when (> length (unsyntax size))
- (error "String too long." (unsyntax string)))
- (when (and (unsyntax (fixed-width-encoding? encoding))
- (< length (unsyntax size)))
- (error "String too short." (unsyntax string)))
- (bytevector-copy! (unsyntax bytevector)
- (unsyntax offset)
- bv)
- (when (not (unsyntax (fixed-width-encoding? encoding)))
- (bytevector-zero! (unsyntax bytevector)
- (+ (unsyntax offset) (bytevector-length bv))
- (+ (unsyntax offset) (unsyntax size))))))
- (let* ((bv (string->bytevector string encoding))
- (length (bytevector-length bv)))
- (when (> length size)
- (error "String too long." string))
- (when (and (fixed-width-encoding? encoding) (< length size))
- (error "String too short." string))
- (bytevector-copy! bytevector offset bv)
- (when (not (fixed-width-encoding? encoding))
- (bytevector-zero! bytevector
- (+ offset (bytevector-length bv))
- (+ offset size))))))
- (make-bytestructure-descriptor size alignment #f getter setter))
-
-;;; string.scm ends here
-;;; struct.scm --- Struct descriptor constructor.
-
-;; Copyright © 2015, 2016, 2021 Taylan Kammer <taylan.kammer@gmail.com>
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This constructor allows the creation of struct descriptors with named and
-;; ordered fields with a specific content descriptor.
-
-;; This code partly uses rational numbers for byte counts and offsets, to
-;; represent granularity down to bits. I.e. 1/8 is a size or offset of one bit.
-
-
-;;; Code:
-
-(define (pack-alignment pack alignment)
- (case pack
- ((#t) 1)
- ((#f) alignment)
- (else (min pack alignment))))
-
-(define-record-type <field>
- (make-field name descriptor size alignment position)
- field?
- (name field-name)
- (descriptor field-descriptor)
- (size field-size)
- (alignment field-alignment)
- (position field-position))
-
-(define (construct-normal-field pack position name descriptor)
- (let*-values
- (((size)
- (bytestructure-descriptor-size descriptor))
- ((alignment)
- (pack-alignment pack (bytestructure-descriptor-alignment descriptor)))
- ((position _boundary _bit-offset)
- (align position size alignment)))
- (values (make-field name descriptor size alignment position)
- (+ position size))))
-
-(define (construct-bit-field pack position name descriptor width)
- (if (zero? width)
- (let* ((alignment (bytestructure-descriptor-alignment descriptor))
- (position (next-boundary position alignment)))
- (values (make-field #f descriptor 0 1 position)
- position))
- (let*-values
- (((int-size)
- (bytestructure-descriptor-size descriptor))
- ((size)
- (* 1/8 width))
- ((int-alignment)
- (bytestructure-descriptor-alignment descriptor))
- ((alignment)
- (pack-alignment pack int-alignment))
- ((position boundary offset)
- (align position size alignment))
- ((descriptor)
- (bitfield-descriptor descriptor offset width)))
- (values (make-field name descriptor int-size alignment boundary)
- (+ position size)))))
-
-(define (construct-fields pack field-specs)
- (let loop ((field-specs field-specs)
- (position 0)
- (fields '()))
- (if (null? field-specs)
- (reverse fields)
- (let* ((field-spec (car field-specs))
- (field-specs (cdr field-specs))
- (name-or-type (car field-spec)))
- (if (and (eq? name-or-type 'union)
- (pair? (cadr field-spec)))
- (let-values (((next-position fields)
- (add-union-fields pack
- position
- (cadr field-spec)
- fields)))
- (loop field-specs
- next-position
- fields))
- (let-values (((field next-position)
- (construct-field pack position field-spec)))
- (loop field-specs
- next-position
- (cons field fields))))))))
-
-(define (add-union-fields pack position field-specs fields)
- (define (field-spec-alignment field-spec)
- (let ((descriptor (cadr field-spec)))
- (bytestructure-descriptor-alignment descriptor)))
- (define (field-spec-size field-spec)
- (let ((descriptor (cadr field-spec)))
- (bytestructure-descriptor-size descriptor)))
- (let* ((alignment (apply max (map field-spec-alignment field-specs)))
- (alignment (pack-alignment pack alignment))
- (size (apply max (map field-spec-size field-specs)))
- (position (align position size alignment)))
- (let loop ((field-specs field-specs)
- (next-position position)
- (fields fields))
- (if (null? field-specs)
- (values next-position fields)
- (let ((field-spec (car field-specs))
- (field-specs (cdr field-specs)))
- (let-values (((field next-position)
- (construct-field pack position field-spec)))
- (loop field-specs
- (max position next-position)
- (cons field fields))))))))
-
-(define (construct-field pack position field-spec)
- (let* ((name (car field-spec))
- (descriptor (cadr field-spec))
- (bitfield? (not (null? (cddr field-spec))))
- (width (if bitfield?
- (car (cddr field-spec))
- #f)))
- (if bitfield?
- (construct-bit-field pack position name descriptor width)
- (construct-normal-field pack position name descriptor))))
-
-(define-record-type <struct-metadata>
- (make-struct-metadata field-alist)
- struct-metadata?
- (field-alist struct-metadata-field-alist))
-
-(define bs:struct
- (case-lambda
- ((field-specs)
- (bs:struct #f field-specs))
- ((pack field-specs)
- (define %fields (construct-fields pack field-specs))
- (define fields (filter field-name %fields))
- (define field-alist (map (lambda (field)
- (cons (field-name field) field))
- fields))
- (define alignment (apply max (map field-alignment fields)))
- (define (field-end field)
- (+ (field-position field) (field-size field)))
- (define size (let ((end (apply max (map field-end %fields))))
- (let-values (((size . _) (next-boundary end alignment)))
- size)))
- (define (unwrapper syntax? bytevector offset index)
- (let* ((index (if syntax? (syntax->datum index) index))
- (field-entry (assq index field-alist))
- (field (if field-entry
- (cdr field-entry)
- (error "No such struct field." index))))
- (let* ((descriptor (field-descriptor field))
- (position (field-position field))
- (offset (if syntax?
- (quasisyntax
- (+ (unsyntax offset) (unsyntax position)))
- (+ offset position))))
- (values bytevector offset descriptor))))
- (define (setter syntax? bytevector offset value)
- (define (count-error fields values)
- (error "Mismatch between number of struct fields and given values."
- fields values))
- (when syntax?
- (error "Writing into struct not supported with macro API."))
- (cond
- ((bytevector? value)
- (bytevector-copy! bytevector offset value 0 size))
- ((vector? value)
- (let loop ((fields fields)
- (values (vector->list value)))
- (if (null? values)
- (when (not (null? fields))
- (count-error fields value))
- (begin
- (when (null? fields)
- (count-error fields value))
- (let* ((field (car fields))
- (value (car values))
- (descriptor (field-descriptor field))
- (position (field-position field))
- (offset (+ offset position)))
- (bytestructure-set!* bytevector offset descriptor value)
- (loop (cdr fields) (cdr values)))))))
- ((pair? value)
- ;; Assumed to be a pseudo-alist like ((k1 v1) (k2 v2) ...).
- (for-each
- (lambda (pair)
- (let ((key (car pair))
- (value (cadr pair)))
- (let-values (((bytevector offset descriptor)
- (unwrapper #f bytevector offset key)))
- (bytestructure-set!* bytevector offset descriptor value))))
- value))
- (else
- (error "Invalid value for writing into struct." value))))
- (define meta
- (let ((simple-field-alist (map (lambda (field)
- (cons (field-name field)
- (field-descriptor field)))
- fields)))
- (make-struct-metadata simple-field-alist)))
- (make-bytestructure-descriptor size alignment unwrapper #f setter meta))))
-
-(define debug-alignment
- (case-lambda
- ((fields) (debug-alignment #f fields))
- ((pack fields)
- (let* ((fields (construct-fields pack fields))
- (alignment (apply max (map field-alignment fields)))
- (size (let* ((field (last fields))
- (end (+ (field-position field) (field-size field))))
- (let-values (((size . _) (next-boundary end alignment)))
- size))))
- (format #t "{\n")
- (for-each (lambda (field)
- (let ((name (field-name field))
- (pos (* 8 (field-position field)))
- (size (* 8 (field-size field)))
- (align (* 8 (field-alignment field))))
- (format #t " ~a - ~a: ~a (~a, ~a)\n"
- pos (+ pos size) name size align)))
- fields)
- (format #t "} = ~a\n" (* 8 size))
- (values)))))
-
-;;; struct.scm ends here
-;;; union.scm --- Union descriptor constructor.
-
-;; Copyright © 2015, 2016 Taylan Kammer <taylan.kammer@gmail.com>
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This constructor allows the creation of union descriptors with named fields
-;; with a specific content descriptor.
-
-
-;;; Code:
-
-(define make-field cons)
-(define field-name car)
-(define field-content cdr)
-(define find-field assq)
-
-(define (construct-fields fields)
- (map (lambda (field)
- (make-field (car field) (cadr field)))
- fields))
-
-(define-record-type <union-metadata>
- (make-union-metadata field-alist)
- union-metadata?
- (field-alist union-metadata-field-alist))
-
-(define (bs:union %fields)
- (define fields (construct-fields %fields))
- (define alignment (apply max (map (lambda (field)
- (bytestructure-descriptor-alignment
- (field-content field)))
- fields)))
- (define size (let ((max-element
- (apply max (map (lambda (field)
- (bytestructure-descriptor-size
- (field-content field)))
- fields))))
- (let-values (((size . _) (next-boundary max-element alignment)))
- size)))
- (define (unwrapper syntax? bytevector offset index)
- (let ((index (if syntax? (syntax->datum index) index)))
- (values bytevector
- offset
- (field-content (find-field index fields)))))
- (define (setter syntax? bytevector offset value)
- (when syntax?
- (error "Writing into union not supported with macro API."))
- (cond
- ((bytevector? value)
- (bytevector-copy! bytevector offset value 0 size))
- ((and (list? value) (= 2 (length value)))
- (let-values (((bytevector* offset* descriptor)
- (unwrapper #f bytevector offset (car value))))
- (bytestructure-set!* bytevector* offset* descriptor (cadr value))))
- (else
- (error "Invalid value for writing into union." value))))
- (define meta (make-union-metadata fields))
- (make-bytestructure-descriptor size alignment unwrapper #f setter meta))
-
-;;; union.scm ends here
-;;; utils.scm --- Utility library for bytestructures.
-
-;; Copyright © 2015 Taylan Kammer <taylan.kammer@gmail.com>
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; Just some utility procedures and macros.
-
-
-;;; Code:
-
-(define-syntax define-syntax-rule
- (syntax-rules ()
- ((_ (<name> . <args>) <expr>)
- (define-syntax <name>
- (syntax-rules ()
- ((_ . <args>)
- <expr>))))))
-
-(cond-expand
- ((or guile syntax-case)
- (define-syntax-rule (if-syntax-case <then> <else>)
- <then>))
- (else
- (define-syntax-rule (if-syntax-case <then> <else>)
- <else>)))
-
-(define-syntax-rule (define-syntax-case-stubs <name> ...)
- (if-syntax-case
- (begin)
- (begin
- (define (<name> . rest)
- (error "Not supported. You need syntax-case."))
- ...)))
-
-(define-syntax-case-stubs
- syntax
- quasisyntax
- unsyntax
- unsyntax-splicing
- syntax->datum
- datum->syntax)
-
-;;; utils.scm ends here
-;;; vector.scm --- Vector descriptor constructor.
-
-;; Copyright © 2015, 2016 Taylan Kammer <taylan.kammer@gmail.com>
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This constructor allows the creation of vector descriptors with a specific
-;; length and element descriptor.
-
-;; Be careful with identifier names here; don't confuse vector descriptor and
-;; Scheme vector APIs and variables.
-
-
-;;; Code:
-
-(define-record-type <vector-metadata>
- (make-vector-metadata length element-descriptor)
- vector-metadata?
- (length vector-metadata-length)
- (element-descriptor vector-metadata-element-descriptor))
-
-(define (bs:vector length descriptor)
- (define element-size (bytestructure-descriptor-size descriptor))
- (define size (* length element-size))
- (define alignment (bytestructure-descriptor-alignment descriptor))
- (define (unwrapper syntax? bytevector offset index)
- (values bytevector
- (if syntax?
- (quasisyntax
- (+ (unsyntax offset)
- (* (unsyntax index) (unsyntax element-size))))
- (+ offset (* index element-size)))
- descriptor))
- (define (setter syntax? bytevector offset value)
- (when syntax?
- (error "Writing into vector not supported with macro API."))
- (cond
- ((bytevector? value)
- (bytevector-copy! bytevector offset value 0 size))
- ((vector? value)
- (do ((i 0 (+ i 1))
- (offset offset (+ offset element-size)))
- ((= i (vector-length value)))
- (bytestructure-set!*
- bytevector offset descriptor (vector-ref value i))))
- (else
- (error "Invalid value for writing into vector." value))))
- (define meta (make-vector-metadata length descriptor))
- (make-bytestructure-descriptor size alignment unwrapper #f setter meta))
-
-;;; vector.scm ends here
-(define-module (bytestructures guile base))
-(import
- (srfi 9)
- (srfi 11)
- (ice-9 format)
- (bytestructures guile bytevectors)
- (bytestructures guile utils))
-(include-from-path "bytestructures/body/base.scm")
-(include-from-path "bytestructures/r7/base.exports.sld")
-
-(import (srfi srfi-9 gnu))
-
-(set-record-type-printer!
- <bytestructure-descriptor>
- (lambda (record port)
- (format port "#<bytestructure-descriptor 0x~x>" (object-address record))))
-
-(set-record-type-printer!
- <bytestructure>
- (lambda (record port)
- (format port "#<bytestructure 0x~x>" (object-address record))))
-(define-module (bytestructures guile bitfields))
-(import
- (srfi 9)
- (srfi 60)
- (bytestructures guile utils)
- (bytestructures guile base)
- (bytestructures guile numeric-metadata))
-(include-from-path "bytestructures/body/bitfields.scm")
-(include-from-path "bytestructures/r7/bitfields.exports.sld")
-;;; Compatibility shim for Guile, because its implementation of utf16->string
-;;; and utf32->string doesn't conform to R6RS.
-(define-module (bytestructures guile bytevectors))
-
-(import
- (rnrs base)
- (rnrs control)
- (bytestructures r6 bytevectors))
-
-(re-export
- endianness native-endianness bytevector?
- make-bytevector bytevector-length bytevector=? bytevector-fill!
- bytevector-copy!
- bytevector-copy
-
- bytevector-u8-ref bytevector-s8-ref
- bytevector-u8-set! bytevector-s8-set! bytevector->u8-list
- u8-list->bytevector
- bytevector-uint-ref bytevector-uint-set!
- bytevector-sint-ref bytevector-sint-set!
- bytevector->sint-list bytevector->uint-list
- uint-list->bytevector sint-list->bytevector
-
- bytevector-u16-ref bytevector-s16-ref
- bytevector-u16-set! bytevector-s16-set!
- bytevector-u16-native-ref bytevector-s16-native-ref
- bytevector-u16-native-set! bytevector-s16-native-set!
-
- bytevector-u32-ref bytevector-s32-ref
- bytevector-u32-set! bytevector-s32-set!
- bytevector-u32-native-ref bytevector-s32-native-ref
- bytevector-u32-native-set! bytevector-s32-native-set!
-
- bytevector-u64-ref bytevector-s64-ref
- bytevector-u64-set! bytevector-s64-set!
- bytevector-u64-native-ref bytevector-s64-native-ref
- bytevector-u64-native-set! bytevector-s64-native-set!
-
- bytevector-ieee-single-ref
- bytevector-ieee-single-set!
- bytevector-ieee-single-native-ref
- bytevector-ieee-single-native-set!
-
- bytevector-ieee-double-ref
- bytevector-ieee-double-set!
- bytevector-ieee-double-native-ref
- bytevector-ieee-double-native-set!
-
- string->utf8
- utf8->string
- string->utf16 string->utf32)
-
-(export
- (r6rs-utf16->string . utf16->string)
- (r6rs-utf32->string . utf32->string))
-
-(define (read-bom16 bv)
- (let ((c0 (bytevector-u8-ref bv 0))
- (c1 (bytevector-u8-ref bv 1)))
- (cond
- ((and (= c0 #xFE) (= c1 #xFF))
- 'big)
- ((and (= c0 #xFF) (= c1 #xFE))
- 'little)
- (else
- #f))))
-
-(define r6rs-utf16->string
- (case-lambda
- ((bv default-endianness)
- (let ((bom-endianness (read-bom16 bv)))
- (if (not bom-endianness)
- (utf16->string bv default-endianness)
- (substring/shared (utf16->string bv bom-endianness) 1))))
- ((bv endianness endianness-mandatory?)
- (if endianness-mandatory?
- (utf16->string bv endianness)
- (r6rs-utf16->string bv endianness)))))
-
-(define (read-bom32 bv)
- (let ((c0 (bytevector-u8-ref bv 0))
- (c1 (bytevector-u8-ref bv 1))
- (c2 (bytevector-u8-ref bv 2))
- (c3 (bytevector-u8-ref bv 3)))
- (cond
- ((and (= c0 #x00) (= c1 #x00) (= c2 #xFE) (= c3 #xFF))
- 'big)
- ((and (= c0 #xFF) (= c1 #xFE) (= c2 #x00) (= c3 #x00))
- 'little)
- (else
- #f))))
-
-(define r6rs-utf32->string
- (case-lambda
- ((bv default-endianness)
- (let ((bom-endianness (read-bom32 bv)))
- (if (not bom-endianness)
- (utf32->string bv default-endianness)
- (substring/shared (utf32->string bv bom-endianness) 1))))
- ((bv endianness endianness-mandatory?)
- (if endianness-mandatory?
- (utf32->string bv endianness)
- (r6rs-utf32->string bv endianness)))))
-;;; cstring-pointer.scm --- Pointers to null-terminated strings.
-
-;; Copyright © 2017 Taylan Kammer <taylan.kammer@gmail.com>
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; The cstring-pointer descriptor represents a pointer to a null-terminated
-;; string, and will return the string as a Scheme string upon a reference
-;; operation. Its setter however does not take Scheme strings, only addresses
-;; to existing strings in memory. The reason is: Guile's string->pointer
-;; creates a new C string in memory, returning an FFI pointer object holding its
-;; address; the string is freed when the pointer object is garbage collected.
-;; We have no means of holding a reference to the FFI pointer object; we can
-;; only write the address it holds into our bytevector, which won't protect the
-;; pointer object from GC.
-
-
-;;; Code:
-
-(define-module (bytestructures guile cstring-pointer))
-(import
- (bytestructures guile base)
- (bytestructures guile numeric)
- (prefix (system foreign) ffi-))
-(export cstring-pointer)
-
-(define (bytevector-address-ref bv offset)
- (bytestructure-ref* bv offset uintptr_t))
-
-(define (bytevector-address-set! bv offset address)
- (bytestructure-set!* bv offset uintptr_t address))
-
-(define cstring-pointer
- (let ()
- (define size (bytestructure-descriptor-size intptr_t))
- (define alignment (bytestructure-descriptor-alignment intptr_t))
- (define unwrapper #f)
- (define (getter syntax? bv offset)
- (if syntax?
- #`(let* ((address (bytevector-address-ref #,bv #,offset))
- (pointer (ffi-make-pointer address)))
- (ffi-pointer->string pointer))
- (let* ((address (bytevector-address-ref bv offset))
- (pointer (ffi-make-pointer address)))
- (ffi-pointer->string pointer))))
- (define (setter syntax? bv offset address)
- (if syntax?
- #`(bytevector-address-set! #,bv #,offset #,address)
- (bytevector-address-set! bv offset address)))
- (make-bytestructure-descriptor size alignment unwrapper getter setter)))
-(define-module (bytestructures guile explicit-endianness))
-(import
- (bytestructures guile bytevectors)
- (bytestructures guile utils))
-(include-from-path "bytestructures/body/explicit-endianness.scm")
-(include-from-path "bytestructures/r7/explicit-endianness.exports.sld")
-;;; ffi.scm --- Convert bytestructure descriptors to Guile/libffi types.
-
-;; Copyright © 2016 Taylan Kammer <taylan.kammer@gmail.com>
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This module offers a way to convert bytestructure descriptors to Guile/libffi
-;; type objects. For instance, the bytestructure descriptor created with
-;; (bs:struct `((x ,uint8) (y ,uint16))) gets converted into a two-element list
-;; containing the libffi codes for uint8 and uint16.
-
-
-;;; Code:
-
-(define-module (bytestructures guile ffi))
-(import
- (ice-9 match)
- (prefix (system foreign) ffi-)
- (bytestructures guile base)
- (bytestructures guile numeric)
- (bytestructures guile vector)
- (bytestructures guile struct)
- (bytestructures guile union)
- (bytestructures guile pointer)
- (bytestructures guile bitfields))
-(export
- bytestructure-descriptor->ffi-descriptor
- bs:pointer->proc
- )
-
-(define numeric-type-mapping
- `((,int8 . ,ffi-int8)
- (,uint8 . ,ffi-uint8)
- (,int16 . ,ffi-int16)
- (,uint16 . ,ffi-uint16)
- (,int32 . ,ffi-int32)
- (,uint32 . ,ffi-uint32)
- (,int64 . ,ffi-int64)
- (,uint64 . ,ffi-uint64)
- (,float32 . ,ffi-float)
- (,float64 . ,ffi-double)))
-
-(define (bytestructure-descriptor->ffi-descriptor descriptor)
- (define (convert descriptor)
- (cond
- ((assq descriptor numeric-type-mapping)
- => (match-lambda ((key . val) val)))
- (else
- (let ((metadata (bytestructure-descriptor-metadata descriptor)))
- (cond
- ((vector-metadata? metadata)
- (make-list
- (vector-metadata-length metadata)
- (convert (vector-metadata-element-descriptor metadata))))
- ((struct-metadata? metadata)
- (map convert (map cdr (struct-metadata-field-alist metadata))))
- ((union-metadata? metadata)
- ;; TODO: Add support once Guile/libffi supports this.
- (error "Unions not supported." descriptor))
- ((pointer-metadata? metadata)
- '*)
- ((bitfield-metadata? metadata)
- ;; TODO: Add support once Guile/libffi supports this.
- (error "Bitfields not supported." descriptor))
- (else
- (error "Unsupported bytestructure descriptor." descriptor)))))))
- (cond
- ((eq? descriptor 'void)
- ffi-void)
- ((vector-metadata? (bytestructure-descriptor-metadata descriptor))
- '*)
- (else
- (convert descriptor))))
-
-(define (bs:pointer->proc ret-type func-ptr arg-types)
- (define (type->raw-type type)
- (if (bytestructure-descriptor? type)
- (bytestructure-descriptor->ffi-descriptor type)
- type))
- (define (value->raw-value value)
- (if (bytestructure? value)
- (ffi-bytevector->pointer (bytestructure-bytevector value))
- value))
- (define (raw-value->value raw-value type)
- (if (bytestructure-descriptor? type)
- (make-bytestructure (ffi-pointer->bytevector
- raw-value
- (bytestructure-descriptor-size type))
- 0
- type)
- raw-value))
- (let* ((raw-ret-type (type->raw-type ret-type))
- (raw-arg-types (map type->raw-type arg-types))
- (raw-proc (ffi-pointer->procedure
- raw-ret-type func-ptr raw-arg-types)))
- (lambda args
- (let* ((raw-args (map value->raw-value args))
- (raw-ret-val (apply raw-proc raw-args)))
- (raw-value->value raw-ret-val ret-type)))))
-(define-module (bytestructures guile numeric-all))
-(import
- (bytestructures guile bytevectors)
- (bytestructures guile utils)
- (bytestructures guile base)
- (bytestructures guile explicit-endianness)
- (bytestructures guile numeric-data-model))
-(include-from-path "bytestructures/body/numeric.scm")
-(include-from-path "bytestructures/r7/numeric.exports.sld")
-(include-from-path "bytestructures/r7/numeric-metadata.exports.sld")
-(define-module (bytestructures guile numeric-data-model))
-
-(import (system foreign))
-(import (system base target))
-
-(define architecture
- (let ((cpu (target-cpu)))
- (cond
- ((member cpu '("i386" "i486" "i586" "i686"))
- 'i386)
- ((string=? "x86_64" cpu)
- 'x86-64)
- ((string-prefix? "arm" cpu)
- 'arm)
- ((string-prefix? "aarch64" cpu)
- 'aarch64))))
-
-(define data-model
- (if (= 4 (sizeof '*))
- (if (= 2 (sizeof int))
- 'lp32
- 'ilp32)
- (cond
- ((= 8 (sizeof int)) 'ilp64)
- ((= 4 (sizeof long)) 'llp64)
- (else 'lp64))))
-
-(cond-expand-provide
- (current-module)
- (list architecture data-model))
-(define-module (bytestructures guile numeric-metadata))
-(import (bytestructures guile numeric-all))
-(re-export
- signed-integer-native-descriptors
- signed-integer-le-descriptors
- signed-integer-be-descriptors
- signed-integer-descriptors
- unsigned-integer-native-descriptors
- unsigned-integer-le-descriptors
- unsigned-integer-be-descriptors
- unsigned-integer-descriptors
- float-native-descriptors
- float-le-descriptors
- float-be-descriptors
- integer-descriptors
- float-descriptors
- numeric-descriptors
- )
-(define-module (bytestructures guile numeric))
-(import (bytestructures guile numeric-all))
-(re-export
-
- int8 uint8 int16 uint16 int32 uint32 int64 uint64
- int16le uint16le int32le uint32le int64le uint64le
- int16be uint16be int32be uint32be int64be uint64be
- float32 float64 float32le float64le float32be float64be
-
- short unsigned-short
- int unsigned-int
- long unsigned-long
- long-long unsigned-long-long
- intptr_t uintptr_t
- size_t ssize_t ptrdiff_t
- float double
-
- complex64 complex128
- complex64le complex128le
- complex64be complex128be
- )
-;;; pointer.scm --- Pointer descriptor constructor.
-
-;; Copyright © 2015 Taylan Kammer <taylan.kammer@gmail.com>
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This constructor allows the creation of pointer descriptors with a specific
-;; pointed-to descriptor.
-
-
-;;; Code:
-
-(define-module (bytestructures guile pointer))
-(import
- (srfi 9)
- (bytestructures guile bytevectors)
- (bytestructures guile utils)
- (bytestructures guile base)
- (prefix (system foreign) ffi-))
-(export
- bs:pointer
- pointer-metadata? pointer-metadata-content-descriptor
- )
-
-(define pointer-size (ffi-sizeof '*))
-
-(define bytevector-address-ref
- (case pointer-size
- ((1) bytevector-u8-ref)
- ((2) bytevector-u16-native-ref)
- ((4) bytevector-u32-native-ref)
- ((8) bytevector-u64-native-ref)))
-
-(define bytevector-address-set!
- (case pointer-size
- ((1) bytevector-u8-set!)
- ((2) bytevector-u16-native-set!)
- ((4) bytevector-u32-native-set!)
- ((8) bytevector-u64-native-set!)))
-
-(define (pointer-ref bytevector offset index content-size)
- (let* ((base-address (bytevector-address-ref bytevector offset))
- (address (+ base-address (* index content-size))))
- (if (zero? base-address)
- (error "Tried to dereference null-pointer.")
- (ffi-pointer->bytevector (ffi-make-pointer address) content-size))))
-
-(define (pointer-set! bytevector offset value)
- (cond
- ((exact-integer? value)
- (bytevector-address-set! bytevector offset value))
- ((bytevector? value)
- (bytevector-address-set! bytevector offset
- (ffi-pointer-address
- (ffi-bytevector->pointer value))))
- ((bytestructure? value)
- (bytevector-address-set! bytevector offset
- (ffi-pointer-address
- (ffi-bytevector->pointer
- (bytestructure-bytevector value)))))))
-
-(define-record-type <pointer-metadata>
- (make-pointer-metadata content-descriptor)
- pointer-metadata?
- (content-descriptor pointer-metadata-content-descriptor))
-
-(define (bs:pointer %descriptor)
- (define (get-descriptor)
- (if (promise? %descriptor)
- (force %descriptor)
- %descriptor))
- (define size pointer-size)
- (define alignment size)
- (define (unwrapper syntax? bytevector offset index)
- (define (syntax-list id . elements)
- (datum->syntax id (map syntax->datum elements)))
- (let ((descriptor (get-descriptor)))
- (when (eq? 'void descriptor)
- (error "Tried to follow void pointer."))
- (let* ((size (bytestructure-descriptor-size descriptor))
- (index-datum (if syntax? (syntax->datum index) index))
- (index (if (eq? '* index-datum) 0 index-datum))
- (bytevector*
- (if syntax?
- #`(pointer-ref #,bytevector #,offset #,index #,size)
- (pointer-ref bytevector offset index size))))
- (values bytevector* 0 descriptor))))
- (define (getter syntax? bytevector offset)
- (if syntax?
- #`(bytevector-address-ref #,bytevector #,offset)
- (bytevector-address-ref bytevector offset)))
- (define (setter syntax? bytevector offset value)
- (if syntax?
- #`(pointer-set! #,bytevector #,offset #,value)
- (pointer-set! bytevector offset value)))
- (define meta (make-pointer-metadata %descriptor))
- (make-bytestructure-descriptor size alignment unwrapper getter setter meta))
-
-;;; pointer.scm ends here
-(define-module (bytestructures guile string))
-(import
- (bytestructures guile bytevectors)
- (bytestructures guile utils)
- (bytestructures guile base))
-(include-from-path "bytestructures/body/string.scm")
-(include-from-path "bytestructures/r7/string.exports.sld")
-(define-module (bytestructures guile struct))
-(import
- (srfi 1)
- (srfi 9)
- (srfi 11)
- (bytestructures guile bytevectors)
- (bytestructures guile utils)
- (bytestructures guile base)
- (bytestructures guile bitfields))
-(include-from-path "bytestructures/body/align.scm")
-(include-from-path "bytestructures/body/struct.scm")
-(include-from-path "bytestructures/r7/struct.exports.sld")
-(define-module (bytestructures guile union))
-(import
- (srfi 9)
- (srfi 11)
- (bytestructures guile bytevectors)
- (bytestructures guile utils)
- (bytestructures guile base))
-(include-from-path "bytestructures/body/align.scm")
-(include-from-path "bytestructures/body/union.scm")
-(include-from-path "bytestructures/r7/union.exports.sld")
-(define-module (bytestructures guile utils))
-(include-from-path "bytestructures/body/utils.scm")
-(export
- if-syntax-case
- define-syntax-case-stubs
- )
-(define-module (bytestructures guile vector))
-(import
- (srfi 9)
- (bytestructures guile bytevectors)
- (bytestructures guile utils)
- (bytestructures guile base))
-(include-from-path "bytestructures/body/vector.scm")
-(include-from-path "bytestructures/r7/vector.exports.sld")
-;;; Compatibility shim for R6RS systems, because R6RS and R7RS have different
-;;; semantics for some procedures of the same name. We use R7RS semantics
-;;; everywhere, so implement them in terms of R6RS.
-(library (bytestructures r6 bytevectors)
- (export
- endianness native-endianness bytevector?
- make-bytevector bytevector-length bytevector=? bytevector-fill!
- (rename (r7rs-bytevector-copy! bytevector-copy!))
- (rename (r7rs-bytevector-copy bytevector-copy))
-
- bytevector-u8-ref bytevector-s8-ref
- bytevector-u8-set! bytevector-s8-set! bytevector->u8-list
- u8-list->bytevector
- bytevector-uint-ref bytevector-uint-set!
- bytevector-sint-ref bytevector-sint-set!
- bytevector->sint-list bytevector->uint-list
- uint-list->bytevector sint-list->bytevector
-
- bytevector-u16-ref bytevector-s16-ref
- bytevector-u16-set! bytevector-s16-set!
- bytevector-u16-native-ref bytevector-s16-native-ref
- bytevector-u16-native-set! bytevector-s16-native-set!
-
- bytevector-u32-ref bytevector-s32-ref
- bytevector-u32-set! bytevector-s32-set!
- bytevector-u32-native-ref bytevector-s32-native-ref
- bytevector-u32-native-set! bytevector-s32-native-set!
-
- bytevector-u64-ref bytevector-s64-ref
- bytevector-u64-set! bytevector-s64-set!
- bytevector-u64-native-ref bytevector-s64-native-ref
- bytevector-u64-native-set! bytevector-s64-native-set!
-
- bytevector-ieee-single-ref
- bytevector-ieee-single-set!
- bytevector-ieee-single-native-ref
- bytevector-ieee-single-native-set!
-
- bytevector-ieee-double-ref
- bytevector-ieee-double-set!
- bytevector-ieee-double-native-ref
- bytevector-ieee-double-native-set!
-
- (rename (r7rs-string->utf8 string->utf8))
- (rename (r7rs-utf8->string utf8->string))
- string->utf16 string->utf32
- utf16->string utf32->string
- )
- (import
- (rnrs base)
- (rnrs control)
- (rnrs bytevectors))
- (define r7rs-bytevector-copy!
- (case-lambda
- ((to at from)
- (bytevector-copy! from 0 to at (bytevector-length from)))
- ((to at from start)
- (bytevector-copy! from start to at (- (bytevector-length from) start)))
- ((to at from start end)
- (bytevector-copy! from start to at (- end start)))))
- (define r7rs-bytevector-copy
- (case-lambda
- ((bytevector)
- (bytevector-copy bytevector))
- ((bytevector start)
- (r7rs-bytevector-copy bytevector start (bytevector-length bytevector)))
- ((bytevector start end)
- (let* ((size (- end start))
- (bytevector* (make-bytevector size)))
- (bytevector-copy! bytevector start bytevector* 0 size)
- bytevector*))))
- (define r7rs-string->utf8
- (case-lambda
- ((string)
- (string->utf8 string))
- ((string start)
- (string->utf8 (substring string start (string-length string))))
- ((string start end)
- (string->utf8 (substring string start end)))))
- (define r7rs-utf8->string
- (case-lambda
- ((bytevector)
- (utf8->string bytevector))
- ((bytevector start)
- (utf8->string (r7rs-bytevector-copy bytevector start)))
- ((bytevector start end)
- (utf8->string (r7rs-bytevector-copy bytevector start end))))))
-(export
- make-bytestructure-descriptor
- bytestructure-descriptor?
- bytestructure-descriptor-size
- bytestructure-descriptor-size/syntax
- bytestructure-descriptor-alignment
- bytestructure-descriptor-unwrapper
- bytestructure-descriptor-getter
- bytestructure-descriptor-setter
- bytestructure-descriptor-metadata
- make-bytestructure
- bytestructure?
- bytestructure-bytevector
- bytestructure-offset
- bytestructure-descriptor
- bytestructure-size
- bytestructure
- bytestructure-unwrap
- bytestructure-unwrap*
- bytestructure-ref
- bytestructure-ref*
- bytestructure-set!
- bytestructure-set!*
- bytestructure-ref/dynamic
- bytestructure-set!/dynamic
- bytestructure-unwrap/syntax
- bytestructure-ref/syntax
- bytestructure-set!/syntax
- define-bytestructure-accessors
- )
-(define-library (bytestructures r7 base)
- (import
- (scheme base)
- (scheme case-lambda)
- (bytestructures r7 utils))
- (cond-expand
- ((library (rnrs syntax-case))
- (import (rnrs syntax-case)))
- (else))
- (include-library-declarations "base.exports.sld")
- (include "body/base.scm"))
-(export
- bitfield-descriptor
- bitfield-metadata?
- bitfield-metadata-int-descriptor
- bitfield-metadata-width
- )
-(define-library (bytestructures r7 bitfields)
- (import
- (scheme base)
- (srfi 60)
- (bytestructures r7 utils)
- (bytestructures r7 base)
- (bytestructures r7 numeric-metadata))
- (include-library-declarations "bitfields.exports.sld")
- (include "body/bitfields.scm"))
-(define-library (bytestructures r7 bytevectors)
- (cond-expand
- ((library (rnrs bytevectors))
- (import (except (rnrs bytevectors)
- bytevector?
- make-bytevector
- bytevector-length
- bytevector-u8-ref
- bytevector-u8-set!
- bytevector-copy
- bytevector-copy!
- string->utf8
- utf8->string)))
- (else
- (import (except (r6rs bytevectors)
- bytevector?
- make-bytevector
- bytevector-length
- bytevector-u8-ref
- bytevector-u8-set!
- bytevector-copy
- bytevector-copy!
- string->utf8
- utf8->string))))
- (export
- endianness
- native-endianness
-
- bytevector=?
- bytevector-fill!
-
- bytevector-s8-ref
- bytevector-s8-set!
- bytevector->u8-list u8-list->bytevector
-
- bytevector-uint-ref bytevector-sint-ref
- bytevector-uint-set! bytevector-sint-set!
- bytevector->uint-list bytevector->sint-list
- uint-list->bytevector sint-list->bytevector
-
- bytevector-u16-ref bytevector-s16-ref
- bytevector-u16-native-ref bytevector-s16-native-ref
- bytevector-u16-set! bytevector-s16-set!
- bytevector-u16-native-set! bytevector-s16-native-set!
-
- bytevector-u32-ref bytevector-s32-ref
- bytevector-u32-native-ref bytevector-s32-native-ref
- bytevector-u32-set! bytevector-s32-set!
- bytevector-u32-native-set! bytevector-s32-native-set!
-
- bytevector-u64-ref bytevector-s64-ref
- bytevector-u64-native-ref bytevector-s64-native-ref
- bytevector-u64-set! bytevector-s64-set!
- bytevector-u64-native-set! bytevector-s64-native-set!
-
- bytevector-ieee-single-native-ref
- bytevector-ieee-single-ref
- bytevector-ieee-double-native-ref
- bytevector-ieee-double-ref
- bytevector-ieee-single-native-set!
- bytevector-ieee-single-set!
- bytevector-ieee-double-native-set!
- bytevector-ieee-double-set!
-
- string->utf16 string->utf32
- utf16->string utf32->string
- ))
-(export
- bytevector-ieee-single-le-ref bytevector-ieee-single-be-ref
- bytevector-ieee-single-le-set! bytevector-ieee-single-be-set!
- bytevector-ieee-double-le-ref bytevector-ieee-double-be-ref
- bytevector-ieee-double-le-set! bytevector-ieee-double-be-set!
- bytevector-s16le-ref bytevector-s16be-ref
- bytevector-s16le-set! bytevector-s16be-set!
- bytevector-u16le-ref bytevector-u16be-ref
- bytevector-u16le-set! bytevector-u16be-set!
- bytevector-s32le-ref bytevector-s32be-ref
- bytevector-s32le-set! bytevector-s32be-set!
- bytevector-u32le-ref bytevector-u32be-ref
- bytevector-u32le-set! bytevector-u32be-set!
- bytevector-s64le-ref bytevector-s64be-ref
- bytevector-s64le-set! bytevector-s64be-set!
- bytevector-u64le-ref bytevector-u64be-ref
- bytevector-u64le-set! bytevector-u64be-set!
- )
-(define-library (bytestructures r7 explicit-endianness)
- (import
- (scheme base)
- (bytestructures r7 utils)
- (bytestructures r7 bytevectors))
- (include-library-declarations "explicit-endianness.exports.sld")
- (include "body/explicit-endianness.scm"))
-(define-library (bytestructures r7 numeric-all)
- (import
- (scheme base)
- (scheme complex)
- (scheme eval)
- (bytestructures r7 utils)
- (bytestructures r7 base)
- (bytestructures r7 bytevectors)
- (bytestructures r7 explicit-endianness))
- (include-library-declarations "numeric.exports.sld")
- (include-library-declarations "numeric-metadata.exports.sld")
- (include "body/numeric.scm"))
-(export
- signed-integer-native-descriptors
- signed-integer-le-descriptors
- signed-integer-be-descriptors
- signed-integer-descriptors
- unsigned-integer-native-descriptors
- unsigned-integer-le-descriptors
- unsigned-integer-be-descriptors
- unsigned-integer-descriptors
- float-native-descriptors
- float-le-descriptors
- float-be-descriptors
- complex-native-descriptors
- complex-le-descriptors
- complex-be-descriptors
- integer-descriptors
- float-descriptors
- complex-descriptors
- numeric-descriptors
- )
-(define-library (bytestructures r7 numeric-metadata)
- (import (bytestructures r7 numeric-all))
- (include-library-declarations "numeric-metadata.exports.sld"))
-(export
- int8 int16 int32 int64
- uint8 uint16 uint32 uint64
- int16le int32le int64le
- uint16le uint32le uint64le
- int16be int32be int64be
- uint16be uint32be uint64be
- float32 float64
- float32le float64le
- float32be float64be
-
- short unsigned-short
- int unsigned-int
- long unsigned-long
- long-long unsigned-long-long
- intptr_t uintptr_t
- size_t ssize_t ptrdiff_t
- float double
-
- complex64 complex128
- complex64le complex128le
- complex64be complex128be
- )
-(define-library (bytestructures r7 numeric)
- (import (bytestructures r7 numeric-all))
- (include-library-declarations "numeric.exports.sld"))
-(export bs:string)
-(cond-expand
- (r6rs
- (export bytevector->string string->bytevector
- ascii utf8 utf16le utf16be utf32le utf32be
- bytevector-zero!))
- (else))
-(define-library (bytestructures r7 string)
- (import
- (scheme base)
- (bytestructures r7 bytevectors)
- (bytestructures r7 utils)
- (bytestructures r7 base))
- (cond-expand
- ((library (rnrs syntax-case))
- (import (rnrs syntax-case)))
- (else))
- (include-library-declarations "string.exports.sld")
- (include "body/string.scm"))
-(export
- bs:struct
- struct-metadata?
- struct-metadata-field-alist
- )
-(define-library (bytestructures r7 struct)
- (import
- (scheme base)
- (scheme case-lambda)
- (srfi 1)
- (srfi 28)
- (bytestructures r7 utils)
- (bytestructures r7 base)
- (bytestructures r7 bitfields))
- (include-library-declarations "struct.exports.sld")
- (include "body/align.scm")
- (include "body/struct.scm"))
-(export
- bs:union
- union-metadata?
- union-metadata-field-alist
- )
-(define-library (bytestructures r7 union)
- (import
- (scheme base)
- (bytestructures r7 utils)
- (bytestructures r7 base))
- (include-library-declarations "union.exports.sld")
- (include "body/align.scm")
- (include "body/union.scm"))
-(define-library (bytestructures r7 utils)
- (import (scheme base))
- (cond-expand
- ((library (rnrs syntax-case))
- (import (rnrs syntax-case)))
- (else))
- (export
- define-syntax-rule
- if-syntax-case
- define-syntax-case-stubs
- quasisyntax
- unsyntax
- unsyntax-splicing
- syntax->datum
- datum->syntax
- )
- (include "body/utils.scm"))
-(export
- bs:vector
- vector-metadata?
- vector-metadata-length
- vector-metadata-element-descriptor
- )
-(define-library (bytestructures r7 vector)
- (import
- (scheme base)
- (bytestructures r7 utils)
- (bytestructures r7 base))
- (include-library-declarations "vector.exports.sld")
- (include "body/vector.scm"))
-(define-module (bytestructures guile))
-
-;;; Note: cstring-pointer import/export hack: Guile 2.0.x has a problem when a
-;;; module has the same name as an identifier defined in it, and the identifier
-;;; is imported and re-exported. To work around it, we import `cstring-pointer'
-;;; with a rename to `_cstring-pointer', define `cstring-pointer' explicitly in
-;;; this module, and export that.
-
-(import
- (bytestructures guile base)
- (bytestructures guile vector)
- (bytestructures guile struct)
- (bytestructures guile union)
- (bytestructures guile pointer)
- (bytestructures guile numeric)
- (bytestructures guile string)
- (rename (bytestructures guile cstring-pointer)
- (cstring-pointer _cstring-pointer)))
-(re-export
- make-bytestructure-descriptor
- bytestructure-descriptor?
- bytestructure-descriptor-size
- bytestructure-descriptor-size/syntax
- bytestructure-descriptor-alignment
- bytestructure-descriptor-unwrapper
- bytestructure-descriptor-getter
- bytestructure-descriptor-setter
- bytestructure-descriptor-metadata
- make-bytestructure
- bytestructure?
- bytestructure-bytevector
- bytestructure-offset
- bytestructure-descriptor
- bytestructure-size
- bytestructure
- bytestructure-unwrap
- bytestructure-unwrap*
- bytestructure-ref
- bytestructure-ref*
- bytestructure-set!
- bytestructure-set!*
- bytestructure-ref/dynamic
- bytestructure-set!/dynamic
- bytestructure-unwrap/syntax
- bytestructure-ref/syntax
- bytestructure-set!/syntax
- define-bytestructure-accessors
-
- bs:vector
- vector-metadata? vector-metadata-length vector-metadata-element-descriptor
-
- bs:struct
- struct-metadata? struct-metadata-field-alist
-
- bs:union
- union-metadata? union-metadata-field-alist
-
- bs:pointer
- pointer-metadata? pointer-metadata-content-descriptor
-
- int8 int16 int32 int64
- int16le int32le int64le
- int16be int32be int64be
- uint8 uint16 uint32 uint64
- uint16le uint32le uint64le
- uint16be uint32be uint64be
- float32 float64
- float32le float64le
- float32be float64be
-
- short unsigned-short
- int unsigned-int
- long unsigned-long
- long-long unsigned-long-long
- intptr_t uintptr_t
- size_t ssize_t ptrdiff_t
- float double
-
- complex64 complex128
- complex64le complex128le
- complex64be complex128be
-
- bs:string
- )
-
-(define cstring-pointer _cstring-pointer)
-
-(export cstring-pointer)
-(define-library (bytestructures r7)
- (import
- (bytestructures r7 base)
- (bytestructures r7 vector)
- (bytestructures r7 struct)
- (bytestructures r7 union)
- (bytestructures r7 numeric)
- (bytestructures r7 string))
- (include-library-declarations "r7/base.exports.sld")
- (include-library-declarations "r7/vector.exports.sld")
- (include-library-declarations "r7/struct.exports.sld")
- (include-library-declarations "r7/union.exports.sld")
- (include-library-declarations "r7/numeric.exports.sld")
- (include-library-declarations "r7/string.exports.sld"))
-;;; Warning: nasal demons.
-;;;
-;;; Will output differences between GCC's behavior and our behavior, but not in
-;;; a very nice format. Zero output is good. The C code and Scheme procedure
-;;; we generate are fairly straightforward so read the code to understand.
-
-(define-module (bytestructures bitfield-tests))
-
-(export run-bitfield-tests)
-
-(use-modules (srfi srfi-1)
- (srfi srfi-9)
- (ice-9 rdelim)
- (bytestructures r6 bytevectors)
- (bytestructures guile))
-
-(define-record-type <struct>
- (make-struct name fields)
- struct?
- (name struct-name)
- (fields struct-fields))
-
-(define-record-type <field>
- (make-field name int-size bit-size signed? value)
- struct?
- (name field-name)
- (int-size field-int-size)
- (bit-size field-bit-size)
- (signed? field-signed?)
- (value field-value))
-
-(define *keep-files* (make-parameter #f))
-
-(define (run-bitfield-tests count random-seed-string keep-files)
- (set! *random-state* (seed->random-state random-seed-string))
- (parameterize ((*keep-files* keep-files))
- (test-structs (generate-structs count))))
-
-(define (generate-structs n)
- (remove-bad-structs (map random-struct (iota n))))
-
-(define (remove-bad-structs structs)
- (filter (lambda (struct)
- (find (lambda (field)
- (not (zero? (field-bit-size field))))
- (struct-fields struct)))
- structs))
-
-(define (random-struct i)
- (let ((field-count (+ 1 (random 50))))
- (make-struct (format #f "s~a" i)
- (map random-field (iota field-count)))))
-
-(define (random-field i)
- (let* ((name (format #f "f~a" i))
- (int-size (* 8 (expt 2 (random 4))))
- (bit-size (random (+ 1 int-size)))
- (signed? (= 0 (random 2)))
- (value (random (expt 2 bit-size)))
- (value (if (and signed? (> value (+ -1 (expt 2 (- bit-size 1)))))
- (- value (expt 2 bit-size))
- value)))
- (make-field name int-size bit-size signed? value)))
-
-(define (test-structs structs)
- (let* ((c-code (c-code-for-structs structs))
- (c-output (get-c-output c-code))
- (scm-code (scm-code-for-structs structs))
- (scm-output (get-scm-output scm-code)))
- (diff-outputs c-output scm-output)))
-
-(define (c-code-for-structs structs)
- (string-concatenate
- (append
- (list "#include <stdio.h>\n"
- "#include <stdint.h>\n"
- "#include <strings.h>\n"
- "int main(){\n")
- (map c-code-for-struct structs)
- (list "return 0;}"))))
-
-(define (c-code-for-struct struct)
- (let ((name (struct-name struct))
- (fields (struct-fields struct)))
- (string-concatenate
- (append
- (list (format #f "struct ~a {\n" name))
- (map c-decl-for-field fields)
- (list "};\n"
- (format #f "{ struct ~a foo;\n" name)
- (format #f " bzero((void*)&foo, sizeof(foo));\n"))
- (map c-assignment-for-field fields)
- (list (format #f " printf(\"struct ~a:\\n\");\n" name)
- " uint8_t *ptr = (void*)&foo;\n"
- " for (int i = 0; i < sizeof(foo); ++i) {\n"
- " printf(\"%d \", *(ptr+i));\n"
- " }\n"
- " printf(\"\\n\");\n"
- "}\n")))))
-
-(define (c-decl-for-field field)
- (let ((name (field-name field))
- (int-size (field-int-size field))
- (bit-size (field-bit-size field))
- (signed? (field-signed? field)))
- (format #f " ~aint~a_t ~a:~a;\n"
- (if signed? "" "u")
- int-size
- (if (zero? bit-size) "" name)
- bit-size)))
-
-(define (c-assignment-for-field field)
- (let ((name (field-name field))
- (bit-size (field-bit-size field))
- (signed? (field-signed? field))
- (value (field-value field)))
- (if (zero? bit-size)
- ""
- (format #f " foo.~a = ~a~a;\n" name value (if signed? "" "u")))))
-
-(define (get-c-output code)
- (let* ((port (mkstemp! (string-copy "/tmp/bitfield-XXXXXX")))
- (file (port-filename port))
- (exe-port (mkstemp! (string-copy "/tmp/bitfield-compiled-XXXXXX")))
- (exe-file (port-filename exe-port))
- (output-port (mkstemp! (string-copy "/tmp/bitfield-output-XXXXXX")))
- (output-file (port-filename output-port)))
- (close-port exe-port)
- (close-port output-port)
- (display code port)
- (force-output port)
- (unless (zero? (system* "gcc" "-x" "c" "-std=c11" file "-o" exe-file))
- (error "gcc failed"))
- (unless (zero? (system (format #f "~a > ~a" exe-file output-file)))
- (error "exe failed"))
- (let ((out (read-string (open output-file O_RDONLY))))
- (unless (*keep-files*)
- (for-each delete-file (list file exe-file output-file)))
- out)))
-
-(define (scm-code-for-structs structs)
- (lambda ()
- (string-concatenate
- (map scm-code-for-struct structs))))
-
-(define (scm-code-for-struct struct)
- (let* ((name (struct-name struct))
- (fields (struct-fields struct))
- (descriptor (struct->descriptor struct))
- (values (map field-value (filter-nonzero-fields fields)))
- (bs (bytestructure descriptor (list->vector values))))
- (string-concatenate
- (append
- (list (format #f "struct ~a:\n" name))
- (let ((bv (bytestructure-bytevector bs)))
- (map (lambda (i)
- (format #f "~a " (bytevector-u8-ref bv i)))
- (iota (bytevector-length bv))))
- (list "\n")))))
-
-(define (struct->descriptor struct)
- (let ((fields (struct-fields struct)))
- (bs:struct (map field->struct-descriptor-field fields))))
-
-(define (field->struct-descriptor-field field)
- (let ((name (field-name field))
- (int-size (field-int-size field))
- (bit-size (field-bit-size field))
- (signed? (field-signed? field)))
- (list name
- (module-ref (resolve-module
- '(bytestructures bitfield-tests))
- (string->symbol
- (format #f "~aint~a"
- (if signed? "" "u")
- int-size)))
- bit-size)))
-
-(define (filter-nonzero-fields fields)
- (filter (lambda (field)
- (not (zero? (field-bit-size field))))
- fields))
-
-(define (get-scm-output code)
- (code))
-
-(define (diff-outputs o1 o2)
- (let* ((p1 (mkstemp! (string-copy "/tmp/bitfield-out1-XXXXXX")))
- (f1 (port-filename p1))
- (p2 (mkstemp! (string-copy "/tmp/bitfield-out2-XXXXXX")))
- (f2 (port-filename p2)))
- (display o1 p1)
- (display o2 p2)
- (flush-all-ports)
- (close-port p1)
- (close-port p2)
- (let ((retval (system* "diff" "-y" "--suppress-common" f1 f2)))
- (unless (*keep-files*)
- (for-each delete-file (list f1 f2)))
- retval)))
-;;; Use this in the REPL. It produces wrong results when ran as a script.
-
-(use-modules (system vm coverage)
- (system vm vm)
- (srfi srfi-11))
-
-(let ((output-directory
- (string-append
- (getenv "HOME") "/srv/http/htdocs/lcov/scheme-bytestructures")))
- (let-values (((data . values)
- (with-code-coverage (the-vm)
- (lambda ()
- (load "run-tests.guile.scm")))))
- (let* ((port (mkstemp! (string-copy "/tmp/bytestructures-coverage-XXXXXX")))
- (file (port-filename port)))
- (coverage-data->lcov data port)
- (close port)
- (when (not (zero? (system* "genhtml" file "-o" output-directory)))
- (error "genhtml failed"))
- (delete-file file))))
-;;; run-tests.body.scm --- Bytestructures test suite.
-
-;; Copyright © 2015, 2021 Taylan Kammer <taylan.kammer@gmail.com>
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; A relatively simple SRFI-64 test suite.
-
-
-;;; Code:
-
-(define-syntax-rule (test-= name expected expr)
- (test-approximate name expected expr 0))
-
-(define-syntax-rule (maybe-skip-syntax . <body>)
- (if-syntax-case
- (begin . <body>)
- (begin)))
-
-(test-begin "bytestructures")
-
-(test-group "numeric"
- (define-syntax test-numeric-descriptors
- (syntax-rules ()
- ((_ <descriptor-id> ...)
- (let ()
- (define (destructure-numeric-descriptor-entry descriptor-entry proc)
- (define descriptor (list-ref descriptor-entry 0))
- (define name (list-ref descriptor-entry 1))
- (define getter (list-ref descriptor-entry 2))
- (define setter (list-ref descriptor-entry 3))
- (define size (bytestructure-descriptor-size descriptor))
- (define float? (assq descriptor float-descriptors))
- (define signed? (or float? (assq descriptor signed-integer-descriptors)))
- (proc descriptor name getter setter size float? signed?))
- (define (get-min/max float? signed? size)
- (cond
- (float? (inexact (expt 2 (case size ((4) 24) ((8) 53)))))
- (signed? (- (expt 256 (- size 1))))
- (else (- (expt 256 size) 1))))
- (destructure-numeric-descriptor-entry
- (assq <descriptor-id> numeric-descriptors)
- (lambda (descriptor name getter setter size float? signed?)
- (test-group (symbol->string name)
- (let ((test-value-1 (if float? 1.0 1))
- (test-value-2 (if float? 2.0 1)))
- (test-group "procedural"
- (define min/max (get-min/max float? signed? size))
- (define bs (bytestructure descriptor))
- (test-eqv "size" size (bytevector-length
- (bytestructure-bytevector bs)))
- (test-= "ref" test-value-1
- (begin
- (setter (bytestructure-bytevector bs) 0 test-value-1)
- (bytestructure-ref bs)))
- (test-= "set" test-value-2
- (begin
- (bytestructure-set! bs test-value-2)
- (getter (bytestructure-bytevector bs) 0)))
- (test-= "min/max" min/max
- (begin
- (bytestructure-set! bs min/max)
- (bytestructure-ref bs))))
- (maybe-skip-syntax
- (test-group "syntactic"
- (define min/max (get-min/max float? signed? size))
- ;; Must insert the top-level reference <descriptor-id> here.
- (define-bytestructure-accessors <descriptor-id>
- bs-unwrapper bs-getter bs-setter)
- (define bv (make-bytevector size))
- (test-= "ref" test-value-1
- (begin
- (setter bv 0 test-value-1)
- (bs-getter bv)))
- (test-= "set" test-value-2
- (begin
- (bs-setter bv test-value-2)
- (getter bv 0)))
- (test-= "min/max" min/max
- (begin
- (bs-setter bv min/max)
- (bs-getter bv)))))))))
- ...))))
- (test-numeric-descriptors
- float32 float32le float32be
- float64 float64le float64be
- int8 int16 int32 int64
- int16le int32le int64le
- int16be int32be int64be
- uint8 uint16 uint32 uint64
- uint16le uint32le uint64le
- uint16be uint32be uint64be))
-
-(test-group "vector"
- (test-assert "create" (bs:vector 3 uint16))
- (test-group "procedural"
- (define bs (bytestructure (bs:vector 3 uint16)))
- (bytevector-u16-native-set! (bytestructure-bytevector bs) 2 321)
- (test-eqv "ref" 321 (bytestructure-ref bs 1))
- (test-eqv "set" 456 (begin (bytestructure-set! bs 1 456)
- (bytestructure-ref bs 1)))
- (test-eqv "init" 321
- (let ((bs (bytestructure (bs:vector 3 uint16) '#(321 123 321))))
- (bytestructure-ref bs 2))))
- (maybe-skip-syntax
- (test-group "syntactic"
- (define-bytestructure-accessors (bs:vector 3 uint16)
- unwrapper getter setter)
- (define bv (make-bytevector 6))
- (bytevector-u16-native-set! bv 2 321)
- (test-eqv "ref" 321 (getter bv 1))
- (test-eqv "set" 456 (begin (setter bv 1 456)
- (getter bv 1))))))
-
-(test-group "struct"
- (test-group "aligned"
- (test-assert "create" (bs:struct `((x ,uint8) (y ,uint16))))
- (test-group "procedural"
- (define bs (bytestructure (bs:struct `((x ,uint8) (y ,uint16)))))
- (bytevector-u16-native-set! (bytestructure-bytevector bs) 2 321)
- (test-eqv "ref" 321 (bytestructure-ref bs 'y))
- (test-eqv "set" 456 (begin (bytestructure-set! bs 'y 456)
- (bytestructure-ref bs 'y)))
- (test-eqv "init" 321
- (let ((bs (bytestructure (bs:struct `((x ,uint8) (y ,uint16)))
- '#(123 321))))
- (bytestructure-ref bs 'y))))
- (maybe-skip-syntax
- (test-group "syntactic"
- (define-bytestructure-accessors (bs:struct `((x ,uint8) (y ,uint16)))
- unwrapper getter setter)
- (define bv (make-bytevector 4))
- (bytevector-u16-native-set! bv 2 321)
- (test-eqv "ref" 321 (getter bv y))
- (test-eqv "set" 456 (begin (setter bv y 456)
- (getter bv y))))))
- (test-group "packed"
- (test-assert "create" (bs:struct #t `((x ,uint8) (y ,uint16))))
- (test-group "procedural"
- (define bs (bytestructure (bs:struct #t `((x ,uint8) (y ,uint16)))))
- ;; u16-native-set! may error on non-aligned access.
- (guard (err (else (test-skip 3)))
- (bytevector-u16-native-set! (bytestructure-bytevector bs) 1 321))
- (test-eqv "ref" 321 (bytestructure-ref bs 'y))
- (test-eqv "set" 456 (begin (bytestructure-set! bs 'y 456)
- (bytestructure-ref bs 'y)))
- (test-eqv "init" 321
- (let ((bs (bytestructure (bs:struct #t `((x ,uint8) (y ,uint16)))
- '#(123 321))))
- (bytestructure-ref bs 'y))))
- (maybe-skip-syntax
- (test-group "syntactic"
- (define-bytestructure-accessors (bs:struct #t `((x ,uint8) (y ,uint16)))
- unwrapper getter setter)
- (define bv (make-bytevector 4))
- ;; u16-native-set! may error on non-aligned access.
- (guard (err (else (test-skip 2)))
- (bytevector-u16-native-set! bv 1 321))
- (test-eqv "ref" 321 (getter bv y))
- (test-eqv "set" 456 (begin (setter bv y 456)
- (getter bv y))))))
-
- (test-group "anonymous-union"
- (test-assert "create"
- (bs:struct
- `((x ,uint8)
- (union
- ((a ,uint16)
- (b ,uint32))))))
- ;; Don't use 64-bit ints; their alignment differs between platforms.
- (test-group "aligned"
- (define bs
- (bytestructure
- (bs:struct
- `((union
- ((x ,uint8)
- (y ,uint16)))
- (union
- ((a ,uint16)
- (b ,uint32)))))))
- (test-eqv "size" 8 (bytevector-length (bytestructure-bytevector bs)))
- (bytevector-u16-native-set! (bytestructure-bytevector bs) 4 321)
- (test-eqv "ref1" 321 (bytestructure-ref bs 'a))
- (bytevector-u32-native-set! (bytestructure-bytevector bs) 4 456)
- (test-eqv "ref2" 456 (bytestructure-ref bs 'b))
- (test-eqv "set1" 789 (begin (bytestructure-set! bs 'a 789)
- (bytestructure-ref bs 'a)))
- (test-eqv "set2" 987 (begin (bytestructure-set! bs 'b 987)
- (bytestructure-ref bs 'b))))
- (test-group "packed"
- (define bs
- (bytestructure
- (bs:struct
- #t
- `((union
- ((x ,uint8)
- (y ,uint16)))
- (union
- ((a ,uint16)
- (b ,uint32)))))))
- (test-eqv "size" 6 (bytevector-length (bytestructure-bytevector bs)))
- (bytevector-u16-native-set! (bytestructure-bytevector bs) 2 321)
- (test-eqv "ref1" 321 (bytestructure-ref bs 'a))
- (bytevector-u32-native-set! (bytestructure-bytevector bs) 2 456)
- (test-eqv "ref2" 456 (bytestructure-ref bs 'b))
- (test-eqv "set1" 789 (begin (bytestructure-set! bs 'a 789)
- (bytestructure-ref bs 'a)))
- (test-eqv "set2" 987 (begin (bytestructure-set! bs 'b 987)
- (bytestructure-ref bs 'b))))))
-
-(test-group "union"
- (test-assert "create" (bs:union `((x ,uint8) (y ,uint16))))
- (test-group "procedural"
- (define bs (bytestructure (bs:union `((x ,uint8) (y ,uint16)))))
- (bytevector-u16-native-set! (bytestructure-bytevector bs) 0 321)
- (test-eqv "ref" 321 (bytestructure-ref bs 'y))
- (test-eqv "set" 456 (begin (bytestructure-set! bs 'y 456)
- (bytestructure-ref bs 'y))))
- (maybe-skip-syntax
- (test-group "syntactic"
- (define-bytestructure-accessors (bs:union `((x ,uint8) (y ,uint16)))
- unwrapper getter setter)
- (define bv (make-bytevector 2))
- (bytevector-u16-native-set! bv 0 321)
- (test-eqv "ref" 321 (getter bv y))
- (test-eqv "set" 456 (begin (setter bv y 456)
- (getter bv y))))))
-
-(test-group "string"
- (test-group "ascii"
- (test-assert "create" (bs:string 4 'ascii))
- (test-group "procedural"
- (define bsd (bs:string 4 'ascii))
- (define bs (make-bytestructure (string->utf8 "1234") 0 bsd))
- (test-equal "ref" "1234" (bytestructure-ref bs))
- (test-equal "set" "4321" (begin
- (bytestructure-set! bs "4321")
- (bytestructure-ref bs)))
- (test-error "too-long" #t (bytestructure-set! bs "12345"))
- (test-error "too-short" #t (bytestructure-set! bs "123"))
- (set! bs (make-bytestructure (string->utf8 "äåãø") 0 bsd))
- (test-error "decoding-error" #t (bytestructure-ref bs))
- (test-error "encoding-error" #t (bytestructure-set! bs "øãåä")))
- (test-group "syntactic"
- (define-bytestructure-accessors (bs:string 4 'ascii)
- unwrapper getter setter)
- (define bv (string->utf8 "1234"))
- (test-equal "ref" "1234" (getter bv))
- (test-equal "set" "4321" (begin
- (setter bv "4321")
- (getter bv)))
- (test-error "too-long" #t (setter bv "12345"))
- (test-error "too-short" #t (setter bv "123"))
- (set! bv (string->utf8 "äåãø"))
- (test-error "ref-error" #t (getter bv))
- (test-error "set-error" #t (setter bv "øãåä"))))
- (test-group "utf8"
- (test-assert "create" (bs:string 4 'utf8))
- (test-group "procedural"
- (define bsd (bs:string 4 'utf8))
- (define bs (make-bytestructure (string->utf8 "1234") 0 bsd))
- (test-equal "ref" "1234" (bytestructure-ref bs))
- (test-equal "set" "4321" (begin
- (bytestructure-set! bs "4321")
- (bytestructure-ref bs)))
- (test-error "too-long" #t (bytestructure-set! bs "äåãø"))
- (test-equal (string-append "123" (string #\nul))
- (begin
- (bytestructure-set! bs "123")
- (bytestructure-ref bs))))
- (test-group "syntactic"
- (define-bytestructure-accessors (bs:string 4 'utf8)
- unwrapper getter setter)
- (define bv (string->utf8 "1234"))
- (test-equal "ref" "1234" (getter bv))
- (test-equal "set" "4321" (begin
- (setter bv "4321")
- (getter bv)))
- (test-error "too-long" #t (setter bv "äåãø"))
- (test-equal (string-append "123" (string #\nul))
- (begin
- (setter bv "123")
- (getter bv)))))
- (let ()
- (define-syntax-rule
- (test-string-encodings
- (<name> <encoding> <endianness> <size> <fixed-width?> <string->utf>)
- ...)
- (begin
- (test-group <name>
- (test-assert "create" (bs:string <size> '<encoding>))
- (test-group "procedural"
- (define bs (make-bytestructure (<string->utf> "1234" '<endianness>)
- 0
- (bs:string <size> '<encoding>)))
- (test-equal "ref" "1234" (bytestructure-ref bs))
- (test-equal "set" "4321" (begin
- (bytestructure-set! bs "4321")
- (bytestructure-ref bs)))
- (test-error "too-long" #t (bytestructure-set! bs "12345"))
- (if <fixed-width?>
- (test-error "too-short" #t (bytestructure-set! bs "123"))
- (test-equal (string-append "123" (string #\nul))
- (begin
- (bytestructure-set! bs "123")
- (bytestructure-ref bs)))))
- (test-group "syntactic"
- (define-bytestructure-accessors (bs:string <size> '<encoding>)
- unwrapper getter setter)
- (define bv (<string->utf> "1234" '<endianness>))
- (test-equal "ref" "1234" (getter bv))
- (test-equal "set" "4321" (begin
- (setter bv "4321")
- (getter bv)))
- (test-error "too-long" #t (setter bv "12345"))
- (if <fixed-width?>
- (test-error "too-short" #t (setter bv "123"))
- (test-equal (string-append "123" (string #\nul))
- (begin
- (setter bv "123")
- (getter bv))))))
- ...))
- (test-string-encodings
- ("utf16le" utf16le little 8 #f string->utf16)
- ("utf16be" utf16be big 8 #f string->utf16)
- ("utf32le" utf32le little 16 #t string->utf32)
- ("utf32be" utf32be big 16 #t string->utf32))))
-
-(cond-expand
- (guile
- (let ()
-
- (define (protect-from-gc-upto-here obj)
- (with-output-to-file *null-device*
- (lambda ()
- (display (eq? #f obj)))))
-
- (define pointer-size (ffi-sizeof '*))
- (define bytevector-address-set!
- (case pointer-size
- ((1) bytevector-u8-set!)
- ((2) bytevector-u16-native-set!)
- ((4) bytevector-u32-native-set!)
- ((8) bytevector-u64-native-set!)))
-
- (test-group "pointer"
- (test-assert "create" (bs:pointer uint16))
- (test-group "procedural"
- (define bs (bytestructure (bs:pointer uint16)))
- (define bv1 (make-bytevector 2))
- (define bv2 (make-bytevector 4))
- (define address1 (ffi-pointer-address (ffi-bytevector->pointer bv1)))
- (define address2 (ffi-pointer-address (ffi-bytevector->pointer bv2)))
- (bytevector-address-set! (bytestructure-bytevector bs) 0 address1)
- (bytevector-u16-native-set! bv1 0 321)
- (test-eqv "ref1" 321 (bytestructure-ref bs '*))
- (test-eqv "set1" 456 (begin (bytestructure-set! bs '* 456)
- (bytestructure-ref bs '*)))
- (test-eqv "ref2" address1 (bytestructure-ref bs))
- (test-eqv "set2" address2 (begin (bytestructure-set! bs address2)
- (bytestructure-ref bs)))
- (bytevector-address-set! (bytestructure-bytevector bs) 0 address2)
- (bytevector-u16-native-set! bv2 2 456)
- (test-eqv "ref3" 456 (bytestructure-ref bs 1))
- (test-eqv "set3" 789 (begin (bytestructure-set! bs 1 789)
- (bytestructure-ref bs 1)))
- (protect-from-gc-upto-here bv1)
- (protect-from-gc-upto-here bv2))
- (test-group "syntactic"
- (define-bytestructure-accessors (bs:pointer uint16)
- unwrapper getter setter)
- (define bv (make-bytevector pointer-size))
- (define bv1 (make-bytevector 2))
- (define bv2 (make-bytevector 4))
- (define address1 (ffi-pointer-address (ffi-bytevector->pointer bv1)))
- (define address2 (ffi-pointer-address (ffi-bytevector->pointer bv2)))
- (bytevector-address-set! bv 0 address1)
- (bytevector-u16-native-set! bv1 0 321)
- (test-eqv "ref" 321 (getter bv *))
- (test-eqv "set" 456 (begin (setter bv * 456)
- (getter bv *)))
- (test-eqv "ref2" address1 (getter bv))
- (test-eqv "set2" address1 (begin (setter bv address1)
- (getter bv)))
- (bytevector-address-set! bv 0 address2)
- (bytevector-u16-native-set! bv2 2 456)
- (test-eqv "ref3" 456 (getter bv 1))
- (test-eqv "set3" 789 (begin (setter bv 1 789)
- (getter bv 1)))
- (protect-from-gc-upto-here bv1)
- (protect-from-gc-upto-here bv2)))
-
- (test-group "cstring-pointer"
- (let* ((cstr1-ptr (ffi-string->pointer "abc"))
- (cstr2-ptr (ffi-string->pointer "cba"))
- (cstr1-addr (ffi-pointer-address cstr1-ptr))
- (cstr2-addr (ffi-pointer-address cstr2-ptr)))
- (test-group "procedural"
- (define bs (bytestructure cstring-pointer))
- (bytevector-address-set! (bytestructure-bytevector bs) 0 cstr1-addr)
- (test-equal "ref" "abc" (bytestructure-ref bs))
- (test-equal "set" "cba" (begin (bytestructure-set! bs cstr2-addr)
- (bytestructure-ref bs))))
- (test-group "syntactic"
- (define-bytestructure-accessors cstring-pointer
- unwrapper getter setter)
- (define bv (make-bytevector pointer-size))
- (bytevector-address-set! bv 0 cstr1-addr)
- (test-equal "ref" "abc" (getter bv))
- (test-equal "set" "cba" (begin (setter bv cstr2-addr)
- (getter bv))))))))
-
- (else
- ))
-
-;; Do this before test-end since it removes the auto-inserted test runner.
-(define success
- (let ((runner (test-runner-current)))
- (and (zero? (test-runner-xpass-count runner))
- (zero? (test-runner-fail-count runner)))))
-
-(test-end "bytestructures")
-
-(exit (if success 0 1))
-
-;; Local Variables:
-;; eval: (put (quote test-group) (quote scheme-indent-function) 1)
-;; eval: (put (quote test-=) (quote scheme-indent-function) 2)
-;; End:
-
-(use-modules
- (srfi srfi-11)
- (srfi srfi-64)
- ((rnrs exceptions) #\select (guard))
- ((system foreign) #\prefix ffi-)
- (bytestructures r6 bytevectors)
- (bytestructures guile utils)
- (bytestructures guile)
- (bytestructures guile numeric-metadata))
-
-(define inexact exact->inexact)
-
-(include-from-path "run-tests.body.scm")
-(import
- (scheme base)
- (srfi 64)
- (bytestructures r7 utils)
- (bytestructures r7)
- (bytestructures r7 numeric-metadata)
- (bytestructures r7 bytevectors)
- (bytestructures r7 explicit-endianness))
-
-(include "run-tests.body.scm")
-;;; commonmark.scm --- An implementation of CommonMark markdown
-
-;; Copyright (C) 2014 Taylan Ulrich Bayirli/Kammer
-
-;; Author: Taylan Ulrich Bayirli/Kammer <taylanbayirli@gmail.com>
-;; Keywords:
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;;
-
-;;; Code:
-
-(define (parse port)
- (let* ((lines (preprocess port))
- (blocks (parse-blocks lines)))
- blocks))
-
-(define (preprocess port)
- (do ((line (read-line port) (read-line port))
- (lines '() (cons (preprocess-line line) lines)))
- ((eof-object? line) (reverse lines))))
-
-(define (preprocess-line line)
- (do ((chars (string->list line) (cdr chars))
- (processed-chars '() (let ((char (car chars)))
- (if (char=? char #\tab)
- (append (make-list 4 #\space)
- processed-chars)
- (cons char processed-chars)))))
- ((null? chars) (apply string (reverse processed-chars)))))
-
-(define (parse-blocks lines)
- (do ((lines lines (cdr lines))
- (blocks '() (let ((blocks* (add-line blocks (car lines))))
- (if blocks*
- blocks*
- (begin (close-block! (car blocks))
- blocks))))))
- ((null? lines) (reverse blocks)))
-
-;;; BLOCKS is in reverse here.
-(define (add-line blocks line)
- (if (null? blocks)
- (cons (new-block line) blocks)
- (let ((last-block (car blocks)))
- (cond
- ((and (open-text-block? last-block)
- (plain-text-line? line))
- (add-line-to-text-block last-block line))
- ((and (open-container-block? last-block)
- ()))))))
-
-;;; commonmark.scm ends here
-(export
-
- )
-(define-library (commonmark r7rs)
- (import (scheme base))
- (include-library-declarations "r7rs-exports.scm")
- (include "commonmark.scm"))
-;;; generic-ref-set --- Generic accessor and modifier operators.
-
-;; Copyright © 2015 Taylan Ulrich Bayırlı/Kammer <taylanbayirli@gmail.com>
-
-;; Permission is hereby granted, free of charge, to any person obtaining
-;; a copy of this software and associated documentation files (the
-;; "Software"), to deal in the Software without restriction, including
-;; without limitation the rights to use, copy, modify, merge, publish,
-;; distribute, sublicense, and/or sell copies of the Software, and to
-;; permit persons to whom the Software is furnished to do so, subject to
-;; the following conditions:
-
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-;; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-;;; Helpers
-
-(define-syntax push!
- (syntax-rules ()
- ((_ <list-var> <x>)
- (set! <list-var> (cons <x> <list-var>)))))
-
-(define (alist->hashtable alist)
- (let ((table (make-eqv-hashtable 100)))
- (for-each (lambda (entry)
- (hashtable-set! table (car entry) (cdr entry)))
- alist)
- table))
-
-;;; Main
-
-(define ref
- (case-lambda
- ((object field)
- (let ((getter (lookup-getter object))
- (sparse? (sparse-type? object)))
- (if sparse?
- (let* ((not-found (cons #f #f))
- (result (getter object field not-found)))
- (if (eqv? result not-found)
- (error "Object has no entry for field." object field)
- result))
- (getter object field))))
- ((object field default)
- (let ((getter (lookup-getter object)))
- (getter object field default)))))
-
-(define-syntax set!
- (syntax-rules ()
- ((set! <place> <expression>)
- (%set! <place> <expression>))
- ((set! <object> <field> <value>)
- (let* ((object <object>)
- (setter (lookup-setter object)))
- (setter object <field> <value>)))))
-
-(set! (setter ref) (lambda (object field value) (set! object field value)))
-
-(define (lookup-getter object)
- (or (hashtable-ref getter-table (type-of object) #f)
- (error "No generic getter for object's type." object)))
-
-(define (lookup-setter object)
- (or (hashtable-ref setter-table (type-of object) #f)
- (error "No generic setter for object's type." object)))
-
-(define (sparse-type? object)
- (memv (type-of object) sparse-types))
-
-(define (type-of object)
- (find (lambda (pred) (pred object)) type-list))
-
-(define getter-table
- (alist->hashtable
- (list (cons bytevector? bytevector-u8-ref)
- (cons hashtable? hashtable-ref)
- (cons pair? list-ref)
- (cons string? string-ref)
- (cons vector? vector-ref))))
-
-(define setter-table
- (alist->hashtable
- (list (cons bytevector? bytevector-u8-set!)
- (cons hashtable? hashtable-set!)
- (cons pair? list-set!)
- (cons string? string-set!)
- (cons vector? vector-set!))))
-
-(define sparse-types
- (list hashtable?))
-
-(define type-list
- (list boolean? bytevector? char? eof-object? hashtable? null? number? pair?
- port? procedure? string? symbol? vector?))
-
-(define-syntax define-record-type
- (syntax-rules ()
- ((_ <name> <constructor> <pred> <field> ...)
- (begin
- (%define-record-type <name> <constructor> <pred> <field> ...)
- (push! type-list <pred>)
- (register-record-getter <pred> <field> ...)
- (register-record-setter <pred> <field> ...)))))
-
-(define-syntax register-record-getter
- (syntax-rules ()
- ((_ <pred> (<field> <getter> . <rest>) ...)
- (let ((getters (alist->hashtable (list (cons '<field> <getter>) ...))))
- (define (getter record field)
- (let ((getter (or (ref getters field #f)
- (error "No such field of record." record field))))
- (getter record field)))
- (set! getter-table <pred> getter)))))
-
-(define-syntax register-record-setter
- (syntax-rules ()
- ((_ . <rest>)
- (%register-record-setter () . <rest>))))
-
-(define-syntax %register-record-setter
- (syntax-rules ()
- ((_ <setters> <pred> (<field> <getter>) . <rest>)
- (%register-record-setter <setters> <pred> . <rest>))
- ((_ <setters> <pred> (<field> <getter> <setter>) . <rest>)
- (%register-record-setter ((<field> <setter>) . <setters>) <pred> . <rest>))
- ((_ ((<field> <setter>) ...) <pred>)
- (let ((setters (alist->hashtable (list (cons '<field> <setter>) ...))))
- (define (setter record field value)
- (let ((setter (or (ref setters field #f)
- (error "No such assignable field of record."
- record field))))
- (setter record value)))
- (set! setter-table <pred> setter)))))
-
-;;; generic-ref-set.body.scm ends here
-(define-library (generic-ref-set)
- (export
- ref set! define-record-type (rename ref $bracket-apply$))
- (import
- (rename (except (scheme base) set!)
- (define-record-type %define-record-type))
- (scheme case-lambda)
- (r6rs hashtables)
- (srfi 1)
- (rename (srfi 17) (set! %set!)))
- (include "generic-ref-set.body.scm"))
-(define-module (ie-reader cre))
-
-(use-modules
- (bytestructures guile))
-
-(define cre-header
- (bs:struct
- `((signature ,(bs:string 4 'ascii))
- (version ,(bs:string 4 'ascii))
- (long-name ))))
-;; One advantage of dlists is that they allow you to write more
-;; efficient programs, while keeping the lucidity of the less
-;; efficient version. Take the naïve version of 'reverse'
-
-(define (reverse l)
- (if (null? l)
- '()
- (append (reverse (cdr l))
- (list (car l)))))
-
-;; The definition is obviously correct, however it isn't very
-;; efficient. For a given step, the cost of the non-trivial case is
-;; dependant on the size of the list we have gotten from the recursive
-;; call. That is, it takes time proportional to the square of its
-;; input list.
-;; Of course, no self respecting functional programmer would write
-;; reverse in this manner, as the trick of using an accumulating
-;; parameter is so well established. Instead we would write
-
-(define (reverse l)
- (define (reverse-helper from to)
- (if (null? from)
- to
- (reverse-helper (cdr from)
- (cons (car from) to))))
- (reverse-helper l '()))
-
-;; By introducing this additional parameter, we have reclaimed a more
-;; reasonable complexity of constant time at each recursive call,
-;; giving us linear complexity overall.
-;; This is a big improvement, and with a little practice, it becomes
-;; easy to convince yourself of the correctness of code written in
-;; this manner.
-
-;; However, why should you have to practice? Why can't there be a
-;; definition as obviously correct as the former, with the efficiency
-;; of the latter?
-;; Turns out, it is possible to do this, by using a different
-;; representation for lists.
-
-(define (reverse* l)
- (if (null? l)
- (dlist)
- (dlist-append (reverse* (cdr l))
- (dlist (car l)))))
-
-(define (reverse l)
- (dlist->list (reverse* l)))
-
-;; Difference lists, or representing lists as functions, gives us a
-;; constant time version of append, thus reducing the complexity of
-;; reverse* to O(n), and the definition differs from the original,
-;; only in the names we use for the append and list procedures. The
-;; final result of this function, however, is a dlist rather than a
-;; list, so we must convert back. This also has linear complexity, so
-;; the overall complexity is still linear.
-
-;; How does this work? Well, let's replace dlist and dlist-append with
-;; their definitions
-(define (reverse* l)
- (if (null? l)
- (lambda (x) (append '() x))
- (compose (reverse* (cdr l))
- (lambda (x) (append (list (car l)) x)))))
-
-(define (reverse l)
- ((reverse* l) '()))
-
-;; Now, we replace compose with its definition
-(define (reverse* l)
- (if (null? l)
- (lambda (x) (append '() x))
- (lambda (x)
- ((reverse* (cdr l))
- ((lambda (x) (append (list (car l)) x)) x)))))
-
-(define (reverse l)
- ((reverse* l) '()))
-
-;; With a few simplifications: substituting x for its definition,
-;; x for (append '() x), and (cons x y) for (append (list x) y)
-(define (reverse* l)
- (if (null? l)
- (lambda (x) x)
- (lambda (x)
- ((reverse* (cdr l))
- (cons (car l) x)))))
-
-(define (reverse l)
- ((reverse* l) '()))
-
-;; Now, if we uncurry reverse*
-(define (reverse* l x)
- (if (null? l)
- x
- (reverse* (cdr l) (cons (car l) x))))
-
-(define (reverse l)
- (reverse* l '()))
-
-;; Then, it turns out the dlist version is the traditional O(n)
-;; implementation in disguise.
-
-;; As an exercise, you can try doing the same thing for the flatten
-;; function
-(define (flatten xs)
- (cond ((null? xs) '())
- ((pair? xs)
- (append (flatten (car xs))
- (flatten (cdr xs))))
- (else (list xs))))
-;;; Functional Breadth First Search
-(import (rnrs)
- (pfds queues))
-
-;; This is the traditional solution using Queues, for a more
-;; interesting solution, see "The Under-Appreciated Unfold" by Jeremy
-;; Gibbons and Geraint Jones.
-
-;; We'll need a tree type, we'll use #f for an empty child.
-(define-record-type tree
- (fields value left right))
-
-;; A small section of the Stern-Brocot Tree
-;; https://en.wikipedia.org/wiki/Stern%E2%80%93Brocot_tree
-(define stern-brocot
- (make-tree 1
- (make-tree 1/2
- (make-tree 1/3
- (make-tree 1/4 #f #f)
- (make-tree 2/5 #f #f))
- (make-tree 2/3
- (make-tree 3/5 #f #f)
- (make-tree 3/4 #f #f)))
- (make-tree 2
- (make-tree 3/2
- (make-tree 4/3 #f #f)
- (make-tree 5/3 #f #f))
- (make-tree 3
- (make-tree 5/2 #f #f)
- (make-tree 4 #f #f)))))
-
-;; We'll search it breadth-first for the first fraction expressed in
-;; fifths.
-(define (fifth? f)
- (= 5 (denominator f)))
-
-;; The queue search
-(define (bfs p? tree)
- (define (step queue)
- (if (queue-empty? queue)
- #f
- (let-values ([(head queue*) (dequeue queue)])
- (cond ((not head) ; empty-tree, skip
- (step queue*))
- ((p? (tree-value head)) (tree-value head))
- (else
- (step (enqueue (enqueue queue* (tree-left head))
- (tree-right head))))))))
-
- (step (enqueue (make-queue) tree)))
-
-(equal? 2/5 (bfs fifth? stern-brocot))
-(define-library (pfds assert)
- (export assert assertion-violation)
- (import (scheme base))
- (begin
- ()))
-;;; bbtrees.sls --- Bounded Balance trees
-
-;; Copyright (C) 2012 Ian Price <ianprice90@googlemail.com>
-;; Copyright (C) 2015 Taylan Ulrich Bayırlı/Kammer <taylanbayirli@gmail.com>
-
-;; Author: Ian Price <ianprice90@googlemail.com>
-
-;; This program is free software, you can redistribute it and/or
-;; modify it under the terms of the new-style BSD license.
-
-;; You should have received a copy of the BSD license along with this
-;; program. If not, see <http://www.debian.org/misc/bsd.license>.
-
-;; Documentation:
-;;
-;; Note: For all procedures which take a key as an argument, the key
-;; must be comparable with the ordering procedure of the bbtree.
-;;
-;; make-bbtree : (any -> any -> boolean) -> bbtree
-;; returns an empty bbtree. bbtrees derived from this one will use the
-;; procedure argument for ordering keys.
-;;
-;; bbtree? : any -> bool
-;; returns #t if the argument is a bbtree, #f otherwise
-;;
-;; bbtree-size : bbtree -> non-negative integer
-;; returns the number of elements in a bbtree
-;;
-;; bbtree-ref : bbtree any [any] -> any
-;; returns the value associated with the key in the bbtree. If the
-;; value is not in the tree, then, if the optional third argument is
-;; passed, it is returned, otherwise an &assertion-violation condition
-;; is raised.
-;;
-;; bbtree-set : bbtree any any -> bbtree
-;; returns a new bbtree with the key associated with the value. If the
-;; key is already in the bbtree, its associated value is replaced with
-;; the new value in the returned bbtree.
-;;
-;; bbtree-update : bbtree any (any -> any) any -> bbtree
-;; returns a new bbtree with the value associated with the key updated
-;; according to the update procedure. If the key was not already in
-;; the bbtree, the update procedure is called on the default value,
-;; and the association is added to the bbtree.
-;;
-;; bbtree-delete : bbtree any -> bbtree
-;; returns a new bbtree with the key and its associated value
-;; removed. If the key is not in the bbtree, the returned bbtree is a
-;; copy of the original
-;;
-;; bbtree-contains? : bbtree any -> boolean
-;; returns #t if there is association for key in the bbtree, false
-;; otherwise
-;;
-;; bbtree-traverse : (any any (any -> any) (any -> any) any) any bbtree -> any
-;; A general tree traversal procedure. Returns the value of applying
-;; the traverser procedure to the current node's key, value, a
-;; procedure to traverse the left subtree, a procedure to traverse the
-;; right subtree, and a base value. The subtree traversal procedures
-;; both take a base argument, and call bbtree-traverse recursively on
-;; the appropriate subtree. It is mostly useful for implementing
-;; other, more specific tree traversal procedures. For example,
-;; (define (l-to-r-pre-order cons base bbtree)
-;; (bbtree-traverse (lambda (key value left right base)
-;; (right (left (cons key value base))))
-;; base
-;; bbtree))
-;; implements a left-to-right pre-order traversal variant of bbtree-fold
-;;
-;; bbtree-fold : (any any any -> any) any bbtree -> any
-;; returns the value obtained by the iterating the combine procedure
-;; over each node in the tree. The combine procedure takes three
-;; arguments, the key and value of the current node, and an
-;; accumulator value, and its return value is used as the accumulator
-;; value for the next node. The initial accumulator value is provided
-;; by the base argument. bbtree-fold performs an left-to-right
-;; in-order traversal or "minimum key to maximum key".
-;;
-;; bbtree-fold-right : (any any any -> any) any bbtree -> any
-;; like bbtree-fold, but it performs a right-to-left in-order
-;; traversal instead (i.e. maximum to minimum).
-;;
-;; bbtree-map : (any -> any) bbtree -> bbtree
-;; returns the tree obtained by updating the value of each node with
-;; the result of applying the procedure to its value.
-;;
-;; bbtree->alist : bbtree -> Listof(Pairs)
-;; returns the key value associations of the bbtree as a list of
-;; pairs. The list returned is in sorted order according to the
-;; ordering procedure of the bbtree. A consequence of this is that one
-;; could write a sort procedure for lists of pairs as
-;; (define (alist-sort alist <)
-;; (bbtree->alist (alist->bbtree alist <)))
-;;
-;; alist->bbtree : Listof(Pairs) -> (any any -> boolean) -> bbtree
-;; returns the bbtree containing each of the key value pairs in the
-;; alist, using the < argument as the ordering procedure.
-;;
-;; bbtree-keys : bbtree -> Listof(any)
-;; returns a list containing all the keys of the bbtree. The keys are
-;; sorted according to the bbtree's ordering procedure.
-;;
-;; bbtree-union : bbtree bbtree -> bbtree
-;; returns a bbtree containing the union of the associations in
-;; bbtree1 and bbtree2. Where the same key occurs in both, the value
-;; in bbtree1 is preferred.
-;;
-;; bbtree-difference : bbtree bbtree -> bbtree
-;; returns a bbtree containing the all the associations in bbtree1,
-;; which do not occur in bbtree2.
-;;
-;; bbtree-intersection : bbtree bbtree -> bbtree
-;; returns a bbtree containing all the associations which appear in
-;; both bbtree1 and bbtree2. The value in bbtree1 are preferred over
-;; those in bbtree2.
-;;
-;; bbtree-index bbtree any -> non-negative integer
-;; returns the index of the key in the bbtree. Index is an integer
-;; between 0 and size - 1, with the a key having a lower index than
-;; another if first-key < second-key, according to the bbtree ordering
-;; procedure.
-;;
-;; bbtree-ref/index bbtree non-negative-integer -> any any
-;; returns the key and value of the association in the bbtree at the
-;; given index.
-;;
-;; bbtree-ordering-procedure : bbtree -> (any any -> bool)
-;; returns the ordering procedure used internally to order the
-;; bbtree.
-(define-library (pfds bbtrees)
-(export make-bbtree
- bbtree?
- bbtree-size
- bbtree-ref
- bbtree-set
- bbtree-update
- bbtree-delete
- bbtree-contains?
- bbtree-ordering-procedure
- bbtree-traverse
- bbtree-fold
- bbtree-fold-right
- bbtree-map
- bbtree->alist
- alist->bbtree
- bbtree-keys
- bbtree-union
- bbtree-difference
- bbtree-intersection
- bbtree-index
- bbtree-ref/index
- )
-
-(import (except (scheme base) min member))
-
-(begin
-
-(define weight 4)
-
-;;; bbtree is the wrapper that you interact with from outside the
-;;; module, so there is no need to deal with empty and node record types
-(define-record-type (bbtree %make-bbtree bbtree?)
- (fields tree ordering-procedure))
-
-(define (update-tree bbtree new-tree)
- (%make-bbtree new-tree (bbtree-ordering-procedure bbtree)))
-
-;;; inner representation of trees
-;;; all non exposed methods can assume a valid tree
-(define-record-type empty)
-
-(define-record-type node
- (fields key value length left right))
-
-;;; smart constructor for nodes, automatically fills in size field
-(define (node* key value left right)
- (make-node key value (+ 1 (size left) (size right)) left right))
-
-(define (size tree)
- (if (empty? tree)
- 0
- (node-length tree)))
-
-;; looks key up in the tree, and applies proc to the value if it finds
-;; it, and calls failure otherwise
-(define (lookup tree key proc failure <)
- (define (search tree)
- (cond ((empty? tree) (failure))
- ((< (node-key tree) key)
- (search (node-right tree)))
- ((< key (node-key tree))
- (search (node-left tree)))
- (else (proc tree))))
- (search tree))
-
-;; returns the key and value of the minimum element in the tree
-(define (min tree)
- (cond ((empty? tree)
- (assertion-violation 'min "Can't take the minimum value of an empty tree"))
- ((empty? (node-left tree))
- (values (node-key tree)
- (node-value tree)))
- (else
- (min (node-left tree)))))
-
-;;; rotations
-(define (rotate-left key value left right)
- (let ((r-key (node-key right))
- (r-value (node-value right))
- (r-left (node-left right))
- (r-right (node-right right)))
- (node* r-key
- r-value
- (node* key value left r-left)
- r-right)))
-
-(define (rotate-right key value left right)
- (let ((l-key (node-key left))
- (l-value (node-value left))
- (l-left (node-left left))
- (l-right (node-right left)))
- (node* l-key
- l-value
- l-left
- (node* key value l-right right))))
-
-(define (rotate-left/double key value left right)
- (let ((r-key (node-key right))
- (r-value (node-value right))
- (r-left (node-left right))
- (r-right (node-right right)))
- (let ((rl-key (node-key r-left))
- (rl-value (node-value r-left))
- (rl-left (node-left r-left))
- (rl-right (node-right r-left)))
- (node* rl-key
- rl-value
- (node* key value left rl-left)
- (node* r-key r-value rl-right r-right)))))
-
-(define (rotate-right/double key value left right)
- (let ((l-key (node-key left))
- (l-value (node-value left))
- (l-left (node-left left))
- (l-right (node-right left)))
- (let ((lr-key (node-key l-right))
- (lr-value (node-value l-right))
- (lr-left (node-left l-right))
- (lr-right (node-right l-right)))
- (node* lr-key
- lr-value
- (node* l-key l-value l-left lr-left)
- (node* key value lr-right right)))))
-
-;;; smart constructor for after adding/removing a node
-(define (T key value left right)
- (let ((l-size (size left))
- (r-size (size right)))
- (cond ((< (+ l-size r-size) 2)
- (node* key value left right))
- ((> r-size (* weight l-size))
- (let ((r-left (node-left right))
- (r-right (node-right right)))
- (if (< (size r-left) (size r-right))
- (rotate-left key value left right)
- (rotate-left/double key value left right))))
- ((> l-size (* weight r-size))
- (let ((l-left (node-left left))
- (l-right (node-right left)))
- (if (< (size l-right) (size l-left))
- (rotate-right key value left right)
- (rotate-right/double key value left right))))
- (else
- (node* key value left right)))))
-
-(define (update tree key proc default <)
- (define (add-to tree)
- (if (empty? tree)
- (make-node key (proc default) 1 (make-empty) (make-empty))
- (let ((k (node-key tree))
- (v (node-value tree))
- (l (node-left tree))
- (r (node-right tree)))
- (cond ((< key k)
- (T k v (add-to l) r))
- ((< k key)
- (T k v l (add-to r)))
- (else
- (node* key (proc v) l r))))))
- (add-to tree))
-
-(define (add tree key value <)
- (define (replace _) value)
- (update tree key replace #f <))
-
-(define (delete tree key <)
- (define (delete-from tree)
- (if (empty? tree)
- tree
- (let ((k (node-key tree))
- (v (node-value tree))
- (l (node-left tree))
- (r (node-right tree)))
- (cond ((< key k)
- (T k v (delete-from l) r))
- ((< k key)
- (T k v l (delete-from r)))
- (else
- (delete* l r))))))
- (delete-from tree))
-
-(define (delete* left right)
- (cond ((empty? left) right)
- ((empty? right) left)
- (else
- (let-values (((k v) (min right)))
- (T k v left (delete-min right))))))
-
-(define (delete-min tree)
- (cond ((empty? tree)
- (assertion-violation 'delete-min
- "Can't delete the minimum value of an empty tree"))
- ((empty? (node-left tree))
- (node-right tree))
- (else
- (T (node-key tree)
- (node-value tree)
- (delete-min (node-left tree))
- (node-right tree)))))
-
-(define (concat3 key value left right lt)
- (cond ((empty? left)
- (add right key value lt))
- ((empty? right)
- (add left key value lt))
- ((< (* weight (size left)) (size right))
- (T (node-key right)
- (node-value right)
- (concat3 key value left (node-left right) lt)
- (node-right right)))
- ((< (* weight (size right)) (size left))
- (T (node-key left)
- (node-value left)
- (node-left left)
- (concat3 key value (node-right left) right lt)))
- (else
- (node* key value left right))))
-
-(define (split-lt tree key <)
- (cond ((empty? tree) tree)
- ((< key (node-key tree))
- (split-lt (node-left tree) key <))
- ((< (node-key tree) key)
- (concat3 (node-key tree)
- (node-value tree)
- (node-left tree)
- (split-lt (node-right tree) key <)
- <))
- (else (node-left tree))))
-
-(define (split-gt tree key <)
- (cond ((empty? tree) tree)
- ((< key (node-key tree))
- (concat3 (node-key tree)
- (node-value tree)
- (split-gt (node-left tree) key <)
- (node-right tree)
- <))
- ((< (node-key tree) key)
- (split-gt (node-right tree) key <))
- (else (node-right tree))))
-
-(define (difference tree1 tree2 <)
- (cond ((empty? tree1) tree1)
- ((empty? tree2) tree1)
- (else
- (let ((l* (split-lt tree1 (node-key tree2) <))
- (r* (split-gt tree1 (node-key tree2) <)))
- (concat (difference l* (node-left tree2) <)
- (difference r* (node-right tree2) <))))))
-
-(define (concat left right)
- (cond ((empty? left) right)
- ((empty? right) left)
- ((< (* weight (size left)) (size right))
- (T (node-key right)
- (node-value right)
- (concat left (node-left right))
- (node-right right)))
- ((< (* weight (size right)) (size left))
- (T (node-key left)
- (node-value left)
- (node-left left)
- (concat (node-right left) right)))
- (else
- (let-values (((k v) (min right)))
- (T k v left (delete-min right))))))
-
-(define (member key tree <)
- (define (yes x) #t)
- (define (no) #f)
- (lookup tree key yes no <))
-
-(define (intersection t1 t2 <)
- (cond ((empty? t1) t1)
- ((empty? t2) t2)
- (else
- (let ((l* (split-lt t2 (node-key t1) <))
- (r* (split-gt t2 (node-key t1) <)))
- (if (member (node-key t1) t2 <)
- (concat3 (node-key t1)
- (node-value t1)
- (intersection (node-left t1) l* <)
- (intersection (node-right t1) r* <)
- <)
- (concat (intersection (node-left t1) l* <)
- (intersection (node-right t1) r* <)))))))
-
-;;; hedge union
-
-;; ensures that tree is either empty, or root lies in range low--high
-(define (trim low high tree <)
- (cond ((empty? tree) tree)
- ((< low (node-key tree))
- (if (< (node-key tree) high)
- tree
- (trim low high (node-left tree) <)))
- (else
- (trim low high (node-right tree) <))))
-
-(define (uni-bd tree1 tree2 low high <)
- (cond ((empty? tree2) tree1)
- ((empty? tree1)
- (concat3 (node-key tree2)
- (node-value tree2)
- (split-gt (node-left tree2) low <)
- (split-lt (node-right tree2) high <)
- <))
- (else
- (let ((key (node-key tree1)))
- (concat3 key
- (node-value tree1)
- (uni-bd (node-left tree1) (trim low key tree2 <) low key <)
- (uni-bd (node-right tree1) (trim key high tree2 <) key high <)
- <)))))
-
-;; specialisation of trim for high=+infinity
-(define (trim-low low tree <)
- (cond ((empty? tree) tree)
- ((< low (node-key tree)) tree)
- (else
- (trim-low low (node-right tree) <))))
-
-;; trim for low=-infinity
-(define (trim-high high tree <)
- (cond ((empty? tree) tree)
- ((< (node-key tree) high) tree)
- (else
- (trim-high high (node-left tree) <))))
-
-;; uni-bd for low=-infinity
-(define (uni-high tree1 tree2 high <)
- (cond ((empty? tree2) tree1)
- ((empty? tree1)
- (concat3 (node-key tree2)
- (node-value tree2)
- (node-left tree2)
- (split-lt (node-right tree2) high <)
- <))
- (else
- (let ((key (node-key tree1)))
- (concat3 key
- (node-value tree1)
- (uni-high (node-left tree1) (trim-high key tree2 <) key <)
- (uni-bd (node-right tree1) (trim key high tree2 <) key high <)
- <)))))
-
-;; uni-bd for high=+infinity
-(define (uni-low tree1 tree2 low <)
- (cond ((empty? tree2) tree1)
- ((empty? tree1)
- (concat3 (node-key tree2)
- (node-value tree2)
- (split-gt (node-left tree2) low <)
- (node-right tree2)
- <))
- (else
- (let ((key (node-key tree1)))
- (concat3 key
- (node-value tree1)
- (uni-bd (node-left tree1) (trim low key tree2 <) low key <)
- (uni-low (node-right tree1) (trim-low key tree2 <) key <)
- <)))))
-
-(define (hedge-union tree1 tree2 <)
- (cond ((empty? tree2) tree1)
- ((empty? tree1) tree2)
- (else
- (let ((key (node-key tree1)))
- (concat3 key
- (node-value tree1)
- (uni-high (node-left tree1) (trim-high key tree2 <) key <)
- (uni-low (node-right tree1) (trim-low key tree2 <) key <)
- <)))))
-
-;;; rank and indexing
-
-(define (rank tree key <)
- (cond ((empty? tree);; error
- (assertion-violation 'rank "Key is not in the tree" key))
- ((< key (node-key tree))
- (rank (node-left tree) key <))
- ((< (node-key tree) key)
- (+ (rank (node-right tree) key <)
- (size (node-left tree))
- 1))
- (else
- (size (node-left tree)))))
-
-(define (index tree idx)
- (if (empty? tree)
- (assertion-violation 'index "No value at index" idx)
- (let ((l-size (size (node-left tree))))
- (cond ((< idx l-size)
- (index (node-left tree) idx))
- ((< l-size idx)
- (index (node-right tree)
- (- idx l-size 1)))
- (else
- (values (node-key tree)
- (node-value tree)))))))
-
-;;; External procedures
-
-(define (make-bbtree <)
- (assert (procedure? <))
- (%make-bbtree (make-empty) <))
-
-(define (bbtree-size bbtree)
- (assert (bbtree? bbtree))
- (size (bbtree-tree bbtree)))
-
-(define bbtree-ref
- (let ((ref (lambda (bbtree key failure)
- (assert (bbtree? bbtree))
- (lookup (bbtree-tree bbtree)
- key
- node-value
- failure
- (bbtree-ordering-procedure bbtree)))))
- (case-lambda
- ((bbtree key)
- (define (fail)
- (assertion-violation 'bbtree-ref "Key is not in the tree" key))
- (ref bbtree key fail))
- ((bbtree key ret)
- (ref bbtree key (lambda () ret))))))
-
-(define (bbtree-set bbtree key value)
- (assert (bbtree? bbtree))
- (update-tree bbtree
- (add (bbtree-tree bbtree)
- key
- value
- (bbtree-ordering-procedure bbtree))))
-
-(define (bbtree-update bbtree key proc default)
- (assert (bbtree? bbtree))
- (update-tree bbtree
- (update (bbtree-tree bbtree)
- key
- proc
- default
- (bbtree-ordering-procedure bbtree))))
-
-(define (bbtree-delete bbtree key)
- (assert (bbtree? bbtree))
- (update-tree bbtree
- (delete (bbtree-tree bbtree)
- key
- (bbtree-ordering-procedure bbtree))))
-
-(define (bbtree-contains? bbtree key)
- (assert (bbtree? bbtree))
- (lookup (bbtree-tree bbtree)
- key
- (lambda (_) #t)
- (lambda () #f)
- (bbtree-ordering-procedure bbtree)))
-
-;; iterators
-
-(define (traverse traverser base tree)
- (define (left base)
- (traverse traverser base (node-left tree)))
- (define (right base)
- (traverse traverser base (node-right tree)))
- (if (empty? tree)
- base
- (traverser (node-key tree)
- (node-value tree)
- left
- right
- base)))
-
-(define (bbtree-traverse traverser base bbtree)
- (assert (bbtree? bbtree))
- (traverse traverser base (bbtree-tree bbtree)))
-
-(define (bbtree-fold combine base bbtree)
- (assert (bbtree? bbtree))
- (traverse (lambda (k v l r n)
- (r (combine k v (l n))))
- base
- (bbtree-tree bbtree)))
-
-(define (bbtree-fold-right combine base bbtree)
- (assert (bbtree? bbtree))
- (traverse (lambda (k v l r n)
- (l (combine k v (r n))))
- base
- (bbtree-tree bbtree)))
-
-;; I could do this more efficiently, but is it worth it?
-(define (bbtree-map mapper bbtree)
- (bbtree-fold (lambda (key value tree)
- (bbtree-set tree key (mapper value)))
- (make-bbtree (bbtree-ordering-procedure bbtree))
- bbtree))
-
-(define (alist-cons a b c)
- (cons (cons a b) c))
-
-(define (bbtree->alist bbtree)
- (bbtree-fold-right alist-cons '() bbtree))
-
-(define (alist->bbtree list <)
- (fold-left (lambda (tree kv-pair)
- (bbtree-set tree (car kv-pair) (cdr kv-pair)))
- (make-bbtree <)
- list))
-
-(define (bbtree-keys bbtree)
- (bbtree-fold-right (lambda (key value base)
- (cons key base))
- '()
- bbtree))
-
-(define (bbtree-union bbtree1 bbtree2)
- (update-tree bbtree1
- (hedge-union (bbtree-tree bbtree1)
- (bbtree-tree bbtree2)
- (bbtree-ordering-procedure bbtree1))))
-
-(define (bbtree-difference bbtree1 bbtree2)
- (update-tree bbtree1
- (difference (bbtree-tree bbtree1)
- (bbtree-tree bbtree2)
- (bbtree-ordering-procedure bbtree1))))
-
-(define (bbtree-intersection bbtree1 bbtree2)
- (update-tree bbtree1
- (intersection (bbtree-tree bbtree1)
- (bbtree-tree bbtree2)
- (bbtree-ordering-procedure bbtree1))))
-
-(define (bbtree-index bbtree key)
- ;; maybe this should return #f instead of throwing an exception?
- (assert (bbtree? bbtree))
- (rank (bbtree-tree bbtree)
- key
- (bbtree-ordering-procedure bbtree)))
-
-(define (bbtree-ref/index bbtree idx)
- (assert (bbtree? bbtree))
- (let ((tree (bbtree-tree bbtree)))
- (unless (and (integer? idx)
- (<= 0 idx (- (size tree) 1)))
- (assertion-violation 'bbtree-ref/index
- "Not a valid index into the bbtree"
- idx))
- (index tree idx)))
-
-))
-
-;;; deques.sls --- Purely functional deques
-
-;; Copyright (C) 2011,2012 Ian Price <ianprice90@googlemail.com>
-
-;; Author: Ian Price <ianprice90@googlemail.com>
-
-;; This program is free software, you can redistribute it and/or
-;; modify it under the terms of the new-style BSD license.
-
-;; You should have received a copy of the BSD license along with this
-;; program. If not, see <http://www.debian.org/misc/bsd.license>.
-
-;; Documentation:
-;;
-;; make-deque : () -> deque
-;; returns a deque containing to items
-;;
-;; deque? : any -> boolean
-;; tests if an object is a deque
-;;
-;; deque-length : deque -> non-negative integer
-;; returns the number of items in the deque
-;;
-;; deque-empty? : deque -> boolean
-;; returns true if there are no items in the deque, false otherwise
-;;
-;; enqueue-front : deque any -> deque
-;; returns a new deque with the inserted item at the front
-;;
-;; enqueue-rear : deque any -> deque
-;; returns a new deque with the inserted item at the rear
-;;
-;; dequeue-front : deque -> any queue
-;; returns two values, the item at the front of the deque, and a new
-;; deque containing all the other items
-;; raises a &deque-empty condition if the deque is empty
-;;
-;; dequeue-rear : deque -> any queue
-;; returns two values, the item at the rear of the deque, and a new
-;; deque containing all the other items
-;; raises a &deque-empty condition if the deque is empty
-;;
-;; deque-empty-condition? : object -> boolean
-;; tests if an object is a &deque-empty condition
-;;
-;; deque->list : deque -> listof(any)
-;; returns a list containing all the elements of the deque. The order
-;; of the elements in the list is the same as the order they would be
-;; dequeued from the front of the deque.
-;;
-;; list->deque : listof(any) -> deque
-;; returns a deque containing all of the elements in the list. The
-;; order of the elements in the deque is the same as the order of the
-;; elements in the list.
-;;
-(library (pfds deques)
-(export make-deque
- deque?
- deque-length
- deque-empty?
- enqueue-front
- enqueue-rear
- dequeue-front
- dequeue-rear
- deque-empty-condition?
- deque->list
- list->deque
- )
-(import (except (rnrs) cons*)
- (pfds deques private condition)
- (pfds private lazy-lists))
-
-(define c 2)
-
-(define (rot1 n l r)
- (if (>= n c)
- (cons* (head l)
- (rot1 (- n c) (tail l) (drop c r)))
- (rot2 l (drop n r) '())))
-
-(define (rot2 l r a)
- (if (empty? l)
- (append* (rev r) a)
- (cons* (head l)
- (rot2 (tail l)
- (drop c r)
- (append* (rev (take c r)) a)))))
-
-(define-record-type (deque %make-deque deque?)
- (fields
- (immutable length)
- (immutable lenL)
- (immutable lenR)
- (immutable l)
- (immutable r)
- (immutable l^)
- (immutable r^)))
-
-(define (make-deque)
- (%make-deque 0 0 0 '() '() '() '()))
-
-(define (deque-empty? deque)
- (zero? (deque-length deque)))
-
-(define (enqueue-front deque item)
- (let ((len (deque-length deque))
- (l (deque-l deque))
- (r (deque-r deque))
- (lenL (deque-lenL deque))
- (lenR (deque-lenR deque))
- (l^ (deque-l^ deque))
- (r^ (deque-r^ deque)))
- (makedq (+ 1 len) (+ 1 lenL) lenR (cons* item l) r (tail l^) (tail r^))))
-
-(define (enqueue-rear deque item)
- (let ((len (deque-length deque))
- (l (deque-l deque))
- (r (deque-r deque))
- (lenL (deque-lenL deque))
- (lenR (deque-lenR deque))
- (l^ (deque-l^ deque))
- (r^ (deque-r^ deque)))
- (makedq (+ 1 len) lenL (+ 1 lenR) l (cons* item r) (tail l^) (tail r^))))
-
-(define (dequeue-front deque)
- (when (deque-empty? deque)
- (raise (condition
- (make-deque-empty-condition)
- (make-who-condition 'dequeue-front)
- (make-message-condition "There are no elements to remove")
- (make-irritants-condition (list deque)))))
- (let ((len (deque-length deque))
- (lenL (deque-lenL deque))
- (lenR (deque-lenR deque))
- (l (deque-l deque))
- (r (deque-r deque))
- (l^ (deque-l^ deque))
- (r^ (deque-r^ deque)))
- (if (empty? l)
- (values (head r) (make-deque))
- (values (head l)
- (makedq (- len 1)
- (- lenL 1)
- lenR
- (tail l)
- r
- (tail (tail l^))
- (tail (tail r^)))))))
-
-(define (dequeue-rear deque)
- (when (deque-empty? deque)
- (raise (condition
- (make-deque-empty-condition)
- (make-who-condition 'dequeue-rear)
- (make-message-condition "There are no elements to remove")
- (make-irritants-condition (list deque)))))
- (let ((len (deque-length deque))
- (lenL (deque-lenL deque))
- (lenR (deque-lenR deque))
- (l (deque-l deque))
- (r (deque-r deque))
- (l^ (deque-l^ deque))
- (r^ (deque-r^ deque)))
- (if (empty? r)
- (values (head l) (make-deque))
- (values (head r)
- (makedq (- len 1)
- lenL
- (- lenR 1)
- l
- (tail r)
- (tail (tail l^))
- (tail (tail r^)))))))
-
-
-
-(define (makedq len lenL lenR l r l^ r^)
- (cond ((> lenL (+ 1 (* c lenR)))
- (let* ((n (floor (/ (+ lenL lenR) 2)))
- (l* (take n l))
- (r* (rot1 n r l)))
- (%make-deque len n (- len n) l* r* l* r*)))
- ((> lenR (+ 1 (* c lenL)))
- (let* ((n (floor (/ (+ lenL lenR) 2)))
- (l* (rot1 n l r))
- (r* (take n r)))
- (%make-deque len (- len n) n l* r* l* r*)))
- (else
- (%make-deque len lenL lenR l r l^ r^))))
-
-(define (list->deque l)
- (fold-left enqueue-rear (make-deque) l))
-
-(define (deque->list deq)
- (define (recur deq l)
- (if (deque-empty? deq)
- l
- (let-values ([(last deq*) (dequeue-rear deq)])
- (recur deq* (cons last l)))))
- (recur deq '()))
-
-)
-
-;;; dlists.sls --- Difference Lists
-
-;; Copyright (C) 2012 Ian Price <ianprice90@googlemail.com>
-
-;; Author: Ian Price <ianprice90@googlemail.com>
-
-;; This program is free software, you can redistribute it and/or
-;; modify it under the terms of the new-style BSD license.
-
-;; You should have received a copy of the BSD license along with this
-;; program. If not, see <http://www.debian.org/misc/bsd.license>.
-
-;;; Commentary:
-;;
-;; Repeatedly appending to a list is a common, if inefficient pattern
-;; in functional programs. Usually the trick we use is to build up the
-;; list in reverse, and then to reverse it as the last action of a
-;; function.
-;;
-;; Dlists are a representation of lists as functions that provide for
-;; constant time append to either the front or end of a dlist that may
-;; be used instead.
-
-;;; Documentation:
-;;
-;; dlist : any ... -> dlist
-;; returns a dlist containing all its arguments.
-;;
-;; dlist? : any -> boolean
-;; returns #t if its argument is a dlist, #f otherwise.
-;;
-;; dlist-cons : any dlist -> dlist
-;; returns a new dlist created by prepending the element to the head
-;; of the dlist argument.
-;;
-;; dlist-snoc : dlist any -> dlist
-;; returns a new dlist created by appending the element to the tail of
-;; the dlist argument.
-;;
-;; dlist-append : dlist dlist -> dlist
-;; returns a new dlist consisting of all the elements of the first
-;; dlist, followed by all the items of the second dlist.
-;;
-;; dlist->list : dlist -> listof(any)
-;; returns a list consisting of all the elements of the dlist.
-;;
-;; list->dlist : listof(any) -> dlist
-;; returns a dlist consisting of all the elements of the list.
-(library (pfds dlists)
-(export (rename (%dlist dlist))
- dlist?
- dlist-cons
- dlist-snoc
- dlist-append
- dlist->list
- list->dlist
- )
-(import (rnrs))
-
-(define-record-type dlist
- (fields
- (immutable proc undl)))
-
-(define (%dlist . args)
- (list->dlist args))
-
-(define (compose f g)
- (lambda (x)
- (f (g x))))
-
-(define (singleton x)
- (list->dlist (list x)))
-
-(define (dlist-append dl1 dl2)
- (make-dlist (compose (undl dl1) (undl dl2))))
-
-(define (dlist-cons element dlist)
- (dlist-append (singleton element) dlist))
-
-(define (dlist-snoc dlist element)
- (dlist-append dlist (singleton element)))
-
-(define (dlist->list dlist)
- ((undl dlist) '()))
-
-(define (list->dlist list)
- (make-dlist
- (lambda (rest)
- (append list rest))))
-
-)
-
-;;; fingertrees.sls --- A Simple General-Purpose Data Structure
-
-;; Copyright (C) 2012 Ian Price <ianprice90@googlemail.com>
-
-;; Author: Ian Price <ianprice90@googlemail.com>
-
-;; This program is free software, you can redistribute it and/or
-;; modify it under the terms of the new-style BSD license.
-
-;; You should have received a copy of the BSD license along with this
-;; program. If not, see <http://www.debian.org/misc/bsd.license>.
-
-;;; Commentary:
-;;
-;; Fingertrees are a generalised form of deque, that you can parameterise
-;; to compute a value, called the "measure" of a fingertree. This measure
-;; will be updated incrementally as you add and remove elements from the
-;; fingertree. Among other things, this allows fingertrees to be used
-;; where you otherwise might have written a custom data structure.
-;;
-;; To compute the measure, fingertrees require pieces of information: a
-;; converter, a combiner, and an identity.
-;;
-;; The converter is a procedure of one argument, that maps values in the
-;; fingertree to other values which are used for computing the measure.
-;;
-;; The combiner is a procedure of two arguments, and combines these into
-;; one value representing them both. A combiner must be associative
-;; i.e. (combine A (combine B C)) must be equivalent to (combine (combine
-;; A B) C) for all values A, B and C.
-;;
-;; An identity is a value that represents the measure of an empty
-;; fingertree. It must obey the rule that (combine X identity), (combine
-;; identity X) and X are always the same.
-;;
-;; To make things more concrete, a simple use of a fingertree is as a
-;; deque that keeps a running total. In this case, the converter can
-;; simply be the function (lambda (x) x) if it is a deque of integers,
-;; the combiner would be +, and the identity 0.
-;;
-;; (define l '(3 1 4 1 5 9))
-;;
-;; (define ft (list->fingertree l 0 + (lambda (x) x)))
-;;
-;; (fingertree-measure ft)
-;; ; => 23
-;; (fingertree-measure (fingertree-snoc ft 2))
-;; ; => 25
-;; (let-values (((head tail) (fingertree-uncons ft)))
-;; (fingertree-measure tail))
-;; ; => 20
-;;
-;; Mathematically speaking, the _return type_ of the converter, the
-;; combiner and the identity element are expected to form a
-;; monoid.
-;;
-;; Below, I use the slightly incorrect terminology of referring to the
-;; combiner, the converter, and the identity, together as a
-;; monoid. Mathematicians, please forgive me. Programmers please forgive
-;; me even more. If you can provide a better name (from a programmers,
-;; not a mathematicians, point of view) that works in most circumstances,
-;; I will be happy to use it.
-;;
-;; (FWIW the Haskell Data.Fingertree package uses odd name of Measured
-;; (which are expected to be instances of Monoid))
-;;
-;; fingertree? : any -> bool
-;; returns #t if argument is a fingertree, #f otherwise.
-;;
-;; fingertree-empty? : fingertree -> bool
-;; returns #t if there are no items in the fingertree, #f otherwise.
-;;
-;; make-fingertree : id combine measure -> fingertree
-;; returns a new fingertree, parameterised by the given monoid.
-;;
-;; fingertree-cons : any fingertree -> fingertree
-;; returns the new fingertree created by adding the element to the front
-;; of the argument fingertree.
-;;
-;; fingertree-snoc : fingertree any -> fingertree
-;; returns the new fingertree created by adding the element to the end of
-;; the fingertree.
-;;
-;; fingertree-uncons : fingertree -> any + fingertree
-;; returns two values: the element at the front of the fingertree, and a
-;; new fingertree containing all but the front element. If the fingertree
-;; is empty, a &fingertree-empty condition is raised.
-;;
-;; fingertree-unsnoc : fingertree -> fingertree + any
-;; returns two values: a new fingertree containing all but the rear
-;; element of the argument fingertree, and the rear element itself. If
-;; the fingertree is empty, a &fingertree-empty-condition is raised.
-;;
-;; fingertree-append : fingertree fingertree -> fingertree
-;; returns a new fingertree which contains all of the elements of the
-;; first fingertree argument, followed by all the elements of the
-;; second. The argument fingertrees are assumed to be parameterised by
-;; the same monoid.
-;;
-;; list->fingertree : (list->fingertree l id append convert)
-;; returns a fingertree containing all of the elements of the argument
-;; list, in the same order.
-;;
-;; fingertree->list : fingertree -> Listof(Any)
-;; returns a list of all the elements in the fingertree, in the order
-;; they would be unconsed.
-;;
-;; fingertree-measure : fingertree -> any
-;; returns the measure of the fingertree, as defined by the fingertree's
-;; monoid.
-;;
-;; fingertree-split : (any -> bool) fingertree -> fingertree + fingertree
-;; returns two values: the first is the largest prefix of the fingertree for
-;; which applying the predicate to it's accumulated measure returns
-;; #f. The second values is a fingertree containing all those elements
-;; not in the first fingertree.
-;;
-;; fingertree-split3: (any -> bool) fingertree -> fingertree + value + fingertree
-;; similar to fingertree-split, however, instead of returning the
-;; remainder as the second argument, it returns the head of the remainder
-;; as the second argument, and tail of the remainder as the third
-;; argument.
-;; TODO: what error should I give if the remainder was empty?
-;;
-;; fingertree-fold : (any -> any -> any) any fingertree
-;; returns the value obtained by iterating the combiner procedure over
-;; the fingertree in left-to-right order. This procedure takes two
-;; arguments, the current value from the fingertree, and an accumulator,
-;; and it's return value is used as the accumulator for the next
-;; iteration. The initial value for the accumulator is given by the base
-;; argument.
-;;
-;; fingertree-fold-right : (any -> any -> any) any fingertree
-;; similar to fingertree-fold, but iterates in right-to-left order.
-;;
-;; fingertree-reverse : fingertree -> fingertree
-;; returns a new fingertree in which the elements are in the opposite
-;; order from the argument fingertree.
-;;
-;; fingertree-empty-condition? : condition -> bool
-;; returns #t if the argument is a &fingertree-empty condition, #f otherwise.
-;;
-(library (pfds fingertrees)
-(export fingertree?
- fingertree-empty?
- make-fingertree
- fingertree-cons
- fingertree-snoc
- fingertree-uncons
- fingertree-unsnoc
- fingertree-append
- list->fingertree
- fingertree->list
- fingertree-measure
- fingertree-split
- fingertree-split3
- fingertree-fold
- fingertree-fold-right
- fingertree-reverse
- fingertree-empty-condition?
- )
-(import (rnrs))
-
-;;; List helpers
-
-(define (snoc l val)
- (append l (list val)))
-
-(define (take l n)
- (if (or (null? l) (zero? n))
- '()
- (cons (car l)
- (take (cdr l) (- n 1)))))
-
-(define (last list)
- (if (null? (cdr list))
- (car list)
- (last (cdr list))))
-
-(define (but-last list)
- (if (null? (cdr list))
- '()
- (cons (car list)
- (but-last (cdr list)))))
-
-(define (map-reverse f l)
- (fold-left (lambda (o n) (cons (f n) o)) '() l))
-
-;;; Node type
-
-(define-record-type node2
- (protocol
- (lambda (new)
- (lambda (monoid a b)
- (define app (mappend monoid))
- (new (app (measure-nodetree a monoid)
- (measure-nodetree b monoid))
- a
- b))))
- (fields measure a b))
-
-(define-record-type node3
- (protocol
- (lambda (new)
- (lambda (monoid a b c)
- (define app (mappend monoid))
- (new (app (app (measure-nodetree a monoid)
- (measure-nodetree b monoid))
- (measure-nodetree c monoid))
- a
- b
- c))))
- (fields measure a b c))
-
-(define (node-case node k2 k3)
- (if (node2? node)
- (k2 (node2-a node) (node2-b node))
- (k3 (node3-a node) (node3-b node) (node3-c node))))
-
-(define (node-fold-right f base node)
- (node-case node
- (lambda (a b)
- (f a (f b base)))
- (lambda (a b c)
- (f a (f b (f c base))))))
-
-(define (node->list node)
- (node-fold-right cons '() node))
-
-(define (nodetree-fold-right f base nodetree)
- (define (foldr node base)
- (cond ((node2? node)
- (foldr (node2-a node)
- (foldr (node2-b node) base)))
- ((node3? node)
- (foldr (node3-a node)
- (foldr (node3-b node)
- (foldr (node3-c node) base))))
- (else (f node base))))
- (foldr nodetree base))
-
-(define (nodetree-fold-left f base nodetree)
- (define (foldl node base)
- (cond ((node2? node)
- (foldl (node2-b node)
- (foldl (node2-a node) base)))
- ((node3? node)
- (foldl (node3-c node)
- (foldl (node3-b node)
- (foldl (node3-a node) base))))
- (else (f node base))))
- (foldl nodetree base))
-
-;;; Tree type
-
-(define-record-type empty)
-
-(define-record-type single
- (fields value))
-
-(define-record-type rib
- (protocol
- (lambda (new)
- (lambda (monoid left middle right)
- (define app (mappend monoid))
- (new (app (app (measure-digit left monoid)
- (measure-ftree middle monoid))
- (measure-digit right monoid))
- left
- middle
- right)
- )))
- ;; left and right expected to be lists of length 0 < l < 5
- (fields measure left middle right))
-
-(define (ftree-case ftree empty-k single-k rib-k)
- (cond ((empty? ftree) (empty-k))
- ((single? ftree)
- (single-k (single-value ftree)))
- (else
- (rib-k (rib-left ftree)
- (rib-middle ftree)
- (rib-right ftree)))))
-
-(define (digits-fold-right f b d)
- (fold-right (lambda (ntree base)
- (nodetree-fold-right f base ntree))
- b
- d))
-
-(define (digits-fold-left f b d)
- (fold-left (lambda (base ntree)
- (nodetree-fold-left f base ntree))
- b
- d))
-
-(define (ftree-fold-right proc base ftree)
- (ftree-case ftree
- (lambda () base)
- (lambda (x) (nodetree-fold-right proc base x))
- (lambda (l x r)
- (define base* (digits-fold-right proc base r))
- (define base** (ftree-fold-right proc base* x))
- (digits-fold-right proc base** l))))
-
-(define (ftree-fold-left proc base ftree)
- (ftree-case ftree
- (lambda () base)
- (lambda (x) (nodetree-fold-left proc base x))
- (lambda (l x r)
- (define base* (digits-fold-left proc base l))
- (define base** (ftree-fold-left proc base* x))
- (digits-fold-left proc base** r))))
-
-(define (insert-front ftree val monoid)
- (ftree-case ftree
- (lambda ()
- (make-single val))
- (lambda (a)
- (make-rib monoid (list val) (make-empty) (list a)))
- (lambda (l m r)
- (if (= (length l) 4)
- (make-rib monoid
- (list val (car l))
- (insert-front m (apply make-node3 monoid (cdr l)) monoid)
- r)
- (make-rib monoid (cons val l) m r)))))
-
-(define (view-front ftree empty-k cons-k monoid)
- (ftree-case ftree
- empty-k
- (lambda (a)
- (cons-k a (make-empty)))
- (lambda (l r m)
- (cons-k (car l)
- (rib-l (cdr l) r m monoid)))))
-
-(define (list->tree l monoid)
- (fold-right (lambda (val tree)
- (insert-front tree val monoid))
- (make-empty)
- l))
-
-(define (rib-l l m r monoid)
- (if (null? l)
- (view-front m
- (lambda ()
- (list->tree r monoid))
- (lambda (x xs)
- (make-rib monoid
- (node->list x)
- xs
- r))
- monoid)
- (make-rib monoid l m r)))
-
-(define (remove-front ftree monoid)
- (view-front ftree
- (lambda ()
- (error 'remove-front "can't remove from an empty tree"))
- values
- monoid))
-
-(define (insert-rear ftree val monoid)
- (ftree-case ftree
- (lambda ()
- (make-single val))
- (lambda (a)
- (make-rib monoid (list a) (make-empty) (list val)))
- (lambda (l m r)
- ;; TODO: should r be maintained in reverse order, rather than
- ;; normal?
- ;; yes! it will make concatenation slightly slower, but will
- ;; speed up inserts and removals
- (if (= (length r) 4)
- (make-rib monoid
- l
- (insert-rear m (apply make-node3 monoid (take r 3)) monoid)
- (list (list-ref r 3) val))
- (make-rib monoid l m (snoc r val))))))
-
-(define (remove-rear ftree monoid)
- (view-rear ftree
- (lambda ()
- (error 'remove-rear "can't remove from an empty tree"))
- values
- monoid))
-
-(define (view-rear ftree empty-k snoc-k monoid)
- (ftree-case ftree
- empty-k
- (lambda (a)
- (snoc-k (make-empty) a))
- (lambda (l r m)
- (snoc-k (rib-r l r (but-last m) monoid)
- (last m)))))
-
-(define (rib-r l m r monoid)
- (if (null? r)
- (view-rear m
- (lambda ()
- (list->tree l monoid))
- (lambda (m* r*)
- (make-rib monoid l m* (node->list r*)))
- monoid)
- (make-rib monoid l m r)))
-
-(define (insert-front/list tree l monoid)
- (fold-right (lambda (val tree)
- (insert-front tree val monoid))
- tree
- l))
-
-(define (insert-rear/list tree l monoid)
- (fold-left (lambda (tree val)
- (insert-rear tree val monoid))
- tree
- l))
-
-(define (app3 ftree1 ts ftree2 monoid)
- (cond ((empty? ftree1)
- (insert-front/list ftree2 ts monoid))
- ((empty? ftree2)
- (insert-rear/list ftree1 ts monoid))
- ((single? ftree1)
- (insert-front (insert-front/list ftree2 ts monoid)
- (single-value ftree1)
- monoid))
- ((single? ftree2)
- (insert-rear (insert-rear/list ftree1 ts monoid)
- (single-value ftree2)
- monoid))
- (else
- (let ((l1 (rib-left ftree1))
- (m1 (rib-middle ftree1))
- (r1 (rib-right ftree1))
- (l2 (rib-left ftree2))
- (m2 (rib-middle ftree2))
- (r2 (rib-right ftree2)))
- (make-rib monoid
- l1
- (app3 m1
- (nodes (append r1 ts l2) monoid)
- m2
- monoid)
- r2)))))
-
-(define (nodes lst monoid)
- ;; *sigh*
- (let ((a (car lst))
- (b (cadr lst)))
- (cond ((null? (cddr lst))
- (list (make-node2 monoid a b)))
- ((null? (cdddr lst))
- (list (make-node3 monoid a b (caddr lst))))
- ((null? (cddddr lst))
- (list (make-node2 monoid a b)
- (make-node2 monoid (caddr lst) (cadddr lst))))
- (else
- (cons (make-node3 monoid a b (caddr lst))
- (nodes (cdddr lst) monoid))))))
-
-(define (reverse-tree tree monoid)
- (ftree-case tree
- (lambda () (make-empty))
- (lambda (x) (make-single (reverse-nodetree x monoid)))
- (lambda (l x r)
- (make-rib monoid
- (reverse-digit r monoid)
- (reverse-tree x monoid)
- (reverse-digit l monoid)))))
-
-(define (reverse-digit l monoid)
- (map-reverse (lambda (a) (reverse-nodetree a monoid)) l))
-
-(define (reverse-nodetree l monoid)
- (cond ((node2? l)
- (make-node2 monoid
- (reverse-nodetree (node2-b l) monoid)
- (reverse-nodetree (node2-a l) monoid)))
- ((node3? l)
- (make-node3 monoid
- (reverse-nodetree (node3-c l) monoid)
- (reverse-nodetree (node3-b l) monoid)
- (reverse-nodetree (node3-a l) monoid)))
- (else l)))
-
-;; generalising fingertrees with monoids
-
-;; I think I'm going to need a "configuration" type and pass it around
-;; in order to generalize over arbitrary monoids
-;; call the type iMeasured or something
-
-(define-record-type monoid*
- ;; a monoid, but augmented with a procedure to convert objects into the
- ;; monoid type
- (fields (immutable empty mempty)
- (immutable append mappend)
- (immutable convert mconvert)))
-
-(define (measure-digit obj monoid)
- (fold-left (lambda (i a)
- ((mappend monoid) i (measure-nodetree a monoid)))
- (mempty monoid)
- obj))
-
-(define (measure-ftree obj monoid)
- (cond ((empty? obj)
- (mempty monoid))
- ((single? obj)
- (measure-nodetree (single-value obj) monoid))
- (else
- (rib-measure obj))))
-
-(define (measure-nodetree obj monoid)
- (cond ((node2? obj) (node2-measure obj))
- ((node3? obj) (node3-measure obj))
- (else ((mconvert monoid) obj))))
-
-(define (split proc tree monoid)
- (if (empty? tree)
- (values (make-empty) (make-empty))
- (if (proc (measure-ftree tree monoid))
- (let-values (((l x r) (split-tree proc (mempty monoid) tree monoid)))
- (values l (insert-front r x monoid)))
- (values tree (make-empty)))))
-
-(define (split-tree proc i tree monoid)
- (ftree-case tree
- (lambda ()
- (error 'split-tree "shouldn't happen?"))
- (lambda (a)
- (values (make-empty) a (make-empty)))
- (lambda (l m r)
- (define app (mappend monoid))
- (define vpr (app i (measure-digit l monoid)))
- (define vm (app vpr (measure-ftree m monoid)))
- (cond ((proc vpr)
- (let-values (((l* x* r*) (split-digit proc i l monoid)))
- (values (list->tree l* monoid)
- x*
- (rib-l r* m r monoid))))
- ((proc vm)
- (let*-values (((ml xs mr) (split-tree proc vpr m monoid))
- ((l* x* r*)
- (split-digit proc
- (app vpr (measure-ftree ml monoid))
- (node->list xs)
- monoid)))
- (values (rib-r l ml l* monoid)
- x*
- (rib-l r* mr r monoid))))
- (else
- (let-values (((l* x* r*) (split-digit proc vm r monoid)))
- (values (rib-r l m l* monoid)
- x*
- (list->tree r* monoid))))))))
-
-(define (split-digit proc i xs monoid)
- (if (null? (cdr xs))
- (values '() (car xs) '())
- (let ((i* ((mappend monoid) i (measure-nodetree (car xs) monoid))))
- (if (proc i*)
- (values '() (car xs) (cdr xs))
- (let-values (((l x r)
- (split-digit proc i* (cdr xs) monoid)))
- (values (cons (car xs) l) x r))))))
-
-;; exported interface
-(define-condition-type &fingertree-empty
- &assertion
- make-fingertree-empty-condition
- fingertree-empty-condition?)
-
-(define-record-type (fingertree %make-fingertree fingertree?)
- (fields tree monoid))
-
-(define (%wrap fingertree tree)
- (%make-fingertree tree
- (fingertree-monoid fingertree)))
-
-(define (make-fingertree id append convert)
- (%make-fingertree (make-empty)
- (make-monoid* id append convert)))
-
-(define (fingertree-cons a fingertree)
- ;; TODO: should it obey normal cons interface, or have fingertree
- ;; first?
- (%wrap fingertree
- (insert-front (fingertree-tree fingertree)
- a
- (fingertree-monoid fingertree))))
-
-(define (fingertree-snoc fingertree a)
- (%wrap fingertree
- (insert-rear (fingertree-tree fingertree)
- a
- (fingertree-monoid fingertree))))
-
-(define (fingertree-uncons fingertree)
- (call-with-values
- (lambda ()
- (define t (fingertree-tree fingertree))
- (when (empty? t)
- (raise
- (condition
- (make-fingertree-empty-condition)
- (make-who-condition 'fingertree-uncons)
- (make-message-condition "There are no elements to uncons")
- (make-irritants-condition (list fingertree)))))
- (remove-front t (fingertree-monoid fingertree)))
- (lambda (val rest)
- (values val
- (%wrap fingertree rest)))))
-
-(define (fingertree-unsnoc fingertree)
- (call-with-values
- (lambda ()
- (define t (fingertree-tree fingertree))
- (when (empty? t)
- (raise
- (condition
- (make-fingertree-empty-condition)
- (make-who-condition 'fingertree-unsnoc)
- (make-message-condition "There are no elements to unsnoc")
- (make-irritants-condition (list fingertree)))))
- (remove-rear t (fingertree-monoid fingertree)))
- (lambda (rest val)
- (values (%wrap fingertree rest) val))))
-
-(define (fingertree-empty? fingertree)
- (empty? (fingertree-tree fingertree)))
-
-(define (fingertree-append fingertree1 fingertree2)
- (%wrap fingertree1
- (app3 (fingertree-tree fingertree1)
- '()
- (fingertree-tree fingertree2)
- (fingertree-monoid fingertree1))))
-
-;; TODO: fix this
-(define (list->fingertree l id append convert)
- (define monoid (make-monoid* id append convert))
- (%make-fingertree (list->tree l monoid) monoid))
-
-(define (fingertree->list t)
- (fingertree-fold-right cons '() t))
-
-(define (fingertree-measure fingertree)
- (measure-ftree (fingertree-tree fingertree)
- (fingertree-monoid fingertree)))
-
-
-(define (fingertree-split p fingertree)
- (call-with-values
- (lambda ()
- (split p
- (fingertree-tree fingertree)
- (fingertree-monoid fingertree)))
- (lambda (a b)
- (values (%wrap fingertree a)
- (%wrap fingertree b)))))
-
-(define (fingertree-split3 p fingertree)
- (call-with-values
- (lambda ()
- (define monoid (fingertree-monoid fingertree))
- (split-tree p
- (mempty monoid)
- (fingertree-tree fingertree)
- monoid))
- (lambda (a b c)
- (values (%wrap fingertree a)
- b
- (%wrap fingertree c)))))
-
-(define (fingertree-fold f b fingertree)
- (ftree-fold-left f b (fingertree-tree fingertree)))
-
-(define (fingertree-fold-right f b fingertree)
- (ftree-fold-right f b (fingertree-tree fingertree)))
-
-(define (fingertree-reverse fingertree)
- (%wrap fingertree
- (reverse-tree (fingertree-tree fingertree)
- (fingertree-monoid fingertree))))
-
-)
-
-;;; hamts.sls --- Hash Array Mapped Tries
-
-;; Copyright (C) 2014 Ian Price <ianprice90@googlemail.com>
-
-;; Author: Ian Price <ianprice90@googlemail.com>
-
-;; This program is free software, you can redistribute it and/or
-;; modify it under the terms of the new-style BSD license.
-
-;; You should have received a copy of the BSD license along with this
-;; program. If not, see <http://www.debian.org/misc/bsd.license>.
-
-;; Documentation:
-;;
-;; Note: For all procedures which take a key as an argument, the key
-;; must be hashable with the hamt hash function, and comparable with
-;; the hamt equivalence predicate.
-;;
-;; make-hamt : (any -> non-negative integer) (any -> any -> boolean) -> hamt
-;; returns a new empty hamt using the given hash and equivalence functions.
-;;
-;; hamt? : any -> boolean
-;; returns #t if argument is a hamt, #f otherwise.
-;;
-;; hamt-size : hamt -> non-negative integer
-;; returns the number of associations in the hamt.
-;;
-;; hamt-ref : hamt any [any] -> any
-;; returns the value associated with the key in the hamt. If there is
-;; no value associated with the key, it returns the default value if
-;; provided, or raises an &assertion-violation if it isn't.
-;;
-;; hamt-contains? : hamt any -> boolean
-;; returns #t if there is an association for the key in the hamt, #f
-;; otherwise.
-;;
-;; hamt-set : hamt any any -> hamt
-;; returns a new hamt with the key associated to the value. If the key
-;; is already associated with a value, it is replaced.
-;;
-;; hamt-update : hamt any (any -> any) any -> hamt
-;; returns a new hamt with the valued associated with the key updated
-;; by the update procedure. If the hamt does not already have a value
-;; associated with the key, then it applies the update procedure to
-;; the default value, and associates the key with that.
-;;
-;; hamt-delete : hamt any -> hamt
-;; returns a hamt with the key and its associated value removed. If
-;; the key is not in the hamt, a copy of the original hamt is
-;; returned.
-;;
-;; hamt-fold : (any any any -> any) any hamt -> hamt
-;; returns the value obtained by iterating the combine procedure over
-;; each key value pair in the hamt. The combine procedure takes three
-;; arguments, the key and value of an association, and an accumulator,
-;; and returns a new accumulator value. The initial value of the
-;; accumulator is provided by the base argument. The order in which
-;; the hamt is traversed is not guaranteed.
-;;
-;; hamt-map : (any -> any) hamt -> hamt
-;; returns the hamt obtained by applying the update procedure to each
-;; of the values in the hamt.
-;;
-;; hamt->alist : hamt -> Listof(Pairs)
-;; returns the key/value associations of the hamt as a list of pairs.
-;; The order of the list is not guaranteed.
-;;
-;; alist->hamt : Listof(Pairs) (any -> non-negative integer) (any -> any -> boolean) -> hamt
-;; returns the hamt containing the associations specified by the pairs
-;; in the alist. If the same key appears in the alist multiple times,
-;; its leftmost value is the one that is used.
-;;
-;; hamt-equivalence-predicate : hamt -> (any -> any -> boolean)
-;; returns the procedure used internally by the hamt to compare keys.
-;;
-;; hamt-hash-function : hamt -> (any -> non-negative integer)
-;; returns the hash procedure used internally by the hamt.
-;;
-(library (pfds hamts)
-(export make-hamt
- hamt?
- hamt-size
- hamt-ref
- hamt-set
- hamt-update
- hamt-delete
- hamt-contains?
- hamt-equivalence-predicate
- hamt-hash-function
- hamt-fold
- hamt-map
- hamt->alist
- alist->hamt
- )
-(import (rnrs)
- (pfds private vectors)
- (pfds private alists)
- (pfds private bitwise))
-
-;;; Helpers
-
-(define cardinality 32) ; 64
-
-(define (mask key level)
- (bitwise-arithmetic-shift-right (bitwise-and key (- (expt 2 5) 1)) level))
-
-(define (level-up level)
- (+ level 5))
-
-(define (ctpop key index)
- (bitwise-bit-count (bitwise-arithmetic-shift-right key (+ 1 index))))
-
-;;; Node types
-
-(define-record-type (subtrie %make-subtrie subtrie?)
- (fields size bitmap vector))
-
-(define (make-subtrie bitmap vector)
- (define vecsize
- (vector-fold (lambda (val accum)
- (+ (size val) accum))
- 0
- vector))
- (%make-subtrie vecsize bitmap vector))
-
-(define-record-type leaf
- (fields key value))
-
-(define-record-type (collision %make-collision collision?)
- (fields size hash alist))
-
-(define (make-collision hash alist)
- (%make-collision (length alist) hash alist))
-
-;;; Main
-
-(define (lookup vector key default hash eqv?)
- (define (handle-subtrie node level)
- (define bitmap (subtrie-bitmap node))
- (define vector (subtrie-vector node))
- (define index (mask h level))
- (if (not (bitwise-bit-set? bitmap index))
- default
- (let ((node (vector-ref vector (ctpop bitmap index))))
- (cond ((leaf? node)
- (handle-leaf node))
- ((collision? node)
- (handle-collision node))
- (else
- (handle-subtrie node (level-up level)))))))
-
- (define (handle-leaf node)
- (if (eqv? key (leaf-key node))
- (leaf-value node)
- default))
-
- (define (handle-collision node)
- (alist-ref (collision-alist node) key default eqv?))
-
- (define h (hash key))
- (define node (vector-ref vector (mask h 0)))
-
- (cond ((not node) default)
- ((leaf? node) (handle-leaf node))
- ((collision? node) (handle-collision node))
- (else
- (handle-subtrie node (level-up 0)))))
-
-(define (insert hvector key update base hash eqv?)
- (define (handle-subtrie subtrie level)
- (define bitmap (subtrie-bitmap subtrie))
- (define vector (subtrie-vector subtrie))
- (define index (mask h level))
- (define (fixup node)
- (make-subtrie bitmap (vector-set vector index node)))
- (if (not (bitwise-bit-set? bitmap index))
- (make-subtrie (bitwise-bit-set bitmap index)
- (vector-insert vector
- (ctpop bitmap index)
- (make-leaf key (update base))))
- (let ((node (vector-ref vector (ctpop bitmap index))))
- (cond ((leaf? node)
- (fixup (handle-leaf node level)))
- ((collision? node)
- (fixup (handle-collision node level)))
- (else
- (fixup (handle-subtrie node (level-up level))))))))
-
- (define (handle-leaf node level)
- (define lkey (leaf-key node))
- (define khash (bitwise-arithmetic-shift-right h level))
- (define lhash (bitwise-arithmetic-shift-right (hash lkey) level))
- (cond ((eqv? key lkey)
- (make-leaf key (update (leaf-value node))))
- ((equal? khash lhash)
- (make-collision lhash
- (list (cons lkey (leaf-value node))
- (cons key (update base)))))
- (else
- (handle-subtrie (wrap-subtrie node lhash) (level-up level)))))
-
- (define (handle-collision node level)
- (define khash (bitwise-arithmetic-shift-right h level))
- (define chash (bitwise-arithmetic-shift-right (collision-hash node) level))
- (if (equal? khash chash)
- (make-collision (collision-hash node)
- (alist-update (collision-alist node) key update base eqv?))
- ;; TODO: there may be a better (more efficient) way to do this
- ;; but simple is better for now (see also handle-leaf)
- (handle-subtrie (wrap-subtrie node chash) (level-up level))))
-
- (define (wrap-subtrie node chash)
- (make-subtrie (bitwise-bit-set 0 (mask chash 0)) (vector node)))
-
- (define h (hash key))
- (define idx (mask h 0))
- (define node (vector-ref hvector idx))
- (define initial-level (level-up 0))
-
- (cond ((not node)
- (vector-set hvector idx (make-leaf key (update base))))
- ((leaf? node)
- (vector-set hvector idx (handle-leaf node initial-level)))
- ((collision? node)
- (vector-set hvector idx (handle-collision node initial-level)))
- (else
- (vector-set hvector idx (handle-subtrie node initial-level)))))
-
-(define (delete vector key hash eqv?)
- (define (handle-subtrie subtrie level)
- (define bitmap (subtrie-bitmap subtrie))
- (define vector (subtrie-vector subtrie))
- (define index (mask h level))
- (define (fixup node)
- (update bitmap vector index node))
- (if (not (bitwise-bit-set? bitmap index))
- subtrie
- (let ((node (vector-ref vector (ctpop bitmap index))))
- (cond ((leaf? node)
- (fixup (handle-leaf node)))
- ((collision? node)
- (fixup (handle-collision node)))
- (else
- (fixup (handle-subtrie node (level-up level))))))))
-
- (define (update bitmap vector index value)
- (if value
- (make-subtrie bitmap (vector-set vector index value))
- (let ((vector* (vector-remove vector index)))
- (if (equal? '#() vector)
- #f
- (make-subtrie (bitwise-bit-unset bitmap index)
- vector*)))))
-
- (define (handle-leaf node)
- (if (eqv? key (leaf-key node))
- #f
- node))
-
- (define (handle-collision node)
- (let ((al (alist-delete (collision-alist node) key eqv?)))
- (cond ((null? (cdr al))
- (make-leaf (car (car al)) (cdr (car al))))
- (else
- (make-collision (collision-hash node) al)))))
-
- (define h (hash key))
- (define idx (mask h 0))
- (define node (vector-ref vector idx))
-
- (cond ((not node) vector)
- ((leaf? node)
- (vector-set vector idx (handle-leaf node)))
- ((collision? node)
- (vector-set vector idx (handle-collision node)))
- (else
- (vector-set vector idx (handle-subtrie node (level-up 0))))))
-
-(define (vec-map mapper vector)
- (define (handle-subtrie trie)
- (make-subtrie (subtrie-bitmap trie)
- (vector-map dispatch (subtrie-vector vector))))
-
- (define (handle-leaf leaf)
- (make-leaf (leaf-key leaf)
- (mapper (leaf-value leaf))))
-
- (define (handle-collision collision)
- (make-collision (collision-hash collision)
- (map (lambda (pair)
- (cons (car pair) (mapper (cdr pair))))
- (collision-alist collision))))
-
- (define (dispatch val)
- (cond ((leaf? val)
- (handle-leaf val))
- ((collision? val)
- (handle-collision val))
- (else
- (handle-subtrie val))))
-
- (vector-map (lambda (val)
- ;; top can have #f values
- (and val (dispatch val)))
- vector))
-
-(define (fold combine initial vector)
- (define (handle-subtrie trie accum)
- (vector-fold dispatch accum (subtrie-vector vector)))
-
- (define (handle-leaf leaf accum)
- (combine (leaf-key leaf) (leaf-value leaf) accum))
-
- (define (handle-collision collision accum)
- (fold-right (lambda (pair acc)
- (combine (car pair) (cdr pair) acc))
- accum
- (collision-alist collision)))
-
- (define (dispatch val accum)
- (cond ((leaf? val)
- (handle-leaf val accum))
- ((collision? val)
- (handle-collision val accum))
- (else
- (handle-subtrie val accum))))
-
- (vector-fold (lambda (val accum)
- ;; top level can have false values
- (if (not val) accum (dispatch val accum)))
- initial
- vector))
-
-(define (size node)
- (cond ((not node) 0)
- ((leaf? node) 1)
- ((collision? node) (collision-size node))
- (else (subtrie-size node))))
-
-;;; Exported Interface
-
-(define-record-type (hamt %make-hamt hamt?)
- (fields size root hash-function equivalence-predicate))
-
-(define (wrap-root root hamt)
- (define vecsize
- (vector-fold (lambda (val accum)
- (+ (size val) accum))
- 0
- root))
- (%make-hamt vecsize
- root
- (hamt-hash-function hamt)
- (hamt-equivalence-predicate hamt)))
-
-(define (make-hamt hash eqv?)
- (%make-hamt 0 (make-vector cardinality #f) hash eqv?))
-
-(define hamt-ref
- (case-lambda
- ((hamt key)
- (define token (cons #f #f))
- (define return-val (hamt-ref hamt key token))
- (when (eqv? token return-val)
- (assertion-violation 'hamt-ref "Key is not in the hamt" key))
- return-val)
- ((hamt key default)
- ;; assert hamt?
- (lookup (hamt-root hamt)
- key
- default
- (hamt-hash-function hamt)
- (hamt-equivalence-predicate hamt)))))
-
-(define (hamt-set hamt key value)
- (define root
- (insert (hamt-root hamt)
- key
- (lambda (old) value)
- 'dummy
- (hamt-hash-function hamt)
- (hamt-equivalence-predicate hamt)))
- (wrap-root root hamt))
-
-(define (hamt-update hamt key proc default)
- (define root
- (insert (hamt-root hamt)
- key
- proc
- default
- (hamt-hash-function hamt)
- (hamt-equivalence-predicate hamt)))
- (wrap-root root hamt))
-
-(define (hamt-delete hamt key)
- (define root
- (delete (hamt-root hamt)
- key
- (hamt-hash-function hamt)
- (hamt-equivalence-predicate hamt)))
- (wrap-root root hamt))
-
-(define (hamt-contains? hamt key)
- (define token (cons #f #f))
- (if (eqv? token (hamt-ref hamt key token))
- #f
- #t))
-
-(define (hamt-map mapper hamt)
- (%make-hamt (hamt-size hamt)
- (vec-map mapper (hamt-root hamt))
- (hamt-hash-function hamt)
- (hamt-equivalence-predicate hamt)))
-
-(define (hamt-fold combine initial hamt)
- (fold combine initial (hamt-root hamt)))
-
-(define (hamt->alist hamt)
- (hamt-fold (lambda (key value accumulator)
- (cons (cons key value) accumulator))
- '()
- hamt))
-
-(define (alist->hamt alist hash eqv?)
- (fold-right (lambda (kv-pair hamt)
- (hamt-set hamt (car kv-pair) (cdr kv-pair)))
- (make-hamt hash eqv?)
- alist))
-
-)
-
-;; Copyright (C) 2012 Ian Price <ianprice90@googlemail.com>
-
-;; Author: Ian Price <ianprice90@googlemail.com>
-
-;; This program is free software, you can redistribute it and/or
-;; modify it under the terms of the new-style BSD license.
-
-;; You should have received a copy of the BSD license along with this
-;; program. If not, see <http://www.debian.org/misc/bsd.license>.
-
-;; Documentation:
-;;
-;; make-heap : (any any -> bool) -> heap
-;; returns a new empty heap which uses the ordering procedure.
-;;
-;; heap : (any any -> bool) any ... -> heap
-;; return a new heap, ordered by the procedure argument, that contains
-;; all the other arguments as elements.
-;;
-;; heap? : any -> bool
-;; returns #t if the argument is a heap, #f otherwise.
-;;
-;; heap-size : heap -> non-negative integer
-;; returns the number of elements in the heap.
-;;
-;; heap-empty? : heap -> bool
-;; returns #t if the heap contains no elements, #f otherwise.
-;;
-;; heap-min : heap -> any
-;; returns the minimum element in the heap, according the heap's
-;; ordering procedure. If there are no elements, a
-;; &heap-empty-condition is raised.
-;;
-;; heap-delete-min : heap -> heap
-;; returns a new heap containing all the elements of the heap
-;; argument, except for the minimum argument, as determined by the
-;; heap's ordering procedure. If there are no elements, a
-;; &heap-empty-condition is raised.
-;;
-;; heap-pop : any + heap
-;; returns two values: the the minimum value, and a heap obtained by
-;; removing the minimum value from the original heap. If the heap is
-;; empty, a &heap-empty-condition is raised.
-;;
-;; heap-insert : heap any -> heap
-;; returns the new heap obtained by adding the element to those in the
-;; argument heap.
-;;
-;; heap->list : heap -> Listof(any)
-;; returns the heap containing all the elements of the heap. The
-;; elements of the list are ordered according to the heap's ordering
-;; procedure.
-;;
-;; list->heap : Listof(any) (any any -> boolean) -> heap
-;; returns the heap containing all the elements of the list, and using
-;; the procedure argument to order the elements.
-;;
-;; heap-merge : heap heap -> heap
-;; returns the heap containing all the elements of the argument
-;; heaps. The argument heaps are assumed to be using the same ordering
-;; procedure.
-;;
-;; heap-sort : (any any -> bool) list -> list
-;; returns a new list that is a permutation of the argument list, such
-;; that all the elements are ordered by the given procedure.
-;;
-;; heap-ordering-procedure : heap -> (any any -> boolean)
-;; returns the ordering procedure used internally by the heap.
-;;
-;; heap-empty-condition? : any -> bool
-;; returns #t if argument is a &heap-empty condition, #f otherwise.
-;;
-(library (pfds heaps)
-(export make-heap
- (rename (%heap heap))
- heap?
- heap-size
- heap-empty?
- heap-min
- heap-delete-min
- heap-insert
- heap-pop
- heap->list
- list->heap
- heap-merge
- heap-sort
- (rename (heap-ordering-predicate heap-ordering-procedure))
- heap-empty-condition?
- )
-(import (rnrs))
-
-(define-record-type (node %make-node node?)
- (fields size height value left right))
-
-(define-record-type leaf)
-
-(define (height x)
- (if (leaf? x)
- 0
- (node-height x)))
-
-(define (size x)
- (if (leaf? x)
- 0
- (node-size x)))
-
-(define (make-node v l r)
- (define sl (height l))
- (define sr (height r))
- (define m (+ 1 (min sl sr)))
- (define sz (+ 1 (size l) (size r)))
- (if (< sl sr)
- (%make-node sz m v r l)
- (%make-node sz m v l r)))
-
-(define (singleton v)
- (%make-node 1 0 v (make-leaf) (make-leaf)))
-
-(define (insert tree value prio<?)
- (merge-trees tree (singleton value) prio<?))
-
-(define (delete-min tree prio<?)
- (merge-trees (node-left tree)
- (node-right tree)
- prio<?))
-
-(define (merge-trees tree1 tree2 prio<?)
- (cond ((leaf? tree1) tree2)
- ((leaf? tree2) tree1)
- ((prio<? (node-value tree2)
- (node-value tree1))
- (make-node (node-value tree2)
- (node-left tree2)
- (merge-trees tree1
- (node-right tree2)
- prio<?)))
- (else
- (make-node (node-value tree1)
- (node-left tree1)
- (merge-trees (node-right tree1)
- tree2
- prio<?)))))
-
-
-;; outside interface
-(define-record-type (heap %make-heap heap?)
- (fields tree ordering-predicate))
-
-(define (make-heap priority<?)
- (%make-heap (make-leaf) priority<?))
-
-(define (%heap < . vals)
- (list->heap vals <))
-
-(define (heap-size heap)
- (size (heap-tree heap)))
-
-(define (heap-empty? heap)
- (leaf? (heap-tree heap)))
-
-(define (heap-min heap)
- (when (heap-empty? heap)
- (raise (condition
- (make-heap-empty-condition)
- (make-who-condition 'heap-min)
- (make-message-condition "There is no minimum element.")
- (make-irritants-condition (list heap)))))
- (node-value (heap-tree heap)))
-
-(define (heap-delete-min heap)
- (when (heap-empty? heap)
- (raise (condition
- (make-heap-empty-condition)
- (make-who-condition 'heap-delete-min)
- (make-message-condition "There is no minimum element.")
- (make-irritants-condition (list heap)))))
- (let ((< (heap-ordering-predicate heap)))
- (%make-heap (delete-min (heap-tree heap) <) <)))
-
-(define (heap-pop heap)
- (when (heap-empty? heap)
- (raise (condition
- (make-heap-empty-condition)
- (make-who-condition 'heap-pop)
- (make-message-condition "There is no minimum element.")
- (make-irritants-condition (list heap)))))
- (let* ((tree (heap-tree heap))
- (top (node-value tree))
- (< (heap-ordering-predicate heap))
- (rest (delete-min tree <)))
- (values top
- (%make-heap rest <))))
-
-(define (heap-insert heap value)
- (assert (heap? heap))
- (let ((< (heap-ordering-predicate heap)))
- (%make-heap (insert (heap-tree heap) value <) <)))
-
-(define (heap->list heap)
- (assert (heap? heap))
- (let ((< (heap-ordering-predicate heap)))
- (let loop ((tree (heap-tree heap)) (list '()))
- (if (leaf? tree)
- (reverse list)
- (loop (delete-min tree <)
- (cons (node-value tree) list))))))
-
-(define (list->heap list <)
- (%make-heap
- (fold-left (lambda (h item)
- (insert h item <))
- (make-leaf)
- list)
- <))
-
-(define (heap-merge heap1 heap2)
- (define < (heap-ordering-predicate heap1))
- (%make-heap
- (merge-trees (heap-tree heap1)
- (heap-tree heap2)
- <)
- <))
-
-(define (heap-sort < list)
- (heap->list (list->heap list <)))
-
-(define-condition-type &heap-empty
- &assertion
- make-heap-empty-condition
- heap-empty-condition?)
-)
-(package (pfds (0 3))
- (depends (wak-trc-testing))
- (synopsis "Purely Functional Data Structures")
- (description
- "A library of data structures for functional programmers."
- "It contains implementations of:"
- "- queues"
- "- deques"
- "- bbtrees"
- "- sets"
- "- dlists"
- "- priority search queues"
- "- heaps"
- "- hamts"
- "- finger trees"
- "- sequences")
- (homepage "http://github.com/ijp/pfds")
- (documentation
- "README.org"
- "LICENSE")
- (libraries
- (sls -> "pfds")
- ("queues" -> ("pdfs" "queues"))
- ("deques" -> ("pdfs" "deques"))
- ("private" -> ("pfds" "private"))))
-
-;;; psqs.sls --- Priority Search Queues
-
-;; Copyright (C) 2012 Ian Price <ianprice90@googlemail.com>
-
-;; Author: Ian Price <ianprice90@googlemail.com>
-
-;; This program is free software, you can redistribute it and/or
-;; modify it under the terms of the new-style BSD license.
-
-;; You should have received a copy of the BSD license along with this
-;; program. If not, see <http://www.debian.org/misc/bsd.license>.
-
-;;;; Documentation
-;;
-;; Priority search queues are a combination of two common abstract
-;; data types: finite maps, and priority queues. As such, it provides
-;; for access, insertion, removal and update on arbitrary keys, as
-;; well as for easy removal of the element with the lowest priority.
-;;
-;; Note: where a procedure takes a key or priority these are expected
-;; to be compatible with the relevant ordering procedures on the psq.
-;;
-;;;; Basic operations
-;;
-;; make-psq : < < -> psq
-;; takes a two ordering procedures, one for keys, and another for
-;; priorities, and returns an empty priority search queue
-;;
-;; psq? : obj -> boolean
-;; returns #t if the object is a priority search queue, #f otherwise.
-;;
-;; psq-empty? : psq -> boolean
-;; returns #t if the priority search queue contains no elements, #f
-;; otherwise.
-;;
-;; psq-size : psq -> non-negative integer
-;; returns the number of associations in the priority search queue
-;;
-;;;; Finite map operations
-;;
-;; psq-ref : psq key -> priority
-;; returns the priority of a key if it is in the priority search
-;; queue. If the key is not in the priority queue an
-;; assertion-violation is raised.
-;;
-;; psq-set : psq key priority -> psq
-;; returns the priority search queue obtained from inserting a key
-;; with a given priority. If the key is already in the priority search
-;; queue, it updates the priority to the new value.
-;;
-;; psq-update : psq key (priority -> priority) priority -> psq
-;; returns the priority search queue obtained by modifying the
-;; priority of key, by the given function. If the key is not in the
-;; priority search queue, it is inserted with the priority obtained by
-;; calling the function on the default value.
-;;
-;; psq-delete : psq key -> psq
-;; returns the priority search queue obtained by removing the
-;; key-priority association from the priority search queue. If the key
-;; is not in the queue, then the returned search queue will be the
-;; same as the original.
-;;
-;; psq-contains? : psq key -> boolean
-;; returns #t if there is an association for the given key in the
-;; priority search queue, #f otherwise.
-;;
-;;;; Priority queue operations
-;;
-;; psq-min : psq -> key
-;;
-;; returns the key of the minimum association in the priority search
-;; queue. If the queue is empty, an assertion violation is raised.
-;;
-;; psq-delete-min : psq -> psq
-;; returns the priority search queue obtained by removing the minimum
-;; association in the priority search queue. If the queue is empty, an
-;; assertion violation is raised.
-;;
-;; psq-pop : psq -> key + psq
-;; returns two values: the minimum key and the priority search queue
-;; obtained by removing the minimum association from the original
-;; queue. If the queue is empty, an assertion violation is raised.
-;;
-;;;; Ranged query functions
-;;
-;; psq-at-most : psq priority -> ListOf(key . priority)
-;; returns an alist containing all the associations in the priority
-;; search queue with priority less than or equal to a given value. The
-;; alist returned is ordered by key according to the predicate for the
-;; psq.
-;;
-;; psq-at-most-range : psq priority key key -> ListOf(key . priority)
-;; Similar to psq-at-most, but it also takes an upper and lower bound,
-;; for the keys it will return. These bounds are inclusive.
-;;
-(library (pfds psqs)
-(export make-psq
- psq?
- psq-empty?
- psq-size
- ;; map operations
- psq-ref
- psq-set
- psq-update
- psq-delete
- psq-contains?
- ;; priority queue operations
- psq-min
- psq-delete-min
- psq-pop
- ;; ranged query operations
- psq-at-most
- psq-at-most-range
- )
-(import (except (rnrs) min))
-
-;;; record types
-
-(define-record-type void)
-
-(define-record-type winner
- (fields key priority loser-tree maximum-key))
-
-(define-record-type start)
-
-(define-record-type (loser %make-loser loser?)
- (fields size key priority left split-key right))
-
-(define (make-loser key priority left split-key right)
- (%make-loser (+ (size left) (size right) 1)
- key
- priority
- left
- split-key
- right))
-
-;;; functions
-(define (maximum-key psq)
- (winner-maximum-key psq))
-
-(define max-key maximum-key)
-
-(define empty (make-void))
-
-(define (singleton key priority)
- (make-winner key priority (make-start) key))
-
-(define (play-match psq1 psq2 key<? prio<?)
- (cond ((void? psq1) psq2)
- ((void? psq2) psq1)
- ((not (prio<? (winner-priority psq2)
- (winner-priority psq1)))
- (let ((k1 (winner-key psq1))
- (p1 (winner-priority psq1))
- (t1 (winner-loser-tree psq1))
- (m1 (winner-maximum-key psq1))
- (k2 (winner-key psq2))
- (p2 (winner-priority psq2))
- (t2 (winner-loser-tree psq2))
- (m2 (winner-maximum-key psq2)))
- (make-winner k1
- p1
- (balance k2 p2 t1 m1 t2 key<? prio<?)
- m2)))
- (else
- (let ((k1 (winner-key psq1))
- (p1 (winner-priority psq1))
- (t1 (winner-loser-tree psq1))
- (m1 (winner-maximum-key psq1))
- (k2 (winner-key psq2))
- (p2 (winner-priority psq2))
- (t2 (winner-loser-tree psq2))
- (m2 (winner-maximum-key psq2)))
- (make-winner k2
- p2
- (balance k1 p1 t1 m1 t2 key<? prio<?)
- m2)))))
-
-(define (second-best ltree key key<? prio<?)
- (if (start? ltree)
- (make-void)
- (let ((k (loser-key ltree))
- (p (loser-priority ltree))
- (l (loser-left ltree))
- (m (loser-split-key ltree))
- (r (loser-right ltree)))
- (if (not (key<? m k))
- (play-match (make-winner k p l m)
- (second-best r key key<? prio<?)
- key<?
- prio<?)
- (play-match (second-best l m key<? prio<?)
- (make-winner k p r key)
- key<?
- prio<?)))))
-
-(define (delete-min psq key<? prio<?)
- ;; maybe void psqs should return void?
- (second-best (winner-loser-tree psq) (winner-maximum-key psq) key<? prio<?))
-
-(define (psq-case psq empty-k singleton-k match-k key<?)
- (if (void? psq)
- (empty-k)
- (let ((k1 (winner-key psq))
- (p1 (winner-priority psq))
- (t (winner-loser-tree psq))
- (m (winner-maximum-key psq)))
- (if (start? t)
- (singleton-k k1 p1)
- (let ((k2 (loser-key t))
- (p2 (loser-priority t))
- (l (loser-left t))
- (s (loser-split-key t))
- (r (loser-right t)))
- (if (not (key<? s k2))
- (match-k (make-winner k2 p2 l s)
- (make-winner k1 p1 r m))
- (match-k (make-winner k1 p1 l s)
- (make-winner k2 p2 r m))))))))
-
-(define (lookup psq key default key<?)
- (psq-case psq
- (lambda () default)
- (lambda (k p)
- (if (or (key<? k key) (key<? key k))
- default
- p))
- (lambda (w1 w2)
- (if (not (key<? (max-key w1) key))
- (lookup w1 key default key<?)
- (lookup w2 key default key<?)))
- key<?))
-
-(define (update psq key f default key<? prio<?)
- (psq-case psq
- (lambda () (singleton key (f default)))
- (lambda (k p)
- (cond ((key<? key k)
- (play-match (singleton key (f default))
- (singleton k p)
- key<?
- prio<?))
- ((key<? k key)
- (play-match (singleton k p)
- (singleton key (f default))
- key<?
- prio<?))
- (else
- (singleton key (f p)))))
- (lambda (w1 w2)
- (if (not (key<? (max-key w1) key))
- (play-match (update w1 key f default key<? prio<?)
- w2
- key<?
- prio<?)
- (play-match w1
- (update w2 key f default key<? prio<?)
- key<?
- prio<?)))
- key<?))
-
-(define (insert psq key val key<? prio<?)
- (psq-case psq
- (lambda () (singleton key val))
- (lambda (k p)
- (cond ((key<? key k)
- (play-match (singleton key val)
- (singleton k p)
- key<?
- prio<?))
- ((key<? k key)
- (play-match (singleton k p)
- (singleton key val)
- key<?
- prio<?))
- (else
- (singleton key val))))
- (lambda (w1 w2)
- (if (not (key<? (max-key w1) key))
- (play-match (insert w1 key val key<? prio<?) w2 key<? prio<?)
- (play-match w1 (insert w2 key val key<? prio<?) key<? prio<?)))
- key<?))
-
-(define (delete psq key key<? prio<?)
- (psq-case psq
- (lambda () empty)
- (lambda (k p)
- (if (or (key<? k key)
- (key<? key k))
- (singleton k p)
- empty))
- (lambda (w1 w2)
- (if (not (key<? (max-key w1) key))
- (play-match (delete w1 key key<? prio<?) w2 key<? prio<?)
- (play-match w1 (delete w2 key key<? prio<?) key<? prio<?)))
- key<?))
-
-(define (min tree)
- (when (void? tree)
- (assertion-violation 'psq-min
- "Can't take the minimum of an empty priority search queue"))
- (winner-key tree))
-
-(define (pop tree key<? prio<?)
- (when (void? tree)
- (assertion-violation 'psq-pop
- "Can't pop from an empty priority search queue"))
- (values (winner-key tree)
- (delete-min tree key<? prio<?)))
-
-;; at-most and at-most-range are perfect examples of when to use
-;; dlists, but we do not do that here
-(define (at-most psq p key<? prio<?)
- (define (at-most psq accum)
- (if (and (winner? psq)
- (prio<? p (winner-priority psq)))
- accum
- (psq-case psq
- (lambda () accum)
- (lambda (k p) (cons (cons k p) accum))
- (lambda (m1 m2)
- (at-most m1 (at-most m2 accum)))
- key<?)))
- (at-most psq '()))
-
-(define (at-most-range psq p lower upper key<? prio<?)
- (define (within-range? key)
- ;; lower <= k <= upper
- (not (or (key<? key lower) (key<? upper key))))
- (define (at-most psq accum)
- (if (and (winner? psq)
- (prio<? p (winner-priority psq)))
- accum
- (psq-case psq
- (lambda () accum)
- (lambda (k p)
- (if (within-range? k)
- (cons (cons k p) accum)
- accum))
- (lambda (m1 m2)
- (let ((accum* (if (key<? upper (max-key m1))
- accum
- (at-most m2 accum))))
- (if (key<? (max-key m1) lower)
- accum*
- (at-most m1 accum*))))
- key<?)))
- (at-most psq '()))
-
-;;; Maintaining balance
-(define weight 4) ; balancing constant
-
-(define (size ltree)
- (if (start? ltree)
- 0
- (loser-size ltree)))
-
-(define (balance key priority left split-key right key<? prio<?)
- (let ((l-size (size left))
- (r-size (size right)))
- (cond ((< (+ l-size r-size) 2)
- (make-loser key priority left split-key right))
- ((> r-size (* weight l-size))
- (balance-left key priority left split-key right key<? prio<?))
- ((> l-size (* weight r-size))
- (balance-right key priority left split-key right key<? prio<?))
- (else
- (make-loser key priority left split-key right)))))
-
-(define (balance-left key priority left split-key right key<? prio<?)
- (if (< (size (loser-left right))
- (size (loser-right right)))
- (single-left key priority left split-key right key<? prio<?)
- (double-left key priority left split-key right key<? prio<?)))
-
-(define (balance-right key priority left split-key right key<? prio<?)
- (if (< (size (loser-right left))
- (size (loser-left left)))
- (single-right key priority left split-key right key<? prio<?)
- (double-right key priority left split-key right key<? prio<?)))
-
-(define (single-left key priority left split-key right key<? prio<?)
- (let ((right-key (loser-key right))
- (right-priority (loser-priority right))
- (right-left (loser-left right))
- (right-split-key (loser-split-key right))
- (right-right (loser-right right)))
- ;; test
- (if (and (not (key<? right-split-key right-key))
- (not (prio<? right-priority priority)))
- (make-loser key
- priority
- (make-loser right-key right-priority left split-key right-left)
- right-split-key
- right-right
- )
- (make-loser right-key
- right-priority
- (make-loser key priority left split-key right-left)
- right-split-key
- right-right))))
-
-(define (double-left key priority left split-key right key<? prio<?)
- (let ((right-key (loser-key right))
- (right-priority (loser-priority right))
- (right-left (loser-left right))
- (right-split-key (loser-split-key right))
- (right-right (loser-right right)))
- (single-left key
- priority
- left
- split-key
- (single-right right-key
- right-priority
- right-left
- right-split-key
- right-right
- key<?
- prio<?)
- key<?
- prio<?)))
-
-(define (single-right key priority left split-key right key<? prio<?)
- (let ((left-key (loser-key left))
- (left-priority (loser-priority left))
- (left-left (loser-left left))
- (left-split-key (loser-split-key left))
- (left-right (loser-right left)))
- (if (and (key<? left-split-key left-key)
- (not (prio<? left-priority priority)))
- (make-loser key
- priority
- left-left
- left-split-key
- (make-loser left-key left-priority left-right split-key right))
- (make-loser left-key
- left-priority
- left-left
- left-split-key
- (make-loser key priority left-right split-key right)))))
-
-(define (double-right key priority left split-key right key<? prio<?)
- (let ((left-key (loser-key left))
- (left-priority (loser-priority left))
- (left-left (loser-left left))
- (left-split-key (loser-split-key left))
- (left-right (loser-right left)))
- (single-right key
- priority
- (single-left left-key
- left-priority
- left-left
- left-split-key
- left-right
- key<?
- prio<?)
- split-key
- right
- key<?
- prio<?)))
-
-;;; Exported Type
-
-(define-record-type (psq %make-psq psq?)
- (fields key<? priority<? tree))
-
-(define (%update-psq psq new-tree)
- (%make-psq (psq-key<? psq)
- (psq-priority<? psq)
- new-tree))
-
-;;; Exported Procedures
-
-(define (make-psq key<? priority<?)
- (%make-psq key<? priority<? (make-void)))
-
-(define (psq-empty? psq)
- (assert (psq? psq))
- (void? (psq-tree psq)))
-
-(define (psq-ref psq key)
- (define cookie (cons #f #f))
- (assert (psq? psq))
- (let ((val (lookup (psq-tree psq) key cookie (psq-key<? psq))))
- (if (eq? val cookie)
- (assertion-violation 'psq-ref "not in tree")
- val)))
-
-(define (psq-set psq key priority)
- (assert (psq? psq))
- (%update-psq psq
- (insert (psq-tree psq) key priority (psq-key<? psq) (psq-priority<? psq))))
-
-(define (psq-update psq key f default)
- (assert (psq? psq))
- (%update-psq psq (update (psq-tree psq) key f default (psq-key<? psq) (psq-priority<? psq))))
-
-(define (psq-delete psq key)
- (assert (psq? psq))
- (%update-psq psq (delete (psq-tree psq) key (psq-key<? psq) (psq-priority<? psq))))
-
-(define (psq-contains? psq key)
- (define cookie (cons #f #f))
- (assert (psq? psq))
- (let ((val (lookup (psq-tree psq) key cookie (psq-key<? psq))))
- (not (eq? val cookie))))
-
-(define (psq-min psq)
- (assert (psq? psq))
- (min (psq-tree psq)))
-
-(define (psq-delete-min psq)
- (assert (and (psq? psq)
- (not (psq-empty? psq))))
- (%update-psq psq (delete-min (psq-tree psq) (psq-key<? psq) (psq-priority<? psq))))
-
-(define (psq-pop psq)
- (assert (psq? psq))
- (let-values (((min rest) (pop (psq-tree psq) (psq-key<? psq) (psq-priority<? psq))))
- (values min (%update-psq psq rest))))
-
-(define (psq-at-most psq max-priority)
- (assert (psq? psq))
- (let ((tree (psq-tree psq))
- (key<? (psq-key<? psq))
- (prio<? (psq-priority<? psq)))
- (at-most tree max-priority key<? prio<?)))
-
-(define (psq-at-most-range psq max-priority min-key max-key)
- (assert (psq? psq))
- (let ((tree (psq-tree psq))
- (key<? (psq-key<? psq))
- (prio<? (psq-priority<? psq)))
- (at-most-range tree max-priority min-key max-key key<? prio<?)))
-
-(define (psq-size psq)
- (assert (psq? psq))
- (let ((tree (psq-tree psq)))
- (if (winner? tree)
- (+ 1 (size (winner-loser-tree tree)))
- 0)))
-
-)
-
-;;; queues.sls --- Purely functional queues
-
-;; Copyright (C) 2011,2012 Ian Price <ianprice90@googlemail.com>
-
-;; Author: Ian Price <ianprice90@googlemail.com>
-
-;; This program is free software, you can redistribute it and/or
-;; modify it under the terms of the new-style BSD license.
-
-;; You should have received a copy of the BSD license along with this
-;; program. If not, see <http://www.debian.org/misc/bsd.license>.
-
-;;; Commentary:
-;;
-;; A scheme translation of "Simple and Efficient Purely Functional
-;; Queues and Deques" by Chris Okazaki
-;;
-;;
-;;; Documentation:
-;;
-;; make-queue : () -> queue
-;; returns a queue containing no items
-;;
-;; queue? : any -> boolean
-;; tests if an object is a queue
-;;
-;; queue-length : queue -> non-negative integer
-;; returns the number of items in the queue
-;;
-;; queue-empty? : queue -> boolean
-;; returns true if there are no items in the queue, false otherwise
-;;
-;; enqueue : queue any -> queue
-;; returns a new queue with the enqueued item at the end
-;;
-;; dequeue : queue -> value queue
-;; returns two values, the item at the front of the queue, and a new
-;; queue containing the all the other items
-;; raises a &queue-empty condition if the queue is empty
-;;
-;; queue-empty-condition? : object -> boolean
-;; tests if an object is a &queue-empty condition
-;;
-;; queue->list : queue -> listof(any)
-;; returns a queue containing all the items in the list. The order of
-;; the elements in the queue is the same as the order of the elements
-;; in the list.
-;;
-;; list->queue : listof(any) -> queue
-;; returns a list containing all the items in the queue. The order of
-;; the items in the list is the same as the order in the queue.
-;; For any list l, (equal? (queue->list (list->queue l)) l) is #t.
-;;
-(library (pfds queues)
-(export make-queue
- queue?
- queue-length
- queue-empty?
- enqueue
- dequeue
- queue-empty-condition?
- list->queue
- queue->list
- )
-(import (except (rnrs) cons*)
- (pfds private lazy-lists)
- (pfds queues private condition)
- (rnrs r5rs))
-
-(define (rotate l r a)
- (if (empty? l)
- (cons* (head r) a)
- (cons* (head l)
- (rotate (tail l)
- (tail r)
- (cons* (head r) a)))))
-
-
-;;; Implementation
-
-(define-record-type (queue %make-queue queue?)
- (fields
- (immutable length)
- (immutable l)
- (immutable r)
- (immutable l^)))
-
-
-(define (make-queue)
- (%make-queue 0 '() '() '()))
-
-(define (enqueue queue item)
- (let ((len (queue-length queue))
- (l (queue-l queue))
- (r (queue-r queue))
- (l^ (queue-l^ queue)))
- (makeq (+ len 1) l (cons* item r) l^)))
-
-(define (dequeue queue)
- (when (queue-empty? queue)
- ;; (error 'dequeue "Can't dequeue empty queue")
- (raise (condition
- (make-queue-empty-condition)
- (make-who-condition 'dequeue)
- (make-message-condition "There are no elements to dequeue")
- (make-irritants-condition (list queue)))))
- (let ((len (queue-length queue))
- (l (queue-l queue))
- (r (queue-r queue))
- (l^ (queue-l^ queue)))
- (values (head l)
- (makeq (- len 1) (tail l) r l^))))
-
-(define (makeq length l r l^)
- (if (empty? l^)
- (let ((l* (rotate l r '())))
- (%make-queue length l* '() l*))
- (%make-queue length l r (tail l^))))
-
-(define (queue-empty? queue)
- (zero? (queue-length queue)))
-
-(define (list->queue list)
- (fold-left enqueue (make-queue) list))
-
-(define (queue->list queue)
- (let loop ((rev-list '()) (queue queue))
- (if (queue-empty? queue)
- (reverse rev-list)
- (let-values (((val queue) (dequeue queue)))
- (loop (cons val rev-list)
- queue)))))
-
-)
-
-;;; sequences.sls --- Purely Functional Sequences
-
-;; Copyright (C) 2012 Ian Price <ianprice90@googlemail.com>
-
-;; Author: Ian Price <ianprice90@googlemail.com>
-
-;; This program is free software, you can redistribute it and/or
-;; modify it under the terms of the new-style BSD license.
-
-;; You should have received a copy of the BSD license along with this
-;; program. If not, see <http://www.debian.org/misc/bsd.license>.
-
-;;; Commentary:
-
-;; Sequences are a general-purpose, variable-length collection,
-;; similar to lists, however they support efficient addition and
-;; removal from both ends, and random-access. Like other Scheme
-;; collections, sequences are zero-indexed.
-;;
-;; make-sequence : () -> sequence
-;; returns a new empty sequence
-;;
-;; sequence any ... -> sequence
-;; returns a new sequence containing all of the argument elements, in the
-;; same order.
-;;
-;; sequence? : any -> bool
-;; returns #t if the argument is a sequence, #f otherwise.
-;;
-;; sequence-empty? : sequence -> bool
-;; returns #t if the argument sequence contains no elements, #f otherwise.
-;;
-;; sequence-size : sequence -> non-negative integer
-;; returns the number of elements in the sequence
-;;
-;; sequence-cons : any sequence -> sequence
-;; return the new sequence created by adding the element to the front of
-;; the sequence.
-;;
-;; sequence-uncons : sequence -> any sequence
-;; returns two values: the first element of the sequence, and a new
-;; sequence containing all but the first element. If the sequence is
-;; empty, a &sequence-empty condition is raised.
-;;
-;; sequence-snoc : sequence any -> sequence
-;; return the new sequence created by adding the element to the end of
-;; the sequence.
-;;
-;; sequence-unsnoc : sequence -> sequence any
-;; returns two values: a new sequence containing all but the last
-;; element of the sequence, and the last element itself. If the
-;; sequence is empty, a &sequence-empty condition is raised.
-;;
-;; sequence-append : sequence sequence -> sequence
-;; returns a new sequence containing all the elements of the first
-;; sequence, followed by all the elements of the second sequence.
-;;
-;; list->sequence : Listof(Any) -> sequence
-;; returns a new sequence containing all the elements of the argument
-;; list, in the same order.
-;;
-;; sequence->list : sequence -> Listof(Any)
-;; returns a new list containing all the elements of the sequence, in the
-;; same order.
-;;
-;; sequence-split-at sequence integer -> sequence + sequence
-;; returns two new sequences, the first containing the first N elements
-;; of the sequence, the second containing the remaining elements. If N is
-;; negative, it returns the empty sequence as the first argument, and the
-;; original sequence as the second argument. Similarly, if N is greater
-;; than the length of the list, it returns the original sequence as the
-;; first argument, and the empty sequence as the second argument.
-;;
-;; Consequently, (let-values (((a b) (sequence-split-at s i)))
-;; (sequence-append a b)) is equivalent to s for all sequences s, and
-;; integers i.
-;;
-;; sequence-take sequence integer -> sequence
-;; returns a new sequence containing the first N elements of the
-;; argument sequence. If N is negative, the empty sequence is
-;; returned. If N is larger than the length of the sequence, the whole
-;; sequence is returned.
-;;
-;; sequence-drop sequence integer -> sequence
-;; returns a new sequence containing all but the first N elements of the
-;; argument sequence. If N is negative, the whole sequence is
-;; returned. If N is larger than the length of the sequence, the empty
-;; sequence is returned.
-;;
-;; sequence-ref : sequence non-negative-integer -> any
-;; returns the element at the specified index in the sequence. If the
-;; index is outside the range 0 <= i < (sequence-size sequence), an
-;; assertion violation is raised.
-;;
-;; sequence-set : sequence non-negative-integer any -> sequence
-;; returns the new sequence obtained by replacing the element at the
-;; specified index in the sequence with the given value. If the index
-;; is outside the range 0 <= i < (sequence-size sequence), an
-;; assertion violation is raised.
-;;
-;; sequence-fold (any -> any -> any) any sequence
-;; returns the value obtained by iterating the combiner procedure over
-;; the sequence in left-to-right order. The combiner procedure takes two
-;; arguments, the value of the position in the sequence, and an
-;; accumulator, and its return value is used as the value of the
-;; accumulator for the next call. The initial accumulator value is given
-;; by the base argument.
-;;
-;; sequence-fold-right (any -> any -> any) any sequence
-;; Like sequence-fold, but the sequence is traversed in right-to-left
-;; order, rather than left-to-right.
-;;
-;; sequence-reverse : sequence -> sequence
-;; returns a new sequence containing all the arguments of the argument
-;; list, in reverse order.
-;;
-;; sequence-map : (any -> any) sequence -> sequence
-;; returns a new sequence obtained by applying the procedure to each
-;; element of the argument sequence in turn.
-;;
-;; sequence-filter : (any -> bool) sequence -> sequence
-;; returns a new sequence containing all the elements of the argument
-;; sequence for which the predicate is true.
-;;
-;; sequence-empty-condition? : any -> bool
-;; returns #t if an object is a &sequence-empty condition, #f otherwise.
-;;
-(library (pfds sequences)
-(export make-sequence
- sequence?
- sequence-empty?
- sequence-size
- sequence-cons
- sequence-uncons
- sequence-snoc
- sequence-unsnoc
- sequence-append
- list->sequence
- sequence->list
- (rename (%sequence sequence))
- sequence-split-at
- sequence-take
- sequence-drop
- sequence-ref
- sequence-set
- sequence-fold
- sequence-fold-right
- sequence-reverse
- sequence-map
- sequence-filter
- sequence-empty-condition?
- )
-
-(import (rnrs)
- (pfds fingertrees))
-
-;; Note: as sequences are not a subtype of fingertrees, but rather a
-;; particular instantiation of them, &sequence-empty is not a subtype
-;; of &fingertree-empty
-(define-condition-type &sequence-empty
- &assertion
- make-sequence-empty-condition
- sequence-empty-condition?)
-
-(define-record-type (sequence %make-sequence sequence?)
- (fields fingertree))
-
-(define (make-sequence)
- (%make-sequence (make-fingertree 0 + (lambda (x) 1))))
-
-(define (sequence-empty? seq)
- (fingertree-empty? (sequence-fingertree seq)))
-
-(define (sequence-size seq)
- (fingertree-measure (sequence-fingertree seq)))
-
-(define (sequence-cons value seq)
- (%make-sequence
- (fingertree-cons value (sequence-fingertree seq))))
-
-(define (sequence-snoc seq value)
- (%make-sequence
- (fingertree-snoc (sequence-fingertree seq) value)))
-
-(define (sequence-uncons seq)
- (call-with-values
- (lambda ()
- (define ft (sequence-fingertree seq))
- (when (fingertree-empty? ft)
- (raise
- (condition
- (make-sequence-empty-condition)
- (make-who-condition 'sequence-uncons)
- (make-message-condition "There are no elements to uncons")
- (make-irritants-condition (list seq)))))
- (fingertree-uncons ft))
- (lambda (head tree)
- (values head (%make-sequence tree)))))
-
-(define (sequence-unsnoc seq)
- (call-with-values
- (lambda ()
- (define ft (sequence-fingertree seq))
- (when (fingertree-empty? ft)
- (raise
- (condition
- (make-sequence-empty-condition)
- (make-who-condition 'sequence-unsnoc)
- (make-message-condition "There are no elements to unsnoc")
- (make-irritants-condition (list seq)))))
- (fingertree-unsnoc ft))
- (lambda (tree last)
- (values (%make-sequence tree) last))))
-
-(define (sequence-append seq1 seq2)
- (%make-sequence
- (fingertree-append (sequence-fingertree seq1)
- (sequence-fingertree seq2))))
-
-(define (list->sequence list)
- (fold-left sequence-snoc
- (make-sequence)
- list))
-
-(define (sequence->list seq)
- (fingertree->list (sequence-fingertree seq)))
-
-(define (%sequence . args)
- (list->sequence args))
-
-(define (sequence-split-at seq i)
- (let-values (((l r)
- (fingertree-split (lambda (x) (< i x))
- (sequence-fingertree seq))))
- (values (%make-sequence l)
- (%make-sequence r))))
-
-(define (sequence-take seq i)
- (let-values (((head tail)
- (sequence-split-at seq i)))
- head))
-
-(define (sequence-drop seq i)
- (let-values (((head tail)
- (sequence-split-at seq i)))
- tail))
-
-(define (sequence-ref seq i)
- (define size (sequence-size seq))
- (unless (and (<= 0 i) (< i size))
- (assertion-violation 'sequence-ref "Index out of range" i))
- (let-values (((_l x _r)
- (fingertree-split3 (lambda (x) (< i x))
- (sequence-fingertree seq))))
- x))
-
-(define (sequence-set seq i val)
- (define size (sequence-size seq))
- (unless (and (<= 0 i) (< i size))
- (assertion-violation 'sequence-set "Index out of range" i))
- (let-values (((l x r)
- (fingertree-split3 (lambda (x) (< i x))
- (sequence-fingertree seq))))
- (%make-sequence
- (fingertree-append l (fingertree-cons val r)))))
-
-(define (sequence-fold proc base seq)
- (fingertree-fold proc base (sequence-fingertree seq)))
-
-(define (sequence-fold-right proc base seq)
- (fingertree-fold-right proc base (sequence-fingertree seq)))
-
-(define (sequence-reverse seq)
- (%make-sequence (fingertree-reverse (sequence-fingertree seq))))
-
-(define (sequence-map proc seq)
- (define (combine element seq)
- (sequence-cons (proc element) seq))
- (sequence-fold-right combine (make-sequence) seq))
-
-(define (sequence-filter pred? seq)
- (define (combine element seq)
- (if (pred? element)
- (sequence-cons element seq)
- seq))
- (sequence-fold-right combine (make-sequence) seq))
-
-)
-
-;;; sets.sls --- Purely Functional Sets
-
-;; Copyright (C) 2012 Ian Price <ianprice90@googlemail.com>
-
-;; Author: Ian Price <ianprice90@googlemail.com>
-
-;; This program is free software, you can redistribute it and/or
-;; modify it under the terms of the new-style BSD license.
-
-;; You should have received a copy of the BSD license along with this
-;; program. If not, see <http://www.debian.org/misc/bsd.license>.
-
-;; Documentation:
-;;
-;; set? : any -> boolean
-;; returns #t if the object is a set, #f otherwise
-;;
-;; make-set : (any any -> boolean) -> set
-;; returns a new empty set ordered by the < procedure
-;;
-;; set-member? : set any -> boolean
-;; returns true if element is in the set
-;;
-;; set-insert : set any -> set
-;; returns a new set created by inserting element into the set argument
-;;
-;; set-remove : set element -> set
-;; returns a new set created by removing element from the set
-;;
-;; set-size : set -> non-negative integer
-;; returns the number of elements in the set
-;;
-;; set<? : set set -> boolean
-;; returns #t if set1 is a proper subset of set2, #f otherwise. That
-;; is, if all elements of set1 are in set2, and there is at least one
-;; element of set2 not in set1.
-;;
-;; set<=? : set set -> boolean
-;; returns #t if set1 is a subset of set2, #f otherwise, i.e. if all
-;; elements of set1 are in set2.
-;;
-;; set=? : set set -> boolean
-;; returns #t if every element of set1 is in set2, and vice versa, #f
-;; otherwise.
-;;
-;; set>=? : set set -> boolean
-;; returns #t if set2 is a subset of set1, #f otherwise.
-;;
-;; set>? : set set -> boolean
-;; returns #t if set2 is a proper subset of set1, #f otherwise.
-;;
-;; subset? : set set -> boolean
-;; same as set<=?
-;;
-;; proper-subset? : set set -> boolean
-;; same as set<?
-;;
-;; set-map : (any -> any) set -> set
-;; returns the new set created by applying proc to each element of the set
-;;
-;; set-fold : (any any -> any) any set -> any
-;; returns the value obtained by iterating the procedure over each
-;; element of the set and an accumulator value. The value of the
-;; accumulator is initially base, and the return value of proc is used
-;; as the accumulator for the next iteration.
-;;
-;; list->set : Listof(any) (any any -> any) -> set
-;; returns the set containing all the elements of the list, ordered by <.
-;;
-;; set->list : set -> Listof(any)
-;; returns all the elements of the set as a list
-;;
-;; set-union : set set -> set
-;; returns the union of set1 and set2, i.e. contains all elements of
-;; set1 and set2.
-;;
-;; set-intersection : set set -> set
-;; returns the intersection of set1 and set2, i.e. the set of all
-;; items that are in both set1 and set2.
-;;
-;; set-difference : set set -> set
-;; returns the difference of set1 and set2, i.e. the set of all items
-;; in set1 that are not in set2.
-;;
-;; set-ordering-procedure : set -> (any any -> boolean)
-;; returns the ordering procedure used internall by the set.
-(library (pfds sets)
-(export set?
- make-set
- set-member?
- set-insert
- set-remove
- set-size
- set<?
- set<=?
- set=?
- set>=?
- set>?
- subset?
- proper-subset?
- set-map
- set-fold
- list->set
- set->list
- set-union
- set-intersection
- set-difference
- set-ordering-procedure
- )
-(import (rnrs)
- (pfds bbtrees))
-
-(define dummy #f)
-
-;;; basic sets
-(define-record-type (set %make-set set?)
- (fields tree))
-
-(define (set-ordering-procedure set)
- (bbtree-ordering-procedure (set-tree set)))
-
-(define (make-set <)
- (%make-set (make-bbtree <)))
-
-;; provide a (make-equal-set) function?
-
-(define (set-member? set element)
- (bbtree-contains? (set-tree set) element))
-
-(define (set-insert set element)
- (%make-set (bbtree-set (set-tree set) element dummy)))
-
-(define (set-remove set element)
- (%make-set (bbtree-delete (set-tree set) element)))
-
-(define (set-size set)
- (bbtree-size (set-tree set)))
-
-;;; set equality
-(define (set<=? set1 set2)
- (let ((t (set-tree set2)))
- (bbtree-traverse (lambda (k _ l r b)
- (and (bbtree-contains? t k)
- (l #t)
- (r #t)))
- #t
- (set-tree set1))))
-
-(define (set<? set1 set2)
- (and (< (set-size set1)
- (set-size set2))
- (set<=? set1 set2)))
-
-(define (set>=? set1 set2)
- (set<=? set2 set1))
-
-(define (set>? set1 set2)
- (set<? set2 set1))
-
-(define (set=? set1 set2)
- (and (set<=? set1 set2)
- (set>=? set1 set2)))
-
-(define subset? set<=?)
-
-(define proper-subset? set<?)
-
-;;; iterators
-(define (set-map proc set)
- ;; currently restricted to returning a set with the same ordering, I
- ;; could weaken this to, say, comparing with < on the object-hash,
- ;; or I make it take a < argument for the result set.
- (let ((tree (set-tree set)))
- (%make-set
- (bbtree-fold (lambda (key _ tree)
- (bbtree-set tree (proc key) dummy))
- (make-bbtree (bbtree-ordering-procedure tree))
- tree))))
-
-(define (set-fold proc base set)
- (bbtree-fold (lambda (key value base)
- (proc key base))
- base
- (set-tree set)))
-
-;;; conversion
-(define (list->set list <)
- (fold-left (lambda (tree element)
- (set-insert tree element))
- (make-set <)
- list))
-
-(define (set->list set)
- (set-fold cons '() set))
-
-;;; set operations
-(define (set-union set1 set2)
- (%make-set (bbtree-union (set-tree set1) (set-tree set2))))
-
-(define (set-intersection set1 set2)
- (%make-set (bbtree-intersection (set-tree set1) (set-tree set2))))
-
-(define (set-difference set1 set2)
- (%make-set (bbtree-difference (set-tree set1) (set-tree set2))))
-
-)
-
-;; Copyright (C) 2011-2014 Ian Price <ianprice90@googlemail.com>
-
-;; Author: Ian Price <ianprice90@googlemail.com>
-
-;; This program is free software, you can redistribute it and/or
-;; modify it under the terms of the new-style BSD license.
-
-;; You should have received a copy of the BSD license along with this
-;; program. If not, see <http://www.debian.org/misc/bsd.license>.
-
-;;; Code:
-(import (rnrs)
- (pfds tests queues)
- (pfds tests deques)
- (pfds tests bbtrees)
- (pfds tests sets)
- (pfds tests psqs)
- (pfds tests heaps)
- (pfds tests fingertrees)
- (pfds tests sequences)
- (pfds tests hamts)
- (pfds tests utils)
- (wak trc-testing))
-
-;; Some schemes use lazy loading of modules, and so I can't just use
-;; (run-test pfds) and rely on the side effects in the other modules
-;; to add them to the pfds parent suite.
-(define-syntax add-tests!
- (syntax-rules ()
- ((add-tests! suite ...)
- (begin (add-test! pfds 'suite suite) ...))))
-
-(add-tests! queues deques bbtrees sets psqs
- heaps fingertrees sequences hamts)
-
-(run-test pfds)
-(define-library (r7rs-extras all)
- (import (r7rs-extras higher-order))
- (include-library-declarations "higher-order.exports.sld")
- (import (r7rs-extras io))
- (include-library-declarations "io.exports.sld")
- (import (r7rs-extras partition))
- (include-library-declarations "partition.exports.sld")
- (import (r7rs-extras arithmetic))
- (include-library-declarations "arithmetic.exports.sld")
- (import (r7rs-extras pushpop))
- (include-library-declarations "pushpop.exports.sld")
- )
-;;; arithmetic.body.scm --- Extra arithmetic operations
-
-;; Copyright © 2014 Taylan Ulrich Bayırlı/Kammer
-;; Copyright © 2015 Taylan Ulrich Bayırlı/Kammer
-
-;; Author: Taylan Ulrich Bayırlı/Kammer <taylanbayirli@gmail.com>
-;; Keywords: extensions arithmetic number
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; If you're desperate for performance, you might benefit from implementing the
-;; euclidean variants in terms of the floor and ceiling variants for positive
-;; and negative values of `y' respectively. The floor variants are in the
-;; (scheme base) library and might be more efficient in your implementation.
-
-;; These might also otherwise have significantly more efficient implementations.
-;; Let me know.
-
-;;; Code:
-
-(define-syntax define-divisions
- (syntax-rules ()
- ((_ div div-doc quotient quotient-doc remainder remainder-doc x y
- quotient-expr)
- (begin
- (define (div x y)
- div-doc
- (let* ((q quotient-expr)
- (r (- x (* y quotient-expr))))
- (values q r)))
- (define (quotient x y)
- quotient-doc
- quotient-expr)
- (define (remainder x y)
- remainder-doc
- (- x (* y quotient-expr)))))))
-
-(define-divisions
- euclidean/
- "Return Q and R in X = Q*Y + R where 0 <= R < |Y|."
- euclidean-quotient
- "Return Q in X = Q*Y + R where 0 <= R < |Y|."
- euclidean-remainder
- "Return R in X = Q*Y + R where 0 <= R < |Y|."
- x y
- (cond ((positive? y)
- (floor (/ x y)))
- ((negative? y)
- (ceiling (/ x y)))
- ((zero? y)
- (error "division by zero"))
- (else +nan.0)))
-
-(define-divisions
- ceiling/
- "Return Q and R in X = Q*Y + R where Q = ceiling(X/Y)."
- ceiling-quotient
- "Return Q in X = Q*Y + R where Q = ceiling(X/Y)."
- ceiling-remainder
- "Return R in X = Q*Y + R where Q = ceiling(X/Y)."
- x y
- (ceiling (/ x y)))
-
-(define-divisions
- centered/
- "Return Q and R in X = Q*Y + R where -|Y/2| <= R < |Y/2|."
- centered-quotient
- "Return Q in X = Q*Y + R where -|Y/2| <= R < |Y/2|."
- centered-remainder
- "Return R in X = Q*Y + R where -|Y/2| <= R < |Y/2|."
- x y
- (cond ((positive? y)
- (floor (+ 1/2 (/ x y))))
- ((negative? y)
- (ceiling (+ -1/2 (/ x y))))
- ((zero? y)
- (error "division by zero"))
- (else +nan.0)))
-
-(define-divisions
- round/
- "Return Q and R in X = Q*Y + R where Q = round(X/Y)."
- round-quotient
- "Return Q in X = Q*Y + R where Q = round(X/Y)."
- round-remainder
- "Return R in X = Q*Y + R where Q = round(X/Y)."
- x y
- (round (/ x y)))
-
-;;; arithmetic.body.scm ends here
-(export
- euclidean/
- euclidean-quotient
- euclidean-remainder
- ceiling/
- ceiling-quotient
- ceiling-remainder
- centered/
- centered-quotient
- centered-remainder
- round/
- round-quotient
- round-remainder
- )
-(define-library (r7rs-extras arithmetic)
- (import (scheme base))
- (include-library-declarations "arithmetic.exports.sld")
- (include "arithmetic.body.scm"))
-;;; higher-order.body.scm --- Auxiliary higher-oder procedures
-
-;; Copyright © 2014 Taylan Ulrich Bayırlı/Kammer
-;; Copyright © 2015 Taylan Ulrich Bayırlı/Kammer
-
-;; Author: Taylan Ulrich Bayırlı/Kammer <taylanbayirli@gmail.com>
-;; Keywords: extensions higher-order
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; Miscellaneous higher-oder procedures for creating constant functions,
-;; negating functions, etc.
-
-;;; Code:
-
-(define (const value)
- "Make a nullary procedure always returning VALUE."
- (lambda () value))
-
-(define (negate proc)
- "Make a procedure negating the application of PROC to its arguments."
- (lambda x (not (apply proc x))))
-
-(define (compose proc . rest)
- "Functional composition; e.g. ((compose x y) a) = (x (y a))."
- (if (null? rest)
- proc
- (let ((rest-proc (apply compose rest)))
- (lambda x
- (let-values ((x (apply rest-proc x)))
- (apply proc x))))))
-
-(define (pipeline proc . rest)
- "Reverse functional composition; e.g. ((pipeline x y) a) = (y (x a))."
- (if (null? rest)
- proc
- (let ((rest-proc (apply pipeline rest)))
- (lambda x
- (let-values ((x (apply proc x)))
- (apply rest-proc x))))))
-
-(define (identity . x)
- "Returns values given to it as-is."
- (apply values x))
-
-(define (and=> value proc)
- "If VALUE is true, call PROC on it, else return false."
- (if value (proc value) value))
-
-;;; higher-order.body.scm ends here
-(export
- const
- negate
- compose
- pipeline
- identity
- and=>
- )
-(define-library (r7rs-extras higher-order)
- (import (scheme base))
- (include-library-declarations "higher-order.exports.sld")
- (include "higher-order.body.scm"))
-;;; io.body.scm --- Input/Output extensions for R7RS
-
-;; Copyright © 2014 Taylan Ulrich Bayırlı/Kammer
-;; Copyright © 2015 Taylan Ulrich Bayırlı/Kammer
-
-;; Author: Taylan Ulrich Bayırlı/Kammer <taylanbayirli@gmail.com>
-;; Keywords: extensions io i/o input output input/output
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; R7RS leaves out some conceivable combinations of:
-;;
-;; [call-]with-(input|output|error)[-(from|to)]-port
-;;
-;; Some of these are nontrivial and annoying to redefine every time one needs
-;; them. Others are actually so trivial that their body could be inlined at any
-;; place of usage, but it's nevertheless distracting having to remember which
-;; ones are or aren't in the base library, so we just define them all.
-
-;;; Code:
-
-(define (call-with-input-string string proc)
- "Applies PROC to an input port fed with STRING."
- (call-with-port (open-input-string string) proc))
-
-(define (call-with-output-string proc)
- "Applies PROC to a port feeding a string which is then returned."
- (let ((port (open-output-string)))
- (call-with-port port proc)
- (get-output-string port)))
-
-(define-syntax with-port
- (syntax-rules ()
- ((with-port port-param port thunk closer)
- (parameterize ((port-param port))
- (call-with-values thunk
- (lambda vals
- (closer port)
- (apply values vals)))))))
-
-(define (with-input-port port thunk)
- "Closes PORT after calling THUNK with it as the `current-input-port'."
- (with-port current-input-port port thunk close-input-port))
-
-(define (with-output-port port thunk)
- "Closes PORT after calling THUNK with it as the `current-output-port'."
- (with-port current-output-port port thunk close-output-port))
-
-(define (with-error-port port thunk)
- "Closes PORT after calling THUNK with it as the `current-error-port'."
- (with-port current-error-port port thunk close-output-port))
-
-(define (with-input-from-port port thunk)
- "Calls THUNK with PORT as the `current-input-port'. Doesn't close PORT."
- (parameterize ((current-input-port port))
- (thunk)))
-
-(define (with-output-to-port port thunk)
- "Calls THUNK with PORT as the `current-output-port'. Doesn't close PORT."
- (parameterize ((current-output-port port))
- (thunk)))
-
-(define (with-error-to-port port thunk)
- "Calls THUNK with PORT as the `current-error-port'. Doesn't close PORT."
- (parameterize ((current-error-port port))
- (thunk)))
-
-(define (with-error-to-file file thunk)
- "Calls THUNK with `current-error-port' bound to FILE."
- (with-error-port (open-output-file file) thunk))
-
-(define (with-input-from-string string thunk)
- "Calls THUNK with `current-input-port' bound to a port fed with STRING."
- (with-input-port (open-input-string string) thunk))
-
-(define (with-output-to-string thunk)
- "Calls THUNK with `current-output-port' bound to a port feeding a string which
-is then returned."
- (let ((port (open-output-string)))
- (with-output-port port thunk)
- (get-output-string port)))
-
-(define (with-error-to-string thunk)
- "Calls THUNK with `current-error-port' bound to a port feeding a string which
-is then returned."
- (let ((port (open-output-string)))
- (with-error-port port thunk)
- (get-output-string port)))
-
-;;; io.body.scm ends here
-(export
- call-with-input-string
- call-with-output-string
- with-input-port
- with-output-port
- with-error-port
- with-input-from-port
- with-output-to-port
- with-error-to-port
- with-error-to-file
- with-input-from-string
- with-output-to-string
- with-error-to-string
- )
-(define-library (r7rs-extras io)
- (import (scheme base)
- (scheme file))
- (include-library-declarations "io.exports.sld")
- (include "io.body.scm"))
-;;; partition.body.scm --- Variable-arity partition procedures
-
-;; Copyright © 2014 Taylan Ulrich Bayırlı/Kammer
-;; Copyright © 2015 Taylan Ulrich Bayırlı/Kammer
-
-;; Author: Taylan Ulrich Bayırlı/Kammer <taylanbayirli@gmail.com>
-;; Keywords: extensions lists partition partitioning
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; `partition' proper is in SRFI-1; we define alternative versions only.
-
-;;; Code:
-
-(define (%partition exclusive? list . procs)
- (if (null? procs)
- list
- (let ((lists (make-list (+ 1 (length procs)) '())))
- (for-each
- (lambda (elt)
- (let loop ((procs procs)
- (lists lists)
- (match? #f))
- (if (null? procs)
- (when (not match?)
- (set-car! lists (cons elt (car lists))))
- (if ((car procs) elt)
- (begin (set-car! lists (cons elt (car lists)))
- (when (not exclusive?)
- (loop (cdr procs) (cdr lists) #t)))
- (loop (cdr procs) (cdr lists) match?)))))
- list)
- (apply values (map reverse lists)))))
-
-(define (partition* list . procs)
- "Partitions LIST via PROCS, returning PROCS + 1 many lists; the last list
-containing elements that didn't match any procedure. The ordering of each list
-obeys that of LIST. If there are elements matching multiple PROCS, it's
-unspecified in which one of the matching lists they appear."
- (apply %partition #t list procs))
-
-(define (partition+ list . procs)
- "This is like the `partition*' procedure, but elements matching multiple
-procedures appear in every corresponding list."
- (apply %partition #f list procs))
-
-;;; partition.body.scm ends here
-(export
- partition*
- partition+
- )
-(define-library (r7rs-extras partition)
- (import (scheme base))
- (include-library-declarations "partition.exports.sld")
- (include "partition.body.scm"))
-;;; pushpop.body.scm --- push! and pop!
-
-;; Copyright © 2014 Taylan Ulrich Bayırlı/Kammer
-;; Copyright © 2015 Taylan Ulrich Bayırlı/Kammer
-
-;; Author: Taylan Ulrich Bayırlı/Kammer <taylanbayirli@gmail.com>
-;; Keywords: extensions push pop
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; Dead simple push! and pop!.
-
-;;; Code:
-
-(define-syntax push!
- (syntax-rules ()
- ((push! pair value)
- (set! pair (cons value pair)))))
-
-(define-syntax pop!
- (syntax-rules ()
- ((pop! pair)
- (let ((value (car pair)))
- (set! pair (cdr pair))
- value))))
-
-;;; pushpop.body.scm ends here
-(export
- push!
- pop!
- )
-(define-library (r7rs-extras pushpop)
- (import (scheme base))
- (include-library-declarations "pushpop.exports.sld")
- (include "pushpop.body.scm"))
-;;; generic-ref-set --- Generic accessor and modifier operators.
-
-;; Copyright © 2015 Taylan Ulrich Bayırlı/Kammer <taylanbayirli@gmail.com>
-
-;; Permission is hereby granted, free of charge, to any person obtaining
-;; a copy of this software and associated documentation files (the
-;; "Software"), to deal in the Software without restriction, including
-;; without limitation the rights to use, copy, modify, merge, publish,
-;; distribute, sublicense, and/or sell copies of the Software, and to
-;; permit persons to whom the Software is furnished to do so, subject to
-;; the following conditions:
-
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-;; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-;;; Helpers
-
-(define-syntax push!
- (syntax-rules ()
- ((_ <list-var> <x>)
- (set! <list-var> (cons <x> <list-var>)))))
-
-(define (alist->hashtable alist)
- (let ((table (make-eqv-hashtable 100)))
- (for-each (lambda (entry)
- (hashtable-set! table (car entry) (cdr entry)))
- alist)
- table))
-
-(define (pair-ref pair key)
- (cond
- ((eqv? 'car key)
- (car pair))
- ((eqv? 'cdr key)
- (cdr pair))
- (else
- (list-ref pair key))))
-
-(define (pair-set! pair key value)
- (cond
- ((eqv? 'car key)
- (set-car! pair value))
- ((eqv? 'cdr key)
- (set-cdr! pair value))
- (else
- (list-set! pair key value))))
-
-;;; Record inspection support
-
-(cond-expand
- ((or (library (srfi 99))
- (library (rnrs records inspection))
- (library (r6rs records inspection)))
- (cond-expand
- ((not (library (srfi 99)))
- (define rtd-accessor record-accessor)
- (define rtd-mutator record-mutator))
- (else))
- (define (record-ref record field)
- (let* ((rtd (record-rtd record))
- (accessor (rtd-accessor rtd field)))
- (accessor record)))
- (define (record-set! record field value)
- (let* ((rtd (record-rtd record))
- (mutator (rtd-mutator rtd field)))
- (mutator record value)))
- (define record-getter
- (list (cons record? record-ref)))
- (define record-setter
- (list (cons record? record-set!)))
- (define record-type
- (list record?)))
- (else
- (define record-getter '())
- (define record-setter '())
- (define record-type '())))
-
-;;; SRFI-4 support
-
-;;; In some implementations, SRFI-4 vectors are also bytevectors. We accomodate
-;;; for those implementations by using generic bytevector-ref/set! procedures
-;;; which possibly dispatch to an SRFI-4 type's getter/setter, but also
-;;; inserting the SRFI-4 getters/setters into the top-level dispatch tables.
-
-(cond-expand
- ((library (srfi 4))
- (define srfi-4-getters
- (list (cons s8vector? s8vector-ref)
- (cons u8vector? u8vector-ref)
- (cons s16vector? s16vector-ref)
- (cons u16vector? u16vector-ref)
- (cons s32vector? s32vector-ref)
- (cons u32vector? u32vector-ref)
- (cons s64vector? s64vector-ref)
- (cons u64vector? u64vector-ref)))
- (define srfi-4-setters
- (list (cons s8vector? s8vector-set!)
- (cons u8vector? u8vector-set!)
- (cons s16vector? s16vector-set!)
- (cons u16vector? u16vector-set!)
- (cons s32vector? s32vector-set!)
- (cons u32vector? u32vector-set!)
- (cons s64vector? s64vector-set!)
- (cons u64vector? u64vector-set!)))
- (define srfi-4-types
- (list s8vector? u8vector? s16vector? u16vector? s32vector? u32vector?
- s64vector? u64vector?))
- (define srfi-4-getters-table (alist->hashtable srfi-4-getters))
- (define srfi-4-setters-table (alist->hashtable srfi-4-setters))
- (define (bytevector-ref bytevector index)
- (let* ((type (find (lambda (pred) (pred bytevector))) srfi-4-types)
- (getter (if type
- (ref srfi-4-getters-table type)
- bytevector-u8-ref)))
- (getter bytevector index)))
- (define (bytevector-set! bytevector index value)
- (let* ((type (find (lambda (pred) (pred bytevector))) srfi-4-types)
- (setter (if type
- (ref srfi-4-setters-table type)
- bytevector-u8-set!)))
- (setter bytevector index value))))
- (else
- (define srfi-4-getters '())
- (define srfi-4-setters '())
- (define srfi-4-types '())
- (define bytevector-ref bytevector-u8-ref)
- (define bytevector-set! bytevector-u8-set!)))
-
-;;; SRFI-111 boxes support
-
-(cond-expand
- ((library (srfi 111))
- (define (box-ref box _field)
- (unbox box))
- (define (box-set! box _field value)
- (set-box! box value))
- (define box-getter (list (cons box? box-ref)))
- (define box-setter (list (cons box? box-set!)))
- (define box-type (list box?)))
- (else
- (define box-getter '())
- (define box-setter '())
- (define box-type '())))
-
-;;; Main
-
-(define %ref
- (case-lambda
- ((object field)
- (let ((getter (lookup-getter object))
- (sparse? (sparse-type? object)))
- (if sparse?
- (let* ((not-found (cons #f #f))
- (result (getter object field not-found)))
- (if (eqv? result not-found)
- (error "Object has no entry for field." object field)
- result))
- (getter object field))))
- ((object field default)
- (let ((getter (lookup-getter object)))
- (getter object field default)))))
-
-(define (%ref* object field . fields)
- (if (null? fields)
- (%ref object field)
- (apply %ref* (%ref object field) fields)))
-
-(define (%set! object field value)
- (let ((setter (lookup-setter object)))
- (setter object field value)))
-
-(define ref
- (getter-with-setter
- %ref
- (lambda (object field value)
- (%set! object field value))))
-
-(define ref*
- (getter-with-setter
- %ref*
- (rec (set!* object field rest0 . rest)
- (if (null? rest)
- (%set! object field rest0)
- (apply set!* (ref object field) rest0 rest)))))
-
-(define ~ ref*)
-
-(define $bracket-apply$ ref*)
-
-(define (lookup-getter object)
- (or (hashtable-ref getter-table (type-of object) #f)
- (error "No generic getter for object's type." object)))
-
-(define (lookup-setter object)
- (or (hashtable-ref setter-table (type-of object) #f)
- (error "No generic setter for object's type." object)))
-
-(define (sparse-type? object)
- (memv (type-of object) sparse-types))
-
-(define (type-of object)
- (find (lambda (pred) (pred object)) type-list))
-
-(define getter-table
- (alist->hashtable
- (append
- (list (cons bytevector? bytevector-ref)
- (cons hashtable? hashtable-ref)
- (cons pair? pair-ref)
- (cons string? string-ref)
- (cons vector? vector-ref))
- record-getter
- srfi-4-getters
- box-getter)))
-
-(define setter-table
- (alist->hashtable
- (append
- (list (cons bytevector? bytevector-set!)
- (cons hashtable? hashtable-set!)
- (cons pair? pair-set!)
- (cons string? string-set!)
- (cons vector? vector-set!))
- record-setter
- srfi-4-setters
- box-setter)))
-
-(define sparse-types
- (list hashtable?))
-
-(define type-list
- ;; Although the whole SRFI intrinsically neglects performance, we still use
- ;; the micro-optimization of ordering this list roughly according to most
- ;; likely match.
- (append
- (list hashtable? vector? pair? bytevector? string?)
- srfi-4-types
- box-type
- ;; The record type must be placed last so specific record types (e.g. box)
- ;; take precedence.
- record-type
- ;; Place those types we don't support really last.
- (list boolean? char? eof-object? null? number? port? procedure? symbol?)))
-
-(define (register-getter-with-setter! type getter sparse?)
- (push! type-list type)
- (set! (~ getter-table type) getter)
- (set! (~ setter-table type) (setter getter))
- (when sparse?
- (push! sparse-types type)))
-
-(cond-expand
- ((not (or (library (srfi 99))
- (library (rnrs records inspection))
- (library (r6rs records inspection))))
- (define-syntax define-record-type
- (syntax-rules ()
- ((_ <name> <constructor> <pred> <field> ...)
- (begin
- (%define-record-type <name> <constructor> <pred> <field> ...)
- ;; Throw-away definition to not disturb an internal definitions
- ;; sequence.
- (define __throwaway
- (begin
- (register-getter-with-setter!
- <pred>
- (getter-with-setter (record-getter <field> ...)
- (record-setter <field> ...))
- #f)
- ;; Return the implementation's preferred "unspecified" value.
- (if #f #f)))))))
-
- (define-syntax record-getter
- (syntax-rules ()
- ((_ (<field> <getter> . <rest>) ...)
- (let ((getters (alist->hashtable (list (cons '<field> <getter>) ...))))
- (lambda (record field)
- (let ((getter (or (ref getters field #f)
- (error "No such field of record." record field))))
- (getter record)))))))
-
- (define-syntax record-setter
- (syntax-rules ()
- ((_ . <rest>)
- (%record-setter () . <rest>))))
-
- (define-syntax %record-setter
- (syntax-rules ()
- ((_ <setters> (<field> <getter>) . <rest>)
- (%record-setter <setters> . <rest>))
- ((_ <setters> (<field> <getter> <setter>) . <rest>)
- (%record-setter ((<field> <setter>) . <setters>) . <rest>))
- ((_ ((<field> <setter>) ...))
- (let ((setters (alist->hashtable (list (cons '<field> <setter>) ...))))
- (lambda (record field value)
- (let ((setter (or (ref setters field #f)
- (error "No such assignable field of record."
- record field))))
- (setter record value)))))))))
-
-;;; generic-ref-set.body.scm ends here
-(define-library (srfi 123)
- (export
- ref ref* ~ register-getter-with-setter!
- $bracket-apply$
- set! setter getter-with-setter)
- (import
- (except (scheme base) set! define-record-type)
- (scheme case-lambda)
- (r6rs hashtables)
- (srfi 1)
- (srfi 17)
- (srfi 31))
- (cond-expand
- ;; Favor SRFI-99.
- ((library (srfi 99))
- (import (srfi 99)))
- ;; We assume that if there's the inspection library, there's also the
- ;; syntactic and procedural libraries.
- ((library (rnrs records inspection))
- (import (rnrs records syntactic))
- (import (rnrs records procedural))
- (import (rnrs records inspection)))
- ((library (r6rs records inspection))
- (import (r6rs records syntactic))
- (import (r6rs records procedural))
- (import (r6rs records inspection)))
- (else
- (import (rename (only (scheme base) define-record-type)
- (define-record-type %define-record-type)))
- (export define-record-type)))
- (cond-expand
- ((library (srfi 4))
- (import (srfi 4)))
- (else))
- (cond-expand
- ((library (srfi 111))
- (import (srfi 111)))
- (else))
- (include "123.body.scm"))
-;;; generic-ref-set --- Generic accessor and modifier operators.
-
-;; Copyright © 2015 Taylan Ulrich Bayırlı/Kammer <taylanbayirli@gmail.com>
-
-;; Permission is hereby granted, free of charge, to any person obtaining
-;; a copy of this software and associated documentation files (the
-;; "Software"), to deal in the Software without restriction, including
-;; without limitation the rights to use, copy, modify, merge, publish,
-;; distribute, sublicense, and/or sell copies of the Software, and to
-;; permit persons to whom the Software is furnished to do so, subject to
-;; the following conditions:
-
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-;; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-(define-library (tests srfi-123)
- (export run-tests)
- (import (except (scheme base) define-record-type set!)
- (r6rs hashtables)
- (srfi 64)
- (srfi 123))
- (cond-expand
- ((library (srfi 99))
- (import (srfi 99)))
- ((library (rnrs records inspection))
- (import (rnrs records syntactic))
- (import (rnrs records procedural)))
- (import (rnrs records inspection))
- ((library (r6rs records inspection))
- (import (r6rs records syntactic))
- (import (r6rs records procedural)))
- (import (r6rs records inspection))
- (else))
- (cond-expand
- ((library (srfi 4))
- (import (srfi 4)))
- (else
- (begin
- ;; Stub to silence compilers.
- (define s16vector #f))))
- (cond-expand
- ((library (srfi 111))
- (import (srfi 111)))
- (else
- (begin
- ;; Stub to silence compilers.
- (define box #f))))
- (begin
-
- (define-record-type <foo> (make-foo a b) foo?
- (a foo-a set-foo-a!)
- (b foo-b))
-
- ;; The SRFI-99 sample implementation contains a bug where immutable fields
- ;; are nevertheless mutable through the procedural API. Test whether we are
- ;; on that implementation.
- (cond-expand
- ((library (srfi 99))
- (define using-broken-srfi99
- (guard (err (else #f))
- (rtd-mutator <foo> 'b))))
- (else
- (define using-broken-srfi99 #f)))
-
- (define (run-tests)
- (let ((runner (test-runner-create)))
- (parameterize ((test-runner-current runner))
- (test-begin "SRFI-123")
-
- (test-begin "ref")
- (test-assert "bytevector" (= 1 (ref (bytevector 0 1 2) 1)))
- (test-assert "hashtable" (let ((table (make-eqv-hashtable)))
- (hashtable-set! table 'foo 0)
- (= 0 (ref table 'foo))))
- (test-assert "hashtable default" (let ((table (make-eqv-hashtable)))
- (= 1 (ref table 0 1))))
- (test-assert "pair" (= 1 (ref (cons 0 1) 'cdr)))
- (test-assert "list" (= 1 (ref (list 0 1 2) 1)))
- (test-assert "string" (char=? #\b (ref "abc" 1)))
- (test-assert "vector" (= 1 (ref (vector 0 1 2) 1)))
- (test-assert "record" (= 1 (ref (make-foo 0 1) 'b)))
- (cond-expand
- ((library (srfi 4)) (values))
- (else (test-skip 1)))
- (test-assert "srfi-4" (= 1 (ref (s16vector 0 1 2) 1)))
- (cond-expand
- ((library (srfi 111)) (values))
- (else (test-skip 1)))
- (test-assert "srfi-111" (= 1 (ref (box 1) '*)))
- (test-end "ref")
-
- (test-assert "ref*" (= 1 (ref* '(_ #(_ (0 . 1) _) _) 1 1 'cdr)))
-
- (test-begin "ref setter")
- (test-assert "bytevector" (let ((bv (bytevector 0 1 2)))
- (set! (ref bv 1) 3)
- (= 3 (ref bv 1))))
- (test-assert "hashtable" (let ((ht (make-eqv-hashtable)))
- (set! (ref ht 'foo) 0)
- (= 0 (ref ht 'foo))))
- (test-assert "pair" (let ((p (cons 0 1)))
- (set! (ref p 'cdr) 2)
- (= 2 (ref p 'cdr))))
- (test-assert "list" (let ((l (list 0 1 2)))
- (set! (ref l 1) 3)
- (= 3 (ref l 1))))
- (test-assert "string" (let ((s (string #\a #\b #\c)))
- (set! (ref s 1) #\d)
- (char=? #\d (ref s 1))))
- (test-assert "vector" (let ((v (vector 0 1 2)))
- (set! (ref v 1) 3)
- (= 3 (ref v 1))))
- (test-assert "record" (let ((r (make-foo 0 1)))
- (set! (ref r 'a) 2)
- (= 2 (ref r 'a))))
- (when using-broken-srfi99
- (test-expect-fail 1))
- (test-assert "bad record assignment"
- (not (guard (err (else #f)) (set! (ref (make-foo 0 1) 'b) 2) #t)))
- (cond-expand
- ((library (srfi 4)) (values))
- (else (test-skip 1)))
- (test-assert "srfi-4" (let ((s16v (s16vector 0 1 2)))
- (set! (ref s16v 1) 3)
- (= 3 (ref s16v 1))))
- (cond-expand
- ((library (srfi 111)) (values))
- (else (test-skip 1)))
- (test-assert "srfi-111" (let ((b (box 0)))
- (set! (ref b '*) 1)
- (= 1 (ref b '*))))
- (test-end "ref setter")
-
- (test-assert "ref* setter"
- (let ((obj (list '_ (vector '_ (cons 0 1) '_) '_)))
- (set! (ref* obj 1 1 'cdr) 2)
- (= 2 (ref* obj 1 1 'cdr))))
-
- (test-end "SRFI-123")
- (and (= 0 (test-runner-xpass-count runner))
- (= 0 (test-runner-fail-count runner))))))
-
- ))
-(import (scheme base)
- (scheme eval)
- (scheme process-context))
-
-(if (eval '(run-tests) (environment '(tests srfi-123)))
- (exit 0)
- (exit 1))
-;;; Copyright 2015 William D 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 and permission 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.
-;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;;; This R7RS code implements (rnrs hashtables) on top of SRFI 69.
-
-;;; Private stuff.
-
-;;; Although SRFI 69 is mostly written as though hash functions take
-;;; just one argument, its reference implementation routinely passes
-;;; a second argument to hash functions, and that arguably incorrect
-;;; behavior has undoubtedly found its way into many implementations
-;;; of SRFI 69.
-;;;
-;;; A unary hash function passed to R6RS make-hashtable is therefore
-;;; unlikely to work when passed to SRFI 69 make-hash-table. We need
-;;; to convert the unary hash function so it will accept a second
-;;; optional argument, and we also need to arrange for the original
-;;; unary hash function to be returned by hashtable-hash-function.
-;;;
-;;; We'd like to accomplish this while preserving interoperability
-;;; between R6RS hashtables and SRFI 69 hash tables. That argues
-;;; against implementing R6RS hashtables by records that encapsulate
-;;; a SRFI 69 hash table, which would otherwise be the easy way to
-;;; go about this.
-;;;
-;;; This association list implements a bidirectional mapping between
-;;; one-argument hash functions of R6RS and their representations as
-;;; two-argument hash functions that will work with SRFI 69.
-
-(define table-of-hash-functions '())
-
-;;; Given a unary hash function, returns a hash function that will
-;;; be acceptable to SRFI 69.
-
-(define (make-srfi-69-hash-function hash-function)
- (lambda (x . rest)
- (if (null? rest)
- (hash-function x)
- (modulo (hash-function x) (car rest)))))
-
-(define (r6rs->srfi69 hash-function)
- (let ((probe (assoc hash-function table-of-hash-functions)))
- (if probe
- (cdr probe)
- (let ((hasher (make-srfi-69-hash-function hash-function)))
- (set! table-of-hash-functions
- (cons (cons hash-function hasher)
- table-of-hash-functions))
- hasher))))
-
-(define (srfi69->r6rs hasher)
- (define (loop table)
- (cond ((null? table)
- hasher)
- ((equal? hasher (cdr (car table)))
- (car (car table)))
- (else
- (loop (cdr table)))))
- (loop table-of-hash-functions))
-
-;;; SRFI 69 doesn't define a hash function that's suitable for use
-;;; with the eqv? predicate, and we need one for make-eqv-hashtable.
-;;;
-;;; The R7RS eqv? predicate behaves the same as eq? for these types:
-;;;
-;;; symbols
-;;; booleans
-;;; empty list
-;;; pairs
-;;; records
-;;; non-empty strings
-;;; non-empty vectors
-;;; non-empty bytevectors
-;;;
-;;; eqv? might behave differently when its arguments are:
-;;;
-;;; procedures that behave the same but have equal location tags
-;;; numbers
-;;; characters
-;;; empty strings
-;;; empty vectors
-;;; empty bytevectors
-;;;
-;;; If eqv? and eq? behave differently on two arguments x and y,
-;;; eqv? returns true and eq? returns false.
-;;;
-;;; FIXME: There is no portable way to define a good hash function
-;;; that's compatible with eqv? on procedures and also runs in
-;;; constant time. This one is compatible with eqv? and runs in
-;;; constant time (on procedures), but isn't any good.
-
-;;; The main thing these numerical constants have in common is that
-;;; they're positive and fit in 24 bits.
-
-(define hash:procedure 9445898)
-(define hash:character 13048478)
-(define hash:empty-string 14079236)
-(define hash:empty-vector 1288342)
-(define hash:empty-bytevector 11753202)
-(define hash:inexact 1134643)
-(define hash:infinity+ 2725891)
-(define hash:infinity- 5984233)
-(define hash:nan 7537847)
-(define hash:complex 9999245)
-
-(define (hash-for-eqv x)
- (cond ((procedure? x)
- hash:procedure)
- ((number? x)
- (cond ((exact-integer? x)
- x)
- ((not (real? x))
- (+ hash:complex (complex-hash x)))
- ((exact? x)
- (+ (numerator x) (denominator x)))
- (else
- (+ hash:inexact (inexact-hash x)))))
- ((char? x)
- (+ hash:character (char->integer x)))
- ((eqv? x "")
- hash:empty-string)
- ((eqv? x '#())
- hash:empty-vector)
- ((eqv? x '#u8())
- hash:empty-bytevector)
- (else
- (hash-by-identity x))))
-
-;;; The R6RS distinguishes mutable from immutable hashtables,
-;;; so we have to keep track of that somehow. Here we remember
-;;; all of the immutable hashtables within a SRFI 69 hash-table.
-;;;
-;;; FIXME: That means the storage occupied by an immutable
-;;; hashtable won't be reclaimed if it becomes otherwise
-;;; inaccessible.
-
-(define immutable-hashtables
- (make-hash-table eqv? (r6rs->srfi69 hash-table-size)))
-
-(define (complain-if-immutable ht complainant)
- (if (hash-table-ref/default immutable-hashtables ht #f)
- (error (string-append (symbol->string complainant)
- ": hashtable is immutable")
- ht)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;
-;;; Exported procedures.
-;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;;; The R6RS make-eq-hashtable procedure is normally called with
-;;; no arguments, but an optional argument specifies the initial
-;;; capacity of the table. That optional argument, if present,
-;;; will be ignored by this implementation because it has no
-;;; counterpart in SRFI 69.
-
-(define (make-eq-hashtable . rest)
- (make-hash-table eq? hash-by-identity))
-
-(define (make-eqv-hashtable . rest)
- (make-hash-table eqv? (r6rs->srfi69 hash-for-eqv)))
-
-;;; As with make-eq-hashtable and make-eqv-hashtable, the optional
-;;; initial capacity will be ignored.
-
-(define (make-hashtable hash-function equiv . rest)
- (make-hash-table equiv (r6rs->srfi69 hash-function)))
-
-(define (hashtable? x)
- (hash-table? x))
-
-(define (hashtable-size ht)
- (hash-table-size ht))
-
-(define (hashtable-ref ht key default)
- (hash-table-ref/default ht key default))
-
-(define (hashtable-set! ht key obj)
- (complain-if-immutable ht 'hashtable-set!)
- (hash-table-set! ht key obj))
-
-(define (hashtable-delete! ht key)
- (complain-if-immutable ht 'hashtable-delete!)
- (hash-table-delete! ht key))
-
-(define (hashtable-contains? ht key)
- (hash-table-exists? ht key))
-
-(define (hashtable-update! ht key proc default)
- (complain-if-immutable ht 'hashtable-update!)
- (hash-table-set! ht
- key
- (proc (hash-table-ref/default ht key default))))
-
-;;; By default, hashtable-copy returns an immutable hashtable.
-;;; The copy is mutable only if a second argument is passed and
-;;; that second argument is true.
-
-(define (hashtable-copy ht . rest)
- (let ((mutable? (and (pair? rest) (car rest)))
- (the-copy (hash-table-copy ht)))
- (if (not mutable?)
- (hash-table-set! immutable-hashtables the-copy #t))
- the-copy))
-
-;;; As usual, the optional "initial" capacity is ignored.
-
-(define (hashtable-clear! ht . rest)
- (complain-if-immutable ht 'hashtable-update!)
- (hash-table-walk ht
- (lambda (key value)
- (hash-table-delete! ht key))))
-
-(define (hashtable-keys ht)
- (list->vector (hash-table-keys ht)))
-
-(define (hashtable-entries ht)
- (let* ((keys (hashtable-keys ht))
- (vals (vector-map (lambda (key)
- (hash-table-ref ht key))
- keys)))
- (values keys vals)))
-
-(define (hashtable-equivalence-function ht)
- (hash-table-equivalence-function ht))
-
-(define (hashtable-hash-function ht)
- (srfi69->r6rs (hash-table-hash-function ht)))
-
-(define (hashtable-mutable? ht)
- (not (hash-table-ref/default immutable-hashtables ht #f)))
-
-(define (equal-hash obj)
- (hash obj))
-
-;;; string-hash is exported by SRFI 69.
-;;; string-ci-hash is exported by SRFI 69.
-
-(define (r6rs:symbol-hash sym)
- (hash-by-identity sym))
-
-;;; Reference implementation of SRFI 69, from
-;;; http://srfi.schemers.org/srfi-69/srfi-69.html
-
-;;; Copyright © Panu Kalliokoski (2005). All Rights Reserved.
-;;; Permission is hereby granted, free of charge, to any person
-;;; obtaining a copy of this software and associated documentation
-;;; files (the "Software"), to deal in the Software without
-;;; restriction, including without limitation the rights to use,
-;;; copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom
-;;; the Software is furnished to do so, subject to the following
-;;; conditions:
-;;;
-;;; The above copyright notice and this permission notice shall
-;;; be included in all copies or substantial portions of the Software.
-;;;
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
-;;; KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
-;;; WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-;;; PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
-;;; OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-;;; OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-;;; OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-;;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-;;; Modification history:
-;;;
-;;; In May 2015, William D Clinger modified this code for use in
-;;; R7RS systems, mainly so it could be used as a last resort in
-;;; the (r6rs hashtables) approximation to (rnrs hashtables).
-;;;
-;;; string-ci-hash was changed to use R7RS string-foldcase
-;;;
-;;; string-hash, symbol-hash, and %string-hash were changed
-;;; to eliminate a now-useless procedure call for each character
-;;;
-;;; whitespace was adjusted because it got messed up during
-;;; conversion from HTML to Scheme code
-
-(define *default-bound* (- (expt 2 29) 3))
-
-(define (%string-hash s bound)
- (let ((hash 31)
- (len (string-length s)))
- (do ((index 0 (+ index 1)))
- ((>= index len) (modulo hash bound))
- (set! hash (modulo (+ (* 37 hash)
- (char->integer (string-ref s index)))
- *default-bound*)))))
-
-(define (string-hash s . maybe-bound)
- (let ((bound (if (null? maybe-bound) *default-bound* (car maybe-bound))))
- (%string-hash s bound)))
-
-(define (string-ci-hash s . maybe-bound)
- (let ((bound (if (null? maybe-bound) *default-bound* (car maybe-bound))))
- (%string-hash (string-foldcase s) bound)))
-
-(define (symbol-hash s . maybe-bound)
- (let ((bound (if (null? maybe-bound) *default-bound* (car maybe-bound))))
- (%string-hash (symbol->string s) bound)))
-
-(define (hash obj . maybe-bound)
- (let ((bound (if (null? maybe-bound) *default-bound* (car maybe-bound))))
- (cond ((integer? obj) (modulo obj bound))
- ((string? obj) (string-hash obj bound))
- ((symbol? obj) (symbol-hash obj bound))
- ((real? obj) (modulo (+ (numerator obj) (denominator obj)) bound))
- ((number? obj)
- (modulo (+ (hash (real-part obj)) (* 3 (hash (imag-part obj))))
- bound))
- ((char? obj) (modulo (char->integer obj) bound))
- ((vector? obj) (vector-hash obj bound))
- ((pair? obj) (modulo (+ (hash (car obj)) (* 3 (hash (cdr obj))))
- bound))
- ((null? obj) 0)
- ((not obj) 0)
- ((procedure? obj) (error "hash: procedures cannot be hashed" obj))
- (else 1))))
-
-(define hash-by-identity hash)
-
-(define (vector-hash v bound)
- (let ((hashvalue 571)
- (len (vector-length v)))
- (do ((index 0 (+ index 1)))
- ((>= index len) (modulo hashvalue bound))
- (set! hashvalue (modulo (+ (* 257 hashvalue) (hash (vector-ref v index)))
- *default-bound*)))))
-
-(define %make-hash-node cons)
-(define %hash-node-set-value! set-cdr!)
-(define %hash-node-key car)
-(define %hash-node-value cdr)
-
-(define-record-type <srfi-hash-table>
- (%make-hash-table size hash compare associate entries)
- hash-table?
- (size hash-table-size hash-table-set-size!)
- (hash hash-table-hash-function)
- (compare hash-table-equivalence-function)
- (associate hash-table-association-function)
- (entries hash-table-entries hash-table-set-entries!))
-
-(define *default-table-size* 64)
-
-(define (appropriate-hash-function-for comparison)
- (or (and (eq? comparison eq?) hash-by-identity)
- (and (eq? comparison string=?) string-hash)
- (and (eq? comparison string-ci=?) string-ci-hash)
- hash))
-
-(define (make-hash-table . args)
- (let* ((comparison (if (null? args) equal? (car args)))
- (hash
- (if (or (null? args) (null? (cdr args)))
- (appropriate-hash-function-for comparison) (cadr args)))
- (size
- (if (or (null? args) (null? (cdr args)) (null? (cddr args)))
- *default-table-size* (caddr args)))
- (association
- (or (and (eq? comparison eq?) assq)
- (and (eq? comparison eqv?) assv)
- (and (eq? comparison equal?) assoc)
- (letrec
- ((associate
- (lambda (val alist)
- (cond ((null? alist) #f)
- ((comparison val (caar alist)) (car alist))
- (else (associate val (cdr alist)))))))
- associate))))
- (%make-hash-table 0 hash comparison association (make-vector size '()))))
-
-(define (make-hash-table-maker comp hash)
- (lambda args (apply make-hash-table (cons comp (cons hash args)))))
-(define make-symbol-hash-table
- (make-hash-table-maker eq? symbol-hash))
-(define make-string-hash-table
- (make-hash-table-maker string=? string-hash))
-(define make-string-ci-hash-table
- (make-hash-table-maker string-ci=? string-ci-hash))
-(define make-integer-hash-table
- (make-hash-table-maker = modulo))
-
-(define (%hash-table-hash hash-table key)
- ((hash-table-hash-function hash-table)
- key (vector-length (hash-table-entries hash-table))))
-
-(define (%hash-table-find entries associate hash key)
- (associate key (vector-ref entries hash)))
-
-(define (%hash-table-add! entries hash key value)
- (vector-set! entries hash
- (cons (%make-hash-node key value)
- (vector-ref entries hash))))
-
-(define (%hash-table-delete! entries compare hash key)
- (let ((entrylist (vector-ref entries hash)))
- (cond ((null? entrylist) #f)
- ((compare key (caar entrylist))
- (vector-set! entries hash (cdr entrylist)) #t)
- (else
- (let loop ((current (cdr entrylist)) (previous entrylist))
- (cond ((null? current) #f)
- ((compare key (caar current))
- (set-cdr! previous (cdr current)) #t)
- (else (loop (cdr current) current))))))))
-
-(define (%hash-table-walk proc entries)
- (do ((index (- (vector-length entries) 1) (- index 1)))
- ((< index 0)) (for-each proc (vector-ref entries index))))
-
-(define (%hash-table-maybe-resize! hash-table)
- (let* ((old-entries (hash-table-entries hash-table))
- (hash-length (vector-length old-entries)))
- (if (> (hash-table-size hash-table) hash-length)
- (let* ((new-length (* 2 hash-length))
- (new-entries (make-vector new-length '()))
- (hash (hash-table-hash-function hash-table)))
- (%hash-table-walk
- (lambda (node)
- (%hash-table-add! new-entries
- (hash (%hash-node-key node) new-length)
- (%hash-node-key node) (%hash-node-value node)))
- old-entries)
- (hash-table-set-entries! hash-table new-entries)))))
-
-(define (hash-table-ref hash-table key . maybe-default)
- (cond ((%hash-table-find (hash-table-entries hash-table)
- (hash-table-association-function hash-table)
- (%hash-table-hash hash-table key) key)
- => %hash-node-value)
- ((null? maybe-default)
- (error "hash-table-ref: no value associated with" key))
- (else ((car maybe-default)))))
-
-(define (hash-table-ref/default hash-table key default)
- (hash-table-ref hash-table key (lambda () default)))
-
-(define (hash-table-set! hash-table key value)
- (let ((hash (%hash-table-hash hash-table key))
- (entries (hash-table-entries hash-table)))
- (cond ((%hash-table-find entries
- (hash-table-association-function hash-table)
- hash key)
- => (lambda (node) (%hash-node-set-value! node value)))
- (else (%hash-table-add! entries hash key value)
- (hash-table-set-size! hash-table
- (+ 1 (hash-table-size hash-table)))
- (%hash-table-maybe-resize! hash-table)))))
-
-(define (hash-table-update! hash-table key function . maybe-default)
- (let ((hash (%hash-table-hash hash-table key))
- (entries (hash-table-entries hash-table)))
- (cond ((%hash-table-find entries
- (hash-table-association-function hash-table)
- hash key)
- => (lambda (node)
- (%hash-node-set-value!
- node (function (%hash-node-value node)))))
- ((null? maybe-default)
- (error "hash-table-update!: no value exists for key" key))
- (else (%hash-table-add! entries hash key
- (function ((car maybe-default))))
- (hash-table-set-size! hash-table
- (+ 1 (hash-table-size hash-table)))
- (%hash-table-maybe-resize! hash-table)))))
-
-(define (hash-table-update!/default hash-table key function default)
- (hash-table-update! hash-table key function (lambda () default)))
-
-(define (hash-table-delete! hash-table key)
- (if (%hash-table-delete! (hash-table-entries hash-table)
- (hash-table-equivalence-function hash-table)
- (%hash-table-hash hash-table key) key)
- (hash-table-set-size! hash-table (- (hash-table-size hash-table) 1))))
-
-(define (hash-table-exists? hash-table key)
- (and (%hash-table-find (hash-table-entries hash-table)
- (hash-table-association-function hash-table)
- (%hash-table-hash hash-table key) key) #t))
-
-(define (hash-table-walk hash-table proc)
- (%hash-table-walk
- (lambda (node) (proc (%hash-node-key node) (%hash-node-value node)))
- (hash-table-entries hash-table)))
-
-(define (hash-table-fold hash-table f acc)
- (hash-table-walk hash-table
- (lambda (key value) (set! acc (f key value acc))))
- acc)
-
-(define (alist->hash-table alist . args)
- (let* ((comparison (if (null? args) equal? (car args)))
- (hash
- (if (or (null? args) (null? (cdr args)))
- (appropriate-hash-function-for comparison) (cadr args)))
- (size
- (if (or (null? args) (null? (cdr args)) (null? (cddr args)))
- (max *default-table-size* (* 2 (length alist))) (caddr args)))
- (hash-table (make-hash-table comparison hash size)))
- (for-each
- (lambda (elem)
- (hash-table-update!/default
- hash-table (car elem) (lambda (x) x) (cdr elem)))
- alist)
- hash-table))
-
-(define (hash-table->alist hash-table)
- (hash-table-fold hash-table
- (lambda (key val acc) (cons (cons key val) acc)) '()))
-
-(define (hash-table-copy hash-table)
- (let ((new (make-hash-table (hash-table-equivalence-function hash-table)
- (hash-table-hash-function hash-table)
- (max *default-table-size*
- (* 2 (hash-table-size hash-table))))))
- (hash-table-walk hash-table
- (lambda (key value) (hash-table-set! new key value)))
- new))
-
-(define (hash-table-merge! hash-table1 hash-table2)
- (hash-table-walk
- hash-table2
- (lambda (key value) (hash-table-set! hash-table1 key value)))
- hash-table1)
-
-(define (hash-table-keys hash-table)
- (hash-table-fold hash-table (lambda (key val acc) (cons key acc)) '()))
-
-(define (hash-table-values hash-table)
- (hash-table-fold hash-table (lambda (key val acc) (cons val acc)) '()))
-
-; eof
-;; Copyright 1991, 1994, 1998 William D Clinger
-;; Copyright 1998 Lars T Hansen
-;; Copyright 1984 - 1993 Lightship Software, Incorporated
-
-;; 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 following restriction: Any publication
-;; or redistribution of this software, whether on its own or
-;; incorporated into other software, must bear the above copyright
-;; notices and the following legend:
-
-;; The Twobit compiler and the Larceny runtime system were
-;; developed by William Clinger and Lars Hansen with the
-;; assistance of Lightship Software and the College of Computer
-;; Science of Northeastern University. This acknowledges that
-;; Clinger et al remain the sole copyright holders to Twobit
-;; and Larceny and that no rights pursuant to that status are
-;; waived or conveyed.
-
-;; Twobit and Larceny are provided as is. The user specifically
-;; acknowledges that Northeastern University, William Clinger, Lars
-;; Hansen, and Lightship Software have not made any representations
-;; or warranty with regard to performance of Twobit and Larceny,
-;; their merchantability, or fitness for a particular purpose. Users
-;; further acknowledge that they have had the opportunity to inspect
-;; Twobit and Larceny and will hold harmless Northeastern University,
-;; William Clinger, Lars Hansen, and Lightship Software from any cost,
-;; liability, or expense arising from, or in any way related to the
-;; use of this software.
-
-(define-library (r6rs hashtables)
-
- (export
-
- make-eq-hashtable
- make-eqv-hashtable
- make-hashtable
- hashtable?
- hashtable-size
- hashtable-ref
- hashtable-set!
- hashtable-delete!
- hashtable-contains?
- hashtable-update!
- hashtable-copy
- hashtable-clear!
- hashtable-keys
- hashtable-entries
- hashtable-equivalence-function
- hashtable-hash-function
- hashtable-mutable?
- equal-hash
- string-hash
- string-ci-hash
- (rename r6rs:symbol-hash symbol-hash) ; see explanation below
- )
-
- (import (scheme base)
- (scheme cxr))
-
- ;; Hashing on inexact and complex numbers depends on whether the
- ;; (scheme inexact) and (scheme complex) libraries are available.
-
- (cond-expand
-
- ((library (rnrs hashtables))) ; nothing to do
-
- ((library (scheme inexact))
- (import (scheme inexact))
- (begin
- (define (inexact-hash x)
- (cond ((finite? x)
- (hash-for-eqv (exact x)))
- ((infinite? x)
- (if (> x 0.0)
- hash:infinity+
- hash:infinity-))
- (else
- hash:nan)))))
-
- (else
- (begin
- (define (inexact-hash x) 0))))
-
- (cond-expand
-
- ((and (library (rnrs hashtables))
- (not (library (r6rs no-rnrs))))
- ;; nothing to do
- )
-
- ((library (scheme complex))
- (import (scheme complex))
- (begin
- (define (complex-hash z)
- (+ (hash-for-eqv (real-part z))
- (hash-for-eqv (imag-part z))))))
-
- (else
- (begin
- (define (complex-hash z) 0))))
-
- ;; If the (rnrs hashtables) library is available, import it.
- ;; Otherwise use SRFI 69 if it's available.
- ;; If SRFI 69 isn't available, use its reference implementation.
- ;;
- ;; The (r6rs hashtables) library must export symbol-hash, which
- ;; has no equivalent among the procedures specified by SRFI 69.
- ;; The SRFI 69 reference implementation does define symbol-hash,
- ;; however, which has led to the current situation in which some
- ;; implementations of (srfi 69) export symbol-hash but others
- ;; don't. The R7RS says it's an error to import symbol-hash
- ;; more than once with different bindings, or to redefine it
- ;; if it's been imported, so this (r6rs hashtables) library
- ;; defines r6rs:symbol-hash and renames it to symbol-hash only
- ;; when it's exported.
-
- (cond-expand
-
- ((and (library (rnrs hashtables))
- (not (library (r6rs no-rnrs))))
- (import (rnrs hashtables))
- (begin (define r6rs:symbol-hash symbol-hash)))
-
- ((library (srfi 69 basic-hash-tables))
- (import (srfi 69 basic-hash-tables))
- (include "hashtables.atop69.scm"))
-
- ((library (srfi 69))
- (import (srfi 69))
- (include "hashtables.atop69.scm"))
-
- ((library (srfi 69 basic-hash-tables))
- (import (srfi 69 basic-hash-tables))
- (include "hashtables.atop69.scm"))
-
- ((library (srfi 69))
- (import (srfi 69))
- (include "hashtables.atop69.scm"))
-
- ((library (scheme char))
- (import (scheme char))
- (include "hashtables.body69.scm")
- (include "hashtables.atop69.scm"))
-
- (else
- (begin (define (string-foldcase s) s)
- (define (string-ci=? s1 s2)
- (string=? s1 s2)))
- (include "hashtables.body69.scm")
- (include "hashtables.atop69.scm")))
-
- )
-(define make-eq-hashtable
- (case-lambda
- (() (make-eq-hashtable #f #f))
- ((capacity) (make-eq-hashtable capacity #f))
- ((capacity weakness)
- (when weakness
- (error "No weak or ephemeral hashtables supported."))
- (if capacity
- (rnrs-make-eq-hashtable capacity)
- (rnrs-make-eq-hashtable)))))
-
-(define make-eqv-hashtable
- (case-lambda
- (() (make-eqv-hashtable #f #f))
- ((capacity) (make-eqv-hashtable capacity #f))
- ((capacity weakness)
- (when weakness
- (error "No weak or ephemeral hashtables supported."))
- (if capacity
- (rnrs-make-eqv-hashtable capacity)
- (rnrs-make-eqv-hashtable)))))
-
-(define make-hashtable
- (case-lambda
- ((hash equiv) (make-hashtable hash equiv #f #f))
- ((hash equiv capacity) (make-hashtable hash equiv capacity #f))
- ((hash equiv capacity weakness)
- (cond
- ((and (not hash) (eq? equiv eq?))
- (make-eq-hashtable capacity weakness))
- ((and (not hash) (eq? equiv eqv?))
- (make-eqv-hashtable capacity weakness))
- (else
- (when weakness
- (error "No weak or ephemeral hashtables supported."))
- (let ((hash (if (pair? hash)
- (car hash)
- hash)))
- (if capacity
- (rnrs-make-hashtable hash equiv capacity)
- (rnrs-make-hashtable hash equiv))))))))
-
-(define (alist->eq-hashtable . args)
- (apply alist->hashtable #f eq? args))
-
-(define (alist->eqv-hashtable . args)
- (apply alist->hashtable #f eqv? args))
-
-(define alist->hashtable
- (case-lambda
- ((hash equiv alist)
- (alist->hashtable hash equiv #f #f alist))
- ((hash equiv capacity alist)
- (alist->hashtable hash equiv capacity #f alist))
- ((hash equiv capacity weakness alist)
- (let ((hashtable (make-hashtable hash equiv capacity weakness)))
- (for-each (lambda (entry)
- (hashtable-set! hashtable (car entry) (cdr entry)))
- (reverse alist))
- hashtable))))
-
-(define-enumeration weakness
- (weak-key
- weak-value
- weak-key-and-value
- ephemeral-key
- ephemeral-value
- ephemeral-key-and-value)
- weakness-set)
-
-(define hashtable? rnrs-hashtable?)
-
-(define hashtable-size rnrs-hashtable-size)
-
-(define nil (cons #f #f))
-(define (nil? obj) (eq? obj nil))
-
-(define hashtable-ref
- (case-lambda
- ((hashtable key)
- (let ((value (rnrs-hashtable-ref hashtable key nil)))
- (if (nil? value)
- (error "No such key in hashtable." hashtable key)
- value)))
- ((hashtable key default)
- (rnrs-hashtable-ref hashtable key default))))
-
-(define hashtable-set! rnrs-hashtable-set!)
-
-(define hashtable-delete! rnrs-hashtable-delete!)
-
-(define hashtable-contains? rnrs-hashtable-contains?)
-
-(define (hashtable-lookup hashtable key)
- (let ((value (rnrs-hashtable-ref hashtable key nil)))
- (if (nil? value)
- (values #f #f)
- (values value #t))))
-
-(define hashtable-update!
- (case-lambda
- ((hashtable key proc) (hashtable-update! hashtable key proc nil))
- ((hashtable key proc default)
- (rnrs-hashtable-update!
- hashtable key
- (lambda (value)
- (if (nil? value)
- (error "No such key in hashtable." hashtable key)
- (proc value)))
- default))))
-
-;;; XXX This could be implemented at the platform level to eliminate the second
-;;; lookup for the key.
-(define (hashtable-intern! hashtable key default-proc)
- (let ((value (rnrs-hashtable-ref hashtable key nil)))
- (if (nil? value)
- (let ((value (default-proc)))
- (hashtable-set! hashtable key value)
- value)
- value)))
-
-(define hashtable-copy
- (case-lambda
- ((hashtable) (hashtable-copy hashtable #f #f))
- ((hashtable mutable) (hashtable-copy hashtable mutable #f))
- ((hashtable mutable weakness)
- (when weakness
- (error "No weak or ephemeral tables supported."))
- (rnrs-hashtable-copy hashtable mutable))))
-
-(define hashtable-clear!
- (case-lambda
- ((hashtable) (rnrs-hashtable-clear! hashtable))
- ((hashtable capacity)
- (if capacity
- (rnrs-hashtable-clear! hashtable capacity)
- (rnrs-hashtable-clear! hashtable)))))
-
-(define hashtable-empty-copy
- (case-lambda
- ((hashtable) (hashtable-empty-copy hashtable #f))
- ((hashtable capacity)
- (make-hashtable (hashtable-hash-function hashtable)
- (hashtable-equivalence-function hashtable)
- (if (eq? #t capacity)
- (hashtable-size hashtable)
- capacity)
- (hashtable-weakness hashtable)))))
-
-(define hashtable-keys rnrs-hashtable-keys)
-
-(define (hashtable-values hashtable)
- (let-values (((keys values) (rnrs-hashtable-entries hashtable)))
- values))
-
-(define hashtable-entries rnrs-hashtable-entries)
-
-(define (hashtable-key-list hashtable)
- (hashtable-map->lset hashtable (lambda (key value) key)))
-
-(define (hashtable-value-list hashtable)
- (hashtable-map->lset hashtable (lambda (key value) value)))
-
-(define (hashtable-entry-lists hashtable)
- (let ((keys '())
- (vals '()))
- (hashtable-walk hashtable
- (lambda (key val)
- (set! keys (cons key keys))
- (set! vals (cons val vals))))
- (values keys vals)))
-
-;;; XXX The procedures hashtable-walk, hashtable-update-all!, hashtable-prune!,
-;;; and hashtable-sum should be implemented more efficiently at the platform
-;;; level. In particular, they should not allocate intermediate vectors or
-;;; lists to hold the keys or values that are being operated on.
-
-(define (hashtable-walk hashtable proc)
- (let-values (((keys values) (rnrs-hashtable-entries hashtable)))
- (vector-for-each proc keys values)))
-
-(define (hashtable-update-all! hashtable proc)
- (let-values (((keys values) (hashtable-entries hashtable)))
- (vector-for-each (lambda (key value)
- (hashtable-set! hashtable key (proc key value)))
- keys values)))
-
-(define (hashtable-prune! hashtable proc)
- (let-values (((keys values) (hashtable-entries hashtable)))
- (vector-for-each (lambda (key value)
- (when (proc key value)
- (hashtable-delete! hashtable key)))
- keys values)))
-
-(define (hashtable-merge! hashtable-dest hashtable-source)
- (hashtable-walk hashtable-source
- (lambda (key value)
- (hashtable-set! hashtable-dest key value)))
- hashtable-dest)
-
-(define (hashtable-sum hashtable init proc)
- (let-values (((keys vals) (hashtable-entry-lists hashtable)))
- (fold proc init keys vals)))
-
-(define (hashtable-map->lset hashtable proc)
- (hashtable-sum hashtable '()
- (lambda (key value accumulator)
- (cons (proc key value) accumulator))))
-
-;;; XXX If available, let-escape-continuation might be more efficient than
-;;; call/cc here.
-(define (hashtable-find hashtable proc)
- (call/cc
- (lambda (return)
- (hashtable-walk hashtable
- (lambda (key value)
- (when (proc key value)
- (return key value #t))))
- (return #f #f #f))))
-
-(define (hashtable-empty? hashtable)
- (zero? (hashtable-size hashtable)))
-
-;;; XXX A platform-level implementation could avoid allocating the constant true
-;;; function and the lookup for the key in the delete operation.
-(define (hashtable-pop! hashtable)
- (if (hashtable-empty? hashtable)
- (error "Cannot pop from empty hashtable." hashtable)
- (let-values (((key value found?)
- (hashtable-find hashtable (lambda (k v) #t))))
- (hashtable-delete! hashtable key)
- (values key value))))
-
-(define hashtable-inc!
- (case-lambda
- ((hashtable key) (hashtable-inc! hashtable key 1))
- ((hashtable key number)
- (hashtable-update! hashtable key (lambda (v) (+ v number)) 0))))
-
-(define hashtable-dec!
- (case-lambda
- ((hashtable key) (hashtable-dec! hashtable key 1))
- ((hashtable key number)
- (hashtable-update! hashtable key (lambda (v) (- v number)) 0))))
-
-(define hashtable-equivalence-function rnrs-hashtable-equivalence-function)
-
-(define hashtable-hash-function rnrs-hashtable-hash-function)
-
-(define (hashtable-weakness hashtable) #f)
-
-(define hashtable-mutable? rnrs-hashtable-mutable?)
-
-(define *hash-salt*
- (let ((seed (get-environment-variable "SRFI_126_HASH_SEED")))
- (if (or (not seed) (string=? seed ""))
- (random-integer (greatest-fixnum))
- (modulo
- (fold (lambda (char result)
- (+ (char->integer char) result))
- 0
- (string->list seed))
- (greatest-fixnum)))))
-
-(define (hash-salt) *hash-salt*)
-
-(define equal-hash rnrs-equal-hash)
-
-(define string-hash rnrs-string-hash)
-
-(define string-ci-hash rnrs-string-ci-hash)
-
-(define symbol-hash rnrs-symbol-hash)
-
-;; Local Variables:
-;; eval: (put 'hashtable-walk 'scheme-indent-function 1)
-;; eval: (put 'hashtable-update-all! 'scheme-indent-function 1)
-;; eval: (put 'hashtable-prune! 'scheme-indent-function 1)
-;; eval: (put 'hashtable-sum 'scheme-indent-function 2)
-;; eval: (put 'hashtable-map->lset 'scheme-indent-function 1)
-;; eval: (put 'hashtable-find 'scheme-indent-function 1)
-;; End:
-(define-library (srfi 126)
- (export
- make-eq-hashtable make-eqv-hashtable make-hashtable
- alist->eq-hashtable alist->eqv-hashtable alist->hashtable
- weakness
- hashtable?
- hashtable-size
- hashtable-ref hashtable-set! hashtable-delete!
- hashtable-contains?
- hashtable-lookup hashtable-update! hashtable-intern!
- hashtable-copy hashtable-clear! hashtable-empty-copy
- hashtable-keys hashtable-values hashtable-entries
- hashtable-key-list hashtable-value-list hashtable-entry-lists
- hashtable-walk hashtable-update-all! hashtable-prune! hashtable-merge!
- hashtable-sum hashtable-map->lset hashtable-find
- hashtable-empty? hashtable-pop! hashtable-inc! hashtable-dec!
- hashtable-equivalence-function hashtable-hash-function hashtable-weakness
- hashtable-mutable?
- hash-salt equal-hash string-hash string-ci-hash symbol-hash)
- (import
- (scheme base)
- (scheme case-lambda)
- (scheme process-context)
- (r6rs enums)
- (prefix (r6rs hashtables) rnrs-)
- (srfi 1)
- (srfi 27))
- (begin
-
- ;; Smallest allowed in R6RS.
- (define (greatest-fixnum) (expt 23 2))
-
- ;; INCLUDE 126.body.scm
-(define make-eq-hashtable
- (case-lambda
- (() (make-eq-hashtable #f #f))
- ((capacity) (make-eq-hashtable capacity #f))
- ((capacity weakness)
- (when weakness
- (error "No weak or ephemeral hashtables supported."))
- (if capacity
- (rnrs-make-eq-hashtable capacity)
- (rnrs-make-eq-hashtable)))))
-
-(define make-eqv-hashtable
- (case-lambda
- (() (make-eqv-hashtable #f #f))
- ((capacity) (make-eqv-hashtable capacity #f))
- ((capacity weakness)
- (when weakness
- (error "No weak or ephemeral hashtables supported."))
- (if capacity
- (rnrs-make-eqv-hashtable capacity)
- (rnrs-make-eqv-hashtable)))))
-
-(define make-hashtable
- (case-lambda
- ((hash equiv) (make-hashtable hash equiv #f #f))
- ((hash equiv capacity) (make-hashtable hash equiv capacity #f))
- ((hash equiv capacity weakness)
- (cond
- ((and (not hash) (eq? equiv eq?))
- (make-eq-hashtable capacity weakness))
- ((and (not hash) (eq? equiv eqv?))
- (make-eqv-hashtable capacity weakness))
- (else
- (when weakness
- (error "No weak or ephemeral hashtables supported."))
- (let ((hash (if (pair? hash)
- (car hash)
- hash)))
- (if capacity
- (rnrs-make-hashtable hash equiv capacity)
- (rnrs-make-hashtable hash equiv))))))))
-
-(define (alist->eq-hashtable . args)
- (apply alist->hashtable #f eq? args))
-
-(define (alist->eqv-hashtable . args)
- (apply alist->hashtable #f eqv? args))
-
-(define alist->hashtable
- (case-lambda
- ((hash equiv alist)
- (alist->hashtable hash equiv #f #f alist))
- ((hash equiv capacity alist)
- (alist->hashtable hash equiv capacity #f alist))
- ((hash equiv capacity weakness alist)
- (let ((hashtable (make-hashtable hash equiv capacity weakness)))
- (for-each (lambda (entry)
- (hashtable-set! hashtable (car entry) (cdr entry)))
- (reverse alist))
- hashtable))))
-
-(define-enumeration weakness
- (weak-key
- weak-value
- weak-key-and-value
- ephemeral-key
- ephemeral-value
- ephemeral-key-and-value)
- weakness-set)
-
-(define hashtable? rnrs-hashtable?)
-
-(define hashtable-size rnrs-hashtable-size)
-
-(define nil (cons #f #f))
-(define (nil? obj) (eq? obj nil))
-
-(define hashtable-ref
- (case-lambda
- ((hashtable key)
- (let ((value (rnrs-hashtable-ref hashtable key nil)))
- (if (nil? value)
- (error "No such key in hashtable." hashtable key)
- value)))
- ((hashtable key default)
- (rnrs-hashtable-ref hashtable key default))))
-
-(define hashtable-set! rnrs-hashtable-set!)
-
-(define hashtable-delete! rnrs-hashtable-delete!)
-
-(define hashtable-contains? rnrs-hashtable-contains?)
-
-(define (hashtable-lookup hashtable key)
- (let ((value (rnrs-hashtable-ref hashtable key nil)))
- (if (nil? value)
- (values #f #f)
- (values value #t))))
-
-(define hashtable-update!
- (case-lambda
- ((hashtable key proc) (hashtable-update! hashtable key proc nil))
- ((hashtable key proc default)
- (rnrs-hashtable-update!
- hashtable key
- (lambda (value)
- (if (nil? value)
- (error "No such key in hashtable." hashtable key)
- (proc value)))
- default))))
-
-;;; XXX This could be implemented at the platform level to eliminate the second
-;;; lookup for the key.
-(define (hashtable-intern! hashtable key default-proc)
- (let ((value (rnrs-hashtable-ref hashtable key nil)))
- (if (nil? value)
- (let ((value (default-proc)))
- (hashtable-set! hashtable key value)
- value)
- value)))
-
-(define hashtable-copy
- (case-lambda
- ((hashtable) (hashtable-copy hashtable #f #f))
- ((hashtable mutable) (hashtable-copy hashtable mutable #f))
- ((hashtable mutable weakness)
- (when weakness
- (error "No weak or ephemeral tables supported."))
- (rnrs-hashtable-copy hashtable mutable))))
-
-(define hashtable-clear!
- (case-lambda
- ((hashtable) (rnrs-hashtable-clear! hashtable))
- ((hashtable capacity)
- (if capacity
- (rnrs-hashtable-clear! hashtable capacity)
- (rnrs-hashtable-clear! hashtable)))))
-
-(define hashtable-empty-copy
- (case-lambda
- ((hashtable) (hashtable-empty-copy hashtable #f))
- ((hashtable capacity)
- (make-hashtable (hashtable-hash-function hashtable)
- (hashtable-equivalence-function hashtable)
- (if (eq? #t capacity)
- (hashtable-size hashtable)
- capacity)
- (hashtable-weakness hashtable)))))
-
-(define hashtable-keys rnrs-hashtable-keys)
-
-(define (hashtable-values hashtable)
- (let-values (((keys values) (rnrs-hashtable-entries hashtable)))
- values))
-
-(define hashtable-entries rnrs-hashtable-entries)
-
-(define (hashtable-key-list hashtable)
- (hashtable-map->lset hashtable (lambda (key value) key)))
-
-(define (hashtable-value-list hashtable)
- (hashtable-map->lset hashtable (lambda (key value) value)))
-
-(define (hashtable-entry-lists hashtable)
- (let ((keys '())
- (vals '()))
- (hashtable-walk hashtable
- (lambda (key val)
- (set! keys (cons key keys))
- (set! vals (cons val vals))))
- (values keys vals)))
-
-;;; XXX The procedures hashtable-walk, hashtable-update-all!, hashtable-prune!,
-;;; and hashtable-sum should be implemented more efficiently at the platform
-;;; level. In particular, they should not allocate intermediate vectors or
-;;; lists to hold the keys or values that are being operated on.
-
-(define (hashtable-walk hashtable proc)
- (let-values (((keys values) (rnrs-hashtable-entries hashtable)))
- (vector-for-each proc keys values)))
-
-(define (hashtable-update-all! hashtable proc)
- (let-values (((keys values) (hashtable-entries hashtable)))
- (vector-for-each (lambda (key value)
- (hashtable-set! hashtable key (proc key value)))
- keys values)))
-
-(define (hashtable-prune! hashtable proc)
- (let-values (((keys values) (hashtable-entries hashtable)))
- (vector-for-each (lambda (key value)
- (when (proc key value)
- (hashtable-delete! hashtable key)))
- keys values)))
-
-(define (hashtable-merge! hashtable-dest hashtable-source)
- (hashtable-walk hashtable-source
- (lambda (key value)
- (hashtable-set! hashtable-dest key value)))
- hashtable-dest)
-
-(define (hashtable-sum hashtable init proc)
- (let-values (((keys vals) (hashtable-entry-lists hashtable)))
- (fold proc init keys vals)))
-
-(define (hashtable-map->lset hashtable proc)
- (hashtable-sum hashtable '()
- (lambda (key value accumulator)
- (cons (proc key value) accumulator))))
-
-;;; XXX If available, let-escape-continuation might be more efficient than
-;;; call/cc here.
-(define (hashtable-find hashtable proc)
- (call/cc
- (lambda (return)
- (hashtable-walk hashtable
- (lambda (key value)
- (when (proc key value)
- (return key value #t))))
- (return #f #f #f))))
-
-(define (hashtable-empty? hashtable)
- (zero? (hashtable-size hashtable)))
-
-;;; XXX A platform-level implementation could avoid allocating the constant true
-;;; function and the lookup for the key in the delete operation.
-(define (hashtable-pop! hashtable)
- (if (hashtable-empty? hashtable)
- (error "Cannot pop from empty hashtable." hashtable)
- (let-values (((key value found?)
- (hashtable-find hashtable (lambda (k v) #t))))
- (hashtable-delete! hashtable key)
- (values key value))))
-
-(define hashtable-inc!
- (case-lambda
- ((hashtable key) (hashtable-inc! hashtable key 1))
- ((hashtable key number)
- (hashtable-update! hashtable key (lambda (v) (+ v number)) 0))))
-
-(define hashtable-dec!
- (case-lambda
- ((hashtable key) (hashtable-dec! hashtable key 1))
- ((hashtable key number)
- (hashtable-update! hashtable key (lambda (v) (- v number)) 0))))
-
-(define hashtable-equivalence-function rnrs-hashtable-equivalence-function)
-
-(define hashtable-hash-function rnrs-hashtable-hash-function)
-
-(define (hashtable-weakness hashtable) #f)
-
-(define hashtable-mutable? rnrs-hashtable-mutable?)
-
-(define *hash-salt*
- (let ((seed (get-environment-variable "SRFI_126_HASH_SEED")))
- (if (or (not seed) (string=? seed ""))
- (random-integer (greatest-fixnum))
- (modulo
- (fold (lambda (char result)
- (+ (char->integer char) result))
- 0
- (string->list seed))
- (greatest-fixnum)))))
-
-(define (hash-salt) *hash-salt*)
-
-(define equal-hash rnrs-equal-hash)
-
-(define string-hash rnrs-string-hash)
-
-(define string-ci-hash rnrs-string-ci-hash)
-
-(define symbol-hash rnrs-symbol-hash)
-
-;; Local Variables:
-;; eval: (put 'hashtable-walk 'scheme-indent-function 1)
-;; eval: (put 'hashtable-update-all! 'scheme-indent-function 1)
-;; eval: (put 'hashtable-prune! 'scheme-indent-function 1)
-;; eval: (put 'hashtable-sum 'scheme-indent-function 2)
-;; eval: (put 'hashtable-map->lset 'scheme-indent-function 1)
-;; eval: (put 'hashtable-find 'scheme-indent-function 1)
-;; End:
-
- ))
-;;; Guile implementation.
-
-(define-module (srfi srfi-126))
-
-(use-modules
- (srfi srfi-1)
- (srfi srfi-9)
- (srfi srfi-9 gnu)
- (srfi srfi-11)
- (ice-9 hash-table)
- (ice-9 control)
- ((rnrs hashtables) #\select
- (equal-hash string-hash string-ci-hash symbol-hash)))
-
-(export
- make-eq-hashtable make-eqv-hashtable make-hashtable
- alist->eq-hashtable alist->eqv-hashtable alist->hashtable
- weakness
- hashtable? hashtable-size
- hashtable-ref hashtable-set! hashtable-delete! hashtable-contains?
- hashtable-lookup hashtable-update! hashtable-intern!
- hashtable-copy hashtable-clear! hashtable-empty-copy
- hashtable-keys hashtable-values hashtable-entries
- hashtable-key-list hashtable-value-list hashtable-entry-lists
- hashtable-walk hashtable-update-all! hashtable-prune! hashtable-merge!
- hashtable-sum hashtable-map->lset hashtable-find
- hashtable-empty? hashtable-pop! hashtable-inc! hashtable-dec!
- hashtable-equivalence-function hashtable-hash-function
- hashtable-weakness hashtable-mutable?
- hash-salt
- )
-
-(re-export equal-hash string-hash string-ci-hash symbol-hash)
-
-(define-record-type <hashtable>
- (%make-hashtable %table %hash %assoc hash equiv weakness mutable)
- hashtable?
- (%table %hashtable-table)
- (%hash %hashtable-hash)
- (%assoc %hashtable-assoc)
- (hash hashtable-hash-function)
- (equiv hashtable-equivalence-function)
- (weakness hashtable-weakness)
- (mutable hashtable-mutable? %hashtable-set-mutable!))
-
-(define nil (cons #f #f))
-(define (nil? obj) (eq? obj nil))
-
-(define (make-table capacity weakness)
- (let ((capacity (or capacity 32)))
- (case weakness
- ((#f) (make-hash-table capacity))
- ((weak-key) (make-weak-key-hash-table capacity))
- ((weak-value) (make-weak-value-hash-table capacity))
- ((weak-key-and-value) (make-doubly-weak-hash-table capacity))
- (else (error "Hashtable weakness not supported." weakness)))))
-
-(define* (make-eq-hashtable #\optional capacity weakness)
- (let ((table (make-table capacity weakness)))
- (%make-hashtable table hashq assq #f eq? weakness #t)))
-
-(define* (make-eqv-hashtable #\optional capacity weakness)
- (let ((table (make-table capacity weakness)))
- (%make-hashtable table hashv assv #f eqv? weakness #t)))
-
-(define* (make-hashtable hash equiv #\optional capacity weakness)
- (cond
- ((and (not hash) (eq? equiv eq?))
- (make-eq-hashtable capacity weakness))
- ((and (not hash) (eq? equiv eqv?))
- (make-eqv-hashtable capacity weakness))
- (else
- (let* ((table (make-table capacity weakness))
- (hash (if (pair? hash)
- (car hash)
- hash))
- (%hash (lambda (obj bound)
- (modulo (hash obj) bound)))
- (assoc (lambda (key alist)
- (find (lambda (entry)
- (equiv (car entry) key))
- alist))))
- (%make-hashtable table %hash assoc hash equiv weakness #t)))))
-
-(define (alist->eq-hashtable . args)
- (apply alist->hashtable #f eq? args))
-
-(define (alist->eqv-hashtable . args)
- (apply alist->hashtable #f eqv? args))
-
-(define alist->hashtable
- (case-lambda
- ((hash equiv alist)
- (alist->hashtable hash equiv #f #f alist))
- ((hash equiv capacity alist)
- (alist->hashtable hash equiv capacity #f alist))
- ((hash equiv capacity weakness alist)
- (let ((ht (make-hashtable hash equiv capacity weakness)))
- (for-each (lambda (entry)
- (hashtable-set! ht (car entry) (cdr entry)))
- (reverse alist))
- ht))))
-
-(define-syntax weakness
- (lambda (stx)
- (syntax-case stx ()
- ((_ <sym>)
- (let ((sym (syntax->datum #'<sym>)))
- (case sym
- ((weak-key weak-value weak-key-and-value ephemeral-key
- ephemeral-value ephemeral-key-and-value)
- #''sym)
- (else
- (error "Bad weakness symbol." sym))))))))
-
-(define (hashtable-size ht)
- (hash-count (const #t) (%hashtable-table ht)))
-
-(define* (%hashtable-ref ht key default)
- (hashx-ref (%hashtable-hash ht) (%hashtable-assoc ht)
- (%hashtable-table ht) key default))
-
-(define* (hashtable-ref ht key #\optional (default nil))
- (let ((val (%hashtable-ref ht key default)))
- (if (nil? val)
- (error "No association for key in hashtable." key ht)
- val)))
-
-(define (assert-mutable ht)
- (when (not (hashtable-mutable? ht))
- (error "Hashtable is immutable." ht)))
-
-(define (hashtable-set! ht key value)
- (assert-mutable ht)
- (hashx-set! (%hashtable-hash ht) (%hashtable-assoc ht)
- (%hashtable-table ht) key value)
- *unspecified*)
-
-(define (hashtable-delete! ht key)
- (assert-mutable ht)
- (hashx-remove! (%hashtable-hash ht) (%hashtable-assoc ht)
- (%hashtable-table ht) key)
- *unspecified*)
-
-(define (hashtable-contains? ht key)
- (not (nil? (%hashtable-ref ht key nil))))
-
-(define (hashtable-lookup ht key)
- (let ((val (%hashtable-ref ht key nil)))
- (if (nil? val)
- (values #f #f)
- (values val #t))))
-
-(define* (hashtable-update! ht key updater #\optional (default nil))
- (assert-mutable ht)
- (let ((handle (hashx-create-handle!
- (%hashtable-hash ht) (%hashtable-assoc ht)
- (%hashtable-table ht) key nil)))
- (if (eq? nil (cdr handle))
- (if (nil? default)
- (error "No association for key in hashtable." key ht)
- (set-cdr! handle (updater default)))
- (set-cdr! handle (updater (cdr handle))))
- (cdr handle)))
-
-(define (hashtable-intern! ht key default-proc)
- (assert-mutable ht)
- (let ((handle (hashx-create-handle!
- (%hashtable-hash ht) (%hashtable-assoc ht)
- (%hashtable-table ht) key nil)))
- (when (nil? (cdr handle))
- (set-cdr! handle (default-proc)))
- (cdr handle)))
-
-(define* (hashtable-copy ht #\optional mutable weakness)
- (let ((copy (hashtable-empty-copy ht (hashtable-size ht) weakness)))
- (hashtable-walk ht
- (lambda (k v)
- (hashtable-set! copy k v)))
- (%hashtable-set-mutable! copy mutable)
- copy))
-
-(define* (hashtable-clear! ht #\optional _capacity)
- (assert-mutable ht)
- (hash-clear! (%hashtable-table ht))
- *unspecified*)
-
-(define* (hashtable-empty-copy ht #\optional capacity weakness)
- (make-hashtable (hashtable-hash-function ht)
- (hashtable-equivalence-function ht)
- (case capacity
- ((#f) #f)
- ((#t) (hashtable-size ht))
- (else capacity))
- (or weakness (hashtable-weakness ht))))
-
-(define (hashtable-keys ht)
- (let ((keys (make-vector (hashtable-size ht))))
- (hashtable-sum ht 0
- (lambda (k v i)
- (vector-set! keys i k)
- (+ i 1)))
- keys))
-
-(define (hashtable-values ht)
- (let ((vals (make-vector (hashtable-size ht))))
- (hashtable-sum ht 0
- (lambda (k v i)
- (vector-set! vals i v)
- (+ i 1)))
- vals))
-
-(define (hashtable-entries ht)
- (let ((keys (make-vector (hashtable-size ht)))
- (vals (make-vector (hashtable-size ht))))
- (hashtable-sum ht 0
- (lambda (k v i)
- (vector-set! keys i k)
- (vector-set! vals i v)
- (+ i 1)))
- (values keys vals)))
-
-(define (hashtable-key-list ht)
- (hashtable-map->lset ht (lambda (k v) k)))
-
-(define (hashtable-value-list ht)
- (hashtable-map->lset ht (lambda (k v) v)))
-
-(define (hashtable-entry-lists ht)
- (let ((keys&vals (cons '() '())))
- (hashtable-walk ht
- (lambda (k v)
- (set-car! keys&vals (cons k (car keys&vals)))
- (set-cdr! keys&vals (cons v (cdr keys&vals)))))
- (car+cdr keys&vals)))
-
-(define (hashtable-walk ht proc)
- (hash-for-each proc (%hashtable-table ht)))
-
-(define (hashtable-update-all! ht proc)
- (assert-mutable ht)
- (hash-for-each-handle
- (lambda (handle)
- (set-cdr! handle (proc (car handle) (cdr handle))))
- (%hashtable-table ht)))
-
-(define (hashtable-prune! ht pred)
- (assert-mutable ht)
- (let ((keys (hashtable-sum ht '()
- (lambda (k v keys-to-delete)
- (if (pred k v)
- (cons k keys-to-delete)
- keys-to-delete)))))
- (for-each (lambda (k)
- (hashtable-delete! ht k))
- keys)))
-
-(define (hashtable-merge! ht-dest ht-src)
- (assert-mutable ht-dest)
- (hashtable-walk ht-src
- (lambda (k v)
- (hashtable-set! ht-dest k v)))
- ht-dest)
-
-(define (hashtable-sum ht init proc)
- (hash-fold proc init (%hashtable-table ht)))
-
-(define (hashtable-map->lset ht proc)
- (hash-map->list proc (%hashtable-table ht)))
-
-(define (hashtable-find ht pred)
- (let/ec return
- (hashtable-walk ht
- (lambda (k v)
- (when (pred k v)
- (return k v #t))))
- (return #f #f #f)))
-
-(define (hashtable-empty? ht)
- (zero? (hashtable-size ht)))
-
-(define (hashtable-pop! ht)
- (assert-mutable ht)
- (when (hashtable-empty? ht)
- (error "Cannot pop from empty hashtable." ht))
- (let-values (((k v found?) (hashtable-find ht (const #t))))
- (hashtable-delete! ht k)
- (values k v)))
-
-(define* (hashtable-inc! ht k #\optional (x 1))
- (assert-mutable ht)
- (hashtable-update! ht k (lambda (v) (+ v x)) 0))
-
-(define* (hashtable-dec! ht k #\optional (x 1))
- (assert-mutable ht)
- (hashtable-update! ht k (lambda (v) (- v x)) 0))
-
-(define (hash-salt) 0)
-
-(set-record-type-printer!
- <hashtable>
- (lambda (ht port)
- (with-output-to-port port
- (lambda ()
- (let ((equal-hash (@ (rnrs hashtables) equal-hash))
- (string-hash (@ (rnrs hashtables) string-hash))
- (string-ci-hash (@ (rnrs hashtables) string-ci-hash))
- (symbol-hash (@ (rnrs hashtables) symbol-hash))
- (hash (hashtable-hash-function ht))
- (equiv (hashtable-equivalence-function ht))
- (alist (hashtable-map->lset ht cons)))
- (cond
- ((and (not hash) (eq? equiv eq?))
- (display "#hasheq")
- (display alist))
- ((and (not hash) (eq? equiv eqv?))
- (display "#hasheqv")
- (display alist))
- (else
- (display "#hash")
- (cond
- ((and (eq? hash (@ (rnrs hashtables) equal-hash)) (eq? equiv equal?))
- (display alist))
- ((and (eq? hash (@ (rnrs hashtables) string-hash)) (eq? equiv string=?))
- (display (cons 'string alist)))
- ((and (eq? hash string-ci-hash) (eq? equiv string-ci=?))
- (display (cons 'string-ci alist)))
- ((and (eq? hash symbol-hash) (eq? equiv eq?))
- (display (cons 'symbol alist)))
- (else
- (display (cons 'custom alist)))))))))))
-
-(read-hash-extend
- #\h
- (lambda (char port)
- (with-input-from-port port
- (lambda ()
- (let ((equal-hash (@ (rnrs hashtables) equal-hash))
- (string-hash (@ (rnrs hashtables) string-hash))
- (string-ci-hash (@ (rnrs hashtables) string-ci-hash))
- (symbol-hash (@ (rnrs hashtables) symbol-hash))
- (type (string-append "h" (symbol->string (read))))
- (alist (read)))
- (cond
- ((string=? type "hasheq")
- (alist->eq-hashtable alist))
- ((string=? type "hasheqv")
- (alist->eqv-hashtable alist))
- (else
- (when (not (string=? type "hash"))
- (error "Unrecognized hash type." type))
- (let* ((has-tag? (symbol? (car alist)))
- (subtype (if has-tag?
- (car alist)
- "equal"))
- (alist (if has-tag?
- (cdr alist)
- alist)))
- (cond
- ((string=? subtype "equal")
- (alist->hashtable equal-hash equal? alist))
- ((string=? subtype "string")
- (alist->hashtable string-hash string=? alist))
- ((string=? subtype "string-ci")
- (alist->hashtable string-ci-hash string-ci=? alist))
- ((string=? subtype "symbol")
- (alist->hashtable symbol-hash eq? alist))
- (else
- (error "Unrecognized hash subtype." subtype)))))))))))
-
-;; Local Variables:
-;; eval: (put 'hashtable-walk 'scheme-indent-function 1)
-;; eval: (put 'hashtable-update-all! 'scheme-indent-function 1)
-;; eval: (put 'hashtable-prune! 'scheme-indent-function 1)
-;; eval: (put 'hashtable-sum 'scheme-indent-function 2)
-;; eval: (put 'hashtable-map->lset 'scheme-indent-function 1)
-;; eval: (put 'hashtable-find 'scheme-indent-function 1)
-;; End:
-;;; This doesn't test weakness, external representation, and quasiquote.
-
-(test-begin "SRFI-126")
-
-(test-group "constructors & inspection"
- (test-group "eq"
- (let ((tables (list (make-eq-hashtable)
- (make-eq-hashtable 10)
- (make-eq-hashtable #f #f)
- (make-hashtable #f eq?)
- (alist->eq-hashtable '((a . b) (c . d)))
- (alist->eq-hashtable 10 '((a . b) (c . d)))
- (alist->eq-hashtable #f #f '((a . b) (c . d))))))
- (do ((tables tables (cdr tables))
- (i 0 (+ i 1)))
- ((null? tables))
- (let ((table (car tables))
- (label (number->string i)))
- (test-assert label (hashtable? table))
- (test-eq label #f (hashtable-hash-function table))
- (test-eq label eq? (hashtable-equivalence-function table))
- (test-eq label #f (hashtable-weakness table))
- (test-assert label (hashtable-mutable? table))))))
- (test-group "eqv"
- (let ((tables (list (make-eqv-hashtable)
- (make-eqv-hashtable 10)
- (make-eqv-hashtable #f #f)
- (make-hashtable #f eqv?)
- (alist->eqv-hashtable '((a . b) (c . d)))
- (alist->eqv-hashtable 10 '((a . b) (c . d)))
- (alist->eqv-hashtable #f #f '((a . b) (c . d))))))
- (do ((tables tables (cdr tables))
- (i 0 (+ i 1)))
- ((null? tables))
- (let ((table (car tables))
- (label (number->string i)))
- (test-assert label (hashtable? table))
- (test-eq label #f (hashtable-hash-function table))
- (test-eq label eqv? (hashtable-equivalence-function table))
- (test-eq label #f (hashtable-weakness table))
- (test-assert label (hashtable-mutable? table))))))
- (test-group "equal"
- (let ((tables (list (make-hashtable equal-hash equal?)
- (make-hashtable equal-hash equal? 10)
- (make-hashtable equal-hash equal? #f #f)
- (alist->hashtable equal-hash equal?
- '((a . b) (c . d)))
- (alist->hashtable equal-hash equal? 10
- '((a . b) (c . d)))
- (alist->hashtable equal-hash equal? #f #f
- '((a . b) (c . d))))))
- (do ((tables tables (cdr tables))
- (i 0 (+ i 1)))
- ((null? tables))
- (let ((table (car tables))
- (label (number->string i)))
- (test-assert label (hashtable? table))
- (test-eq label equal-hash (hashtable-hash-function table))
- (test-eq label equal? (hashtable-equivalence-function table))
- (test-eq label #f (hashtable-weakness table))
- (test-assert label (hashtable-mutable? table))))
- (let ((table (make-hashtable (cons equal-hash equal-hash) equal?)))
- (let ((hash (hashtable-hash-function table)))
- (test-assert (or (eq? equal-hash hash)
- (and (eq? equal-hash (car hash))
- (eq? equal-hash (cdr hash)))))))))
- (test-group "alist"
- (let ((tables (list (alist->eq-hashtable '((a . b) (a . c)))
- (alist->eqv-hashtable '((a . b) (a . c)))
- (alist->hashtable equal-hash equal?
- '((a . b) (a . c))))))
- (do ((tables tables (cdr tables))
- (i 0 (+ i 1)))
- ((null? tables))
- (let ((table (car tables))
- (label (number->string i)))
- (test-eq label 'b (hashtable-ref table 'a)))))))
-
-(test-group "procedures"
- (test-group "basics"
- (let ((table (make-eq-hashtable)))
- (test-group "ref"
- (test-error (hashtable-ref table 'a))
- (test-eq 'b (hashtable-ref table 'a 'b))
- (test-assert (not (hashtable-contains? table 'a)))
- (test-eqv 0 (hashtable-size table)))
- (test-group "set"
- (hashtable-set! table 'a 'c)
- (test-eq 'c (hashtable-ref table 'a))
- (test-eq 'c (hashtable-ref table 'a 'b))
- (test-assert (hashtable-contains? table 'a))
- (test-eqv 1 (hashtable-size table)))
- (test-group "delete"
- (hashtable-delete! table 'a)
- (test-error (hashtable-ref table 'a))
- (test-eq 'b (hashtable-ref table 'a 'b))
- (test-assert (not (hashtable-contains? table 'a)))
- (test-eqv 0 (hashtable-size table)))))
- (test-group "advanced"
- (let ((table (make-eq-hashtable)))
- (test-group "lookup"
- (let-values (((x found?) (hashtable-lookup table 'a)))
- (test-assert (not found?))))
- (test-group "update"
- (test-error (hashtable-update! table 'a (lambda (x) (+ x 1))))
- (hashtable-update! table 'a (lambda (x) (+ x 1)) 0)
- (let-values (((x found?) (hashtable-lookup table 'a)))
- (test-eqv 1 x)
- (test-assert found?))
- (hashtable-update! table 'a (lambda (x) (+ x 1)))
- (let-values (((x found?) (hashtable-lookup table 'a)))
- (test-eqv x 2)
- (test-assert found?))
- (hashtable-update! table 'a (lambda (x) (+ x 1)) 0)
- (let-values (((x found?) (hashtable-lookup table 'a)))
- (test-eqv x 3)
- (test-assert found?)))
- (test-group "intern"
- (test-eqv 0 (hashtable-intern! table 'b (lambda () 0)))
- (test-eqv 0 (hashtable-intern! table 'b (lambda () 1))))))
- (test-group "copy/clear"
- (let ((table (alist->hashtable equal-hash equal? '((a . b)))))
- (test-group "copy"
- (let ((table2 (hashtable-copy table)))
- (test-eq equal-hash (hashtable-hash-function table2))
- (test-eq equal? (hashtable-equivalence-function table2))
- (test-eq 'b (hashtable-ref table2 'a))
- (test-error (hashtable-set! table2 'a 'c)))
- (let ((table2 (hashtable-copy table #f)))
- (test-eq equal-hash (hashtable-hash-function table2))
- (test-eq equal? (hashtable-equivalence-function table2))
- (test-eq 'b (hashtable-ref table2 'a))
- (test-error (hashtable-set! table2 'a 'c)))
- (let ((table2 (hashtable-copy table #t)))
- (test-eq equal-hash (hashtable-hash-function table2))
- (test-eq equal? (hashtable-equivalence-function table2))
- (test-eq 'b (hashtable-ref table2 'a))
- (hashtable-set! table2 'a 'c)
- (test-eq 'c (hashtable-ref table2 'a)))
- (let ((table2 (hashtable-copy table #f #f)))
- (test-eq equal-hash (hashtable-hash-function table2))
- (test-eq equal? (hashtable-equivalence-function table2))
- (test-eq #f (hashtable-weakness table2))))
- (test-group "clear"
- (let ((table2 (hashtable-copy table #t)))
- (hashtable-clear! table2)
- (test-eqv 0 (hashtable-size table2)))
- (let ((table2 (hashtable-copy table #t)))
- (hashtable-clear! table2 10)
- (test-eqv 0 (hashtable-size table2))))
- (test-group "empty-copy"
- (let ((table2 (hashtable-empty-copy table)))
- (test-eq equal-hash (hashtable-hash-function table2))
- (test-eq equal? (hashtable-equivalence-function table2))
- (test-eqv 0 (hashtable-size table2)))
- (let ((table2 (hashtable-empty-copy table 10)))
- (test-eq equal-hash (hashtable-hash-function table2))
- (test-eq equal? (hashtable-equivalence-function table2))
- (test-eqv 0 (hashtable-size table2))))))
- (test-group "keys/values"
- (let ((table (alist->eq-hashtable '((a . b) (c . d)))))
- (test-assert (lset= eq? '(a c) (vector->list (hashtable-keys table))))
- (test-assert (lset= eq? '(b d) (vector->list (hashtable-values table))))
- (let-values (((keys values) (hashtable-entries table)))
- (test-assert (lset= eq? '(a c) (vector->list keys)))
- (test-assert (lset= eq? '(b d) (vector->list values))))
- (test-assert (lset= eq? '(a c) (hashtable-key-list table)))
- (test-assert (lset= eq? '(b d) (hashtable-value-list table)))
- (let-values (((keys values) (hashtable-entry-lists table)))
- (test-assert (lset= eq? '(a c) keys))
- (test-assert (lset= eq? '(b d) values)))))
- (test-group "iteration"
- (test-group "walk"
- (let ((keys '())
- (values '()))
- (hashtable-walk (alist->eq-hashtable '((a . b) (c . d)))
- (lambda (k v)
- (set! keys (cons k keys))
- (set! values (cons v values))))
- (test-assert (lset= eq? '(a c) keys))
- (test-assert (lset= eq? '(b d) values))))
- (test-group "update-all"
- (let ((table (alist->eq-hashtable '((a . b) (c . d)))))
- (hashtable-update-all! table
- (lambda (k v)
- (string->symbol (string-append (symbol->string v) "x"))))
- (test-assert (lset= eq? '(a c) (hashtable-key-list table)))
- (test-assert (lset= eq? '(bx dx) (hashtable-value-list table)))))
- (test-group "prune"
- (let ((table (alist->eq-hashtable '((a . b) (c . d)))))
- (hashtable-prune! table (lambda (k v) (eq? k 'a)))
- (test-assert (not (hashtable-contains? table 'a)))
- (test-assert (hashtable-contains? table 'c))))
- (test-group "merge"
- (let ((table (alist->eq-hashtable '((a . b) (c . d))))
- (table2 (alist->eq-hashtable '((a . x) (e . f)))))
- (hashtable-merge! table table2)
- (test-assert (lset= eq? '(a c e) (hashtable-key-list table)))
- (test-assert (lset= eq? '(x d f) (hashtable-value-list table)))))
- (test-group "sum"
- (let ((table (alist->eq-hashtable '((a . b) (c . d)))))
- (test-assert (lset= eq? '(a b c d)
- (hashtable-sum table '()
- (lambda (k v acc)
- (lset-adjoin eq? acc k v)))))))
- (test-group "map->lset"
- (let ((table (alist->eq-hashtable '((a . b) (c . d)))))
- (test-assert (lset= equal? '((a . b) (c . d))
- (hashtable-map->lset table cons)))))
- (test-group "find"
- (let ((table (alist->eq-hashtable '((a . b) (c . d)))))
- (let-values (((k v f?) (hashtable-find table
- (lambda (k v)
- (eq? k 'a)))))
- (test-assert (and f? (eq? k 'a) (eq? v 'b))))
- (let-values (((k v f?) (hashtable-find table (lambda (k v) #f))))
- (test-assert (not f?)))))
- (test-group "misc"
- (test-group "empty?"
- (test-assert (hashtable-empty? (alist->eq-hashtable '())))
- (test-assert (not (hashtable-empty? (alist->eq-hashtable '((a . b)))))))
- (test-group "pop!"
- (test-error (hashtable-pop! (make-eq-hashtable)))
- (let ((table (alist->eq-hashtable '((a . b)))))
- (let-values (((k v) (hashtable-pop! table)))
- (test-eq 'a k)
- (test-eq 'b v)
- (test-assert (hashtable-empty? table)))))
- (test-group "inc!"
- (let ((table (alist->eq-hashtable '((a . 0)))))
- (hashtable-inc! table 'a)
- (test-eqv 1 (hashtable-ref table 'a))
- (hashtable-inc! table 'a 2)
- (test-eqv 3 (hashtable-ref table 'a))))
- (test-group "dec!"
- (let ((table (alist->eq-hashtable '((a . 0)))))
- (hashtable-dec! table 'a)
- (test-eqv -1 (hashtable-ref table 'a))
- (hashtable-dec! table 'a 2)
- (test-eqv -3 (hashtable-ref table 'a)))))))
-
-(test-group "hashing"
- (test-assert (and (exact-integer? (hash-salt))))
- (test-assert (not (negative? (hash-salt))))
- (test-assert (= (equal-hash (list "foo" 'bar 42))
- (equal-hash (list "foo" 'bar 42))))
- (test-assert (= (string-hash (string-copy "foo"))
- (string-hash (string-copy "foo"))))
- (test-assert (= (string-ci-hash (string-copy "foo"))
- (string-ci-hash (string-copy "FOO"))))
- (test-assert (= (symbol-hash (string->symbol "foo"))
- (symbol-hash (string->symbol "foo")))))
-
-(test-end "SRFI-126")
-
-(display
- (string-append
- "\n"
- "NOTE: On implementations using the (r6rs hashtables) library from Larceny,\n"
- " 14 tests are expected to fail in relation to make-eq-hashtable and\n"
- " make-eqv-hashtable returning hashtables whose hash functions are\n"
- " exposed instead of being #f. We have no obvious way to detect this\n"
- " within this portable test suite, hence no XFAIL results.\n"))
-
-;; Local Variables:
-;; eval: (put (quote test-group) (quote scheme-indent-function) 1)
-;; End:
-(import
- (scheme base)
- (scheme write)
- (srfi 1)
- (srfi 64)
- (srfi 126))
-
-;; INCLUDE test-suite.body.scm
-;;; This doesn't test weakness, external representation, and quasiquote.
-
-(test-begin "SRFI-126")
-
-(test-group "constructors & inspection"
- (test-group "eq"
- (let ((tables (list (make-eq-hashtable)
- (make-eq-hashtable 10)
- (make-eq-hashtable #f #f)
- (make-hashtable #f eq?)
- (alist->eq-hashtable '((a . b) (c . d)))
- (alist->eq-hashtable 10 '((a . b) (c . d)))
- (alist->eq-hashtable #f #f '((a . b) (c . d))))))
- (do ((tables tables (cdr tables))
- (i 0 (+ i 1)))
- ((null? tables))
- (let ((table (car tables))
- (label (number->string i)))
- (test-assert label (hashtable? table))
- (test-eq label #f (hashtable-hash-function table))
- (test-eq label eq? (hashtable-equivalence-function table))
- (test-eq label #f (hashtable-weakness table))
- (test-assert label (hashtable-mutable? table))))))
- (test-group "eqv"
- (let ((tables (list (make-eqv-hashtable)
- (make-eqv-hashtable 10)
- (make-eqv-hashtable #f #f)
- (make-hashtable #f eqv?)
- (alist->eqv-hashtable '((a . b) (c . d)))
- (alist->eqv-hashtable 10 '((a . b) (c . d)))
- (alist->eqv-hashtable #f #f '((a . b) (c . d))))))
- (do ((tables tables (cdr tables))
- (i 0 (+ i 1)))
- ((null? tables))
- (let ((table (car tables))
- (label (number->string i)))
- (test-assert label (hashtable? table))
- (test-eq label #f (hashtable-hash-function table))
- (test-eq label eqv? (hashtable-equivalence-function table))
- (test-eq label #f (hashtable-weakness table))
- (test-assert label (hashtable-mutable? table))))))
- (test-group "equal"
- (let ((tables (list (make-hashtable equal-hash equal?)
- (make-hashtable equal-hash equal? 10)
- (make-hashtable equal-hash equal? #f #f)
- (alist->hashtable equal-hash equal?
- '((a . b) (c . d)))
- (alist->hashtable equal-hash equal? 10
- '((a . b) (c . d)))
- (alist->hashtable equal-hash equal? #f #f
- '((a . b) (c . d))))))
- (do ((tables tables (cdr tables))
- (i 0 (+ i 1)))
- ((null? tables))
- (let ((table (car tables))
- (label (number->string i)))
- (test-assert label (hashtable? table))
- (test-eq label equal-hash (hashtable-hash-function table))
- (test-eq label equal? (hashtable-equivalence-function table))
- (test-eq label #f (hashtable-weakness table))
- (test-assert label (hashtable-mutable? table))))
- (let ((table (make-hashtable (cons equal-hash equal-hash) equal?)))
- (let ((hash (hashtable-hash-function table)))
- (test-assert (or (eq? equal-hash hash)
- (and (eq? equal-hash (car hash))
- (eq? equal-hash (cdr hash)))))))))
- (test-group "alist"
- (let ((tables (list (alist->eq-hashtable '((a . b) (a . c)))
- (alist->eqv-hashtable '((a . b) (a . c)))
- (alist->hashtable equal-hash equal?
- '((a . b) (a . c))))))
- (do ((tables tables (cdr tables))
- (i 0 (+ i 1)))
- ((null? tables))
- (let ((table (car tables))
- (label (number->string i)))
- (test-eq label 'b (hashtable-ref table 'a)))))))
-
-(test-group "procedures"
- (test-group "basics"
- (let ((table (make-eq-hashtable)))
- (test-group "ref"
- (test-error (hashtable-ref table 'a))
- (test-eq 'b (hashtable-ref table 'a 'b))
- (test-assert (not (hashtable-contains? table 'a)))
- (test-eqv 0 (hashtable-size table)))
- (test-group "set"
- (hashtable-set! table 'a 'c)
- (test-eq 'c (hashtable-ref table 'a))
- (test-eq 'c (hashtable-ref table 'a 'b))
- (test-assert (hashtable-contains? table 'a))
- (test-eqv 1 (hashtable-size table)))
- (test-group "delete"
- (hashtable-delete! table 'a)
- (test-error (hashtable-ref table 'a))
- (test-eq 'b (hashtable-ref table 'a 'b))
- (test-assert (not (hashtable-contains? table 'a)))
- (test-eqv 0 (hashtable-size table)))))
- (test-group "advanced"
- (let ((table (make-eq-hashtable)))
- (test-group "lookup"
- (let-values (((x found?) (hashtable-lookup table 'a)))
- (test-assert (not found?))))
- (test-group "update"
- (test-error (hashtable-update! table 'a (lambda (x) (+ x 1))))
- (hashtable-update! table 'a (lambda (x) (+ x 1)) 0)
- (let-values (((x found?) (hashtable-lookup table 'a)))
- (test-eqv 1 x)
- (test-assert found?))
- (hashtable-update! table 'a (lambda (x) (+ x 1)))
- (let-values (((x found?) (hashtable-lookup table 'a)))
- (test-eqv x 2)
- (test-assert found?))
- (hashtable-update! table 'a (lambda (x) (+ x 1)) 0)
- (let-values (((x found?) (hashtable-lookup table 'a)))
- (test-eqv x 3)
- (test-assert found?)))
- (test-group "intern"
- (test-eqv 0 (hashtable-intern! table 'b (lambda () 0)))
- (test-eqv 0 (hashtable-intern! table 'b (lambda () 1))))))
- (test-group "copy/clear"
- (let ((table (alist->hashtable equal-hash equal? '((a . b)))))
- (test-group "copy"
- (let ((table2 (hashtable-copy table)))
- (test-eq equal-hash (hashtable-hash-function table2))
- (test-eq equal? (hashtable-equivalence-function table2))
- (test-eq 'b (hashtable-ref table2 'a))
- (test-error (hashtable-set! table2 'a 'c)))
- (let ((table2 (hashtable-copy table #f)))
- (test-eq equal-hash (hashtable-hash-function table2))
- (test-eq equal? (hashtable-equivalence-function table2))
- (test-eq 'b (hashtable-ref table2 'a))
- (test-error (hashtable-set! table2 'a 'c)))
- (let ((table2 (hashtable-copy table #t)))
- (test-eq equal-hash (hashtable-hash-function table2))
- (test-eq equal? (hashtable-equivalence-function table2))
- (test-eq 'b (hashtable-ref table2 'a))
- (hashtable-set! table2 'a 'c)
- (test-eq 'c (hashtable-ref table2 'a)))
- (let ((table2 (hashtable-copy table #f #f)))
- (test-eq equal-hash (hashtable-hash-function table2))
- (test-eq equal? (hashtable-equivalence-function table2))
- (test-eq #f (hashtable-weakness table2))))
- (test-group "clear"
- (let ((table2 (hashtable-copy table #t)))
- (hashtable-clear! table2)
- (test-eqv 0 (hashtable-size table2)))
- (let ((table2 (hashtable-copy table #t)))
- (hashtable-clear! table2 10)
- (test-eqv 0 (hashtable-size table2))))
- (test-group "empty-copy"
- (let ((table2 (hashtable-empty-copy table)))
- (test-eq equal-hash (hashtable-hash-function table2))
- (test-eq equal? (hashtable-equivalence-function table2))
- (test-eqv 0 (hashtable-size table2)))
- (let ((table2 (hashtable-empty-copy table 10)))
- (test-eq equal-hash (hashtable-hash-function table2))
- (test-eq equal? (hashtable-equivalence-function table2))
- (test-eqv 0 (hashtable-size table2))))))
- (test-group "keys/values"
- (let ((table (alist->eq-hashtable '((a . b) (c . d)))))
- (test-assert (lset= eq? '(a c) (vector->list (hashtable-keys table))))
- (test-assert (lset= eq? '(b d) (vector->list (hashtable-values table))))
- (let-values (((keys values) (hashtable-entries table)))
- (test-assert (lset= eq? '(a c) (vector->list keys)))
- (test-assert (lset= eq? '(b d) (vector->list values))))
- (test-assert (lset= eq? '(a c) (hashtable-key-list table)))
- (test-assert (lset= eq? '(b d) (hashtable-value-list table)))
- (let-values (((keys values) (hashtable-entry-lists table)))
- (test-assert (lset= eq? '(a c) keys))
- (test-assert (lset= eq? '(b d) values)))))
- (test-group "iteration"
- (test-group "walk"
- (let ((keys '())
- (values '()))
- (hashtable-walk (alist->eq-hashtable '((a . b) (c . d)))
- (lambda (k v)
- (set! keys (cons k keys))
- (set! values (cons v values))))
- (test-assert (lset= eq? '(a c) keys))
- (test-assert (lset= eq? '(b d) values))))
- (test-group "update-all"
- (let ((table (alist->eq-hashtable '((a . b) (c . d)))))
- (hashtable-update-all! table
- (lambda (k v)
- (string->symbol (string-append (symbol->string v) "x"))))
- (test-assert (lset= eq? '(a c) (hashtable-key-list table)))
- (test-assert (lset= eq? '(bx dx) (hashtable-value-list table)))))
- (test-group "prune"
- (let ((table (alist->eq-hashtable '((a . b) (c . d)))))
- (hashtable-prune! table (lambda (k v) (eq? k 'a)))
- (test-assert (not (hashtable-contains? table 'a)))
- (test-assert (hashtable-contains? table 'c))))
- (test-group "merge"
- (let ((table (alist->eq-hashtable '((a . b) (c . d))))
- (table2 (alist->eq-hashtable '((a . x) (e . f)))))
- (hashtable-merge! table table2)
- (test-assert (lset= eq? '(a c e) (hashtable-key-list table)))
- (test-assert (lset= eq? '(x d f) (hashtable-value-list table)))))
- (test-group "sum"
- (let ((table (alist->eq-hashtable '((a . b) (c . d)))))
- (test-assert (lset= eq? '(a b c d)
- (hashtable-sum table '()
- (lambda (k v acc)
- (lset-adjoin eq? acc k v)))))))
- (test-group "map->lset"
- (let ((table (alist->eq-hashtable '((a . b) (c . d)))))
- (test-assert (lset= equal? '((a . b) (c . d))
- (hashtable-map->lset table cons)))))
- (test-group "find"
- (let ((table (alist->eq-hashtable '((a . b) (c . d)))))
- (let-values (((k v f?) (hashtable-find table
- (lambda (k v)
- (eq? k 'a)))))
- (test-assert (and f? (eq? k 'a) (eq? v 'b))))
- (let-values (((k v f?) (hashtable-find table (lambda (k v) #f))))
- (test-assert (not f?)))))
- (test-group "misc"
- (test-group "empty?"
- (test-assert (hashtable-empty? (alist->eq-hashtable '())))
- (test-assert (not (hashtable-empty? (alist->eq-hashtable '((a . b)))))))
- (test-group "pop!"
- (test-error (hashtable-pop! (make-eq-hashtable)))
- (let ((table (alist->eq-hashtable '((a . b)))))
- (let-values (((k v) (hashtable-pop! table)))
- (test-eq 'a k)
- (test-eq 'b v)
- (test-assert (hashtable-empty? table)))))
- (test-group "inc!"
- (let ((table (alist->eq-hashtable '((a . 0)))))
- (hashtable-inc! table 'a)
- (test-eqv 1 (hashtable-ref table 'a))
- (hashtable-inc! table 'a 2)
- (test-eqv 3 (hashtable-ref table 'a))))
- (test-group "dec!"
- (let ((table (alist->eq-hashtable '((a . 0)))))
- (hashtable-dec! table 'a)
- (test-eqv -1 (hashtable-ref table 'a))
- (hashtable-dec! table 'a 2)
- (test-eqv -3 (hashtable-ref table 'a)))))))
-
-(test-group "hashing"
- (test-assert (and (exact-integer? (hash-salt))))
- (test-assert (not (negative? (hash-salt))))
- (test-assert (= (equal-hash (list "foo" 'bar 42))
- (equal-hash (list "foo" 'bar 42))))
- (test-assert (= (string-hash (string-copy "foo"))
- (string-hash (string-copy "foo"))))
- (test-assert (= (string-ci-hash (string-copy "foo"))
- (string-ci-hash (string-copy "FOO"))))
- (test-assert (= (symbol-hash (string->symbol "foo"))
- (symbol-hash (string->symbol "foo")))))
-
-(test-end "SRFI-126")
-
-(display
- (string-append
- "\n"
- "NOTE: On implementations using the (r6rs hashtables) library from Larceny,\n"
- " 14 tests are expected to fail in relation to make-eq-hashtable and\n"
- " make-eqv-hashtable returning hashtables whose hash functions are\n"
- " exposed instead of being #f. We have no obvious way to detect this\n"
- " within this portable test suite, hence no XFAIL results.\n"))
-
-;; Local Variables:
-;; eval: (put (quote test-group) (quote scheme-indent-function) 1)
-;; End:
-;; Copyright (c) 2005, 2006, 2007, 2012, 2013 Per Bothner
-;; Added "full" support for Chicken, Gauche, Guile and SISC.
-;; Alex Shinn, Copyright (c) 2005.
-;; Modified for Scheme Spheres by Álvaro Castro-Castilla, Copyright (c) 2012.
-;; Support for Guile 2 by Mark H Weaver <mhw@netris.org>, Copyright (c) 2014.
-;; Refactored by Taylan Ulrich Bayırlı/Kammer, Copyright (c) 2014, 2015.
-;;
-;; Permission is hereby granted, free of charge, to any person
-;; obtaining a copy of this software and associated documentation
-;; files (the "Software"), to deal in the Software without
-;; restriction, including without limitation the rights to use, copy,
-;; modify, merge, publish, distribute, sublicense, and/or sell copies
-;; of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-;;
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-;;
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-;;; Note: to prevent producing massive amounts of code from the macro-expand
-;;; phase (which makes compile times suffer and may hit code size limits in some
-;;; systems), keep macro bodies minimal by delegating work to procedures.
-
-
-;;; Grouping
-
-(define (maybe-install-default-runner suite-name)
- (when (not (test-runner-current))
- (let* ((log-file (string-append suite-name ".srfi64.log"))
- (runner (test-runner-simple log-file)))
- (%test-runner-auto-installed! runner #t)
- (test-runner-current runner))))
-
-(define (maybe-uninstall-default-runner)
- (when (%test-runner-auto-installed? (test-runner-current))
- (test-runner-current #f)))
-
-(define test-begin
- (case-lambda
- ((name)
- (test-begin name #f))
- ((name count)
- (maybe-install-default-runner name)
- (let ((r (test-runner-current)))
- (let ((skip-list (%test-runner-skip-list r))
- (skip-save (%test-runner-skip-save r))
- (fail-list (%test-runner-fail-list r))
- (fail-save (%test-runner-fail-save r))
- (total-count (%test-runner-total-count r))
- (count-list (%test-runner-count-list r))
- (group-stack (test-runner-group-stack r)))
- ((test-runner-on-group-begin r) r name count)
- (%test-runner-skip-save! r (cons skip-list skip-save))
- (%test-runner-fail-save! r (cons fail-list fail-save))
- (%test-runner-count-list! r (cons (cons total-count count)
- count-list))
- (test-runner-group-stack! r (cons name group-stack)))))))
-
-(define test-end
- (case-lambda
- (()
- (test-end #f))
- ((name)
- (let* ((r (test-runner-get))
- (groups (test-runner-group-stack r)))
- (test-result-clear r)
- (when (null? groups)
- (error "test-end not in a group"))
- (when (and name (not (equal? name (car groups))))
- ((test-runner-on-bad-end-name r) r name (car groups)))
- (let* ((count-list (%test-runner-count-list r))
- (expected-count (cdar count-list))
- (saved-count (caar count-list))
- (group-count (- (%test-runner-total-count r) saved-count)))
- (when (and expected-count
- (not (= expected-count group-count)))
- ((test-runner-on-bad-count r) r group-count expected-count))
- ((test-runner-on-group-end r) r)
- (test-runner-group-stack! r (cdr (test-runner-group-stack r)))
- (%test-runner-skip-list! r (car (%test-runner-skip-save r)))
- (%test-runner-skip-save! r (cdr (%test-runner-skip-save r)))
- (%test-runner-fail-list! r (car (%test-runner-fail-save r)))
- (%test-runner-fail-save! r (cdr (%test-runner-fail-save r)))
- (%test-runner-count-list! r (cdr count-list))
- (when (null? (test-runner-group-stack r))
- ((test-runner-on-final r) r)
- (maybe-uninstall-default-runner)))))))
-
-(define-syntax test-group
- (syntax-rules ()
- ((_ <name> <body> . <body>*)
- (%test-group <name> (lambda () <body> . <body>*)))))
-
-(define (%test-group name thunk)
- (begin
- (maybe-install-default-runner name)
- (let ((runner (test-runner-get)))
- (test-result-clear runner)
- (test-result-set! runner 'name name)
- (unless (test-skip? runner)
- (dynamic-wind
- (lambda () (test-begin name))
- thunk
- (lambda () (test-end name)))))))
-
-(define-syntax test-group-with-cleanup
- (syntax-rules ()
- ((_ <name> <body> <body>* ... <cleanup>)
- (test-group <name>
- (dynamic-wind (lambda () #f)
- (lambda () <body> <body>* ...)
- (lambda () <cleanup>))))))
-
-
-;;; Skipping, expected-failing, matching
-
-(define (test-skip . specs)
- (let ((runner (test-runner-get)))
- (%test-runner-skip-list!
- runner (cons (apply test-match-all specs)
- (%test-runner-skip-list runner)))))
-
-(define (test-skip? runner)
- (let ((run-list (%test-runner-run-list runner))
- (skip-list (%test-runner-skip-list runner)))
- (or (and run-list (not (any-pred run-list runner)))
- (any-pred skip-list runner))))
-
-(define (test-expect-fail . specs)
- (let ((runner (test-runner-get)))
- (%test-runner-fail-list!
- runner (cons (apply test-match-all specs)
- (%test-runner-fail-list runner)))))
-
-(define (test-match-any . specs)
- (let ((preds (map make-pred specs)))
- (lambda (runner)
- (any-pred preds runner))))
-
-(define (test-match-all . specs)
- (let ((preds (map make-pred specs)))
- (lambda (runner)
- (every-pred preds runner))))
-
-(define (make-pred spec)
- (cond
- ((procedure? spec)
- spec)
- ((integer? spec)
- (test-match-nth 1 spec))
- ((string? spec)
- (test-match-name spec))
- (else
- (error "not a valid test specifier" spec))))
-
-(define test-match-nth
- (case-lambda
- ((n) (test-match-nth n 1))
- ((n count)
- (let ((i 0))
- (lambda (runner)
- (set! i (+ i 1))
- (and (>= i n) (< i (+ n count))))))))
-
-(define (test-match-name name)
- (lambda (runner)
- (equal? name (test-runner-test-name runner))))
-
-;;; Beware: all predicates must be called because they might have side-effects;
-;;; no early returning or and/or short-circuiting of procedure calls allowed.
-
-(define (any-pred preds object)
- (let loop ((matched? #f)
- (preds preds))
- (if (null? preds)
- matched?
- (let ((result ((car preds) object)))
- (loop (or matched? result)
- (cdr preds))))))
-
-(define (every-pred preds object)
- (let loop ((failed? #f)
- (preds preds))
- (if (null? preds)
- (not failed?)
- (let ((result ((car preds) object)))
- (loop (or failed? (not result))
- (cdr preds))))))
-
-;;; Actual testing
-
-(define-syntax false-if-error
- (syntax-rules ()
- ((_ <expression> <runner>)
- (guard (error
- (else
- (test-result-set! <runner> 'actual-error error)
- #f))
- <expression>))))
-
-(define (test-prelude source-info runner name form)
- (test-result-clear runner)
- (set-source-info! runner source-info)
- (when name
- (test-result-set! runner 'name name))
- (test-result-set! runner 'source-form form)
- (let ((skip? (test-skip? runner)))
- (if skip?
- (test-result-set! runner 'result-kind 'skip)
- (let ((fail-list (%test-runner-fail-list runner)))
- (when (any-pred fail-list runner)
- ;; For later inspection only.
- (test-result-set! runner 'result-kind 'xfail))))
- ((test-runner-on-test-begin runner) runner)
- (not skip?)))
-
-(define (test-postlude runner)
- (let ((result-kind (test-result-kind runner)))
- (case result-kind
- ((pass)
- (test-runner-pass-count! runner (+ 1 (test-runner-pass-count runner))))
- ((fail)
- (test-runner-fail-count! runner (+ 1 (test-runner-fail-count runner))))
- ((xpass)
- (test-runner-xpass-count! runner (+ 1 (test-runner-xpass-count runner))))
- ((xfail)
- (test-runner-xfail-count! runner (+ 1 (test-runner-xfail-count runner))))
- ((skip)
- (test-runner-skip-count! runner (+ 1 (test-runner-skip-count runner)))))
- (%test-runner-total-count! runner (+ 1 (%test-runner-total-count runner)))
- ((test-runner-on-test-end runner) runner)))
-
-(define (set-result-kind! runner pass?)
- (test-result-set! runner 'result-kind
- (if (eq? (test-result-kind runner) 'xfail)
- (if pass? 'xpass 'xfail)
- (if pass? 'pass 'fail))))
-
-;;; We need to use some trickery to get the source info right. The important
-;;; thing is to pass a syntax object that is a pair to `source-info', and make
-;;; sure this syntax object comes from user code and not from ourselves.
-
-(define-syntax test-assert
- (syntax-rules ()
- ((_ . <rest>)
- (test-assert/source-info (source-info <rest>) . <rest>))))
-
-(define-syntax test-assert/source-info
- (syntax-rules ()
- ((_ <source-info> <expr>)
- (test-assert/source-info <source-info> #f <expr>))
- ((_ <source-info> <name> <expr>)
- (%test-assert <source-info> <name> '<expr> (lambda () <expr>)))))
-
-(define (%test-assert source-info name form thunk)
- (let ((runner (test-runner-get)))
- (when (test-prelude source-info runner name form)
- (let ((val (false-if-error (thunk) runner)))
- (test-result-set! runner 'actual-value val)
- (set-result-kind! runner val)))
- (test-postlude runner)))
-
-(define-syntax test-compare
- (syntax-rules ()
- ((_ . <rest>)
- (test-compare/source-info (source-info <rest>) . <rest>))))
-
-(define-syntax test-compare/source-info
- (syntax-rules ()
- ((_ <source-info> <compare> <expected> <expr>)
- (test-compare/source-info <source-info> <compare> #f <expected> <expr>))
- ((_ <source-info> <compare> <name> <expected> <expr>)
- (%test-compare <source-info> <compare> <name> <expected> '<expr>
- (lambda () <expr>)))))
-
-(define (%test-compare source-info compare name expected form thunk)
- (let ((runner (test-runner-get)))
- (when (test-prelude source-info runner name form)
- (test-result-set! runner 'expected-value expected)
- (let ((pass? (false-if-error
- (let ((val (thunk)))
- (test-result-set! runner 'actual-value val)
- (compare expected val))
- runner)))
- (set-result-kind! runner pass?)))
- (test-postlude runner)))
-
-(define-syntax test-equal
- (syntax-rules ()
- ((_ . <rest>)
- (test-compare/source-info (source-info <rest>) equal? . <rest>))))
-
-(define-syntax test-eqv
- (syntax-rules ()
- ((_ . <rest>)
- (test-compare/source-info (source-info <rest>) eqv? . <rest>))))
-
-(define-syntax test-eq
- (syntax-rules ()
- ((_ . <rest>)
- (test-compare/source-info (source-info <rest>) eq? . <rest>))))
-
-(define (approx= margin)
- (lambda (value expected)
- (let ((rval (real-part value))
- (ival (imag-part value))
- (rexp (real-part expected))
- (iexp (imag-part expected)))
- (and (>= rval (- rexp margin))
- (>= ival (- iexp margin))
- (<= rval (+ rexp margin))
- (<= ival (+ iexp margin))))))
-
-(define-syntax test-approximate
- (syntax-rules ()
- ((_ . <rest>)
- (test-approximate/source-info (source-info <rest>) . <rest>))))
-
-(define-syntax test-approximate/source-info
- (syntax-rules ()
- ((_ <source-info> <expected> <expr> <error-margin>)
- (test-approximate/source-info
- <source-info> #f <expected> <expr> <error-margin>))
- ((_ <source-info> <name> <expected> <expr> <error-margin>)
- (test-compare/source-info
- <source-info> (approx= <error-margin>) <name> <expected> <expr>))))
-
-(define (error-matches? error type)
- (cond
- ((eq? type #t)
- #t)
- ((condition-type? type)
- (and (condition? error) (condition-has-type? error type)))
- ((procedure? type)
- (type error))
- (else
- (let ((runner (test-runner-get)))
- ((%test-runner-on-bad-error-type runner) runner type error))
- #f)))
-
-(define-syntax test-error
- (syntax-rules ()
- ((_ . <rest>)
- (test-error/source-info (source-info <rest>) . <rest>))))
-
-(define-syntax test-error/source-info
- (syntax-rules ()
- ((_ <source-info> <expr>)
- (test-error/source-info <source-info> #f #t <expr>))
- ((_ <source-info> <error-type> <expr>)
- (test-error/source-info <source-info> #f <error-type> <expr>))
- ((_ <source-info> <name> <error-type> <expr>)
- (%test-error <source-info> <name> <error-type> '<expr>
- (lambda () <expr>)))))
-
-(define (%test-error source-info name error-type form thunk)
- (let ((runner (test-runner-get)))
- (when (test-prelude source-info runner name form)
- (test-result-set! runner 'expected-error error-type)
- (let ((pass? (guard (error (else (test-result-set!
- runner 'actual-error error)
- (error-matches? error error-type)))
- (let ((val (thunk)))
- (test-result-set! runner 'actual-value val))
- #f)))
- (set-result-kind! runner pass?)))
- (test-postlude runner)))
-
-(define (default-module)
- (cond-expand
- (guile (current-module))
- (else #f)))
-
-(define test-read-eval-string
- (case-lambda
- ((string)
- (test-read-eval-string string (default-module)))
- ((string env)
- (let* ((port (open-input-string string))
- (form (read port)))
- (if (eof-object? (read-char port))
- (if env
- (eval form env)
- (eval form))
- (error "(not at eof)"))))))
-
-
-;;; Test runner control flow
-
-(define-syntax test-with-runner
- (syntax-rules ()
- ((_ <runner> <body> . <body>*)
- (let ((saved-runner (test-runner-current)))
- (dynamic-wind
- (lambda () (test-runner-current <runner>))
- (lambda () <body> . <body>*)
- (lambda () (test-runner-current saved-runner)))))))
-
-(define (test-apply first . rest)
- (let ((runner (if (test-runner? first)
- first
- (or (test-runner-current) (test-runner-create))))
- (run-list (if (test-runner? first)
- (drop-right rest 1)
- (cons first (drop-right rest 1))))
- (proc (last rest)))
- (test-with-runner runner
- (let ((saved-run-list (%test-runner-run-list runner)))
- (%test-runner-run-list! runner run-list)
- (proc)
- (%test-runner-run-list! runner saved-run-list)))))
-
-
-;;; Indicate success/failure via exit status
-
-(define (test-exit)
- (let ((runner (test-runner-current)))
- (when (not runner)
- (error "No test runner installed. Might have been auto-removed
-by test-end if you had not installed one explicitly."))
- (if (and (zero? (test-runner-xpass-count runner))
- (zero? (test-runner-fail-count runner)))
- (exit 0)
- (exit 1))))
-
-;;; execution.scm ends here
-;; Copyright (c) 2015 Taylan Ulrich Bayırlı/Kammer <taylanbayirli@gmail.com>
-;;
-;; Permission is hereby granted, free of charge, to any person
-;; obtaining a copy of this software and associated documentation
-;; files (the "Software"), to deal in the Software without
-;; restriction, including without limitation the rights to use, copy,
-;; modify, merge, publish, distribute, sublicense, and/or sell copies
-;; of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-;;
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-;;
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-;;; In some systems, a macro use like (source-info ...), that resides in a
-;;; syntax-rules macro body, first gets inserted into the place where the
-;;; syntax-rules macro was used, and then the transformer of 'source-info' is
-;;; called with a syntax object that has the source location information of that
-;;; position. That works fine when the user calls e.g. (test-assert ...), whose
-;;; body contains (source-info ...); the user gets the source location of the
-;;; (test-assert ...) call as intended, and not the source location of the real
-;;; (source-info ...) call.
-
-;;; In other systems, *first* the (source-info ...) is processed to get its real
-;;; position, which is within the body of a syntax-rules macro like test-assert,
-;;; so no matter where the user calls (test-assert ...), they get source
-;;; location information of where we defined test-assert with the call to
-;;; (source-info ...) in its body. That's arguably more correct behavior,
-;;; although in this case it makes our job a bit harder; we need to get the
-;;; source location from an argument to 'source-info' instead.
-
-(define (canonical-syntax form arg)
- (cond-expand
- (kawa arg)
- (guile-2 form)
- (else #f)))
-
-(cond-expand
- ((or kawa guile-2)
- (define-syntax source-info
- (lambda (stx)
- (syntax-case stx ()
- ((_ <x>)
- (let* ((stx (canonical-syntax stx (syntax <x>)))
- (file (syntax-source-file stx))
- (line (syntax-source-line stx)))
- (quasisyntax
- (cons (unsyntax file) (unsyntax line)))))))))
- (else
- (define-syntax source-info
- (syntax-rules ()
- ((_ <x>)
- #f)))))
-
-(define (syntax-source-file stx)
- (cond-expand
- (kawa
- (syntax-source stx))
- (guile-2
- (let ((source (syntax-source stx)))
- (and source (assq-ref source 'filename))))
- (else
- #f)))
-
-(define (syntax-source-line stx)
- (cond-expand
- (kawa
- (syntax-line stx))
- (guile-2
- (let ((source (syntax-source stx)))
- (and source (assq-ref source 'line))))
- (else
- #f)))
-
-(define (set-source-info! runner source-info)
- (when source-info
- (test-result-set! runner 'source-file (car source-info))
- (test-result-set! runner 'source-line (cdr source-info))))
-
-;;; source-info.body.scm ends here
-;; Copyright (c) 2005, 2006, 2007, 2012, 2013 Per Bothner
-;; Added "full" support for Chicken, Gauche, Guile and SISC.
-;; Alex Shinn, Copyright (c) 2005.
-;; Modified for Scheme Spheres by Álvaro Castro-Castilla, Copyright (c) 2012.
-;; Support for Guile 2 by Mark H Weaver <mhw@netris.org>, Copyright (c) 2014.
-;; Refactored by Taylan Ulrich Bayırlı/Kammer, Copyright (c) 2014, 2015.
-;;
-;; Permission is hereby granted, free of charge, to any person
-;; obtaining a copy of this software and associated documentation
-;; files (the "Software"), to deal in the Software without
-;; restriction, including without limitation the rights to use, copy,
-;; modify, merge, publish, distribute, sublicense, and/or sell copies
-;; of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-;;
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-;;
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-;;; Helpers
-
-(define (string-join strings delimiter)
- (if (null? strings)
- ""
- (let loop ((result (car strings))
- (rest (cdr strings)))
- (if (null? rest)
- result
- (loop (string-append result delimiter (car rest))
- (cdr rest))))))
-
-(define (truncate-string string length)
- (define (newline->space c) (if (char=? #\newline c) #\space c))
- (let* ((string (string-map newline->space string))
- (fill "...")
- (fill-len (string-length fill))
- (string-len (string-length string)))
- (if (<= string-len (+ length fill-len))
- string
- (let-values (((q r) (floor/ length 4)))
- ;; Left part gets 3/4 plus the remainder.
- (let ((left-end (+ (* q 3) r))
- (right-start (- string-len q)))
- (string-append (substring string 0 left-end)
- fill
- (substring string right-start string-len)))))))
-
-(define (print runner format-string . args)
- (apply format #t format-string args)
- (let ((port (%test-runner-log-port runner)))
- (when port
- (apply format port format-string args))))
-
-;;; Main
-
-(define test-runner-simple
- (case-lambda
- (()
- (test-runner-simple #f))
- ((log-file)
- (let ((runner (test-runner-null)))
- (test-runner-reset runner)
- (test-runner-on-group-begin! runner test-on-group-begin-simple)
- (test-runner-on-group-end! runner test-on-group-end-simple)
- (test-runner-on-final! runner test-on-final-simple)
- (test-runner-on-test-begin! runner test-on-test-begin-simple)
- (test-runner-on-test-end! runner test-on-test-end-simple)
- (test-runner-on-bad-count! runner test-on-bad-count-simple)
- (test-runner-on-bad-end-name! runner test-on-bad-end-name-simple)
- (%test-runner-on-bad-error-type! runner on-bad-error-type)
- (%test-runner-log-file! runner log-file)
- runner))))
-
-(when (not (test-runner-factory))
- (test-runner-factory test-runner-simple))
-
-(define (test-on-group-begin-simple runner name count)
- (when (null? (test-runner-group-stack runner))
- (maybe-start-logging runner)
- (print runner "Test suite begin: ~a~%" name)))
-
-(define (test-on-group-end-simple runner)
- (let ((name (car (test-runner-group-stack runner))))
- (when (= 1 (length (test-runner-group-stack runner)))
- (print runner "Test suite end: ~a~%" name))))
-
-(define (test-on-final-simple runner)
- (print runner "Passes: ~a\n" (test-runner-pass-count runner))
- (print runner "Expected failures: ~a\n" (test-runner-xfail-count runner))
- (print runner "Failures: ~a\n" (test-runner-fail-count runner))
- (print runner "Unexpected passes: ~a\n" (test-runner-xpass-count runner))
- (print runner "Skipped tests: ~a~%" (test-runner-skip-count runner))
- (maybe-finish-logging runner))
-
-(define (maybe-start-logging runner)
- (let ((log-file (%test-runner-log-file runner)))
- (when log-file
- ;; The possible race-condition here doesn't bother us.
- (when (file-exists? log-file)
- (delete-file log-file))
- (%test-runner-log-port! runner (open-output-file log-file))
- (print runner "Writing log file: ~a~%" log-file))))
-
-(define (maybe-finish-logging runner)
- (let ((log-file (%test-runner-log-file runner)))
- (when log-file
- (print runner "Wrote log file: ~a~%" log-file)
- (close-output-port (%test-runner-log-port runner)))))
-
-(define (test-on-test-begin-simple runner)
- (values))
-
-(define (test-on-test-end-simple runner)
- (let* ((result-kind (test-result-kind runner))
- (result-kind-name (case result-kind
- ((pass) "PASS") ((fail) "FAIL")
- ((xpass) "XPASS") ((xfail) "XFAIL")
- ((skip) "SKIP")))
- (name (let ((name (test-runner-test-name runner)))
- (if (string=? "" name)
- (truncate-string
- (format #f "~a" (test-result-ref runner 'source-form))
- 30)
- name)))
- (label (string-join (append (test-runner-group-path runner)
- (list name))
- ": ")))
- (print runner "[~a] ~a~%" result-kind-name label)
- (when (memq result-kind '(fail xpass))
- (let ((nil (cons #f #f)))
- (define (found? value)
- (not (eq? nil value)))
- (define (maybe-print value message)
- (when (found? value)
- (print runner message value)))
- (let ((file (test-result-ref runner 'source-file "(unknown file)"))
- (line (test-result-ref runner 'source-line "(unknown line)"))
- (expression (test-result-ref runner 'source-form))
- (expected-value (test-result-ref runner 'expected-value nil))
- (actual-value (test-result-ref runner 'actual-value nil))
- (expected-error (test-result-ref runner 'expected-error nil))
- (actual-error (test-result-ref runner 'actual-error nil)))
- (print runner "~a:~a: ~s~%" file line expression)
- (maybe-print expected-value "Expected value: ~s~%")
- (maybe-print expected-error "Expected error: ~a~%")
- (when (or (found? expected-value) (found? expected-error))
- (maybe-print actual-value "Returned value: ~s~%"))
- (maybe-print actual-error "Raised error: ~a~%")
- (newline))))))
-
-(define (test-on-bad-count-simple runner count expected-count)
- (print runner "*** Total number of tests was ~a but should be ~a. ***~%"
- count expected-count)
- (print runner
- "*** Discrepancy indicates testsuite error or exceptions. ***~%"))
-
-(define (test-on-bad-end-name-simple runner begin-name end-name)
- (error (format #f "Test-end \"~a\" does not match test-begin \"~a\"."
- end-name begin-name)))
-
-(define (on-bad-error-type runner type error)
- (print runner "WARNING: unknown error type predicate: ~a~%" type)
- (print runner " error was: ~a~%" error))
-
-;;; test-runner-simple.scm ends here
-;; Copyright (c) 2005, 2006, 2007, 2012, 2013 Per Bothner
-;; Added "full" support for Chicken, Gauche, Guile and SISC.
-;; Alex Shinn, Copyright (c) 2005.
-;; Modified for Scheme Spheres by Álvaro Castro-Castilla, Copyright (c) 2012.
-;; Support for Guile 2 by Mark H Weaver <mhw@netris.org>, Copyright (c) 2014.
-;; Refactored by Taylan Ulrich Bayırlı/Kammer, Copyright (c) 2014, 2015.
-;;
-;; Permission is hereby granted, free of charge, to any person
-;; obtaining a copy of this software and associated documentation
-;; files (the "Software"), to deal in the Software without
-;; restriction, including without limitation the rights to use, copy,
-;; modify, merge, publish, distribute, sublicense, and/or sell copies
-;; of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-;;
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-;;
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-
-;;; The data type
-
-(define-record-type <test-runner>
- (make-test-runner) test-runner?
-
- (result-alist test-result-alist test-result-alist!)
-
- (pass-count test-runner-pass-count test-runner-pass-count!)
- (fail-count test-runner-fail-count test-runner-fail-count!)
- (xpass-count test-runner-xpass-count test-runner-xpass-count!)
- (xfail-count test-runner-xfail-count test-runner-xfail-count!)
- (skip-count test-runner-skip-count test-runner-skip-count!)
- (total-count %test-runner-total-count %test-runner-total-count!)
-
- ;; Stack (list) of (count-at-start . expected-count):
- (count-list %test-runner-count-list %test-runner-count-list!)
-
- ;; Normally #f, except when in a test-apply.
- (run-list %test-runner-run-list %test-runner-run-list!)
-
- (skip-list %test-runner-skip-list %test-runner-skip-list!)
- (fail-list %test-runner-fail-list %test-runner-fail-list!)
-
- (skip-save %test-runner-skip-save %test-runner-skip-save!)
- (fail-save %test-runner-fail-save %test-runner-fail-save!)
-
- (group-stack test-runner-group-stack test-runner-group-stack!)
-
- ;; Note: on-test-begin and on-test-end are unrelated to the test-begin and
- ;; test-end forms in the execution library. They're called at the
- ;; beginning/end of each individual test, whereas the test-begin and test-end
- ;; forms demarcate test groups.
-
- (on-group-begin test-runner-on-group-begin test-runner-on-group-begin!)
- (on-test-begin test-runner-on-test-begin test-runner-on-test-begin!)
- (on-test-end test-runner-on-test-end test-runner-on-test-end!)
- (on-group-end test-runner-on-group-end test-runner-on-group-end!)
- (on-final test-runner-on-final test-runner-on-final!)
- (on-bad-count test-runner-on-bad-count test-runner-on-bad-count!)
- (on-bad-end-name test-runner-on-bad-end-name test-runner-on-bad-end-name!)
-
- (on-bad-error-type %test-runner-on-bad-error-type
- %test-runner-on-bad-error-type!)
-
- (aux-value test-runner-aux-value test-runner-aux-value!)
-
- (auto-installed %test-runner-auto-installed? %test-runner-auto-installed!)
-
- (log-file %test-runner-log-file %test-runner-log-file!)
- (log-port %test-runner-log-port %test-runner-log-port!))
-
-(define (test-runner-group-path runner)
- (reverse (test-runner-group-stack runner)))
-
-(define (test-runner-reset runner)
- (test-result-alist! runner '())
- (test-runner-pass-count! runner 0)
- (test-runner-fail-count! runner 0)
- (test-runner-xpass-count! runner 0)
- (test-runner-xfail-count! runner 0)
- (test-runner-skip-count! runner 0)
- (%test-runner-total-count! runner 0)
- (%test-runner-count-list! runner '())
- (%test-runner-run-list! runner #f)
- (%test-runner-skip-list! runner '())
- (%test-runner-fail-list! runner '())
- (%test-runner-skip-save! runner '())
- (%test-runner-fail-save! runner '())
- (test-runner-group-stack! runner '()))
-
-(define (test-runner-null)
- (define (test-null-callback . args) #f)
- (let ((runner (make-test-runner)))
- (test-runner-reset runner)
- (test-runner-on-group-begin! runner test-null-callback)
- (test-runner-on-group-end! runner test-null-callback)
- (test-runner-on-final! runner test-null-callback)
- (test-runner-on-test-begin! runner test-null-callback)
- (test-runner-on-test-end! runner test-null-callback)
- (test-runner-on-bad-count! runner test-null-callback)
- (test-runner-on-bad-end-name! runner test-null-callback)
- (%test-runner-on-bad-error-type! runner test-null-callback)
- (%test-runner-auto-installed! runner #f)
- (%test-runner-log-file! runner #f)
- (%test-runner-log-port! runner #f)
- runner))
-
-
-;;; State
-
-(define test-result-ref
- (case-lambda
- ((runner key)
- (test-result-ref runner key #f))
- ((runner key default)
- (let ((entry (assq key (test-result-alist runner))))
- (if entry (cdr entry) default)))))
-
-(define (test-result-set! runner key value)
- (let* ((alist (test-result-alist runner))
- (entry (assq key alist)))
- (if entry
- (set-cdr! entry value)
- (test-result-alist! runner (cons (cons key value) alist)))))
-
-(define (test-result-remove runner key)
- (test-result-alist! runner (remove (lambda (entry)
- (eq? key (car entry)))
- (test-result-alist runner))))
-
-(define (test-result-clear runner)
- (test-result-alist! runner '()))
-
-(define (test-runner-test-name runner)
- (or (test-result-ref runner 'name) ""))
-
-(define test-result-kind
- (case-lambda
- (() (test-result-kind (test-runner-get)))
- ((runner) (test-result-ref runner 'result-kind))))
-
-(define test-passed?
- (case-lambda
- (() (test-passed? (test-runner-get)))
- ((runner) (memq (test-result-kind runner) '(pass xpass)))))
-
-
-;;; Factory and current instance
-
-(define test-runner-factory (make-parameter #f))
-
-(define (test-runner-create) ((test-runner-factory)))
-
-(define test-runner-current (make-parameter #f))
-
-(define (test-runner-get)
- (or (test-runner-current)
- (error "test-runner not initialized - test-begin missing?")))
-
-;;; test-runner.scm ends here
-(define-module (srfi srfi-64)
- #\export
- (test-begin
- test-end test-assert test-eqv test-eq test-equal
- test-approximate test-assert test-error test-apply test-with-runner
- test-match-nth test-match-all test-match-any test-match-name
- test-skip test-expect-fail test-read-eval-string
- test-runner-group-path test-group test-group-with-cleanup
- test-exit
- test-result-ref test-result-set! test-result-clear test-result-remove
- test-result-kind test-passed?
- test-runner? test-runner-reset test-runner-null
- test-runner-simple test-runner-current test-runner-factory test-runner-get
- test-runner-create test-runner-test-name
- test-runner-pass-count test-runner-pass-count!
- test-runner-fail-count test-runner-fail-count!
- test-runner-xpass-count test-runner-xpass-count!
- test-runner-xfail-count test-runner-xfail-count!
- test-runner-skip-count test-runner-skip-count!
- test-runner-group-stack test-runner-group-stack!
- test-runner-on-test-begin test-runner-on-test-begin!
- test-runner-on-test-end test-runner-on-test-end!
- test-runner-on-group-begin test-runner-on-group-begin!
- test-runner-on-group-end test-runner-on-group-end!
- test-runner-on-final test-runner-on-final!
- test-runner-on-bad-count test-runner-on-bad-count!
- test-runner-on-bad-end-name test-runner-on-bad-end-name!
- test-result-alist test-result-alist!
- test-runner-aux-value test-runner-aux-value!
- test-on-group-begin-simple test-on-group-end-simple
- test-on-bad-count-simple test-on-bad-end-name-simple
- test-on-final-simple test-on-test-end-simple
- test-on-final-simple))
-
-(cond-expand-provide (current-module) '(srfi-64))
-
-(import
- (only (rnrs exceptions) guard)
- (srfi srfi-1)
- (srfi srfi-9)
- (srfi srfi-11)
- (srfi srfi-35))
-(include-from-path "srfi/srfi-64/source-info.body.scm")
-(include-from-path "srfi/srfi-64/test-runner.body.scm")
-(include-from-path "srfi/srfi-64/test-runner-simple.body.scm")
-(include-from-path "srfi/srfi-64/execution.body.scm")
-(define-library (srfi-tests aux)
- (export define-tests)
- (import
- (scheme base)
- (scheme write)
- (scheme case-lambda)
- (srfi 64))
- (begin
- (define-syntax define-tests
- (syntax-rules ()
- ((_ proc-name suite-name form ...)
- (define proc-name
- (case-lambda
- (() (proc-name (test-runner-create)))
- ((runner)
- (parameterize ((test-runner-current runner))
- (test-begin suite-name)
- form ...
- (test-end suite-name)
- (and (= 0 (test-runner-xpass-count runner))
- (= 0 (test-runner-fail-count runner))))))))))
- ))
-;; Copyright (C) Oleg Kiselyov (1998). All Rights Reserved.
-
-;; Made an R7RS library by Taylan Ulrich Bayırlı/Kammer, Copyright (C) 2014.
-
-;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;; of this software and associated documentation files (the "Software"), to deal
-;; in the Software without restriction, including without limitation the rights
-;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-;; copies of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-
-;; The above copyright notice and this permission notice shall be included in
-;; all copies or substantial portions of the Software.
-
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-(define-library (srfi-tests srfi-2)
- (export run-tests)
- (import
- (scheme base)
- (scheme eval)
- (srfi 2)
- (srfi 64)
- (srfi-tests aux))
- (begin
-
- (define (test-eval form)
- (eval form (environment '(scheme base) '(srfi 2))))
-
- ;; We want to check whether 'form' has indeed wrong syntax. We eval it and
- ;; check for any exception, which is our best approximation.
- (define-syntax test-syntax-error
- (syntax-rules ()
- ((_ form)
- (test-error (test-eval 'form)))))
-
- (define-tests run-tests "SRFI-2"
- (test-equal 1 (and-let* () 1))
- (test-equal 2 (and-let* () 1 2))
- (test-equal #t (and-let* ()))
-
- (test-equal #f (let ((x #f)) (and-let* (x))))
- (test-equal 1 (let ((x 1)) (and-let* (x))))
- (test-equal #f (and-let* ((x #f))))
- (test-equal 1 (and-let* ((x 1))))
- (test-equal #f (and-let* ((#f) (x 1))))
- (test-equal 1 (and-let* ((2) (x 1))))
- ;; Gauche allows let-binding a constant, thus fails to signal an error on
- ;; the following two tests.
- (cond-expand
- (gauche (test-expect-fail 2))
- (else))
- (test-syntax-error (and-let* (#f (x 1))))
- (test-syntax-error (and-let* (2 (x 1))))
- (test-equal 2 (and-let* ((x 1) (2))))
- (test-equal #f (let ((x #f)) (and-let* (x) x)))
- (test-equal "" (let ((x "")) (and-let* (x) x)))
- (test-equal "" (let ((x "")) (and-let* (x))))
- (test-equal 2 (let ((x 1)) (and-let* (x) (+ x 1))))
- (test-equal #f (let ((x #f)) (and-let* (x) (+ x 1))))
- (test-equal 2 (let ((x 1)) (and-let* (((positive? x))) (+ x 1))))
- (test-equal #t (let ((x 1)) (and-let* (((positive? x))))))
- (test-equal #f (let ((x 0)) (and-let* (((positive? x))) (+ x 1))))
- (test-equal 3
- (let ((x 1)) (and-let* (((positive? x)) (x (+ x 1))) (+ x 1))))
- ;; This is marked as must-be-error in the original test suite; see
- ;; comments in the implementation for our rationale for intentionally
- ;; breaking off from the specification.
- (test-equal 4
- (let ((x 1))
- (and-let* (((positive? x)) (x (+ x 1)) (x (+ x 1))) (+ x 1))))
-
- (test-equal 2
- (let ((x 1)) (and-let* (x ((positive? x))) (+ x 1))))
- (test-equal 2
- (let ((x 1)) (and-let* (((begin x)) ((positive? x))) (+ x 1))))
- (test-equal #f
- (let ((x 0)) (and-let* (x ((positive? x))) (+ x 1))))
- (test-equal #f
- (let ((x #f)) (and-let* (x ((positive? x))) (+ x 1))))
- (test-equal #f
- (let ((x #f)) (and-let* (((begin x)) ((positive? x))) (+ x 1))))
-
- (test-equal #f
- (let ((x 1)) (and-let* (x (y (- x 1)) ((positive? y))) (/ x y))))
- (test-equal #f
- (let ((x 0)) (and-let* (x (y (- x 1)) ((positive? y))) (/ x y))))
- (test-equal #f
- (let ((x #f)) (and-let* (x (y (- x 1)) ((positive? y))) (/ x y))))
- (test-equal 3/2
- (let ((x 3)) (and-let* (x (y (- x 1)) ((positive? y))) (/ x y)))))
-
- ))
-(define-library (srfi-tests srfi-26)
- (export run-tests)
- (import
- (scheme base)
- (srfi 26)
- (srfi 64)
- (srfi-tests aux))
- (begin
- (define-tests run-tests "SRFI-26"
- ;; cut
- (test-equal '() ((cut list)))
- (test-equal '() ((cut list <___>)))
- (test-equal '(1) ((cut list 1)))
- (test-equal '(1) ((cut list <>) 1))
- (test-equal '(1) ((cut list <___>) 1))
- (test-equal '(1 2) ((cut list 1 2)))
- (test-equal '(1 2) ((cut list 1 <>) 2))
- (test-equal '(1 2) ((cut list 1 <___>) 2))
- (test-equal '(1 2 3 4) ((cut list 1 <___>) 2 3 4))
- (test-equal '(1 2 3 4) ((cut list 1 <> 3 <>) 2 4))
- (test-equal '(1 2 3 4 5 6) ((cut list 1 <> 3 <___>) 2 4 5 6))
- (test-equal '(ok) (let* ((x 'wrong) (y (cut list x))) (set! x 'ok) (y)))
- (test-equal 2 (let ((a 0))
- (map (cut + (begin (set! a (+ a 1)) a) <>)
- '(1 2))
- a))
- ;; cute
- (test-equal '() ((cute list)))
- (test-equal '() ((cute list <___>)))
- (test-equal '(1) ((cute list 1)))
- (test-equal '(1) ((cute list <>) 1))
- (test-equal '(1) ((cute list <___>) 1))
- (test-equal '(1 2) ((cute list 1 2)))
- (test-equal '(1 2) ((cute list 1 <>) 2))
- (test-equal '(1 2) ((cute list 1 <___>) 2))
- (test-equal '(1 2 3 4) ((cute list 1 <___>) 2 3 4))
- (test-equal '(1 2 3 4) ((cute list 1 <> 3 <>) 2 4))
- (test-equal '(1 2 3 4 5 6) ((cute list 1 <> 3 <___>) 2 4 5 6))
- (test-equal 1 (let ((a 0))
- (map (cute + (begin (set! a (+ a 1)) a) <>)
- '(1 2))
- a)))))
-(define-library (srfi-tests srfi-31)
- (export run-tests)
- (import
- (scheme base)
- (scheme lazy)
- (srfi 31)
- (srfi 64)
- (srfi-tests aux))
- (begin
- (define-tests run-tests "SRFI-31"
- (test-eqv "factorial" 3628800
- ((rec (! n)
- (if (zero? n)
- 1
- (* n (! (- n 1)))))
- 10))
- (test-eqv "lazy stream" 'x
- (car (force (cdr (force (cdr (rec xs (cons 'x (delay xs))))))))))))
-
-
-(define-library (srfi-tests srfi-54)
- (export run-tests)
- (import
- (scheme base)
- (scheme char)
- (scheme write)
- (srfi 54)
- (srfi 64)
- (srfi-tests aux))
- (begin
-
- (define-syntax value-and-output
- (syntax-rules ()
- ((_ expr)
- (let ((port (open-output-string)))
- (parameterize ((current-output-port port))
- (let ((value expr))
- (list value (get-output-string port))))))))
-
- (define (string-reverse string)
- (list->string (reverse (string->list string))))
-
- (define-tests run-tests "SRFI-54"
- (test-equal "130.00 " (cat 129.995 -10 2.))
- (test-equal " 130.00" (cat 129.995 10 2.))
- (test-equal " 129.98" (cat 129.985 10 2.))
- (test-equal " 129.99" (cat 129.985001 10 2.))
- (test-equal "#e130.00" (cat 129.995 2. 'exact))
- (test-equal "129.00" (cat 129 -2.))
- (test-equal "#e129.00" (cat 129 2.))
- (test-equal "#e+0129.00" (cat 129 10 2. #\0 'sign))
- (test-equal "*#e+129.00" (cat 129 10 2. #\* 'sign))
- (test-equal "1/3" (cat 1/3))
- (test-equal " #e0.33" (cat 1/3 10 2.))
- (test-equal " 0.33" (cat 1/3 10 -2.))
- (test-equal " 1,29.99,5" (cat 129.995 10 '(#\, 2)))
- (test-equal " +129,995" (cat 129995 10 '(#\,) 'sign))
- (test-equal "130" (cat (cat 129.995 0.) '(0 -1)))
- ;; These produce different results on Chibi, but I don't know if that's a
- ;; bug or whether the result is implementation-dependent.
- ;; (test-equal "#i#o+307/2" (cat 99.5 10 'sign 'octal))
- ;; (test-equal " #o+307/2" (cat 99.5 10 'sign 'octal 'exact))
- (test-equal "#o+443" (cat #x123 'octal 'sign))
- (test-equal "#e+291.00*" (cat #x123 -10 2. 'sign #\*))
- ;; These produce different results on Larceny, but I don't know if that's
- ;; a bug or whether the result is implementation-dependent.
- ;; (test-equal "-1.234e+15+1.236e-15i" (cat -1.2345e+15+1.2355e-15i 3.))
- ;; (test-equal "+1.234e+15" (cat 1.2345e+15 10 3. 'sign))
- (test-equal "string " (cat "string" -10))
- (test-equal " STRING" (cat "string" 10 (list string-upcase)))
- (test-equal " RING" (cat "string" 10 (list string-upcase) '(-2)))
- (test-equal " STING" (cat "string" 10 `(,string-upcase) '(2 3)))
- (test-equal "GNIRTS" (cat "string" `(,string-reverse ,string-upcase)))
- (test-equal " a" (cat #\a 10))
- (test-equal " symbol" (cat 'symbol 10))
- (test-equal "#(#\\a \"str\" s)" (cat '#(#\a "str" s)))
- (test-equal "(#\\a \"str\" s)" (cat '(#\a "str" s)))
- (test-equal '("(#\\a \"str\" s)" "(#\\a \"str\" s)")
- (value-and-output (cat '(#\a "str" s) #t)))
- (test-equal '("(#\\a \"str\" s)" "(#\\a \"str\" s)")
- (value-and-output (cat '(#\a "str" s) (current-output-port))))
- (test-equal "3s \"str\"" (cat 3 (cat 's) " " (cat "str" write)))
- (test-equal '("3s \"str\"" "3s \"str\"")
- (value-and-output (cat 3 #t (cat 's) " " (cat "str" write))))
- (test-equal '("3s \"str\"" "s3s \"str\"")
- (value-and-output (cat 3 #t (cat 's #t) " " (cat "str" write)))))
-
- ))
-(import
- (scheme base)
- (scheme process-context)
- (srfi 64))
-
-;;;
-;;; This is a test suite written in the notation of
-;;; SRFI-64, A Scheme API for test suites
-;;;
-
-(test-begin "SRFI 64 - Meta-Test Suite")
-
-;;;
-;;; Ironically, in order to set up the meta-test environment,
-;;; we have to invoke one of the most sophisticated features:
-;;; custom test runners
-;;;
-
-;;; The `prop-runner' invokes `thunk' in the context of a new
-;;; test runner, and returns the indicated properties of the
-;;; last-executed test result.
-
-(define (prop-runner props thunk)
- (let ((r (test-runner-null))
- (plist '()))
- ;;
- (test-runner-on-test-end!
- r
- (lambda (runner)
- (set! plist (test-result-alist runner))))
- ;;
- (test-with-runner r (thunk))
- ;; reorder the properties so they are in the order
- ;; given by `props'. Note that any property listed in `props'
- ;; that is not in the property alist will occur as #f
- (map (lambda (k)
- (assq k plist))
- props)))
-
-;;; `on-test-runner' creates a null test runner and then
-;;; arranged for `visit' to be called with the runner
-;;; whenever a test is run. The results of the calls to
-;;; `visit' are returned in a list
-
-(define (on-test-runner thunk visit)
- (let ((r (test-runner-null))
- (results '()))
- ;;
- (test-runner-on-test-end!
- r
- (lambda (runner)
- (set! results (cons (visit r) results))))
- ;;
- (test-with-runner r (thunk))
- (reverse results)))
-
-;;;
-;;; The `triv-runner' invokes `thunk'
-;;; and returns a list of 6 lists, the first 5 of which
-;;; are a list of the names of the tests that, respectively,
-;;; PASS, FAIL, XFAIL, XPASS, and SKIP.
-;;; The last item is a list of counts.
-;;;
-
-(define (triv-runner thunk)
- (let ((r (test-runner-null))
- (accum-pass '())
- (accum-fail '())
- (accum-xfail '())
- (accum-xpass '())
- (accum-skip '()))
- ;;
- (test-runner-on-bad-count!
- r
- (lambda (runner count expected-count)
- (error (string-append "bad count " (number->string count)
- " but expected "
- (number->string expected-count)))))
- (test-runner-on-bad-end-name!
- r
- (lambda (runner begin end)
- (error (string-append "bad end group name " end
- " but expected " begin))))
- (test-runner-on-test-end!
- r
- (lambda (runner)
- (let ((n (test-runner-test-name runner)))
- (case (test-result-kind runner)
- ((pass) (set! accum-pass (cons n accum-pass)))
- ((fail) (set! accum-fail (cons n accum-fail)))
- ((xpass) (set! accum-xpass (cons n accum-xpass)))
- ((xfail) (set! accum-xfail (cons n accum-xfail)))
- ((skip) (set! accum-skip (cons n accum-skip)))))))
- ;;
- (test-with-runner r (thunk))
- (list (reverse accum-pass) ; passed as expected
- (reverse accum-fail) ; failed, but was expected to pass
- (reverse accum-xfail) ; failed as expected
- (reverse accum-xpass) ; passed, but was expected to fail
- (reverse accum-skip) ; was not executed
- (list (test-runner-pass-count r)
- (test-runner-fail-count r)
- (test-runner-xfail-count r)
- (test-runner-xpass-count r)
- (test-runner-skip-count r)))))
-
-(define (path-revealing-runner thunk)
- (let ((r (test-runner-null))
- (seq '()))
- ;;
- (test-runner-on-test-end!
- r
- (lambda (runner)
- (set! seq (cons (list (test-runner-group-path runner)
- (test-runner-test-name runner))
- seq))))
- (test-with-runner r (thunk))
- (reverse seq)))
-
-;;;
-;;; Now we can start testing compliance with SRFI-64
-;;;
-
-(test-begin "1. Simple test-cases")
-
-(test-begin "1.1. test-assert")
-
-(define (t)
- (triv-runner
- (lambda ()
- (test-assert "a" #t)
- (test-assert "b" #f))))
-
-(test-equal
- "1.1.1. Very simple"
- '(("a") ("b") () () () (1 1 0 0 0))
- (t))
-
-(test-equal
- "1.1.2. A test with no name"
- '(("a") ("") () () () (1 1 0 0 0))
- (triv-runner (lambda () (test-assert "a" #t) (test-assert #f))))
-
-(test-equal
- "1.1.3. Tests can have the same name"
- '(("a" "a") () () () () (2 0 0 0 0))
- (triv-runner (lambda () (test-assert "a" #t) (test-assert "a" #t))))
-
-(define (choke)
- (vector-ref '#(1 2) 3))
-
-(test-equal
- "1.1.4. One way to FAIL is to throw an error"
- '(() ("a") () () () (0 1 0 0 0))
- (triv-runner (lambda () (test-assert "a" (choke)))))
-
-(test-end);1.1
-
-(test-begin "1.2. test-eqv")
-
-(define (mean x y)
- (/ (+ x y) 2.0))
-
-(test-equal
- "1.2.1. Simple numerical equivalence"
- '(("c") ("a" "b") () () () (1 2 0 0 0))
- (triv-runner
- (lambda ()
- (test-eqv "a" (mean 3 5) 4)
- (test-eqv "b" (mean 3 5) 4.5)
- (test-eqv "c" (mean 3 5) 4.0))))
-
-(test-end);1.2
-
-(test-begin "1.3. test-approximate")
-
-(test-equal
- "1.3.1. Simple numerical approximation"
- '(("a" "c") ("b") () () () (2 1 0 0 0))
- (triv-runner
- (lambda ()
- (test-approximate "a" (mean 3 5) 4 0.001)
- (test-approximate "b" (mean 3 5) 4.5 0.001)
- (test-approximate "c" (mean 3 5) 4.0 0.001))))
-
-(test-end);1.3
-
-(test-end "1. Simple test-cases")
-
-;;;
-;;;
-;;;
-
-(test-begin "2. Tests for catching errors")
-
-(test-begin "2.1. test-error")
-
-(test-equal
- "2.1.1. Baseline test; PASS with no optional args"
- '(("") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- ;; PASS
- (test-error (vector-ref '#(1 2) 9)))))
-
-(test-equal
- "2.1.2. Baseline test; FAIL with no optional args"
- '(() ("") () () () (0 1 0 0 0))
- (triv-runner
- (lambda ()
- ;; FAIL: the expr does not raise an error and `test-error' is
- ;; claiming that it will, so this test should FAIL
- (test-error (vector-ref '#(1 2) 0)))))
-
-(test-equal
- "2.1.3. PASS with a test name and error type"
- '(("a") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- ;; PASS
- (test-error "a" #t (vector-ref '#(1 2) 9)))))
-
-(test-equal
- "2.1.4. FAIL with a test name and error type"
- '(() ("a") () () () (0 1 0 0 0))
- (triv-runner
- (lambda ()
- ;; FAIL
- (test-error "a" #t (vector-ref '#(1 2) 0)))))
-
-(test-equal
- "2.1.5. PASS with an error type but no name"
- '(("") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- ;; PASS
- (test-error #t (vector-ref '#(1 2) 9)))))
-
-(test-equal
- "2.1.6. FAIL with an error type but no name"
- '(() ("") () () () (0 1 0 0 0))
- (triv-runner
- (lambda ()
- ;; FAIL
- (test-error #t (vector-ref '#(1 2) 0)))))
-
-(test-end "2.1. test-error")
-
-(test-end "2. Tests for catching errors")
-
-;;;
-;;;
-;;;
-
-(test-begin "3. Test groups and paths")
-
-(test-equal
- "3.1. test-begin with unspecific test-end"
- '(("b") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "b" #t)
- (test-end))))
-
-(test-equal
- "3.2. test-begin with name-matching test-end"
- '(("b") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "b" #t)
- (test-end "a"))))
-
-;;; since the error raised by `test-end' on a mismatch is not a test
-;;; error, we actually expect the triv-runner itself to fail
-
-(test-error
- "3.3. test-begin with mismatched test-end"
-#t
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "b" #t)
- (test-end "x"))))
-
-(test-equal
- "3.4. test-begin with name and count"
- '(("b" "c") () () () () (2 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-begin "a" 2)
- (test-assert "b" #t)
- (test-assert "c" #t)
- (test-end "a"))))
-
-;; similarly here, a mismatched count is a lexical error
-;; and not a test failure...
-
-(test-error
- "3.5. test-begin with mismatched count"
- #t
- (triv-runner
- (lambda ()
- (test-begin "a" 99)
- (test-assert "b" #t)
- (test-end "a"))))
-
-(test-equal
- "3.6. introspecting on the group path"
- '((() "w")
- (("a" "b") "x")
- (("a" "b") "y")
- (("a") "z"))
- ;;
- ;; `path-revealing-runner' is designed to return a list
- ;; of the tests executed, in order. Each entry is a list
- ;; (GROUP-PATH TEST-NAME), and each GROUP-PATH is a list
- ;; of test groups starting from the topmost
- ;;
- (path-revealing-runner
- (lambda ()
- (test-assert "w" #t)
- (test-begin "a")
- (test-begin "b")
- (test-assert "x" #t)
- (test-assert "y" #t)
- (test-end)
- (test-assert "z" #t))))
-
-
-(test-end "3. Test groups and paths")
-
-;;;
-;;;
-;;;
-
-(test-begin "4. Handling set-up and cleanup")
-
-(test-equal "4.1. Normal exit path"
- '(in 1 2 out)
- (let ((ex '()))
- (define (do s)
- (set! ex (cons s ex)))
- ;;
- (triv-runner
- (lambda ()
- (test-group-with-cleanup
- "foo"
- (do 'in)
- (do 1)
- (do 2)
- (do 'out))))
- (reverse ex)))
-
-(test-equal "4.2. Exception exit path"
- '(in 1 out)
- (let ((ex '()))
- (define (do s)
- (set! ex (cons s ex)))
- ;;
- ;; the outer runner is to run the `test-error' in, to
- ;; catch the exception raised in the inner runner,
- ;; since we don't want to depend on any other
- ;; exception-catching support
- ;;
- (triv-runner
- (lambda ()
- (test-error
- (triv-runner
- (lambda ()
- (test-group-with-cleanup
- "foo"
- (do 'in) (test-assert #t)
- (do 1) (test-assert #t)
- (choke) (test-assert #t)
- (do 2) (test-assert #t)
- (do 'out)))))))
- (reverse ex)))
-
-(test-end "4. Handling set-up and cleanup")
-
-;;;
-;;;
-;;;
-
-(test-begin "5. Test specifiers")
-
-(test-begin "5.1. test-match-named")
-
-(test-equal "5.1.1. match test names"
- '(("y") () () () ("x") (1 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-skip (test-match-name "x"))
- (test-assert "x" #t)
- (test-assert "y" #t))))
-
-(test-equal "5.1.2. but not group names"
- '(("z") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-skip (test-match-name "x"))
- (test-begin "x")
- (test-assert "z" #t)
- (test-end))))
-
-(test-end)
-
-(test-begin "5.2. test-match-nth")
-;; See also: [6.4. Short-circuit evaluation]
-
-(test-equal "5.2.1. skip the nth one after"
- '(("v" "w" "y" "z") () () () ("x") (4 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-nth 2))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP
- (test-assert "y" #t) ; 3
- (test-assert "z" #t)))) ; 4
-
-(test-equal "5.2.2. skip m, starting at n"
- '(("v" "w" "z") () () () ("x" "y") (3 0 0 0 2))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-nth 2 2))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP
- (test-assert "y" #t) ; 3 SKIP
- (test-assert "z" #t)))) ; 4
-
-(test-end)
-
-(test-begin "5.3. test-match-any")
-(test-equal "5.3.1. basic disjunction"
- '(("v" "w" "z") () () () ("x" "y") (3 0 0 0 2))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-any (test-match-nth 3)
- (test-match-name "x")))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP(NAME)
- (test-assert "y" #t) ; 3 SKIP(COUNT)
- (test-assert "z" #t)))) ; 4
-
-(test-equal "5.3.2. disjunction is commutative"
- '(("v" "w" "z") () () () ("x" "y") (3 0 0 0 2))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-any (test-match-name "x")
- (test-match-nth 3)))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP(NAME)
- (test-assert "y" #t) ; 3 SKIP(COUNT)
- (test-assert "z" #t)))) ; 4
-
-(test-end)
-
-(test-begin "5.4. test-match-all")
-(test-equal "5.4.1. basic conjunction"
- '(("v" "w" "y" "z") () () () ("x") (4 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-all (test-match-nth 2 2)
- (test-match-name "x")))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP(NAME) & SKIP(COUNT)
- (test-assert "y" #t) ; 3 SKIP(COUNT)
- (test-assert "z" #t)))) ; 4
-
-(test-equal "5.4.2. conjunction is commutative"
- '(("v" "w" "y" "z") () () () ("x") (4 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-all (test-match-name "x")
- (test-match-nth 2 2)))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP(NAME) & SKIP(COUNT)
- (test-assert "y" #t) ; 3 SKIP(COUNT)
- (test-assert "z" #t)))) ; 4
-
-(test-end)
-
-(test-end "5. Test specifiers")
-
-;;;
-;;;
-;;;
-
-(test-begin "6. Skipping selected tests")
-
-(test-equal
- "6.1. Skip by specifier - match-name"
- '(("x") () () () ("y") (1 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip (test-match-name "y"))
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; SKIP
- (test-end))))
-
-(test-equal
- "6.2. Shorthand specifiers"
- '(("x") () () () ("y") (1 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "y")
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; SKIP
- (test-end))))
-
-(test-begin "6.3. Specifier Stack")
-
-(test-equal
- "6.3.1. Clearing the Specifier Stack"
- '(("x" "x") ("y") () () ("y") (2 1 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "y")
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; SKIP
- (test-end)
- (test-begin "b")
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; FAIL
- (test-end))))
-
-(test-equal
- "6.3.2. Inheriting the Specifier Stack"
- '(("x" "x") () () () ("y" "y") (2 0 0 0 2))
- (triv-runner
- (lambda ()
- (test-skip "y")
- (test-begin "a")
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; SKIP
- (test-end)
- (test-begin "b")
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; SKIP
- (test-end))))
-
-(test-end);6.3
-
-(test-begin "6.4. Short-circuit evaluation")
-
-(test-equal
- "6.4.1. In test-match-all"
- '(("x") ("y" "x" "z") () () ("y") (1 3 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip (test-match-all "y" (test-match-nth 2)))
- ;; let's label the substructure forms so we can
- ;; see which one `test-match-nth' is going to skip
- ;; ; # "y" 2 result
- (test-assert "x" #t) ; 1 - #f #f PASS
- (test-assert "y" #f) ; 2 - #t #t SKIP
- (test-assert "y" #f) ; 3 - #t #f FAIL
- (test-assert "x" #f) ; 4 - #f #f FAIL
- (test-assert "z" #f) ; 5 - #f #f FAIL
- (test-end))))
-
-(test-equal
- "6.4.2. In separate skip-list entries"
- '(("x") ("x" "z") () () ("y" "y") (1 2 0 0 2))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "y")
- (test-skip (test-match-nth 2))
- ;; let's label the substructure forms so we can
- ;; see which one `test-match-nth' is going to skip
- ;; ; # "y" 2 result
- (test-assert "x" #t) ; 1 - #f #f PASS
- (test-assert "y" #f) ; 2 - #t #t SKIP
- (test-assert "y" #f) ; 3 - #t #f SKIP
- (test-assert "x" #f) ; 4 - #f #f FAIL
- (test-assert "z" #f) ; 5 - #f #f FAIL
- (test-end))))
-
-(test-begin "6.4.3. Skipping test suites")
-
-(test-equal
- "6.4.3.1. Introduced using 'test-begin'"
- '(("x") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "b")
- (test-begin "b") ; not skipped
- (test-assert "x" #t)
- (test-end "b")
- (test-end "a"))))
-
-(test-expect-fail 1) ;; ???
-(test-equal
- "6.4.3.2. Introduced using 'test-group'"
- '(() () () () () (0 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "b")
- (test-group
- "b" ; skipped
- (test-assert "x" #t))
- (test-end "a"))))
-
-(test-equal
- "6.4.3.3. Non-skipped 'test-group'"
- '(("x") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "c")
- (test-group "b" (test-assert "x" #t))
- (test-end "a"))))
-
-(test-end) ; 6.4.3
-
-(test-end);6.4
-
-(test-end "6. Skipping selected tests")
-
-;;;
-;;;
-;;;
-
-(test-begin "7. Expected failures")
-
-(test-equal "7.1. Simple example"
- '(() ("x") ("z") () () (0 1 1 0 0))
- (triv-runner
- (lambda ()
- (test-assert "x" #f)
- (test-expect-fail "z")
- (test-assert "z" #f))))
-
-(test-equal "7.2. Expected exception"
- '(() ("x") ("z") () () (0 1 1 0 0))
- (triv-runner
- (lambda ()
- (test-assert "x" #f)
- (test-expect-fail "z")
- (test-assert "z" (choke)))))
-
-(test-equal "7.3. Unexpectedly PASS"
- '(() () ("y") ("x") () (0 0 1 1 0))
- (triv-runner
- (lambda ()
- (test-expect-fail "x")
- (test-expect-fail "y")
- (test-assert "x" #t)
- (test-assert "y" #f))))
-
-
-
-(test-end "7. Expected failures")
-
-;;;
-;;;
-;;;
-
-(test-begin "8. Test-runner")
-
-;;;
-;;; Because we want this test suite to be accurate even
-;;; when the underlying implementation chooses to use, e.g.,
-;;; a global variable to implement what could be thread variables
-;;; or SRFI-39 parameter objects, we really need to save and restore
-;;; their state ourselves
-;;;
-(define (with-factory-saved thunk)
- (let* ((saved (test-runner-factory))
- (result (thunk)))
- (test-runner-factory saved)
- result))
-
-(test-begin "8.1. test-runner-current")
-(test-assert "8.1.1. automatically restored"
- (let ((a 0)
- (b 1)
- (c 2))
- ;
- (triv-runner
- (lambda ()
- (set! a (test-runner-current))
- ;;
- (triv-runner
- (lambda ()
- (set! b (test-runner-current))))
- ;;
- (set! c (test-runner-current))))
- ;;
- (and (eq? a c)
- (not (eq? a b)))))
-
-(test-end)
-
-(test-begin "8.2. test-runner-simple")
-(test-assert "8.2.1. default on-test hook"
- (eq? (test-runner-on-test-end (test-runner-simple))
- test-on-test-end-simple))
-(test-assert "8.2.2. default on-final hook"
- (eq? (test-runner-on-final (test-runner-simple))
- test-on-final-simple))
-(test-end)
-
-(test-begin "8.3. test-runner-factory")
-
-(test-assert "8.3.1. default factory"
- (eq? (test-runner-factory) test-runner-simple))
-
-(test-assert "8.3.2. settable factory"
- (with-factory-saved
- (lambda ()
- (test-runner-factory test-runner-null)
- ;; we have no way, without bringing in other SRFIs,
- ;; to make sure the following doesn't print anything,
- ;; but it shouldn't:
- (test-with-runner
- (test-runner-create)
- (lambda ()
- (test-begin "a")
- (test-assert #t) ; pass
- (test-assert #f) ; fail
- (test-assert (vector-ref '#(3) 10)) ; fail with error
- (test-end "a")))
- (eq? (test-runner-factory) test-runner-null))))
-
-(test-end)
-
-;;; This got tested about as well as it could in 8.3.2
-
-(test-begin "8.4. test-runner-create")
-(test-end)
-
-;;; This got tested about as well as it could in 8.3.2
-
-(test-begin "8.5. test-runner-factory")
-(test-end)
-
-(test-begin "8.6. test-apply")
-(test-equal "8.6.1. Simple (form 1) test-apply"
- '(("w" "p" "v") () () () ("x") (3 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "w" #t)
- (test-apply
- (test-match-name "p")
- (lambda ()
- (test-begin "p")
- (test-assert "x" #t)
- (test-end)
- (test-begin "z")
- (test-assert "p" #t) ; only this one should execute in here
- (test-end)))
- (test-assert "v" #t))))
-
-(test-equal "8.6.2. Simple (form 2) test-apply"
- '(("w" "p" "v") () () () ("x") (3 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "w" #t)
- (test-apply
- (test-runner-current)
- (test-match-name "p")
- (lambda ()
- (test-begin "p")
- (test-assert "x" #t)
- (test-end)
- (test-begin "z")
- (test-assert "p" #t) ; only this one should execute in here
- (test-end)))
- (test-assert "v" #t))))
-
-(test-expect-fail 1) ;; depends on all test-match-nth being called.
-(test-equal "8.6.3. test-apply with skips"
- '(("w" "q" "v") () () () ("x" "p" "x") (3 0 0 0 3))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "w" #t)
- (test-skip (test-match-nth 2))
- (test-skip (test-match-nth 4))
- (test-apply
- (test-runner-current)
- (test-match-name "p")
- (test-match-name "q")
- (lambda ()
- ; only execute if SKIP=no and APPLY=yes
- (test-assert "x" #t) ; # 1 SKIP=no APPLY=no
- (test-assert "p" #t) ; # 2 SKIP=yes APPLY=yes
- (test-assert "q" #t) ; # 3 SKIP=no APPLY=yes
- (test-assert "x" #f) ; # 4 SKIP=yes APPLY=no
- 0))
- (test-assert "v" #t))))
-
-;;; Unfortunately, since there is no way to UNBIND the current test runner,
-;;; there is no way to test the behavior of `test-apply' in the absence
-;;; of a current runner within our little meta-test framework.
-;;;
-;;; To test the behavior manually, you should be able to invoke:
-;;;
-;;; (test-apply "a" (lambda () (test-assert "a" #t)))
-;;;
-;;; from the top level (with SRFI 64 available) and it should create a
-;;; new, default (simple) test runner.
-
-(test-end)
-
-;;; This entire suite depends heavily on 'test-with-runner'. If it didn't
-;;; work, this suite would probably go down in flames
-(test-begin "8.7. test-with-runner")
-(test-end)
-
-;;; Again, this suite depends heavily on many of the test-runner
-;;; components. We'll just test those that aren't being exercised
-;;; by the meta-test framework
-(test-begin "8.8. test-runner components")
-
-(define (auxtrack-runner thunk)
- (let ((r (test-runner-null)))
- (test-runner-aux-value! r '())
- (test-runner-on-test-end! r (lambda (r)
- (test-runner-aux-value!
- r
- (cons (test-runner-test-name r)
- (test-runner-aux-value r)))))
- (test-with-runner r (thunk))
- (reverse (test-runner-aux-value r))))
-
-(test-equal "8.8.1. test-runner-aux-value"
- '("x" "" "y")
- (auxtrack-runner
- (lambda ()
- (test-assert "x" #t)
- (test-begin "a")
- (test-assert #t)
- (test-end)
- (test-assert "y" #f))))
-
-(test-end) ; 8.8
-
-(test-end "8. Test-runner")
-
-(test-begin "9. Test Result Properties")
-
-(test-begin "9.1. test-result-alist")
-
-(define (symbol-alist? l)
- (if (null? l)
- #t
- (and (pair? l)
- (pair? (car l))
- (symbol? (caar l))
- (symbol-alist? (cdr l)))))
-
-;;; check the various syntactic forms
-
-(test-assert (symbol-alist?
- (car (on-test-runner
- (lambda ()
- (test-assert #t))
- (lambda (r)
- (test-result-alist r))))))
-
-(test-assert (symbol-alist?
- (car (on-test-runner
- (lambda ()
- (test-assert #t))
- (lambda (r)
- (test-result-alist r))))))
-
-;;; check to make sure the required properties are returned
-
-(test-equal '((result-kind . pass))
- (prop-runner
- '(result-kind)
- (lambda ()
- (test-assert #t)))
- )
-
-(test-equal
- '((result-kind . fail)
- (expected-value . 2)
- (actual-value . 3))
- (prop-runner
- '(result-kind expected-value actual-value)
- (lambda ()
- (test-equal 2 (+ 1 2)))))
-
-(test-end "9.1. test-result-alist")
-
-(test-begin "9.2. test-result-ref")
-
-(test-equal '(pass)
- (on-test-runner
- (lambda ()
- (test-assert #t))
- (lambda (r)
- (test-result-ref r 'result-kind))))
-
-(test-equal '(pass)
- (on-test-runner
- (lambda ()
- (test-assert #t))
- (lambda (r)
- (test-result-ref r 'result-kind))))
-
-(test-equal '(fail pass)
- (on-test-runner
- (lambda ()
- (test-assert (= 1 2))
- (test-assert (= 1 1)))
- (lambda (r)
- (test-result-ref r 'result-kind))))
-
-(test-end "9.2. test-result-ref")
-
-(test-begin "9.3. test-result-set!")
-
-(test-equal '(100 100)
- (on-test-runner
- (lambda ()
- (test-assert (= 1 2))
- (test-assert (= 1 1)))
- (lambda (r)
- (test-result-set! r 'foo 100)
- (test-result-ref r 'foo))))
-
-(test-end "9.3. test-result-set!")
-
-(test-end "9. Test Result Properties")
-
-;;;
-;;;
-;;;
-
-;#| Time to stop having fun...
-;
-;(test-begin "9. For fun, some meta-test errors")
-;
-;(test-equal
-; "9.1. Really PASSes, but test like it should FAIL"
-; '(() ("b") () () ())
-; (triv-runner
-; (lambda ()
-; (test-assert "b" #t))))
-;
-;(test-expect-fail "9.2. Expect to FAIL and do so")
-;(test-expect-fail "9.3. Expect to FAIL but PASS")
-;(test-skip "9.4. SKIP this one")
-;
-;(test-assert "9.2. Expect to FAIL and do so" #f)
-;(test-assert "9.3. Expect to FAIL but PASS" #t)
-;(test-assert "9.4. SKIP this one" #t)
-;
-;(test-end)
-; |#
-
-(test-end "SRFI 64 - Meta-Test Suite")
-
-(let ((runner (test-runner-current)))
- (unless (and (= 0 (test-runner-xpass-count runner))
- (= 0 (test-runner-fail-count runner)))
- (exit 1)))
-
-;;;
-;;;
-;;; This is a test suite written in the notation of
-;;; SRFI-64, A Scheme API for test suites
-;;;
-
-(test-begin "SRFI 64 - Meta-Test Suite")
-
-;;;
-;;; Ironically, in order to set up the meta-test environment,
-;;; we have to invoke one of the most sophisticated features:
-;;; custom test runners
-;;;
-
-;;; The `prop-runner' invokes `thunk' in the context of a new
-;;; test runner, and returns the indicated properties of the
-;;; last-executed test result.
-
-(define (prop-runner props thunk)
- (let ((r (test-runner-null))
- (plist '()))
- ;;
- (test-runner-on-test-end!
- r
- (lambda (runner)
- (set! plist (test-result-alist runner))))
- ;;
- (test-with-runner r (thunk))
- ;; reorder the properties so they are in the order
- ;; given by `props'. Note that any property listed in `props'
- ;; that is not in the property alist will occur as #f
- (map (lambda (k)
- (assq k plist))
- props)))
-
-;;; `on-test-runner' creates a null test runner and then
-;;; arranged for `visit' to be called with the runner
-;;; whenever a test is run. The results of the calls to
-;;; `visit' are returned in a list
-
-(define (on-test-runner thunk visit)
- (let ((r (test-runner-null))
- (results '()))
- ;;
- (test-runner-on-test-end!
- r
- (lambda (runner)
- (set! results (cons (visit r) results))))
- ;;
- (test-with-runner r (thunk))
- (reverse results)))
-
-;;;
-;;; The `triv-runner' invokes `thunk'
-;;; and returns a list of 6 lists, the first 5 of which
-;;; are a list of the names of the tests that, respectively,
-;;; PASS, FAIL, XFAIL, XPASS, and SKIP.
-;;; The last item is a list of counts.
-;;;
-
-(define (triv-runner thunk)
- (let ((r (test-runner-null))
- (accum-pass '())
- (accum-fail '())
- (accum-xfail '())
- (accum-xpass '())
- (accum-skip '()))
- ;;
- (test-runner-on-bad-count!
- r
- (lambda (runner count expected-count)
- (error (string-append "bad count " (number->string count)
- " but expected "
- (number->string expected-count)))))
- (test-runner-on-bad-end-name!
- r
- (lambda (runner begin end)
- (error (string-append "bad end group name " end
- " but expected " begin))))
- (test-runner-on-test-end!
- r
- (lambda (runner)
- (let ((n (test-runner-test-name runner)))
- (case (test-result-kind runner)
- ((pass) (set! accum-pass (cons n accum-pass)))
- ((fail) (set! accum-fail (cons n accum-fail)))
- ((xpass) (set! accum-xpass (cons n accum-xpass)))
- ((xfail) (set! accum-xfail (cons n accum-xfail)))
- ((skip) (set! accum-skip (cons n accum-skip)))))))
- ;;
- (test-with-runner r (thunk))
- (list (reverse accum-pass) ; passed as expected
- (reverse accum-fail) ; failed, but was expected to pass
- (reverse accum-xfail) ; failed as expected
- (reverse accum-xpass) ; passed, but was expected to fail
- (reverse accum-skip) ; was not executed
- (list (test-runner-pass-count r)
- (test-runner-fail-count r)
- (test-runner-xfail-count r)
- (test-runner-xpass-count r)
- (test-runner-skip-count r)))))
-
-(define (path-revealing-runner thunk)
- (let ((r (test-runner-null))
- (seq '()))
- ;;
- (test-runner-on-test-end!
- r
- (lambda (runner)
- (set! seq (cons (list (test-runner-group-path runner)
- (test-runner-test-name runner))
- seq))))
- (test-with-runner r (thunk))
- (reverse seq)))
-
-;;;
-;;; Now we can start testing compliance with SRFI-64
-;;;
-
-(test-begin "1. Simple test-cases")
-
-(test-begin "1.1. test-assert")
-
-(define (t)
- (triv-runner
- (lambda ()
- (test-assert "a" #t)
- (test-assert "b" #f))))
-
-(test-equal
- "1.1.1. Very simple"
- '(("a") ("b") () () () (1 1 0 0 0))
- (t))
-
-(test-equal
- "1.1.2. A test with no name"
- '(("a") ("") () () () (1 1 0 0 0))
- (triv-runner (lambda () (test-assert "a" #t) (test-assert #f))))
-
-(test-equal
- "1.1.3. Tests can have the same name"
- '(("a" "a") () () () () (2 0 0 0 0))
- (triv-runner (lambda () (test-assert "a" #t) (test-assert "a" #t))))
-
-(define (choke)
- (vector-ref '#(1 2) 3))
-
-(test-equal
- "1.1.4. One way to FAIL is to throw an error"
- '(() ("a") () () () (0 1 0 0 0))
- (triv-runner (lambda () (test-assert "a" (choke)))))
-
-(test-end);1.1
-
-(test-begin "1.2. test-eqv")
-
-(define (mean x y)
- (/ (+ x y) 2.0))
-
-(test-equal
- "1.2.1. Simple numerical equivalence"
- '(("c") ("a" "b") () () () (1 2 0 0 0))
- (triv-runner
- (lambda ()
- (test-eqv "a" (mean 3 5) 4)
- (test-eqv "b" (mean 3 5) 4.5)
- (test-eqv "c" (mean 3 5) 4.0))))
-
-(test-end);1.2
-
-(test-end "1. Simple test-cases")
-
-;;;
-;;;
-;;;
-
-(test-begin "2. Tests for catching errors")
-
-(test-begin "2.1. test-error")
-
-(test-equal
- "2.1.1. Baseline test; PASS with no optional args"
- '(("") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- ;; PASS
- (test-error (vector-ref '#(1 2) 9)))))
-
-(test-equal
- "2.1.2. Baseline test; FAIL with no optional args"
- '(() ("") () () () (0 1 0 0 0))
- (triv-runner
- (lambda ()
- ;; FAIL: the expr does not raise an error and `test-error' is
- ;; claiming that it will, so this test should FAIL
- (test-error (vector-ref '#(1 2) 0)))))
-
-(test-equal
- "2.1.3. PASS with a test name and error type"
- '(("a") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- ;; PASS
- (test-error "a" #t (vector-ref '#(1 2) 9)))))
-
-(test-end "2.1. test-error")
-
-(test-end "2. Tests for catching errors")
-
-;;;
-;;;
-;;;
-
-(test-begin "3. Test groups and paths")
-
-(test-equal
- "3.1. test-begin with unspecific test-end"
- '(("b") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "b" #t)
- (test-end))))
-
-(test-equal
- "3.2. test-begin with name-matching test-end"
- '(("b") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "b" #t)
- (test-end "a"))))
-
-;;; since the error raised by `test-end' on a mismatch is not a test
-;;; error, we actually expect the triv-runner itself to fail
-
-(test-error
- "3.3. test-begin with mismatched test-end"
-#t
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "b" #t)
- (test-end "x"))))
-
-(test-equal
- "3.4. test-begin with name and count"
- '(("b" "c") () () () () (2 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-begin "a" 2)
- (test-assert "b" #t)
- (test-assert "c" #t)
- (test-end "a"))))
-
-;; similarly here, a mismatched count is a lexical error
-;; and not a test failure...
-
-(test-error
- "3.5. test-begin with mismatched count"
- #t
- (triv-runner
- (lambda ()
- (test-begin "a" 99)
- (test-assert "b" #t)
- (test-end "a"))))
-
-(test-equal
- "3.6. introspecting on the group path"
- '((() "w")
- (("a" "b") "x")
- (("a" "b") "y")
- (("a") "z"))
- ;;
- ;; `path-revealing-runner' is designed to return a list
- ;; of the tests executed, in order. Each entry is a list
- ;; (GROUP-PATH TEST-NAME), and each GROUP-PATH is a list
- ;; of test groups starting from the topmost
- ;;
- (path-revealing-runner
- (lambda ()
- (test-assert "w" #t)
- (test-begin "a")
- (test-begin "b")
- (test-assert "x" #t)
- (test-assert "y" #t)
- (test-end)
- (test-assert "z" #t))))
-
-
-(test-end "3. Test groups and paths")
-
-;;;
-;;;
-;;;
-
-(test-begin "4. Handling set-up and cleanup")
-
-(test-equal "4.1. Normal exit path"
- '(in 1 2 out)
- (let ((ex '()))
- (define (do s)
- (set! ex (cons s ex)))
- ;;
- (triv-runner
- (lambda ()
- (test-group-with-cleanup
- "foo"
- (do 'in)
- (do 1)
- (do 2)
- (do 'out))))
- (reverse ex)))
-
-(test-equal "4.2. Exception exit path"
- '(in 1 out)
- (let ((ex '()))
- (define (do s)
- (set! ex (cons s ex)))
- ;;
- ;; the outer runner is to run the `test-error' in, to
- ;; catch the exception raised in the inner runner,
- ;; since we don't want to depend on any other
- ;; exception-catching support
- ;;
- (triv-runner
- (lambda ()
- (test-error
- (triv-runner
- (lambda ()
- (test-group-with-cleanup
- "foo"
- (do 'in) (test-assert #t)
- (do 1) (test-assert #t)
- (choke) (test-assert #t)
- (do 2) (test-assert #t)
- (do 'out)))))))
- (reverse ex)))
-
-(test-end "4. Handling set-up and cleanup")
-
-;;;
-;;;
-;;;
-
-(test-begin "5. Test specifiers")
-
-(test-begin "5.1. test-match-named")
-
-(test-equal "5.1.1. match test names"
- '(("y") () () () ("x") (1 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-skip (test-match-name "x"))
- (test-assert "x" #t)
- (test-assert "y" #t))))
-
-(test-equal "5.1.2. but not group names"
- '(("z") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-skip (test-match-name "x"))
- (test-begin "x")
- (test-assert "z" #t)
- (test-end))))
-
-(test-end)
-
-(test-begin "5.2. test-match-nth")
-;; See also: [6.4. Short-circuit evaluation]
-
-(test-equal "5.2.1. skip the nth one after"
- '(("v" "w" "y" "z") () () () ("x") (4 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-nth 2))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP
- (test-assert "y" #t) ; 3
- (test-assert "z" #t)))) ; 4
-
-(test-equal "5.2.2. skip m, starting at n"
- '(("v" "w" "z") () () () ("x" "y") (3 0 0 0 2))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-nth 2 2))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP
- (test-assert "y" #t) ; 3 SKIP
- (test-assert "z" #t)))) ; 4
-
-(test-end)
-
-(test-begin "5.3. test-match-any")
-(test-equal "5.3.1. basic disjunction"
- '(("v" "w" "z") () () () ("x" "y") (3 0 0 0 2))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-any (test-match-nth 3)
- (test-match-name "x")))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP(NAME)
- (test-assert "y" #t) ; 3 SKIP(COUNT)
- (test-assert "z" #t)))) ; 4
-
-(test-equal "5.3.2. disjunction is commutative"
- '(("v" "w" "z") () () () ("x" "y") (3 0 0 0 2))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-any (test-match-name "x")
- (test-match-nth 3)))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP(NAME)
- (test-assert "y" #t) ; 3 SKIP(COUNT)
- (test-assert "z" #t)))) ; 4
-
-(test-end)
-
-(test-begin "5.4. test-match-all")
-(test-equal "5.4.1. basic conjunction"
- '(("v" "w" "y" "z") () () () ("x") (4 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-all (test-match-nth 2 2)
- (test-match-name "x")))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP(NAME) & SKIP(COUNT)
- (test-assert "y" #t) ; 3 SKIP(COUNT)
- (test-assert "z" #t)))) ; 4
-
-(test-equal "5.4.2. conjunction is commutative"
- '(("v" "w" "y" "z") () () () ("x") (4 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-all (test-match-name "x")
- (test-match-nth 2 2)))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP(NAME) & SKIP(COUNT)
- (test-assert "y" #t) ; 3 SKIP(COUNT)
- (test-assert "z" #t)))) ; 4
-
-(test-end)
-
-(test-end "5. Test specifiers")
-
-;;;
-;;;
-;;;
-
-(test-begin "6. Skipping selected tests")
-
-(test-equal
- "6.1. Skip by specifier - match-name"
- '(("x") () () () ("y") (1 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip (test-match-name "y"))
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; SKIP
- (test-end))))
-
-(test-equal
- "6.2. Shorthand specifiers"
- '(("x") () () () ("y") (1 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "y")
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; SKIP
- (test-end))))
-
-(test-begin "6.3. Specifier Stack")
-
-(test-equal
- "6.3.1. Clearing the Specifier Stack"
- '(("x" "x") ("y") () () ("y") (2 1 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "y")
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; SKIP
- (test-end)
- (test-begin "b")
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; FAIL
- (test-end))))
-
-(test-equal
- "6.3.2. Inheriting the Specifier Stack"
- '(("x" "x") () () () ("y" "y") (2 0 0 0 2))
- (triv-runner
- (lambda ()
- (test-skip "y")
- (test-begin "a")
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; SKIP
- (test-end)
- (test-begin "b")
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; SKIP
- (test-end))))
-
-(test-end);6.3
-
-(test-begin "6.4. Short-circuit evaluation")
-
-(test-equal
- "6.4.1. In test-match-all"
- '(("x") ("y" "x" "z") () () ("y") (1 3 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip (test-match-all "y" (test-match-nth 2)))
- ;; let's label the substructure forms so we can
- ;; see which one `test-match-nth' is going to skip
- ;; ; # "y" 2 result
- (test-assert "x" #t) ; 1 - #f #f PASS
- (test-assert "y" #f) ; 2 - #t #t SKIP
- (test-assert "y" #f) ; 3 - #t #f FAIL
- (test-assert "x" #f) ; 4 - #f #f FAIL
- (test-assert "z" #f) ; 5 - #f #f FAIL
- (test-end))))
-
-(test-equal
- "6.4.2. In separate skip-list entries"
- '(("x") ("x" "z") () () ("y" "y") (1 2 0 0 2))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "y")
- (test-skip (test-match-nth 2))
- ;; let's label the substructure forms so we can
- ;; see which one `test-match-nth' is going to skip
- ;; ; # "y" 2 result
- (test-assert "x" #t) ; 1 - #f #f PASS
- (test-assert "y" #f) ; 2 - #t #t SKIP
- (test-assert "y" #f) ; 3 - #t #f SKIP
- (test-assert "x" #f) ; 4 - #f #f FAIL
- (test-assert "z" #f) ; 5 - #f #f FAIL
- (test-end))))
-
-(test-begin "6.4.3. Skipping test suites")
-
-(test-equal
- "6.4.3.1. Introduced using 'test-begin'"
- '(("x") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "b")
- (test-begin "b") ; not skipped
- (test-assert "x" #t)
- (test-end "b")
- (test-end "a"))))
-
-(test-expect-fail 1) ;; ???
-(test-equal
- "6.4.3.2. Introduced using 'test-group'"
- '(() () () () () (0 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "b")
- (test-group
- "b" ; skipped
- (test-assert "x" #t))
- (test-end "a"))))
-
-(test-equal
- "6.4.3.3. Non-skipped 'test-group'"
- '(("x") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "c")
- (test-group "b" (test-assert "x" #t))
- (test-end "a"))))
-
-(test-end) ; 6.4.3
-
-(test-end);6.4
-
-(test-end "6. Skipping selected tests")
-
-;;;
-;;;
-;;;
-
-(test-begin "7. Expected failures")
-
-(test-equal "7.1. Simple example"
- '(() ("x") ("z") () () (0 1 1 0 0))
- (triv-runner
- (lambda ()
- (test-assert "x" #f)
- (test-expect-fail "z")
- (test-assert "z" #f))))
-
-(test-equal "7.2. Expected exception"
- '(() ("x") ("z") () () (0 1 1 0 0))
- (triv-runner
- (lambda ()
- (test-assert "x" #f)
- (test-expect-fail "z")
- (test-assert "z" (choke)))))
-
-(test-equal "7.3. Unexpectedly PASS"
- '(() () ("y") ("x") () (0 0 1 1 0))
- (triv-runner
- (lambda ()
- (test-expect-fail "x")
- (test-expect-fail "y")
- (test-assert "x" #t)
- (test-assert "y" #f))))
-
-
-
-(test-end "7. Expected failures")
-
-;;;
-;;;
-;;;
-
-(test-begin "8. Test-runner")
-
-;;;
-;;; Because we want this test suite to be accurate even
-;;; when the underlying implementation chooses to use, e.g.,
-;;; a global variable to implement what could be thread variables
-;;; or SRFI-39 parameter objects, we really need to save and restore
-;;; their state ourselves
-;;;
-(define (with-factory-saved thunk)
- (let* ((saved (test-runner-factory))
- (result (thunk)))
- (test-runner-factory saved)
- result))
-
-(test-begin "8.1. test-runner-current")
-(test-assert "8.1.1. automatically restored"
- (let ((a 0)
- (b 1)
- (c 2))
- ;
- (triv-runner
- (lambda ()
- (set! a (test-runner-current))
- ;;
- (triv-runner
- (lambda ()
- (set! b (test-runner-current))))
- ;;
- (set! c (test-runner-current))))
- ;;
- (and (eq? a c)
- (not (eq? a b)))))
-
-(test-end)
-
-(test-begin "8.2. test-runner-simple")
-(test-assert "8.2.1. default on-test hook"
- (eq? (test-runner-on-test-end (test-runner-simple))
- test-on-test-end-simple))
-(test-assert "8.2.2. default on-final hook"
- (eq? (test-runner-on-final (test-runner-simple))
- test-on-final-simple))
-(test-end)
-
-(test-begin "8.3. test-runner-factory")
-
-(test-assert "8.3.1. default factory"
- (eq? (test-runner-factory) test-runner-simple))
-
-(test-assert "8.3.2. settable factory"
- (with-factory-saved
- (lambda ()
- (test-runner-factory test-runner-null)
- ;; we have no way, without bringing in other SRFIs,
- ;; to make sure the following doesn't print anything,
- ;; but it shouldn't:
- (test-with-runner
- (test-runner-create)
- (lambda ()
- (test-begin "a")
- (test-assert #t) ; pass
- (test-assert #f) ; fail
- (test-assert (vector-ref '#(3) 10)) ; fail with error
- (test-end "a")))
- (eq? (test-runner-factory) test-runner-null))))
-
-(test-end)
-
-;;; This got tested about as well as it could in 8.3.2
-
-(test-begin "8.4. test-runner-create")
-(test-end)
-
-;;; This got tested about as well as it could in 8.3.2
-
-(test-begin "8.5. test-runner-factory")
-(test-end)
-
-(test-begin "8.6. test-apply")
-(test-equal "8.6.1. Simple (form 1) test-apply"
- '(("w" "p" "v") () () () ("x") (3 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "w" #t)
- (test-apply
- (test-match-name "p")
- (lambda ()
- (test-begin "p")
- (test-assert "x" #t)
- (test-end)
- (test-begin "z")
- (test-assert "p" #t) ; only this one should execute in here
- (test-end)))
- (test-assert "v" #t))))
-
-(test-equal "8.6.2. Simple (form 2) test-apply"
- '(("w" "p" "v") () () () ("x") (3 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "w" #t)
- (test-apply
- (test-runner-current)
- (test-match-name "p")
- (lambda ()
- (test-begin "p")
- (test-assert "x" #t)
- (test-end)
- (test-begin "z")
- (test-assert "p" #t) ; only this one should execute in here
- (test-end)))
- (test-assert "v" #t))))
-
-(test-expect-fail 1) ;; depends on all test-match-nth being called.
-(test-equal "8.6.3. test-apply with skips"
- '(("w" "q" "v") () () () ("x" "p" "x") (3 0 0 0 3))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "w" #t)
- (test-skip (test-match-nth 2))
- (test-skip (test-match-nth 4))
- (test-apply
- (test-runner-current)
- (test-match-name "p")
- (test-match-name "q")
- (lambda ()
- ; only execute if SKIP=no and APPLY=yes
- (test-assert "x" #t) ; # 1 SKIP=no APPLY=no
- (test-assert "p" #t) ; # 2 SKIP=yes APPLY=yes
- (test-assert "q" #t) ; # 3 SKIP=no APPLY=yes
- (test-assert "x" #f) ; # 4 SKIP=yes APPLY=no
- 0))
- (test-assert "v" #t))))
-
-;;; Unfortunately, since there is no way to UNBIND the current test runner,
-;;; there is no way to test the behavior of `test-apply' in the absence
-;;; of a current runner within our little meta-test framework.
-;;;
-;;; To test the behavior manually, you should be able to invoke:
-;;;
-;;; (test-apply "a" (lambda () (test-assert "a" #t)))
-;;;
-;;; from the top level (with SRFI 64 available) and it should create a
-;;; new, default (simple) test runner.
-
-(test-end)
-
-;;; This entire suite depends heavily on 'test-with-runner'. If it didn't
-;;; work, this suite would probably go down in flames
-(test-begin "8.7. test-with-runner")
-(test-end)
-
-;;; Again, this suite depends heavily on many of the test-runner
-;;; components. We'll just test those that aren't being exercised
-;;; by the meta-test framework
-(test-begin "8.8. test-runner components")
-
-(define (auxtrack-runner thunk)
- (let ((r (test-runner-null)))
- (test-runner-aux-value! r '())
- (test-runner-on-test-end! r (lambda (r)
- (test-runner-aux-value!
- r
- (cons (test-runner-test-name r)
- (test-runner-aux-value r)))))
- (test-with-runner r (thunk))
- (reverse (test-runner-aux-value r))))
-
-(test-equal "8.8.1. test-runner-aux-value"
- '("x" "" "y")
- (auxtrack-runner
- (lambda ()
- (test-assert "x" #t)
- (test-begin "a")
- (test-assert #t)
- (test-end)
- (test-assert "y" #f))))
-
-(test-end) ; 8.8
-
-(test-end "8. Test-runner")
-
-(test-begin "9. Test Result Properties")
-
-(test-begin "9.1. test-result-alist")
-
-(define (symbol-alist? l)
- (if (null? l)
- #t
- (and (pair? l)
- (pair? (car l))
- (symbol? (caar l))
- (symbol-alist? (cdr l)))))
-
-;;; check the various syntactic forms
-
-(test-assert (symbol-alist?
- (car (on-test-runner
- (lambda ()
- (test-assert #t))
- (lambda (r)
- (test-result-alist r))))))
-
-(test-assert (symbol-alist?
- (car (on-test-runner
- (lambda ()
- (test-assert #t))
- (lambda (r)
- (test-result-alist r))))))
-
-;;; check to make sure the required properties are returned
-
-(test-equal '((result-kind . pass))
- (prop-runner
- '(result-kind)
- (lambda ()
- (test-assert #t)))
- )
-
-(test-equal
- '((result-kind . fail)
- (expected-value . 2)
- (actual-value . 3))
- (prop-runner
- '(result-kind expected-value actual-value)
- (lambda ()
- (test-equal 2 (+ 1 2)))))
-
-(test-end "9.1. test-result-alist")
-
-(test-begin "9.2. test-result-ref")
-
-(test-equal '(pass)
- (on-test-runner
- (lambda ()
- (test-assert #t))
- (lambda (r)
- (test-result-ref r 'result-kind))))
-
-(test-equal '(pass)
- (on-test-runner
- (lambda ()
- (test-assert #t))
- (lambda (r)
- (test-result-ref r 'result-kind))))
-
-(test-equal '(fail pass)
- (on-test-runner
- (lambda ()
- (test-assert (= 1 2))
- (test-assert (= 1 1)))
- (lambda (r)
- (test-result-ref r 'result-kind))))
-
-(test-end "9.2. test-result-ref")
-
-(test-begin "9.3. test-result-set!")
-
-(test-equal '(100 100)
- (on-test-runner
- (lambda ()
- (test-assert (= 1 2))
- (test-assert (= 1 1)))
- (lambda (r)
- (test-result-set! r 'foo 100)
- (test-result-ref r 'foo))))
-
-(test-end "9.3. test-result-set!")
-
-(test-end "9. Test Result Properties")
-
-;;;
-;;;
-;;;
-
-;#| Time to stop having fun...
-;
-;(test-begin "9. For fun, some meta-test errors")
-;
-;(test-equal
-; "9.1. Really PASSes, but test like it should FAIL"
-; '(() ("b") () () ())
-; (triv-runner
-; (lambda ()
-; (test-assert "b" #t))))
-;
-;(test-expect-fail "9.2. Expect to FAIL and do so")
-;(test-expect-fail "9.3. Expect to FAIL but PASS")
-;(test-skip "9.4. SKIP this one")
-;
-;(test-assert "9.2. Expect to FAIL and do so" #f)
-;(test-assert "9.3. Expect to FAIL but PASS" #t)
-;(test-assert "9.4. SKIP this one" #t)
-;
-;(test-end)
-; |#
-
-(test-end "SRFI 64 - Meta-Test Suite")
-
-;;;
-;;; Copyright (C) Philip L. Bewig (2007). All Rights Reserved.
-
-;;; Made an R7RS library by Taylan Ulrich Bayırlı/Kammer, Copyright (C) 2014.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-;;; Eval these in Emacs:
-;; (put 'stream-lambda 'scheme-indent-function 1)
-;; (put 'stream-let 'scheme-indent-function 2)
-
-(define-syntax define-stream
- (syntax-rules ()
- ((define-stream (name . formal) body0 body1 ...)
- (define name (stream-lambda formal body0 body1 ...)))))
-
-(define (list->stream objs)
- (define list->stream
- (stream-lambda (objs)
- (if (null? objs)
- stream-null
- (stream-cons (car objs) (list->stream (cdr objs))))))
- (if (not (list? objs))
- (error "non-list argument" objs)
- (list->stream objs)))
-
-(define (port->stream . port)
- (define port->stream
- (stream-lambda (p)
- (let ((c (read-char p)))
- (if (eof-object? c)
- stream-null
- (stream-cons c (port->stream p))))))
- (let ((p (if (null? port) (current-input-port) (car port))))
- (if (not (input-port? p))
- (error "non-input-port argument" p)
- (port->stream p))))
-
-(define-syntax stream
- (syntax-rules ()
- ((stream) stream-null)
- ((stream x y ...) (stream-cons x (stream y ...)))))
-
-(define (stream->list . args)
- (let ((n (if (= 1 (length args)) #f (car args)))
- (strm (if (= 1 (length args)) (car args) (cadr args))))
- (cond
- ((not (stream? strm)) (error "non-stream argument" strm))
- ((and n (not (integer? n))) (error "non-integer count" n))
- ((and n (negative? n)) (error "negative count" n))
- (else (let loop ((n (if n n -1)) (strm strm))
- (if (or (zero? n) (stream-null? strm))
- '()
- (cons (stream-car strm)
- (loop (- n 1) (stream-cdr strm)))))))))
-
-(define (stream-append . strms)
- (define stream-append
- (stream-lambda (strms)
- (cond
- ((null? (cdr strms)) (car strms))
- ((stream-null? (car strms)) (stream-append (cdr strms)))
- (else (stream-cons (stream-car (car strms))
- (stream-append (cons (stream-cdr (car strms))
- (cdr strms))))))))
- (cond
- ((null? strms) stream-null)
- ((find (lambda (x) (not (stream? x))) strms)
- => (lambda (strm)
- (error "non-stream argument" strm)))
- (else (stream-append strms))))
-
-(define (stream-concat strms)
- (define stream-concat
- (stream-lambda (strms)
- (cond
- ((stream-null? strms) stream-null)
- ((not (stream? (stream-car strms)))
- (error "non-stream object in input stream" strms))
- ((stream-null? (stream-car strms))
- (stream-concat (stream-cdr strms)))
- (else (stream-cons
- (stream-car (stream-car strms))
- (stream-concat
- (stream-cons (stream-cdr (stream-car strms))
- (stream-cdr strms))))))))
- (if (not (stream? strms))
- (error "non-stream argument" strms)
- (stream-concat strms)))
-
-(define stream-constant
- (stream-lambda objs
- (cond
- ((null? objs) stream-null)
- ((null? (cdr objs)) (stream-cons (car objs)
- (stream-constant (car objs))))
- (else (stream-cons (car objs)
- (apply stream-constant
- (append (cdr objs) (list (car objs)))))))))
-
-(define (stream-drop n strm)
- (define stream-drop
- (stream-lambda (n strm)
- (if (or (zero? n) (stream-null? strm))
- strm
- (stream-drop (- n 1) (stream-cdr strm)))))
- (cond
- ((not (integer? n)) (error "non-integer argument" n))
- ((negative? n) (error "negative argument" n))
- ((not (stream? strm)) (error "non-stream argument" strm))
- (else (stream-drop n strm))))
-
-(define (stream-drop-while pred? strm)
- (define stream-drop-while
- (stream-lambda (strm)
- (if (and (stream-pair? strm) (pred? (stream-car strm)))
- (stream-drop-while (stream-cdr strm))
- strm)))
- (cond
- ((not (procedure? pred?)) (error "non-procedural argument" pred?))
- ((not (stream? strm)) (error "non-stream argument" strm))
- (else (stream-drop-while strm))))
-
-(define (stream-filter pred? strm)
- (define stream-filter
- (stream-lambda (strm)
- (cond
- ((stream-null? strm) stream-null)
- ((pred? (stream-car strm))
- (stream-cons (stream-car strm) (stream-filter (stream-cdr strm))))
- (else (stream-filter (stream-cdr strm))))))
- (cond
- ((not (procedure? pred?)) (error "non-procedural argument" pred?))
- ((not (stream? strm)) (error "non-stream argument" strm))
- (else (stream-filter strm))))
-
-(define (stream-fold proc base strm)
- (cond
- ((not (procedure? proc)) (error "non-procedural argument" proc))
- ((not (stream? strm)) (error "non-stream argument" strm))
- (else (let loop ((base base) (strm strm))
- (if (stream-null? strm)
- base
- (loop (proc base (stream-car strm)) (stream-cdr strm)))))))
-
-(define (stream-for-each proc . strms)
- (define (stream-for-each strms)
- (if (not (find stream-null? strms))
- (begin (apply proc (map stream-car strms))
- (stream-for-each (map stream-cdr strms)))))
- (cond
- ((not (procedure? proc)) (error "non-procedural argument" proc))
- ((null? strms) (error "no stream arguments"))
- ((find (lambda (x) (not (stream? x))) strms)
- => (lambda (strm)
- (error "non-stream argument" strm)))
- (else (stream-for-each strms))))
-
-(define (stream-from first . step)
- (define stream-from
- (stream-lambda (first delta)
- (stream-cons first (stream-from (+ first delta) delta))))
- (let ((delta (if (null? step) 1 (car step))))
- (cond
- ((not (number? first)) (error "non-numeric starting number" first))
- ((not (number? delta)) (error "non-numeric step size" delta))
- (else (stream-from first delta)))))
-
-(define (stream-iterate proc base)
- (define stream-iterate
- (stream-lambda (base)
- (stream-cons base (stream-iterate (proc base)))))
- (if (not (procedure? proc))
- (error "non-procedural argument" proc)
- (stream-iterate base)))
-
-(define (stream-length strm)
- (if (not (stream? strm))
- (error "non-stream argument" strm)
- (let loop ((len 0) (strm strm))
- (if (stream-null? strm)
- len
- (loop (+ len 1) (stream-cdr strm))))))
-
-(define-syntax stream-let
- (syntax-rules ()
- ((stream-let tag ((name val) ...) body1 body2 ...)
- ((letrec ((tag (stream-lambda (name ...) body1 body2 ...)))
- tag)
- val ...))))
-
-(define (stream-map proc . strms)
- (define stream-map
- (stream-lambda (strms)
- (if (find stream-null? strms)
- stream-null
- (stream-cons (apply proc (map stream-car strms))
- (stream-map (map stream-cdr strms))))))
- (cond
- ((not (procedure? proc)) (error "non-procedural argument" proc))
- ((null? strms) (error "no stream arguments"))
- ((find (lambda (x) (not (stream? x))) strms)
- => (lambda (strm)
- (error "non-stream argument" strm)))
- (else (stream-map strms))))
-
-(define-syntax stream-match
- (syntax-rules ()
- ((stream-match strm-expr clause ...)
- (let ((strm strm-expr))
- (cond
- ((not (stream? strm)) (error "non-stream argument" strm))
- ((stream-match-test strm clause) => car) ...
- (else (error "pattern failure")))))))
-
-(define-syntax stream-match-test
- (syntax-rules ()
- ((stream-match-test strm (pattern fender expr))
- (stream-match-pattern strm pattern () (and fender (list expr))))
- ((stream-match-test strm (pattern expr))
- (stream-match-pattern strm pattern () (list expr)))))
-
-(define-syntax stream-match-pattern
- (syntax-rules (_)
- ((stream-match-pattern strm () (binding ...) body)
- (and (stream-null? strm) (let (binding ...) body)))
- ((stream-match-pattern strm (_ . rest) (binding ...) body)
- (and (stream-pair? strm)
- (let ((strm (stream-cdr strm)))
- (stream-match-pattern strm rest (binding ...) body))))
- ((stream-match-pattern strm (var . rest) (binding ...) body)
- (and (stream-pair? strm)
- (let ((temp (stream-car strm)) (strm (stream-cdr strm)))
- (stream-match-pattern strm rest ((var temp) binding ...) body))))
- ((stream-match-pattern strm _ (binding ...) body)
- (let (binding ...) body))
- ((stream-match-pattern strm var (binding ...) body)
- (let ((var strm) binding ...) body))))
-
-(define-syntax stream-of
- (syntax-rules ()
- ((_ expr rest ...)
- (stream-of-aux expr stream-null rest ...))))
-
-(define-syntax stream-of-aux
- (syntax-rules (in is)
- ((stream-of-aux expr base)
- (stream-cons expr base))
- ((stream-of-aux expr base (var in stream) rest ...)
- (stream-let loop ((strm stream))
- (if (stream-null? strm)
- base
- (let ((var (stream-car strm)))
- (stream-of-aux expr (loop (stream-cdr strm)) rest ...)))))
- ((stream-of-aux expr base (var is exp) rest ...)
- (let ((var exp)) (stream-of-aux expr base rest ...)))
- ((stream-of-aux expr base pred? rest ...)
- (if pred? (stream-of-aux expr base rest ...) base))))
-
-(define (stream-range first past . step)
- (define stream-range
- (stream-lambda (first past delta lt?)
- (if (lt? first past)
- (stream-cons first (stream-range (+ first delta) past delta lt?))
- stream-null)))
- (cond
- ((not (number? first)) (error "non-numeric starting number" first))
- ((not (number? past)) (error "non-numeric ending number" past))
- (else (let ((delta (cond ((pair? step) (car step))
- ((< first past) 1)
- (else -1))))
- (if (not (number? delta))
- (error "non-numeric step size" delta)
- (let ((lt? (if (< 0 delta) < >)))
- (stream-range first past delta lt?)))))))
-
-(define (stream-ref strm n)
- (cond
- ((not (stream? strm)) (error "non-stream argument" strm))
- ((not (integer? n)) (error "non-integer argument" n))
- ((negative? n) (error "negative argument" n))
- (else (let loop ((strm strm) (n n))
- (cond
- ((stream-null? strm) (error "beyond end of stream" strm))
- ((zero? n) (stream-car strm))
- (else (loop (stream-cdr strm) (- n 1))))))))
-
-(define (stream-reverse strm)
- (define stream-reverse
- (stream-lambda (strm rev)
- (if (stream-null? strm)
- rev
- (stream-reverse (stream-cdr strm)
- (stream-cons (stream-car strm) rev)))))
- (if (not (stream? strm))
- (error "non-stream argument" strm)
- (stream-reverse strm stream-null)))
-
-(define (stream-scan proc base strm)
- (define stream-scan
- (stream-lambda (base strm)
- (if (stream-null? strm)
- (stream base)
- (stream-cons base (stream-scan (proc base (stream-car strm))
- (stream-cdr strm))))))
- (cond
- ((not (procedure? proc)) (error "non-procedural argument" proc))
- ((not (stream? strm)) (error "non-stream argument" strm))
- (else (stream-scan base strm))))
-
-(define (stream-take n strm)
- (define stream-take
- (stream-lambda (n strm)
- (if (or (stream-null? strm) (zero? n))
- stream-null
- (stream-cons (stream-car strm)
- (stream-take (- n 1) (stream-cdr strm))))))
- (cond
- ((not (stream? strm)) (error "non-stream argument" strm))
- ((not (integer? n)) (error "non-integer argument" n))
- ((negative? n) (error "negative argument" n))
- (else (stream-take n strm))))
-
-(define (stream-take-while pred? strm)
- (define stream-take-while
- (stream-lambda (strm)
- (cond
- ((stream-null? strm) stream-null)
- ((pred? (stream-car strm))
- (stream-cons (stream-car strm)
- (stream-take-while (stream-cdr strm))))
- (else stream-null))))
- (cond
- ((not (stream? strm)) (error "non-stream argument" strm))
- ((not (procedure? pred?)) (error "non-procedural argument" pred?))
- (else (stream-take-while strm))))
-
-(define (stream-unfold mapper pred? generator base)
- (define stream-unfold
- (stream-lambda (base)
- (if (pred? base)
- (stream-cons (mapper base) (stream-unfold (generator base)))
- stream-null)))
- (cond
- ((not (procedure? mapper)) (error "non-procedural mapper" mapper))
- ((not (procedure? pred?)) (error "non-procedural pred?" pred?))
- ((not (procedure? generator)) (error "non-procedural generator" generator))
- (else (stream-unfold base))))
-
-(define (stream-unfolds gen seed)
- (define (len-values gen seed)
- (call-with-values
- (lambda () (gen seed))
- (lambda vs (- (length vs) 1))))
- (define unfold-result-stream
- (stream-lambda (gen seed)
- (call-with-values
- (lambda () (gen seed))
- (lambda (next . results)
- (stream-cons results (unfold-result-stream gen next))))))
- (define result-stream->output-stream
- (stream-lambda (result-stream i)
- (let ((result (list-ref (stream-car result-stream) (- i 1))))
- (cond
- ((pair? result)
- (stream-cons
- (car result)
- (result-stream->output-stream (stream-cdr result-stream) i)))
- ((not result)
- (result-stream->output-stream (stream-cdr result-stream) i))
- ((null? result) stream-null)
- (else (error "can't happen"))))))
- (define (result-stream->output-streams result-stream)
- (let loop ((i (len-values gen seed)) (outputs '()))
- (if (zero? i)
- (apply values outputs)
- (loop (- i 1) (cons (result-stream->output-stream result-stream i)
- outputs)))))
- (if (not (procedure? gen))
- (error "non-procedural argument" gen)
- (result-stream->output-streams (unfold-result-stream gen seed))))
-
-(define (stream-zip . strms)
- (define stream-zip
- (stream-lambda (strms)
- (if (find stream-null? strms)
- stream-null
- (stream-cons (map stream-car strms)
- (stream-zip (map stream-cdr strms))))))
- (cond
- ((null? strms) (error "no stream arguments"))
- ((find (lambda (x) (not (stream? x))) strms)
- => (lambda (strm)
- (error "non-stream argument" strm)))
- (else (stream-zip strms))))
-(define-library (srfi 41 derived)
- (export
- stream-null stream-cons stream? stream-null? stream-pair? stream-car
- stream-cdr stream-lambda define-stream list->stream port->stream stream
- stream->list stream-append stream-concat stream-constant stream-drop
- stream-drop-while stream-filter stream-fold stream-for-each stream-from
- stream-iterate stream-length stream-let stream-map stream-match _
- stream-of stream-range stream-ref stream-reverse stream-scan stream-take
- stream-take-while stream-unfold stream-unfolds stream-zip
- )
- (import
- (scheme base)
- (srfi 1)
- (srfi 41 primitive))
- (include "derived.body.scm"))
-;;; Copyright (C) Philip L. Bewig (2007). All Rights Reserved.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-(library (streams derived)
-
- (export stream-null stream-cons stream? stream-null? stream-pair? stream-car
- stream-cdr stream-lambda define-stream list->stream port->stream stream
- stream->list stream-append stream-concat stream-constant stream-drop
- stream-drop-while stream-filter stream-fold stream-for-each stream-from
- stream-iterate stream-length stream-let stream-map stream-match _
- stream-of stream-range stream-ref stream-reverse stream-scan stream-take
- stream-take-while stream-unfold stream-unfolds stream-zip)
-
- (import (rnrs) (streams primitive))
-
- (define-syntax define-stream
- (syntax-rules ()
- ((define-stream (name . formal) body0 body1 ...)
- (define name (stream-lambda formal body0 body1 ...)))))
-
- (define (list->stream objs)
- (define list->stream
- (stream-lambda (objs)
- (if (null? objs)
- stream-null
- (stream-cons (car objs) (list->stream (cdr objs))))))
- (if (not (list? objs))
- (error 'list->stream "non-list argument")
- (list->stream objs)))
-
- (define (port->stream . port)
- (define port->stream
- (stream-lambda (p)
- (let ((c (read-char p)))
- (if (eof-object? c)
- stream-null
- (stream-cons c (port->stream p))))))
- (let ((p (if (null? port) (current-input-port) (car port))))
- (if (not (input-port? p))
- (error 'port->stream "non-input-port argument")
- (port->stream p))))
-
- (define-syntax stream
- (syntax-rules ()
- ((stream) stream-null)
- ((stream x y ...) (stream-cons x (stream y ...)))))
-
- (define (stream->list . args)
- (let ((n (if (= 1 (length args)) #f (car args)))
- (strm (if (= 1 (length args)) (car args) (cadr args))))
- (cond ((not (stream? strm)) (error 'stream->list "non-stream argument"))
- ((and n (not (integer? n))) (error 'stream->list "non-integer count"))
- ((and n (negative? n)) (error 'stream->list "negative count"))
- (else (let loop ((n (if n n -1)) (strm strm))
- (if (or (zero? n) (stream-null? strm))
- '()
- (cons (stream-car strm) (loop (- n 1) (stream-cdr strm)))))))))
-
- (define (stream-append . strms)
- (define stream-append
- (stream-lambda (strms)
- (cond ((null? (cdr strms)) (car strms))
- ((stream-null? (car strms)) (stream-append (cdr strms)))
- (else (stream-cons (stream-car (car strms))
- (stream-append (cons (stream-cdr (car strms)) (cdr strms))))))))
- (cond ((null? strms) stream-null)
- ((exists (lambda (x) (not (stream? x))) strms)
- (error 'stream-append "non-stream argument"))
- (else (stream-append strms))))
-
- (define (stream-concat strms)
- (define stream-concat
- (stream-lambda (strms)
- (cond ((stream-null? strms) stream-null)
- ((not (stream? (stream-car strms)))
- (error 'stream-concat "non-stream object in input stream"))
- ((stream-null? (stream-car strms))
- (stream-concat (stream-cdr strms)))
- (else (stream-cons
- (stream-car (stream-car strms))
- (stream-concat
- (stream-cons (stream-cdr (stream-car strms)) (stream-cdr strms))))))))
- (if (not (stream? strms))
- (error 'stream-concat "non-stream argument")
- (stream-concat strms)))
-
- (define stream-constant
- (stream-lambda objs
- (cond ((null? objs) stream-null)
- ((null? (cdr objs)) (stream-cons (car objs) (stream-constant (car objs))))
- (else (stream-cons (car objs)
- (apply stream-constant (append (cdr objs) (list (car objs)))))))))
-
- (define (stream-drop n strm)
- (define stream-drop
- (stream-lambda (n strm)
- (if (or (zero? n) (stream-null? strm))
- strm
- (stream-drop (- n 1) (stream-cdr strm)))))
- (cond ((not (integer? n)) (error 'stream-drop "non-integer argument"))
- ((negative? n) (error 'stream-drop "negative argument"))
- ((not (stream? strm)) (error 'stream-drop "non-stream argument"))
- (else (stream-drop n strm))))
-
- (define (stream-drop-while pred? strm)
- (define stream-drop-while
- (stream-lambda (strm)
- (if (and (stream-pair? strm) (pred? (stream-car strm)))
- (stream-drop-while (stream-cdr strm))
- strm)))
- (cond ((not (procedure? pred?)) (error 'stream-drop-while "non-procedural argument"))
- ((not (stream? strm)) (error 'stream-drop-while "non-stream argument"))
- (else (stream-drop-while strm))))
-
- (define (stream-filter pred? strm)
- (define stream-filter
- (stream-lambda (strm)
- (cond ((stream-null? strm) stream-null)
- ((pred? (stream-car strm))
- (stream-cons (stream-car strm) (stream-filter (stream-cdr strm))))
- (else (stream-filter (stream-cdr strm))))))
- (cond ((not (procedure? pred?)) (error 'stream-filter "non-procedural argument"))
- ((not (stream? strm)) (error 'stream-filter "non-stream argument"))
- (else (stream-filter strm))))
-
- (define (stream-fold proc base strm)
- (cond ((not (procedure? proc)) (error 'stream-fold "non-procedural argument"))
- ((not (stream? strm)) (error 'stream-fold "non-stream argument"))
- (else (let loop ((base base) (strm strm))
- (if (stream-null? strm)
- base
- (loop (proc base (stream-car strm)) (stream-cdr strm)))))))
-
- (define (stream-for-each proc . strms)
- (define (stream-for-each strms)
- (if (not (exists stream-null? strms))
- (begin (apply proc (map stream-car strms))
- (stream-for-each (map stream-cdr strms)))))
- (cond ((not (procedure? proc)) (error 'stream-for-each "non-procedural argument"))
- ((null? strms) (error 'stream-for-each "no stream arguments"))
- ((exists (lambda (x) (not (stream? x))) strms)
- (error 'stream-for-each "non-stream argument"))
- (else (stream-for-each strms))))
-
- (define (stream-from first . step)
- (define stream-from
- (stream-lambda (first delta)
- (stream-cons first (stream-from (+ first delta) delta))))
- (let ((delta (if (null? step) 1 (car step))))
- (cond ((not (number? first)) (error 'stream-from "non-numeric starting number"))
- ((not (number? delta)) (error 'stream-from "non-numeric step size"))
- (else (stream-from first delta)))))
-
- (define (stream-iterate proc base)
- (define stream-iterate
- (stream-lambda (base)
- (stream-cons base (stream-iterate (proc base)))))
- (if (not (procedure? proc))
- (error 'stream-iterate "non-procedural argument")
- (stream-iterate base)))
-
- (define (stream-length strm)
- (if (not (stream? strm))
- (error 'stream-length "non-stream argument")
- (let loop ((len 0) (strm strm))
- (if (stream-null? strm)
- len
- (loop (+ len 1) (stream-cdr strm))))))
-
- (define-syntax stream-let
- (syntax-rules ()
- ((stream-let tag ((name val) ...) body1 body2 ...)
- ((letrec ((tag (stream-lambda (name ...) body1 body2 ...))) tag) val ...))))
-
- (define (stream-map proc . strms)
- (define stream-map
- (stream-lambda (strms)
- (if (exists stream-null? strms)
- stream-null
- (stream-cons (apply proc (map stream-car strms))
- (stream-map (map stream-cdr strms))))))
- (cond ((not (procedure? proc)) (error 'stream-map "non-procedural argument"))
- ((null? strms) (error 'stream-map "no stream arguments"))
- ((exists (lambda (x) (not (stream? x))) strms)
- (error 'stream-map "non-stream argument"))
- (else (stream-map strms))))
-
- (define-syntax stream-match
- (syntax-rules ()
- ((stream-match strm-expr clause ...)
- (let ((strm strm-expr))
- (cond
- ((not (stream? strm)) (error 'stream-match "non-stream argument"))
- ((stream-match-test strm clause) => car) ...
- (else (error 'stream-match "pattern failure")))))))
-
- (define-syntax stream-match-test
- (syntax-rules ()
- ((stream-match-test strm (pattern fender expr))
- (stream-match-pattern strm pattern () (and fender (list expr))))
- ((stream-match-test strm (pattern expr))
- (stream-match-pattern strm pattern () (list expr)))))
-
- (define-syntax stream-match-pattern
- (lambda (x)
- (define (wildcard? x)
- (and (identifier? x)
- (free-identifier=? x (syntax _))))
- (syntax-case x ()
- ((stream-match-pattern strm () (binding ...) body)
- (syntax (and (stream-null? strm) (let (binding ...) body))))
- ((stream-match-pattern strm (w? . rest) (binding ...) body)
- (wildcard? #'w?)
- (syntax (and (stream-pair? strm)
- (let ((strm (stream-cdr strm)))
- (stream-match-pattern strm rest (binding ...) body)))))
- ((stream-match-pattern strm (var . rest) (binding ...) body)
- (syntax (and (stream-pair? strm)
- (let ((temp (stream-car strm)) (strm (stream-cdr strm)))
- (stream-match-pattern strm rest ((var temp) binding ...) body)))))
- ((stream-match-pattern strm w? (binding ...) body)
- (wildcard? #'w?)
- (syntax (let (binding ...) body)))
- ((stream-match-pattern strm var (binding ...) body)
- (syntax (let ((var strm) binding ...) body))))))
-
- (define-syntax stream-of
- (syntax-rules ()
- ((_ expr rest ...)
- (stream-of-aux expr stream-null rest ...))))
-
- (define-syntax stream-of-aux
- (syntax-rules (in is)
- ((stream-of-aux expr base)
- (stream-cons expr base))
- ((stream-of-aux expr base (var in stream) rest ...)
- (stream-let loop ((strm stream))
- (if (stream-null? strm)
- base
- (let ((var (stream-car strm)))
- (stream-of-aux expr (loop (stream-cdr strm)) rest ...)))))
- ((stream-of-aux expr base (var is exp) rest ...)
- (let ((var exp)) (stream-of-aux expr base rest ...)))
- ((stream-of-aux expr base pred? rest ...)
- (if pred? (stream-of-aux expr base rest ...) base))))
-
- (define (stream-range first past . step)
- (define stream-range
- (stream-lambda (first past delta lt?)
- (if (lt? first past)
- (stream-cons first (stream-range (+ first delta) past delta lt?))
- stream-null)))
- (cond ((not (number? first)) (error 'stream-range "non-numeric starting number"))
- ((not (number? past)) (error 'stream-range "non-numeric ending number"))
- (else (let ((delta (cond ((pair? step) (car step)) ((< first past) 1) (else -1))))
- (if (not (number? delta))
- (error 'stream-range "non-numeric step size")
- (let ((lt? (if (< 0 delta) < >)))
- (stream-range first past delta lt?)))))))
-
- (define (stream-ref strm n)
- (cond ((not (stream? strm)) (error 'stream-ref "non-stream argument"))
- ((not (integer? n)) (error 'stream-ref "non-integer argument"))
- ((negative? n) (error 'stream-ref "negative argument"))
- (else (let loop ((strm strm) (n n))
- (cond ((stream-null? strm) (error 'stream-ref "beyond end of stream"))
- ((zero? n) (stream-car strm))
- (else (loop (stream-cdr strm) (- n 1))))))))
-
- (define (stream-reverse strm)
- (define stream-reverse
- (stream-lambda (strm rev)
- (if (stream-null? strm)
- rev
- (stream-reverse (stream-cdr strm) (stream-cons (stream-car strm) rev)))))
- (if (not (stream? strm))
- (error 'stream-reverse "non-stream argument")
- (stream-reverse strm stream-null)))
-
- (define (stream-scan proc base strm)
- (define stream-scan
- (stream-lambda (base strm)
- (if (stream-null? strm)
- (stream base)
- (stream-cons base (stream-scan (proc base (stream-car strm)) (stream-cdr strm))))))
- (cond ((not (procedure? proc)) (error 'stream-scan "non-procedural argument"))
- ((not (stream? strm)) (error 'stream-scan "non-stream argument"))
- (else (stream-scan base strm))))
-
- (define (stream-take n strm)
- (define stream-take
- (stream-lambda (n strm)
- (if (or (stream-null? strm) (zero? n))
- stream-null
- (stream-cons (stream-car strm) (stream-take (- n 1) (stream-cdr strm))))))
- (cond ((not (stream? strm)) (error 'stream-take "non-stream argument"))
- ((not (integer? n)) (error 'stream-take "non-integer argument"))
- ((negative? n) (error 'stream-take "negative argument"))
- (else (stream-take n strm))))
-
- (define (stream-take-while pred? strm)
- (define stream-take-while
- (stream-lambda (strm)
- (cond ((stream-null? strm) stream-null)
- ((pred? (stream-car strm))
- (stream-cons (stream-car strm) (stream-take-while (stream-cdr strm))))
- (else stream-null))))
- (cond ((not (stream? strm)) (error 'stream-take-while "non-stream argument"))
- ((not (procedure? pred?)) (error 'stream-take-while "non-procedural argument"))
- (else (stream-take-while strm))))
-
- (define (stream-unfold mapper pred? generator base)
- (define stream-unfold
- (stream-lambda (base)
- (if (pred? base)
- (stream-cons (mapper base) (stream-unfold (generator base)))
- stream-null)))
- (cond ((not (procedure? mapper)) (error 'stream-unfold "non-procedural mapper"))
- ((not (procedure? pred?)) (error 'stream-unfold "non-procedural pred?"))
- ((not (procedure? generator)) (error 'stream-unfold "non-procedural generator"))
- (else (stream-unfold base))))
-
- (define (stream-unfolds gen seed)
- (define (len-values gen seed)
- (call-with-values
- (lambda () (gen seed))
- (lambda vs (- (length vs) 1))))
- (define unfold-result-stream
- (stream-lambda (gen seed)
- (call-with-values
- (lambda () (gen seed))
- (lambda (next . results)
- (stream-cons results (unfold-result-stream gen next))))))
- (define result-stream->output-stream
- (stream-lambda (result-stream i)
- (let ((result (list-ref (stream-car result-stream) (- i 1))))
- (cond ((pair? result)
- (stream-cons
- (car result)
- (result-stream->output-stream (stream-cdr result-stream) i)))
- ((not result)
- (result-stream->output-stream (stream-cdr result-stream) i))
- ((null? result) stream-null)
- (else (error 'stream-unfolds "can't happen"))))))
- (define (result-stream->output-streams result-stream)
- (let loop ((i (len-values gen seed)) (outputs '()))
- (if (zero? i)
- (apply values outputs)
- (loop (- i 1) (cons (result-stream->output-stream result-stream i) outputs)))))
- (if (not (procedure? gen))
- (error 'stream-unfolds "non-procedural argument")
- (result-stream->output-streams (unfold-result-stream gen seed))))
-
- (define (stream-zip . strms)
- (define stream-zip
- (stream-lambda (strms)
- (if (exists stream-null? strms)
- stream-null
- (stream-cons (map stream-car strms) (stream-zip (map stream-cdr strms))))))
- (cond ((null? strms) (error 'stream-zip "no stream arguments"))
- ((exists (lambda (x) (not (stream? x))) strms)
- (error 'stream-zip "non-stream argument"))
- (else (stream-zip strms)))))
-;;; Copyright (C) Philip L. Bewig (2007). All Rights Reserved.
-
-;;; Made an R7RS library by Taylan Ulrich Bayırlı/Kammer, Copyright (C) 2014.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-(define-record-type <stream>
- (make-stream promise)
- stream?
- (promise stream-promise stream-promise!))
-
-(define-syntax stream-lazy
- (syntax-rules ()
- ((stream-lazy expr)
- (make-stream
- (cons 'lazy (lambda () expr))))))
-
-(define (stream-eager expr)
- (make-stream
- (cons 'eager expr)))
-
-(define-syntax stream-delay
- (syntax-rules ()
- ((stream-delay expr)
- (stream-lazy (stream-eager expr)))))
-
-(define (stream-force promise)
- (let ((content (stream-promise promise)))
- (case (car content)
- ((eager) (cdr content))
- ((lazy) (let* ((promise* ((cdr content)))
- (content (stream-promise promise)))
- (if (not (eqv? (car content) 'eager))
- (begin (set-car! content (car (stream-promise promise*)))
- (set-cdr! content (cdr (stream-promise promise*)))
- (stream-promise! promise* content)))
- (stream-force promise))))))
-
-(define stream-null (stream-delay (cons 'stream 'null)))
-
-(define-record-type <stream-pare>
- (make-stream-pare kar kdr)
- stream-pare?
- (kar stream-kar)
- (kdr stream-kdr))
-
-(define (stream-pair? obj)
- (and (stream? obj) (stream-pare? (stream-force obj))))
-
-(define (stream-null? obj)
- (and (stream? obj)
- (eqv? (stream-force obj)
- (stream-force stream-null))))
-
-(define-syntax stream-cons
- (syntax-rules ()
- ((stream-cons obj strm)
- (stream-eager (make-stream-pare (stream-delay obj) (stream-lazy strm))))))
-
-(define (stream-car strm)
- (cond ((not (stream? strm)) (error "non-stream" strm))
- ((stream-null? strm) (error "null stream" strm))
- (else (stream-force (stream-kar (stream-force strm))))))
-
-(define (stream-cdr strm)
- (cond ((not (stream? strm)) (error "non-stream" strm))
- ((stream-null? strm) (error "null stream" strm))
- (else (stream-kdr (stream-force strm)))))
-
-(define-syntax stream-lambda
- (syntax-rules ()
- ((stream-lambda formals body0 body1 ...)
- (lambda formals (stream-lazy (let () body0 body1 ...))))))
-(define-library (srfi 41 primitive)
- (export
- stream-null stream-cons stream? stream-null? stream-pair?
- stream-car stream-cdr stream-lambda
- )
- (import (scheme base))
- (include "primitive.body.scm"))
-;;; Copyright (C) Philip L. Bewig (2007). All Rights Reserved.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-(library (streams primitive)
-
- (export stream-null stream-cons stream? stream-null? stream-pair?
- stream-car stream-cdr stream-lambda)
-
- (import (rnrs) (rnrs mutable-pairs))
-
- (define-record-type (stream-type make-stream stream?)
- (fields (mutable box stream-promise stream-promise!)))
-
- (define-syntax stream-lazy
- (syntax-rules ()
- ((stream-lazy expr)
- (make-stream
- (cons 'lazy (lambda () expr))))))
-
- (define (stream-eager expr)
- (make-stream
- (cons 'eager expr)))
-
- (define-syntax stream-delay
- (syntax-rules ()
- ((stream-delay expr)
- (stream-lazy (stream-eager expr)))))
-
- (define (stream-force promise)
- (let ((content (stream-promise promise)))
- (case (car content)
- ((eager) (cdr content))
- ((lazy) (let* ((promise* ((cdr content)))
- (content (stream-promise promise)))
- (if (not (eqv? (car content) 'eager))
- (begin (set-car! content (car (stream-promise promise*)))
- (set-cdr! content (cdr (stream-promise promise*)))
- (stream-promise! promise* content)))
- (stream-force promise))))))
-
- (define stream-null (stream-delay (cons 'stream 'null)))
-
- (define-record-type (stream-pare-type make-stream-pare stream-pare?)
- (fields (immutable kar stream-kar) (immutable kdr stream-kdr)))
-
- (define (stream-pair? obj)
- (and (stream? obj) (stream-pare? (stream-force obj))))
-
- (define (stream-null? obj)
- (and (stream? obj)
- (eqv? (stream-force obj)
- (stream-force stream-null))))
-
- (define-syntax stream-cons
- (syntax-rules ()
- ((stream-cons obj strm)
- (stream-eager (make-stream-pare (stream-delay obj) (stream-lazy strm))))))
-
- (define (stream-car strm)
- (cond ((not (stream? strm)) (error 'stream-car "non-stream"))
- ((stream-null? strm) (error 'stream-car "null stream"))
- (else (stream-force (stream-kar (stream-force strm))))))
-
- (define (stream-cdr strm)
- (cond ((not (stream? strm)) (error 'stream-cdr "non-stream"))
- ((stream-null? strm) (error 'stream-cdr "null stream"))
- (else (stream-kdr (stream-force strm)))))
-
- (define-syntax stream-lambda
- (syntax-rules ()
- ((stream-lambda formals body0 body1 ...)
- (lambda formals (stream-lazy (let () body0 body1 ...)))))))
-;; Copyright (c) 2005, 2006, 2007, 2012, 2013 Per Bothner
-;; Added "full" support for Chicken, Gauche, Guile and SISC.
-;; Alex Shinn, Copyright (c) 2005.
-;; Modified for Scheme Spheres by Álvaro Castro-Castilla, Copyright (c) 2012.
-;; Support for Guile 2 by Mark H Weaver <mhw@netris.org>, Copyright (c) 2014.
-;; Refactored by Taylan Ulrich Bayırlı/Kammer, Copyright (c) 2014, 2015.
-;;
-;; Permission is hereby granted, free of charge, to any person
-;; obtaining a copy of this software and associated documentation
-;; files (the "Software"), to deal in the Software without
-;; restriction, including without limitation the rights to use, copy,
-;; modify, merge, publish, distribute, sublicense, and/or sell copies
-;; of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-;;
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-;;
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-;;; Note: to prevent producing massive amounts of code from the macro-expand
-;;; phase (which makes compile times suffer and may hit code size limits in some
-;;; systems), keep macro bodies minimal by delegating work to procedures.
-
-
-;;; Grouping
-
-(define (maybe-install-default-runner suite-name)
- (when (not (test-runner-current))
- (let* ((log-file (string-append suite-name ".srfi64.log"))
- (runner (test-runner-simple log-file)))
- (%test-runner-auto-installed! runner #t)
- (test-runner-current runner))))
-
-(define (maybe-uninstall-default-runner)
- (when (%test-runner-auto-installed? (test-runner-current))
- (test-runner-current #f)))
-
-(define test-begin
- (case-lambda
- ((name)
- (test-begin name #f))
- ((name count)
- (maybe-install-default-runner name)
- (let ((r (test-runner-current)))
- (let ((skip-list (%test-runner-skip-list r))
- (skip-save (%test-runner-skip-save r))
- (fail-list (%test-runner-fail-list r))
- (fail-save (%test-runner-fail-save r))
- (total-count (%test-runner-total-count r))
- (count-list (%test-runner-count-list r))
- (group-stack (test-runner-group-stack r)))
- ((test-runner-on-group-begin r) r name count)
- (%test-runner-skip-save! r (cons skip-list skip-save))
- (%test-runner-fail-save! r (cons fail-list fail-save))
- (%test-runner-count-list! r (cons (cons total-count count)
- count-list))
- (test-runner-group-stack! r (cons name group-stack)))))))
-
-(define test-end
- (case-lambda
- (()
- (test-end #f))
- ((name)
- (let* ((r (test-runner-get))
- (groups (test-runner-group-stack r)))
- (test-result-clear r)
- (when (null? groups)
- (error "test-end not in a group"))
- (when (and name (not (equal? name (car groups))))
- ((test-runner-on-bad-end-name r) r name (car groups)))
- (let* ((count-list (%test-runner-count-list r))
- (expected-count (cdar count-list))
- (saved-count (caar count-list))
- (group-count (- (%test-runner-total-count r) saved-count)))
- (when (and expected-count
- (not (= expected-count group-count)))
- ((test-runner-on-bad-count r) r group-count expected-count))
- ((test-runner-on-group-end r) r)
- (test-runner-group-stack! r (cdr (test-runner-group-stack r)))
- (%test-runner-skip-list! r (car (%test-runner-skip-save r)))
- (%test-runner-skip-save! r (cdr (%test-runner-skip-save r)))
- (%test-runner-fail-list! r (car (%test-runner-fail-save r)))
- (%test-runner-fail-save! r (cdr (%test-runner-fail-save r)))
- (%test-runner-count-list! r (cdr count-list))
- (when (null? (test-runner-group-stack r))
- ((test-runner-on-final r) r)
- (maybe-uninstall-default-runner)))))))
-
-(define-syntax test-group
- (syntax-rules ()
- ((_ <name> <body> . <body>*)
- (%test-group <name> (lambda () <body> . <body>*)))))
-
-(define (%test-group name thunk)
- (begin
- (maybe-install-default-runner name)
- (let ((runner (test-runner-get)))
- (test-result-clear runner)
- (test-result-set! runner 'name name)
- (unless (test-skip? runner)
- (dynamic-wind
- (lambda () (test-begin name))
- thunk
- (lambda () (test-end name)))))))
-
-(define-syntax test-group-with-cleanup
- (syntax-rules ()
- ((_ <name> <body> <body>* ... <cleanup>)
- (test-group <name>
- (dynamic-wind (lambda () #f)
- (lambda () <body> <body>* ...)
- (lambda () <cleanup>))))))
-
-
-;;; Skipping, expected-failing, matching
-
-(define (test-skip . specs)
- (let ((runner (test-runner-get)))
- (%test-runner-skip-list!
- runner (cons (apply test-match-all specs)
- (%test-runner-skip-list runner)))))
-
-(define (test-skip? runner)
- (let ((run-list (%test-runner-run-list runner))
- (skip-list (%test-runner-skip-list runner)))
- (or (and run-list (not (any-pred run-list runner)))
- (any-pred skip-list runner))))
-
-(define (test-expect-fail . specs)
- (let ((runner (test-runner-get)))
- (%test-runner-fail-list!
- runner (cons (apply test-match-all specs)
- (%test-runner-fail-list runner)))))
-
-(define (test-match-any . specs)
- (let ((preds (map make-pred specs)))
- (lambda (runner)
- (any-pred preds runner))))
-
-(define (test-match-all . specs)
- (let ((preds (map make-pred specs)))
- (lambda (runner)
- (every-pred preds runner))))
-
-(define (make-pred spec)
- (cond
- ((procedure? spec)
- spec)
- ((integer? spec)
- (test-match-nth 1 spec))
- ((string? spec)
- (test-match-name spec))
- (else
- (error "not a valid test specifier" spec))))
-
-(define test-match-nth
- (case-lambda
- ((n) (test-match-nth n 1))
- ((n count)
- (let ((i 0))
- (lambda (runner)
- (set! i (+ i 1))
- (and (>= i n) (< i (+ n count))))))))
-
-(define (test-match-name name)
- (lambda (runner)
- (equal? name (test-runner-test-name runner))))
-
-;;; Beware: all predicates must be called because they might have side-effects;
-;;; no early returning or and/or short-circuiting of procedure calls allowed.
-
-(define (any-pred preds object)
- (let loop ((matched? #f)
- (preds preds))
- (if (null? preds)
- matched?
- (let ((result ((car preds) object)))
- (loop (or matched? result)
- (cdr preds))))))
-
-(define (every-pred preds object)
- (let loop ((failed? #f)
- (preds preds))
- (if (null? preds)
- (not failed?)
- (let ((result ((car preds) object)))
- (loop (or failed? (not result))
- (cdr preds))))))
-
-;;; Actual testing
-
-(define-syntax false-if-error
- (syntax-rules ()
- ((_ <expression> <runner>)
- (guard (error
- (else
- (test-result-set! <runner> 'actual-error error)
- #f))
- <expression>))))
-
-(define (test-prelude source-info runner name form)
- (test-result-clear runner)
- (set-source-info! runner source-info)
- (when name
- (test-result-set! runner 'name name))
- (test-result-set! runner 'source-form form)
- (let ((skip? (test-skip? runner)))
- (if skip?
- (test-result-set! runner 'result-kind 'skip)
- (let ((fail-list (%test-runner-fail-list runner)))
- (when (any-pred fail-list runner)
- ;; For later inspection only.
- (test-result-set! runner 'result-kind 'xfail))))
- ((test-runner-on-test-begin runner) runner)
- (not skip?)))
-
-(define (test-postlude runner)
- (let ((result-kind (test-result-kind runner)))
- (case result-kind
- ((pass)
- (test-runner-pass-count! runner (+ 1 (test-runner-pass-count runner))))
- ((fail)
- (test-runner-fail-count! runner (+ 1 (test-runner-fail-count runner))))
- ((xpass)
- (test-runner-xpass-count! runner (+ 1 (test-runner-xpass-count runner))))
- ((xfail)
- (test-runner-xfail-count! runner (+ 1 (test-runner-xfail-count runner))))
- ((skip)
- (test-runner-skip-count! runner (+ 1 (test-runner-skip-count runner)))))
- (%test-runner-total-count! runner (+ 1 (%test-runner-total-count runner)))
- ((test-runner-on-test-end runner) runner)))
-
-(define (set-result-kind! runner pass?)
- (test-result-set! runner 'result-kind
- (if (eq? (test-result-kind runner) 'xfail)
- (if pass? 'xpass 'xfail)
- (if pass? 'pass 'fail))))
-
-;;; We need to use some trickery to get the source info right. The important
-;;; thing is to pass a syntax object that is a pair to `source-info', and make
-;;; sure this syntax object comes from user code and not from ourselves.
-
-(define-syntax test-assert
- (syntax-rules ()
- ((_ . <rest>)
- (test-assert/source-info (source-info <rest>) . <rest>))))
-
-(define-syntax test-assert/source-info
- (syntax-rules ()
- ((_ <source-info> <expr>)
- (test-assert/source-info <source-info> #f <expr>))
- ((_ <source-info> <name> <expr>)
- (%test-assert <source-info> <name> '<expr> (lambda () <expr>)))))
-
-(define (%test-assert source-info name form thunk)
- (let ((runner (test-runner-get)))
- (when (test-prelude source-info runner name form)
- (let ((val (false-if-error (thunk) runner)))
- (test-result-set! runner 'actual-value val)
- (set-result-kind! runner val)))
- (test-postlude runner)))
-
-(define-syntax test-compare
- (syntax-rules ()
- ((_ . <rest>)
- (test-compare/source-info (source-info <rest>) . <rest>))))
-
-(define-syntax test-compare/source-info
- (syntax-rules ()
- ((_ <source-info> <compare> <expected> <expr>)
- (test-compare/source-info <source-info> <compare> #f <expected> <expr>))
- ((_ <source-info> <compare> <name> <expected> <expr>)
- (%test-compare <source-info> <compare> <name> <expected> '<expr>
- (lambda () <expr>)))))
-
-(define (%test-compare source-info compare name expected form thunk)
- (let ((runner (test-runner-get)))
- (when (test-prelude source-info runner name form)
- (test-result-set! runner 'expected-value expected)
- (let ((pass? (false-if-error
- (let ((val (thunk)))
- (test-result-set! runner 'actual-value val)
- (compare expected val))
- runner)))
- (set-result-kind! runner pass?)))
- (test-postlude runner)))
-
-(define-syntax test-equal
- (syntax-rules ()
- ((_ . <rest>)
- (test-compare/source-info (source-info <rest>) equal? . <rest>))))
-
-(define-syntax test-eqv
- (syntax-rules ()
- ((_ . <rest>)
- (test-compare/source-info (source-info <rest>) eqv? . <rest>))))
-
-(define-syntax test-eq
- (syntax-rules ()
- ((_ . <rest>)
- (test-compare/source-info (source-info <rest>) eq? . <rest>))))
-
-(define (approx= margin)
- (lambda (value expected)
- (let ((rval (real-part value))
- (ival (imag-part value))
- (rexp (real-part expected))
- (iexp (imag-part expected)))
- (and (>= rval (- rexp margin))
- (>= ival (- iexp margin))
- (<= rval (+ rexp margin))
- (<= ival (+ iexp margin))))))
-
-(define-syntax test-approximate
- (syntax-rules ()
- ((_ . <rest>)
- (test-approximate/source-info (source-info <rest>) . <rest>))))
-
-(define-syntax test-approximate/source-info
- (syntax-rules ()
- ((_ <source-info> <expected> <expr> <error-margin>)
- (test-approximate/source-info
- <source-info> #f <expected> <expr> <error-margin>))
- ((_ <source-info> <name> <expected> <expr> <error-margin>)
- (test-compare/source-info
- <source-info> (approx= <error-margin>) <name> <expected> <expr>))))
-
-(define (error-matches? error type)
- (cond
- ((eq? type #t)
- #t)
- ((condition-type? type)
- (and (condition? error) (condition-has-type? error type)))
- ((procedure? type)
- (type error))
- (else
- (let ((runner (test-runner-get)))
- ((%test-runner-on-bad-error-type runner) runner type error))
- #f)))
-
-(define-syntax test-error
- (syntax-rules ()
- ((_ . <rest>)
- (test-error/source-info (source-info <rest>) . <rest>))))
-
-(define-syntax test-error/source-info
- (syntax-rules ()
- ((_ <source-info> <expr>)
- (test-error/source-info <source-info> #f #t <expr>))
- ((_ <source-info> <error-type> <expr>)
- (test-error/source-info <source-info> #f <error-type> <expr>))
- ((_ <source-info> <name> <error-type> <expr>)
- (%test-error <source-info> <name> <error-type> '<expr>
- (lambda () <expr>)))))
-
-(define (%test-error source-info name error-type form thunk)
- (let ((runner (test-runner-get)))
- (when (test-prelude source-info runner name form)
- (test-result-set! runner 'expected-error error-type)
- (let ((pass? (guard (error (else (test-result-set!
- runner 'actual-error error)
- (error-matches? error error-type)))
- (let ((val (thunk)))
- (test-result-set! runner 'actual-value val))
- #f)))
- (set-result-kind! runner pass?)))
- (test-postlude runner)))
-
-(define (default-module)
- (cond-expand
- (guile (current-module))
- (else #f)))
-
-(define test-read-eval-string
- (case-lambda
- ((string)
- (test-read-eval-string string (default-module)))
- ((string env)
- (let* ((port (open-input-string string))
- (form (read port)))
- (if (eof-object? (read-char port))
- (if env
- (eval form env)
- (eval form))
- (error "(not at eof)"))))))
-
-
-;;; Test runner control flow
-
-(define-syntax test-with-runner
- (syntax-rules ()
- ((_ <runner> <body> . <body>*)
- (let ((saved-runner (test-runner-current)))
- (dynamic-wind
- (lambda () (test-runner-current <runner>))
- (lambda () <body> . <body>*)
- (lambda () (test-runner-current saved-runner)))))))
-
-(define (test-apply first . rest)
- (let ((runner (if (test-runner? first)
- first
- (or (test-runner-current) (test-runner-create))))
- (run-list (if (test-runner? first)
- (drop-right rest 1)
- (cons first (drop-right rest 1))))
- (proc (last rest)))
- (test-with-runner runner
- (let ((saved-run-list (%test-runner-run-list runner)))
- (%test-runner-run-list! runner run-list)
- (proc)
- (%test-runner-run-list! runner saved-run-list)))))
-
-
-;;; Indicate success/failure via exit status
-
-(define (test-exit)
- (let ((runner (test-runner-current)))
- (when (not runner)
- (error "No test runner installed. Might have been auto-removed
-by test-end if you had not installed one explicitly."))
- (if (and (zero? (test-runner-xpass-count runner))
- (zero? (test-runner-fail-count runner)))
- (exit 0)
- (exit 1))))
-
-;;; execution.scm ends here
-(export
- test-begin test-end test-group test-group-with-cleanup
-
- test-skip test-expect-fail
- test-match-name test-match-nth
- test-match-all test-match-any
-
- test-assert test-eqv test-eq test-equal test-approximate
- test-error test-read-eval-string
-
- test-apply test-with-runner
-
- test-exit
- )
-(define-library (srfi 64 execution)
- (import
- (scheme base)
- (scheme case-lambda)
- (scheme complex)
- (scheme eval)
- (scheme process-context)
- (scheme read)
- (srfi 1)
- (srfi 35)
- (srfi 48)
- (srfi 64 source-info)
- (srfi 64 test-runner)
- (srfi 64 test-runner-simple))
- (include-library-declarations "execution.exports.sld")
- (include "execution.body.scm"))
-;; Copyright (c) 2015 Taylan Ulrich Bayırlı/Kammer <taylanbayirli@gmail.com>
-;;
-;; Permission is hereby granted, free of charge, to any person
-;; obtaining a copy of this software and associated documentation
-;; files (the "Software"), to deal in the Software without
-;; restriction, including without limitation the rights to use, copy,
-;; modify, merge, publish, distribute, sublicense, and/or sell copies
-;; of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-;;
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-;;
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-;;; In some systems, a macro use like (source-info ...), that resides in a
-;;; syntax-rules macro body, first gets inserted into the place where the
-;;; syntax-rules macro was used, and then the transformer of 'source-info' is
-;;; called with a syntax object that has the source location information of that
-;;; position. That works fine when the user calls e.g. (test-assert ...), whose
-;;; body contains (source-info ...); the user gets the source location of the
-;;; (test-assert ...) call as intended, and not the source location of the real
-;;; (source-info ...) call.
-
-;;; In other systems, *first* the (source-info ...) is processed to get its real
-;;; position, which is within the body of a syntax-rules macro like test-assert,
-;;; so no matter where the user calls (test-assert ...), they get source
-;;; location information of where we defined test-assert with the call to
-;;; (source-info ...) in its body. That's arguably more correct behavior,
-;;; although in this case it makes our job a bit harder; we need to get the
-;;; source location from an argument to 'source-info' instead.
-
-(define (canonical-syntax form arg)
- (cond-expand
- (kawa arg)
- (guile-2 form)
- (else #f)))
-
-(cond-expand
- ((or kawa guile-2)
- (define-syntax source-info
- (lambda (stx)
- (syntax-case stx ()
- ((_ <x>)
- (let* ((stx (canonical-syntax stx (syntax <x>)))
- (file (syntax-source-file stx))
- (line (syntax-source-line stx)))
- (quasisyntax
- (cons (unsyntax file) (unsyntax line)))))))))
- (else
- (define-syntax source-info
- (syntax-rules ()
- ((_ <x>)
- #f)))))
-
-(define (syntax-source-file stx)
- (cond-expand
- (kawa
- (syntax-source stx))
- (guile-2
- (let ((source (syntax-source stx)))
- (and source (assq-ref source 'filename))))
- (else
- #f)))
-
-(define (syntax-source-line stx)
- (cond-expand
- (kawa
- (syntax-line stx))
- (guile-2
- (let ((source (syntax-source stx)))
- (and source (assq-ref source 'line))))
- (else
- #f)))
-
-(define (set-source-info! runner source-info)
- (when source-info
- (test-result-set! runner 'source-file (car source-info))
- (test-result-set! runner 'source-line (cdr source-info))))
-
-;;; source-info.body.scm ends here
-(define-library (srfi 64 source-info)
- (import
- (scheme base)
- (srfi 64 test-runner))
- (export source-info set-source-info!)
- (include "source-info.body.scm"))
-;; Copyright (c) 2005, 2006, 2007, 2012, 2013 Per Bothner
-;; Added "full" support for Chicken, Gauche, Guile and SISC.
-;; Alex Shinn, Copyright (c) 2005.
-;; Modified for Scheme Spheres by Álvaro Castro-Castilla, Copyright (c) 2012.
-;; Support for Guile 2 by Mark H Weaver <mhw@netris.org>, Copyright (c) 2014.
-;; Refactored by Taylan Ulrich Bayırlı/Kammer, Copyright (c) 2014, 2015.
-;;
-;; Permission is hereby granted, free of charge, to any person
-;; obtaining a copy of this software and associated documentation
-;; files (the "Software"), to deal in the Software without
-;; restriction, including without limitation the rights to use, copy,
-;; modify, merge, publish, distribute, sublicense, and/or sell copies
-;; of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-;;
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-;;
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-;;; Helpers
-
-(define (string-join strings delimiter)
- (if (null? strings)
- ""
- (let loop ((result (car strings))
- (rest (cdr strings)))
- (if (null? rest)
- result
- (loop (string-append result delimiter (car rest))
- (cdr rest))))))
-
-(define (truncate-string string length)
- (define (newline->space c) (if (char=? #\newline c) #\space c))
- (let* ((string (string-map newline->space string))
- (fill "...")
- (fill-len (string-length fill))
- (string-len (string-length string)))
- (if (<= string-len (+ length fill-len))
- string
- (let-values (((q r) (floor/ length 4)))
- ;; Left part gets 3/4 plus the remainder.
- (let ((left-end (+ (* q 3) r))
- (right-start (- string-len q)))
- (string-append (substring string 0 left-end)
- fill
- (substring string right-start string-len)))))))
-
-(define (print runner format-string . args)
- (apply format #t format-string args)
- (let ((port (%test-runner-log-port runner)))
- (when port
- (apply format port format-string args))))
-
-;;; Main
-
-(define test-runner-simple
- (case-lambda
- (()
- (test-runner-simple #f))
- ((log-file)
- (let ((runner (test-runner-null)))
- (test-runner-reset runner)
- (test-runner-on-group-begin! runner test-on-group-begin-simple)
- (test-runner-on-group-end! runner test-on-group-end-simple)
- (test-runner-on-final! runner test-on-final-simple)
- (test-runner-on-test-begin! runner test-on-test-begin-simple)
- (test-runner-on-test-end! runner test-on-test-end-simple)
- (test-runner-on-bad-count! runner test-on-bad-count-simple)
- (test-runner-on-bad-end-name! runner test-on-bad-end-name-simple)
- (%test-runner-on-bad-error-type! runner on-bad-error-type)
- (%test-runner-log-file! runner log-file)
- runner))))
-
-(when (not (test-runner-factory))
- (test-runner-factory test-runner-simple))
-
-(define (test-on-group-begin-simple runner name count)
- (when (null? (test-runner-group-stack runner))
- (maybe-start-logging runner)
- (print runner "Test suite begin: ~a~%" name)))
-
-(define (test-on-group-end-simple runner)
- (let ((name (car (test-runner-group-stack runner))))
- (when (= 1 (length (test-runner-group-stack runner)))
- (print runner "Test suite end: ~a~%" name))))
-
-(define (test-on-final-simple runner)
- (print runner "Passes: ~a\n" (test-runner-pass-count runner))
- (print runner "Expected failures: ~a\n" (test-runner-xfail-count runner))
- (print runner "Failures: ~a\n" (test-runner-fail-count runner))
- (print runner "Unexpected passes: ~a\n" (test-runner-xpass-count runner))
- (print runner "Skipped tests: ~a~%" (test-runner-skip-count runner))
- (maybe-finish-logging runner))
-
-(define (maybe-start-logging runner)
- (let ((log-file (%test-runner-log-file runner)))
- (when log-file
- ;; The possible race-condition here doesn't bother us.
- (when (file-exists? log-file)
- (delete-file log-file))
- (%test-runner-log-port! runner (open-output-file log-file))
- (print runner "Writing log file: ~a~%" log-file))))
-
-(define (maybe-finish-logging runner)
- (let ((log-file (%test-runner-log-file runner)))
- (when log-file
- (print runner "Wrote log file: ~a~%" log-file)
- (close-output-port (%test-runner-log-port runner)))))
-
-(define (test-on-test-begin-simple runner)
- (values))
-
-(define (test-on-test-end-simple runner)
- (let* ((result-kind (test-result-kind runner))
- (result-kind-name (case result-kind
- ((pass) "PASS") ((fail) "FAIL")
- ((xpass) "XPASS") ((xfail) "XFAIL")
- ((skip) "SKIP")))
- (name (let ((name (test-runner-test-name runner)))
- (if (string=? "" name)
- (truncate-string
- (format #f "~a" (test-result-ref runner 'source-form))
- 30)
- name)))
- (label (string-join (append (test-runner-group-path runner)
- (list name))
- ": ")))
- (print runner "[~a] ~a~%" result-kind-name label)
- (when (memq result-kind '(fail xpass))
- (let ((nil (cons #f #f)))
- (define (found? value)
- (not (eq? nil value)))
- (define (maybe-print value message)
- (when (found? value)
- (print runner message value)))
- (let ((file (test-result-ref runner 'source-file "(unknown file)"))
- (line (test-result-ref runner 'source-line "(unknown line)"))
- (expression (test-result-ref runner 'source-form))
- (expected-value (test-result-ref runner 'expected-value nil))
- (actual-value (test-result-ref runner 'actual-value nil))
- (expected-error (test-result-ref runner 'expected-error nil))
- (actual-error (test-result-ref runner 'actual-error nil)))
- (print runner "~a:~a: ~s~%" file line expression)
- (maybe-print expected-value "Expected value: ~s~%")
- (maybe-print expected-error "Expected error: ~a~%")
- (when (or (found? expected-value) (found? expected-error))
- (maybe-print actual-value "Returned value: ~s~%"))
- (maybe-print actual-error "Raised error: ~a~%")
- (newline))))))
-
-(define (test-on-bad-count-simple runner count expected-count)
- (print runner "*** Total number of tests was ~a but should be ~a. ***~%"
- count expected-count)
- (print runner
- "*** Discrepancy indicates testsuite error or exceptions. ***~%"))
-
-(define (test-on-bad-end-name-simple runner begin-name end-name)
- (error (format #f "Test-end \"~a\" does not match test-begin \"~a\"."
- end-name begin-name)))
-
-(define (on-bad-error-type runner type error)
- (print runner "WARNING: unknown error type predicate: ~a~%" type)
- (print runner " error was: ~a~%" error))
-
-;;; test-runner-simple.scm ends here
-(export
- test-runner-simple
- ;; The following are exported so you can leverage their existing functionality
- ;; when making more complex test runners.
- test-on-group-begin-simple test-on-group-end-simple test-on-final-simple
- test-on-test-begin-simple test-on-test-end-simple
- test-on-bad-count-simple test-on-bad-end-name-simple
- )
-(define-library (srfi 64 test-runner-simple)
- (import
- (scheme base)
- (scheme case-lambda)
- (scheme file)
- (scheme write)
- (srfi 48)
- (srfi 64 test-runner))
- (include-library-declarations "test-runner-simple.exports.sld")
- (include "test-runner-simple.body.scm"))
-;; Copyright (c) 2005, 2006, 2007, 2012, 2013 Per Bothner
-;; Added "full" support for Chicken, Gauche, Guile and SISC.
-;; Alex Shinn, Copyright (c) 2005.
-;; Modified for Scheme Spheres by Álvaro Castro-Castilla, Copyright (c) 2012.
-;; Support for Guile 2 by Mark H Weaver <mhw@netris.org>, Copyright (c) 2014.
-;; Refactored by Taylan Ulrich Bayırlı/Kammer, Copyright (c) 2014, 2015.
-;;
-;; Permission is hereby granted, free of charge, to any person
-;; obtaining a copy of this software and associated documentation
-;; files (the "Software"), to deal in the Software without
-;; restriction, including without limitation the rights to use, copy,
-;; modify, merge, publish, distribute, sublicense, and/or sell copies
-;; of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-;;
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-;;
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-
-;;; The data type
-
-(define-record-type <test-runner>
- (make-test-runner) test-runner?
-
- (result-alist test-result-alist test-result-alist!)
-
- (pass-count test-runner-pass-count test-runner-pass-count!)
- (fail-count test-runner-fail-count test-runner-fail-count!)
- (xpass-count test-runner-xpass-count test-runner-xpass-count!)
- (xfail-count test-runner-xfail-count test-runner-xfail-count!)
- (skip-count test-runner-skip-count test-runner-skip-count!)
- (total-count %test-runner-total-count %test-runner-total-count!)
-
- ;; Stack (list) of (count-at-start . expected-count):
- (count-list %test-runner-count-list %test-runner-count-list!)
-
- ;; Normally #f, except when in a test-apply.
- (run-list %test-runner-run-list %test-runner-run-list!)
-
- (skip-list %test-runner-skip-list %test-runner-skip-list!)
- (fail-list %test-runner-fail-list %test-runner-fail-list!)
-
- (skip-save %test-runner-skip-save %test-runner-skip-save!)
- (fail-save %test-runner-fail-save %test-runner-fail-save!)
-
- (group-stack test-runner-group-stack test-runner-group-stack!)
-
- ;; Note: on-test-begin and on-test-end are unrelated to the test-begin and
- ;; test-end forms in the execution library. They're called at the
- ;; beginning/end of each individual test, whereas the test-begin and test-end
- ;; forms demarcate test groups.
-
- (on-group-begin test-runner-on-group-begin test-runner-on-group-begin!)
- (on-test-begin test-runner-on-test-begin test-runner-on-test-begin!)
- (on-test-end test-runner-on-test-end test-runner-on-test-end!)
- (on-group-end test-runner-on-group-end test-runner-on-group-end!)
- (on-final test-runner-on-final test-runner-on-final!)
- (on-bad-count test-runner-on-bad-count test-runner-on-bad-count!)
- (on-bad-end-name test-runner-on-bad-end-name test-runner-on-bad-end-name!)
-
- (on-bad-error-type %test-runner-on-bad-error-type
- %test-runner-on-bad-error-type!)
-
- (aux-value test-runner-aux-value test-runner-aux-value!)
-
- (auto-installed %test-runner-auto-installed? %test-runner-auto-installed!)
-
- (log-file %test-runner-log-file %test-runner-log-file!)
- (log-port %test-runner-log-port %test-runner-log-port!))
-
-(define (test-runner-group-path runner)
- (reverse (test-runner-group-stack runner)))
-
-(define (test-runner-reset runner)
- (test-result-alist! runner '())
- (test-runner-pass-count! runner 0)
- (test-runner-fail-count! runner 0)
- (test-runner-xpass-count! runner 0)
- (test-runner-xfail-count! runner 0)
- (test-runner-skip-count! runner 0)
- (%test-runner-total-count! runner 0)
- (%test-runner-count-list! runner '())
- (%test-runner-run-list! runner #f)
- (%test-runner-skip-list! runner '())
- (%test-runner-fail-list! runner '())
- (%test-runner-skip-save! runner '())
- (%test-runner-fail-save! runner '())
- (test-runner-group-stack! runner '()))
-
-(define (test-runner-null)
- (define (test-null-callback . args) #f)
- (let ((runner (make-test-runner)))
- (test-runner-reset runner)
- (test-runner-on-group-begin! runner test-null-callback)
- (test-runner-on-group-end! runner test-null-callback)
- (test-runner-on-final! runner test-null-callback)
- (test-runner-on-test-begin! runner test-null-callback)
- (test-runner-on-test-end! runner test-null-callback)
- (test-runner-on-bad-count! runner test-null-callback)
- (test-runner-on-bad-end-name! runner test-null-callback)
- (%test-runner-on-bad-error-type! runner test-null-callback)
- (%test-runner-auto-installed! runner #f)
- (%test-runner-log-file! runner #f)
- (%test-runner-log-port! runner #f)
- runner))
-
-
-;;; State
-
-(define test-result-ref
- (case-lambda
- ((runner key)
- (test-result-ref runner key #f))
- ((runner key default)
- (let ((entry (assq key (test-result-alist runner))))
- (if entry (cdr entry) default)))))
-
-(define (test-result-set! runner key value)
- (let* ((alist (test-result-alist runner))
- (entry (assq key alist)))
- (if entry
- (set-cdr! entry value)
- (test-result-alist! runner (cons (cons key value) alist)))))
-
-(define (test-result-remove runner key)
- (test-result-alist! runner (remove (lambda (entry)
- (eq? key (car entry)))
- (test-result-alist runner))))
-
-(define (test-result-clear runner)
- (test-result-alist! runner '()))
-
-(define (test-runner-test-name runner)
- (or (test-result-ref runner 'name) ""))
-
-(define test-result-kind
- (case-lambda
- (() (test-result-kind (test-runner-get)))
- ((runner) (test-result-ref runner 'result-kind))))
-
-(define test-passed?
- (case-lambda
- (() (test-passed? (test-runner-get)))
- ((runner) (memq (test-result-kind runner) '(pass xpass)))))
-
-
-;;; Factory and current instance
-
-(define test-runner-factory (make-parameter #f))
-
-(define (test-runner-create) ((test-runner-factory)))
-
-(define test-runner-current (make-parameter #f))
-
-(define (test-runner-get)
- (or (test-runner-current)
- (error "test-runner not initialized - test-begin missing?")))
-
-;;; test-runner.scm ends here
-(export
- ;; The data type
- test-runner-null test-runner? test-runner-reset
-
- test-result-alist test-result-alist!
-
- test-runner-pass-count test-runner-pass-count!
- test-runner-fail-count test-runner-fail-count!
- test-runner-xpass-count test-runner-xpass-count!
- test-runner-xfail-count test-runner-xfail-count!
- test-runner-skip-count test-runner-skip-count!
- %test-runner-total-count %test-runner-total-count!
-
- %test-runner-count-list %test-runner-count-list!
-
- %test-runner-run-list %test-runner-run-list!
-
- %test-runner-skip-list %test-runner-skip-list!
- %test-runner-fail-list %test-runner-fail-list!
-
- %test-runner-skip-save %test-runner-skip-save!
- %test-runner-fail-save %test-runner-fail-save!
-
- test-runner-group-stack test-runner-group-stack!
- test-runner-group-path
-
- test-runner-on-test-begin test-runner-on-test-begin!
- test-runner-on-test-end test-runner-on-test-end!
- test-runner-on-group-begin test-runner-on-group-begin!
- test-runner-on-group-end test-runner-on-group-end!
- test-runner-on-final test-runner-on-final!
- test-runner-on-bad-count test-runner-on-bad-count!
- test-runner-on-bad-end-name test-runner-on-bad-end-name!
-
- %test-runner-on-bad-error-type %test-runner-on-bad-error-type!
-
- test-runner-aux-value test-runner-aux-value!
-
- %test-runner-log-file %test-runner-log-file!
- %test-runner-log-port %test-runner-log-port!
-
- ;; State
- test-result-ref test-result-set!
- test-result-remove test-result-clear
- test-runner-test-name test-result-kind test-passed?
-
- ;; Factory and current instance
- test-runner-factory test-runner-create
- test-runner-current test-runner-get
- )
-(define-library (srfi 64 test-runner)
- (import
- (scheme base)
- (scheme case-lambda)
- (srfi 1))
- (include-library-declarations "test-runner.exports.sld")
- (include "test-runner.body.scm"))
-;;; SRFI-1 list-processing library -*- Scheme -*-
-;;; Reference implementation
-;;;
-;;; Copyright (c) 1998, 1999 by Olin Shivers. You may do as you please with
-;;; this code as long as you do not remove this copyright notice or
-;;; hold me liable for its use. Please send bug reports to shivers@ai.mit.edu.
-;;; -Olin
-;;;
-;;; Made an R7RS library by Taylan Ulrich Bayırlı/Kammer, Copyright (c) 2014.
-
-;;; See 1.upstream.scm in the same repository for a bunch of comments which I
-;;; removed here because what they document does not necessarily correspond with
-;;; the code anymore. Diff with the same file to see changes in the code.
-
-;;; Constructors
-;;;;;;;;;;;;;;;;
-
-;;; Occasionally useful as a value to be passed to a fold or other
-;;; higher-order procedure.
-(define (xcons d a) (cons a d))
-
-;;;; Recursively copy every cons.
-;(define (tree-copy x)
-; (let recur ((x x))
-; (if (not (pair? x)) x
-; (cons (recur (car x)) (recur (cdr x))))))
-
-
-;(define (list . ans) ans) ; R4RS
-
-
-;;; Make a list of length LEN. Elt i is (PROC i) for 0 <= i < LEN.
-
-(define (list-tabulate len proc)
- (check-arg (lambda (n) (and (integer? n) (>= n 0))) len list-tabulate)
- (check-arg procedure? proc list-tabulate)
- (do ((i (- len 1) (- i 1))
- (ans '() (cons (proc i) ans)))
- ((< i 0) ans)))
-
-;;; (cons* a1 a2 ... an) = (cons a1 (cons a2 (cons ... an)))
-;;; (cons* a1) = a1 (cons* a1 a2 ...) = (cons a1 (cons* a2 ...))
-;;;
-;;; (cons first (unfold not-pair? car cdr rest values))
-
-(define (cons* first . rest)
- (let recur ((x first) (rest rest))
- (if (pair? rest)
- (cons x (recur (car rest) (cdr rest)))
- x)))
-
-;;; IOTA count [start step] (start start+step ... start+(count-1)*step)
-
-(define (nonnegative? x)
- (not (negative? x)))
-
-(define/opt (iota count (start 0) (step 1))
- (check-arg integer? count iota)
- (check-arg nonnegative? count iota)
- (check-arg number? start iota)
- (check-arg number? step iota)
- (let loop ((n 0) (r '()))
- (if (= n count)
- (reverse r)
- (loop (+ 1 n)
- (cons (+ start (* n step)) r)))))
-
-;;; I thought these were lovely, but the public at large did not share my
-;;; enthusiasm...
-;;; :IOTA to (0 ... to-1)
-;;; :IOTA from to (from ... to-1)
-;;; :IOTA from to step (from from+step ...)
-
-;;; IOTA: to (1 ... to)
-;;; IOTA: from to (from+1 ... to)
-;;; IOTA: from to step (from+step from+2step ...)
-
-;(define (%parse-iota-args arg1 rest-args proc)
-; (let ((check (lambda (n) (check-arg integer? n proc))))
-; (check arg1)
-; (if (pair? rest-args)
-; (let ((arg2 (check (car rest-args)))
-; (rest (cdr rest-args)))
-; (if (pair? rest)
-; (let ((arg3 (check (car rest)))
-; (rest (cdr rest)))
-; (if (pair? rest) (error "Too many parameters" proc arg1 rest-args)
-; (values arg1 arg2 arg3)))
-; (values arg1 arg2 1)))
-; (values 0 arg1 1))))
-;
-;(define (iota: arg1 . rest-args)
-; (receive (from to step) (%parse-iota-args arg1 rest-args iota:)
-; (let* ((numsteps (floor (/ (- to from) step)))
-; (last-val (+ from (* step numsteps))))
-; (if (< numsteps 0) (error "Negative step count" iota: from to step))
-; (do ((steps-left numsteps (- steps-left 1))
-; (val last-val (- val step))
-; (ans '() (cons val ans)))
-; ((<= steps-left 0) ans)))))
-;
-;
-;(define (\:iota arg1 . rest-args)
-; (receive (from to step) (%parse-iota-args arg1 rest-args :iota)
-; (let* ((numsteps (ceiling (/ (- to from) step)))
-; (last-val (+ from (* step (- numsteps 1)))))
-; (if (< numsteps 0) (error "Negative step count" :iota from to step))
-; (do ((steps-left numsteps (- steps-left 1))
-; (val last-val (- val step))
-; (ans '() (cons val ans)))
-; ((<= steps-left 0) ans)))))
-
-
-
-(define (circular-list val1 . vals)
- (let ((ans (cons val1 vals)))
- (set-cdr! (last-pair ans) ans)
- ans))
-
-;;; <proper-list> ::= () ; Empty proper list
-;;; | (cons <x> <proper-list>) ; Proper-list pair
-;;; Note that this definition rules out circular lists -- and this
-;;; function is required to detect this case and return false.
-
-(define (proper-list? x)
- (let lp ((x x) (lag x))
- (if (pair? x)
- (let ((x (cdr x)))
- (if (pair? x)
- (let ((x (cdr x))
- (lag (cdr lag)))
- (and (not (eq? x lag)) (lp x lag)))
- (null? x)))
- (null? x))))
-
-
-;;; A dotted list is a finite list (possibly of length 0) terminated
-;;; by a non-nil value. Any non-cons, non-nil value (e.g., "foo" or 5)
-;;; is a dotted list of length 0.
-;;;
-;;; <dotted-list> ::= <non-nil,non-pair> ; Empty dotted list
-;;; | (cons <x> <dotted-list>) ; Proper-list pair
-
-(define (dotted-list? x)
- (let lp ((x x) (lag x))
- (if (pair? x)
- (let ((x (cdr x)))
- (if (pair? x)
- (let ((x (cdr x))
- (lag (cdr lag)))
- (and (not (eq? x lag)) (lp x lag)))
- (not (null? x))))
- (not (null? x)))))
-
-(define (circular-list? x)
- (let lp ((x x) (lag x))
- (and (pair? x)
- (let ((x (cdr x)))
- (and (pair? x)
- (let ((x (cdr x))
- (lag (cdr lag)))
- (or (eq? x lag) (lp x lag))))))))
-
-(define (not-pair? x) (not (pair? x))) ; Inline me.
-
-;;; This is a legal definition which is fast and sloppy:
-;;; (define null-list? not-pair?)
-;;; but we'll provide a more careful one:
-(define (null-list? l)
- (cond ((pair? l) #f)
- ((null? l) #t)
- (else (error "null-list?: argument out of domain" l))))
-
-
-(define (list= = . lists)
- (or (null? lists) ; special case
-
- (let lp1 ((list-a (car lists)) (others (cdr lists)))
- (or (null? others)
- (let ((list-b (car others))
- (others (cdr others)))
- (if (eq? list-a list-b) ; EQ? => LIST=
- (lp1 list-b others)
- (let lp2 ((list-a list-a) (list-b list-b))
- (if (null-list? list-a)
- (and (null-list? list-b)
- (lp1 list-b others))
- (and (not (null-list? list-b))
- (= (car list-a) (car list-b))
- (lp2 (cdr list-a) (cdr list-b)))))))))))
-
-
-
-;;; R4RS, so commented out.
-;(define (length x) ; LENGTH may diverge or
-; (let lp ((x x) (len 0)) ; raise an error if X is
-; (if (pair? x) ; a circular list. This version
-; (lp (cdr x) (+ len 1)) ; diverges.
-; len)))
-
-(define (length+ x) ; Returns #f if X is circular.
- (let lp ((x x) (lag x) (len 0))
- (if (pair? x)
- (let ((x (cdr x))
- (len (+ len 1)))
- (if (pair? x)
- (let ((x (cdr x))
- (lag (cdr lag))
- (len (+ len 1)))
- (and (not (eq? x lag)) (lp x lag len)))
- len))
- len)))
-
-(define (zip list1 . more-lists) (apply map list list1 more-lists))
-
-
-;;; Selectors
-;;;;;;;;;;;;;
-
-;;; R4RS non-primitives:
-;(define (caar x) (car (car x)))
-;(define (cadr x) (car (cdr x)))
-;(define (cdar x) (cdr (car x)))
-;(define (cddr x) (cdr (cdr x)))
-;
-;(define (caaar x) (caar (car x)))
-;(define (caadr x) (caar (cdr x)))
-;(define (cadar x) (cadr (car x)))
-;(define (caddr x) (cadr (cdr x)))
-;(define (cdaar x) (cdar (car x)))
-;(define (cdadr x) (cdar (cdr x)))
-;(define (cddar x) (cddr (car x)))
-;(define (cdddr x) (cddr (cdr x)))
-;
-;(define (caaaar x) (caaar (car x)))
-;(define (caaadr x) (caaar (cdr x)))
-;(define (caadar x) (caadr (car x)))
-;(define (caaddr x) (caadr (cdr x)))
-;(define (cadaar x) (cadar (car x)))
-;(define (cadadr x) (cadar (cdr x)))
-;(define (caddar x) (caddr (car x)))
-;(define (cadddr x) (caddr (cdr x)))
-;(define (cdaaar x) (cdaar (car x)))
-;(define (cdaadr x) (cdaar (cdr x)))
-;(define (cdadar x) (cdadr (car x)))
-;(define (cdaddr x) (cdadr (cdr x)))
-;(define (cddaar x) (cddar (car x)))
-;(define (cddadr x) (cddar (cdr x)))
-;(define (cdddar x) (cdddr (car x)))
-;(define (cddddr x) (cdddr (cdr x)))
-
-
-(define first car)
-(define second cadr)
-(define third caddr)
-(define fourth cadddr)
-(define (fifth x) (car (cddddr x)))
-(define (sixth x) (cadr (cddddr x)))
-(define (seventh x) (caddr (cddddr x)))
-(define (eighth x) (cadddr (cddddr x)))
-(define (ninth x) (car (cddddr (cddddr x))))
-(define (tenth x) (cadr (cddddr (cddddr x))))
-
-(define (car+cdr pair) (values (car pair) (cdr pair)))
-
-;;; take & drop
-
-(define (take lis k)
- (check-arg integer? k take)
- (let recur ((lis lis) (k k))
- (if (zero? k) '()
- (cons (car lis)
- (recur (cdr lis) (- k 1))))))
-
-(define (drop lis k)
- (check-arg integer? k drop)
- (let iter ((lis lis) (k k))
- (if (zero? k) lis (iter (cdr lis) (- k 1)))))
-
-(define (take! lis k)
- (check-arg integer? k take!)
- (if (zero? k) '()
- (begin (set-cdr! (drop lis (- k 1)) '())
- lis)))
-
-;;; TAKE-RIGHT and DROP-RIGHT work by getting two pointers into the list,
-;;; off by K, then chasing down the list until the lead pointer falls off
-;;; the end.
-
-(define (take-right lis k)
- (check-arg integer? k take-right)
- (let lp ((lag lis) (lead (drop lis k)))
- (if (pair? lead)
- (lp (cdr lag) (cdr lead))
- lag)))
-
-(define (drop-right lis k)
- (check-arg integer? k drop-right)
- (let recur ((lag lis) (lead (drop lis k)))
- (if (pair? lead)
- (cons (car lag) (recur (cdr lag) (cdr lead)))
- '())))
-
-;;; In this function, LEAD is actually K+1 ahead of LAG. This lets
-;;; us stop LAG one step early, in time to smash its cdr to ().
-(define (drop-right! lis k)
- (check-arg integer? k drop-right!)
- (let ((lead (drop lis k)))
- (if (pair? lead)
-
- (let lp ((lag lis) (lead (cdr lead))) ; Standard case
- (if (pair? lead)
- (lp (cdr lag) (cdr lead))
- (begin (set-cdr! lag '())
- lis)))
-
- '()))) ; Special case dropping everything -- no cons to side-effect.
-
-;(define (list-ref lis i) (car (drop lis i))) ; R4RS
-
-;;; These use the APL convention, whereby negative indices mean
-;;; "from the right." I liked them, but they didn't win over the
-;;; SRFI reviewers.
-;;; K >= 0: Take and drop K elts from the front of the list.
-;;; K <= 0: Take and drop -K elts from the end of the list.
-
-;(define (take lis k)
-; (check-arg integer? k take)
-; (if (negative? k)
-; (list-tail lis (+ k (length lis)))
-; (let recur ((lis lis) (k k))
-; (if (zero? k) '()
-; (cons (car lis)
-; (recur (cdr lis) (- k 1)))))))
-;
-;(define (drop lis k)
-; (check-arg integer? k drop)
-; (if (negative? k)
-; (let recur ((lis lis) (nelts (+ k (length lis))))
-; (if (zero? nelts) '()
-; (cons (car lis)
-; (recur (cdr lis) (- nelts 1)))))
-; (list-tail lis k)))
-;
-;
-;(define (take! lis k)
-; (check-arg integer? k take!)
-; (cond ((zero? k) '())
-; ((positive? k)
-; (set-cdr! (list-tail lis (- k 1)) '())
-; lis)
-; (else (list-tail lis (+ k (length lis))))))
-;
-;(define (drop! lis k)
-; (check-arg integer? k drop!)
-; (if (negative? k)
-; (let ((nelts (+ k (length lis))))
-; (if (zero? nelts) '()
-; (begin (set-cdr! (list-tail lis (- nelts 1)) '())
-; lis)))
-; (list-tail lis k)))
-
-(define (split-at x k)
- (check-arg integer? k split-at)
- (let recur ((lis x) (k k))
- (if (zero? k) (values '() lis)
- (receive (prefix suffix) (recur (cdr lis) (- k 1))
- (values (cons (car lis) prefix) suffix)))))
-
-(define (split-at! x k)
- (check-arg integer? k split-at!)
- (if (zero? k) (values '() x)
- (let* ((prev (drop x (- k 1)))
- (suffix (cdr prev)))
- (set-cdr! prev '())
- (values x suffix))))
-
-
-(define (last lis) (car (last-pair lis)))
-
-(define (last-pair lis)
- (check-arg pair? lis last-pair)
- (let lp ((lis lis))
- (let ((tail (cdr lis)))
- (if (pair? tail) (lp tail) lis))))
-
-
-;;; Unzippers -- 1 through 5
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define (unzip1 lis) (map car lis))
-
-(define (unzip2 lis)
- (let recur ((lis lis))
- (if (null-list? lis) (values lis lis) ; Use NOT-PAIR? to handle
- (let ((elt (car lis))) ; dotted lists.
- (receive (a b) (recur (cdr lis))
- (values (cons (car elt) a)
- (cons (cadr elt) b)))))))
-
-(define (unzip3 lis)
- (let recur ((lis lis))
- (if (null-list? lis) (values lis lis lis)
- (let ((elt (car lis)))
- (receive (a b c) (recur (cdr lis))
- (values (cons (car elt) a)
- (cons (cadr elt) b)
- (cons (caddr elt) c)))))))
-
-(define (unzip4 lis)
- (let recur ((lis lis))
- (if (null-list? lis) (values lis lis lis lis)
- (let ((elt (car lis)))
- (receive (a b c d) (recur (cdr lis))
- (values (cons (car elt) a)
- (cons (cadr elt) b)
- (cons (caddr elt) c)
- (cons (cadddr elt) d)))))))
-
-(define (unzip5 lis)
- (let recur ((lis lis))
- (if (null-list? lis) (values lis lis lis lis lis)
- (let ((elt (car lis)))
- (receive (a b c d e) (recur (cdr lis))
- (values (cons (car elt) a)
- (cons (cadr elt) b)
- (cons (caddr elt) c)
- (cons (cadddr elt) d)
- (cons (car (cddddr elt)) e)))))))
-
-
-;;; append! append-reverse append-reverse! concatenate concatenate!
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define (append! . lists)
- ;; First, scan through lists looking for a non-empty one.
- (let lp ((lists lists) (prev '()))
- (if (not (pair? lists)) prev
- (let ((first (car lists))
- (rest (cdr lists)))
- (if (not (pair? first)) (lp rest first)
-
- ;; Now, do the splicing.
- (let lp2 ((tail-cons (last-pair first))
- (rest rest))
- (if (pair? rest)
- (let ((next (car rest))
- (rest (cdr rest)))
- (set-cdr! tail-cons next)
- (lp2 (if (pair? next) (last-pair next) tail-cons)
- rest))
- first)))))))
-
-;;; APPEND is R4RS.
-;(define (append . lists)
-; (if (pair? lists)
-; (let recur ((list1 (car lists)) (lists (cdr lists)))
-; (if (pair? lists)
-; (let ((tail (recur (car lists) (cdr lists))))
-; (fold-right cons tail list1)) ; Append LIST1 & TAIL.
-; list1))
-; '()))
-
-;(define (append-reverse rev-head tail) (fold cons tail rev-head))
-
-;(define (append-reverse! rev-head tail)
-; (pair-fold (lambda (pair tail) (set-cdr! pair tail) pair)
-; tail
-; rev-head))
-
-;;; Hand-inline the FOLD and PAIR-FOLD ops for speed.
-
-(define (append-reverse rev-head tail)
- (let lp ((rev-head rev-head) (tail tail))
- (if (null-list? rev-head) tail
- (lp (cdr rev-head) (cons (car rev-head) tail)))))
-
-(define (append-reverse! rev-head tail)
- (let lp ((rev-head rev-head) (tail tail))
- (if (null-list? rev-head) tail
- (let ((next-rev (cdr rev-head)))
- (set-cdr! rev-head tail)
- (lp next-rev rev-head)))))
-
-
-(define (concatenate lists) (reduce-right append '() lists))
-(define (concatenate! lists) (reduce-right append! '() lists))
-
-;;; Fold/map internal utilities
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; These little internal utilities are used by the general
-;;; fold & mapper funs for the n-ary cases . It'd be nice if they got inlined.
-;;; One the other hand, the n-ary cases are painfully inefficient as it is.
-;;; An aggressive implementation should simply re-write these functions
-;;; for raw efficiency; I have written them for as much clarity, portability,
-;;; and simplicity as can be achieved.
-;;;
-;;; I use the dreaded call/cc to do local aborts. A good compiler could
-;;; handle this with extreme efficiency. An implementation that provides
-;;; a one-shot, non-persistent continuation grabber could help the compiler
-;;; out by using that in place of the call/cc's in these routines.
-;;;
-;;; These functions have funky definitions that are precisely tuned to
-;;; the needs of the fold/map procs -- for example, to minimize the number
-;;; of times the argument lists need to be examined.
-
-;;; Return (map cdr lists).
-;;; However, if any element of LISTS is empty, just abort and return '().
-(define (%cdrs lists)
- (call-with-current-continuation
- (lambda (abort)
- (let recur ((lists lists))
- (if (pair? lists)
- (let ((lis (car lists)))
- (if (null-list? lis) (abort '())
- (cons (cdr lis) (recur (cdr lists)))))
- '())))))
-
-(define (%cars+ lists last-elt) ; (append! (map car lists) (list last-elt))
- (let recur ((lists lists))
- (if (pair? lists) (cons (caar lists) (recur (cdr lists))) (list last-elt))))
-
-;;; LISTS is a (not very long) non-empty list of lists.
-;;; Return two lists: the cars & the cdrs of the lists.
-;;; However, if any of the lists is empty, just abort and return [() ()].
-
-(define (%cars+cdrs lists)
- (call-with-current-continuation
- (lambda (abort)
- (let recur ((lists lists))
- (if (pair? lists)
- (receive (list other-lists) (car+cdr lists)
- (if (null-list? list) (abort '() '()) ; LIST is empty -- bail out
- (receive (a d) (car+cdr list)
- (receive (cars cdrs) (recur other-lists)
- (values (cons a cars) (cons d cdrs))))))
- (values '() '()))))))
-
-;;; Like %CARS+CDRS, but we pass in a final elt tacked onto the end of the
-;;; cars list. What a hack.
-(define (%cars+cdrs+ lists cars-final)
- (call-with-current-continuation
- (lambda (abort)
- (let recur ((lists lists))
- (if (pair? lists)
- (receive (list other-lists) (car+cdr lists)
- (if (null-list? list) (abort '() '()) ; LIST is empty -- bail out
- (receive (a d) (car+cdr list)
- (receive (cars cdrs) (recur other-lists)
- (values (cons a cars) (cons d cdrs))))))
- (values (list cars-final) '()))))))
-
-;;; Like %CARS+CDRS, but blow up if any list is empty.
-(define (%cars+cdrs/no-test lists)
- (let recur ((lists lists))
- (if (pair? lists)
- (receive (list other-lists) (car+cdr lists)
- (receive (a d) (car+cdr list)
- (receive (cars cdrs) (recur other-lists)
- (values (cons a cars) (cons d cdrs)))))
- (values '() '()))))
-
-
-;;; count
-;;;;;;;;;
-(define (count pred list1 . lists)
- (check-arg procedure? pred count)
- (if (pair? lists)
-
- ;; N-ary case
- (let lp ((list1 list1) (lists lists) (i 0))
- (if (null-list? list1) i
- (receive (as ds) (%cars+cdrs lists)
- (if (null? as) i
- (lp (cdr list1) ds
- (if (apply pred (car list1) as) (+ i 1) i))))))
-
- ;; Fast path
- (let lp ((lis list1) (i 0))
- (if (null-list? lis) i
- (lp (cdr lis) (if (pred (car lis)) (+ i 1) i))))))
-
-
-;;; fold/unfold
-;;;;;;;;;;;;;;;
-
-(define/opt (unfold-right p f g seed (tail '()))
- (check-arg procedure? p unfold-right)
- (check-arg procedure? f unfold-right)
- (check-arg procedure? g unfold-right)
- (let lp ((seed seed) (ans tail))
- (if (p seed) ans
- (lp (g seed)
- (cons (f seed) ans)))))
-
-
-(define/opt (unfold p f g seed (tail-gen #f))
- (check-arg procedure? p unfold)
- (check-arg procedure? f unfold)
- (check-arg procedure? g unfold)
- (check-arg procedure? tail-gen unfold)
- (let recur ((seed seed))
- (if (p seed)
- (if tail-gen (tail-gen seed) '())
- (cons (f seed) (recur (g seed))))))
-
-
-(define (fold kons knil lis1 . lists)
- (check-arg procedure? kons fold)
- (if (pair? lists)
- (let lp ((lists (cons lis1 lists)) (ans knil)) ; N-ary case
- (receive (cars+ans cdrs) (%cars+cdrs+ lists ans)
- (if (null? cars+ans) ans ; Done.
- (lp cdrs (apply kons cars+ans)))))
-
- (let lp ((lis lis1) (ans knil)) ; Fast path
- (if (null-list? lis) ans
- (lp (cdr lis) (kons (car lis) ans))))))
-
-
-(define (fold-right kons knil lis1 . lists)
- (check-arg procedure? kons fold-right)
- (if (pair? lists)
- (let recur ((lists (cons lis1 lists))) ; N-ary case
- (let ((cdrs (%cdrs lists)))
- (if (null? cdrs) knil
- (apply kons (%cars+ lists (recur cdrs))))))
-
- (let recur ((lis lis1)) ; Fast path
- (if (null-list? lis) knil
- (let ((head (car lis)))
- (kons head (recur (cdr lis))))))))
-
-
-(define (pair-fold-right f zero lis1 . lists)
- (check-arg procedure? f pair-fold-right)
- (if (pair? lists)
- (let recur ((lists (cons lis1 lists))) ; N-ary case
- (let ((cdrs (%cdrs lists)))
- (if (null? cdrs) zero
- (apply f (append! lists (list (recur cdrs)))))))
-
- (let recur ((lis lis1)) ; Fast path
- (if (null-list? lis) zero (f lis (recur (cdr lis)))))))
-
-(define (pair-fold f zero lis1 . lists)
- (check-arg procedure? f pair-fold)
- (if (pair? lists)
- (let lp ((lists (cons lis1 lists)) (ans zero)) ; N-ary case
- (let ((tails (%cdrs lists)))
- (if (null? tails) ans
- (lp tails (apply f (append! lists (list ans)))))))
-
- (let lp ((lis lis1) (ans zero))
- (if (null-list? lis) ans
- (let ((tail (cdr lis))) ; Grab the cdr now,
- (lp tail (f lis ans))))))) ; in case F SET-CDR!s LIS.
-
-
-;;; REDUCE and REDUCE-RIGHT only use RIDENTITY in the empty-list case.
-;;; These cannot meaningfully be n-ary.
-
-(define (reduce f ridentity lis)
- (check-arg procedure? f reduce)
- (if (null-list? lis) ridentity
- (fold f (car lis) (cdr lis))))
-
-(define (reduce-right f ridentity lis)
- (check-arg procedure? f reduce-right)
- (if (null-list? lis) ridentity
- (let recur ((head (car lis)) (lis (cdr lis)))
- (if (pair? lis)
- (f head (recur (car lis) (cdr lis)))
- head))))
-
-
-
-;;; Mappers: append-map append-map! pair-for-each map! filter-map map-in-order
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define (append-map f lis1 . lists)
- (really-append-map append-map append f lis1 lists))
-(define (append-map! f lis1 . lists)
- (really-append-map append-map! append! f lis1 lists))
-
-(define (really-append-map who appender f lis1 lists)
- (check-arg procedure? f who)
- (if (pair? lists)
- (receive (cars cdrs) (%cars+cdrs (cons lis1 lists))
- (if (null? cars) '()
- (let recur ((cars cars) (cdrs cdrs))
- (let ((vals (apply f cars)))
- (receive (cars2 cdrs2) (%cars+cdrs cdrs)
- (if (null? cars2) vals
- (appender vals (recur cars2 cdrs2))))))))
-
- ;; Fast path
- (if (null-list? lis1) '()
- (let recur ((elt (car lis1)) (rest (cdr lis1)))
- (let ((vals (f elt)))
- (if (null-list? rest) vals
- (appender vals (recur (car rest) (cdr rest)))))))))
-
-
-(define (pair-for-each proc lis1 . lists)
- (check-arg procedure? proc pair-for-each)
- (if (pair? lists)
-
- (let lp ((lists (cons lis1 lists)))
- (let ((tails (%cdrs lists)))
- (if (pair? tails)
- (begin (apply proc lists)
- (lp tails)))))
-
- ;; Fast path.
- (let lp ((lis lis1))
- (if (not (null-list? lis))
- (let ((tail (cdr lis))) ; Grab the cdr now,
- (proc lis) ; in case PROC SET-CDR!s LIS.
- (lp tail))))))
-
-;;; We stop when LIS1 runs out, not when any list runs out.
-(define (map! f lis1 . lists)
- (check-arg procedure? f map!)
- (if (pair? lists)
- (let lp ((lis1 lis1) (lists lists))
- (if (not (null-list? lis1))
- (receive (heads tails) (%cars+cdrs/no-test lists)
- (set-car! lis1 (apply f (car lis1) heads))
- (lp (cdr lis1) tails))))
-
- ;; Fast path.
- (pair-for-each (lambda (pair) (set-car! pair (f (car pair)))) lis1))
- lis1)
-
-
-;;; Map F across L, and save up all the non-false results.
-(define (filter-map f lis1 . lists)
- (check-arg procedure? f filter-map)
- (if (pair? lists)
- (let recur ((lists (cons lis1 lists)))
- (receive (cars cdrs) (%cars+cdrs lists)
- (if (pair? cars)
- (cond ((apply f cars) => (lambda (x) (cons x (recur cdrs))))
- (else (recur cdrs))) ; Tail call in this arm.
- '())))
-
- ;; Fast path.
- (let recur ((lis lis1))
- (if (null-list? lis) lis
- (let ((tail (recur (cdr lis))))
- (cond ((f (car lis)) => (lambda (x) (cons x tail)))
- (else tail)))))))
-
-
-;;; Map F across lists, guaranteeing to go left-to-right.
-;;; NOTE: Some implementations of R5RS MAP are compliant with this spec;
-;;; in which case this procedure may simply be defined as a synonym for MAP.
-
-(define (map-in-order f lis1 . lists)
- (check-arg procedure? f map-in-order)
- (if (pair? lists)
- (let recur ((lists (cons lis1 lists)))
- (receive (cars cdrs) (%cars+cdrs lists)
- (if (pair? cars)
- (let ((x (apply f cars))) ; Do head first,
- (cons x (recur cdrs))) ; then tail.
- '())))
-
- ;; Fast path.
- (let recur ((lis lis1))
- (if (null-list? lis) lis
- (let ((tail (cdr lis))
- (x (f (car lis)))) ; Do head first,
- (cons x (recur tail))))))) ; then tail.
-
-
-;;; We extend MAP to handle arguments of unequal length.
-(define map map-in-order)
-
-
-;;; filter, remove, partition
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; FILTER, REMOVE, PARTITION and their destructive counterparts do not
-;;; disorder the elements of their argument.
-
-;; This FILTER shares the longest tail of L that has no deleted elements.
-;; If Scheme had multi-continuation calls, they could be made more efficient.
-
-(define (filter pred lis) ; Sleazing with EQ? makes this
- (check-arg procedure? pred filter) ; one faster.
- (let recur ((lis lis))
- (if (null-list? lis) lis ; Use NOT-PAIR? to handle dotted lists.
- (let ((head (car lis))
- (tail (cdr lis)))
- (if (pred head)
- (let ((new-tail (recur tail))) ; Replicate the RECUR call so
- (if (eq? tail new-tail) lis
- (cons head new-tail)))
- (recur tail)))))) ; this one can be a tail call.
-
-
-;;; Another version that shares longest tail.
-;(define (filter pred lis)
-; (receive (ans no-del?)
-; ;; (recur l) returns L with (pred x) values filtered.
-; ;; It also returns a flag NO-DEL? if the returned value
-; ;; is EQ? to L, i.e. if it didn't have to delete anything.
-; (let recur ((l l))
-; (if (null-list? l) (values l #t)
-; (let ((x (car l))
-; (tl (cdr l)))
-; (if (pred x)
-; (receive (ans no-del?) (recur tl)
-; (if no-del?
-; (values l #t)
-; (values (cons x ans) #f)))
-; (receive (ans no-del?) (recur tl) ; Delete X.
-; (values ans #f))))))
-; ans))
-
-
-
-;(define (filter! pred lis) ; Things are much simpler
-; (let recur ((lis lis)) ; if you are willing to
-; (if (pair? lis) ; push N stack frames & do N
-; (cond ((pred (car lis)) ; SET-CDR! writes, where N is
-; (set-cdr! lis (recur (cdr lis))); the length of the answer.
-; lis)
-; (else (recur (cdr lis))))
-; lis)))
-
-
-;;; This implementation of FILTER!
-;;; - doesn't cons, and uses no stack;
-;;; - is careful not to do redundant SET-CDR! writes, as writes to memory are
-;;; usually expensive on modern machines, and can be extremely expensive on
-;;; modern Schemes (e.g., ones that have generational GC's).
-;;; It just zips down contiguous runs of in and out elts in LIS doing the
-;;; minimal number of SET-CDR!s to splice the tail of one run of ins to the
-;;; beginning of the next.
-
-(define (filter! pred lis)
- (check-arg procedure? pred filter!)
- (let lp ((ans lis))
- (cond ((null-list? ans) ans) ; Scan looking for
- ((not (pred (car ans))) (lp (cdr ans))) ; first cons of result.
-
- ;; ANS is the eventual answer.
- ;; SCAN-IN: (CDR PREV) = LIS and (CAR PREV) satisfies PRED.
- ;; Scan over a contiguous segment of the list that
- ;; satisfies PRED.
- ;; SCAN-OUT: (CAR PREV) satisfies PRED. Scan over a contiguous
- ;; segment of the list that *doesn't* satisfy PRED.
- ;; When the segment ends, patch in a link from PREV
- ;; to the start of the next good segment, and jump to
- ;; SCAN-IN.
- (else (letrec ((scan-in (lambda (prev lis)
- (if (pair? lis)
- (if (pred (car lis))
- (scan-in lis (cdr lis))
- (scan-out prev (cdr lis))))))
- (scan-out (lambda (prev lis)
- (let lp ((lis lis))
- (if (pair? lis)
- (if (pred (car lis))
- (begin (set-cdr! prev lis)
- (scan-in lis (cdr lis)))
- (lp (cdr lis)))
- (set-cdr! prev lis))))))
- (scan-in ans (cdr ans))
- ans)))))
-
-
-
-;;; Answers share common tail with LIS where possible;
-;;; the technique is slightly subtle.
-
-(define (partition pred lis)
- (check-arg procedure? pred partition)
- (let recur ((lis lis))
- (if (null-list? lis) (values lis lis) ; Use NOT-PAIR? to handle dotted lists.
- (let ((elt (car lis))
- (tail (cdr lis)))
- (receive (in out) (recur tail)
- (if (pred elt)
- (values (if (pair? out) (cons elt in) lis) out)
- (values in (if (pair? in) (cons elt out) lis))))))))
-
-
-
-;(define (partition! pred lis) ; Things are much simpler
-; (let recur ((lis lis)) ; if you are willing to
-; (if (null-list? lis) (values lis lis) ; push N stack frames & do N
-; (let ((elt (car lis))) ; SET-CDR! writes, where N is
-; (receive (in out) (recur (cdr lis)) ; the length of LIS.
-; (cond ((pred elt)
-; (set-cdr! lis in)
-; (values lis out))
-; (else (set-cdr! lis out)
-; (values in lis))))))))
-
-
-;;; This implementation of PARTITION!
-;;; - doesn't cons, and uses no stack;
-;;; - is careful not to do redundant SET-CDR! writes, as writes to memory are
-;;; usually expensive on modern machines, and can be extremely expensive on
-;;; modern Schemes (e.g., ones that have generational GC's).
-;;; It just zips down contiguous runs of in and out elts in LIS doing the
-;;; minimal number of SET-CDR!s to splice these runs together into the result
-;;; lists.
-
-(define (partition! pred lis)
- (check-arg procedure? pred partition!)
- (if (null-list? lis) (values lis lis)
-
- ;; This pair of loops zips down contiguous in & out runs of the
- ;; list, splicing the runs together. The invariants are
- ;; SCAN-IN: (cdr in-prev) = LIS.
- ;; SCAN-OUT: (cdr out-prev) = LIS.
- (letrec ((scan-in (lambda (in-prev out-prev lis)
- (let lp ((in-prev in-prev) (lis lis))
- (if (pair? lis)
- (if (pred (car lis))
- (lp lis (cdr lis))
- (begin (set-cdr! out-prev lis)
- (scan-out in-prev lis (cdr lis))))
- (set-cdr! out-prev lis))))) ; Done.
-
- (scan-out (lambda (in-prev out-prev lis)
- (let lp ((out-prev out-prev) (lis lis))
- (if (pair? lis)
- (if (pred (car lis))
- (begin (set-cdr! in-prev lis)
- (scan-in lis out-prev (cdr lis)))
- (lp lis (cdr lis)))
- (set-cdr! in-prev lis)))))) ; Done.
-
- ;; Crank up the scan&splice loops.
- (if (pred (car lis))
- ;; LIS begins in-list. Search for out-list's first pair.
- (let lp ((prev-l lis) (l (cdr lis)))
- (cond ((not (pair? l)) (values lis l))
- ((pred (car l)) (lp l (cdr l)))
- (else (scan-out prev-l l (cdr l))
- (values lis l)))) ; Done.
-
- ;; LIS begins out-list. Search for in-list's first pair.
- (let lp ((prev-l lis) (l (cdr lis)))
- (cond ((not (pair? l)) (values l lis))
- ((pred (car l))
- (scan-in l prev-l (cdr l))
- (values l lis)) ; Done.
- (else (lp l (cdr l)))))))))
-
-
-;;; Inline us, please.
-(define (remove pred l) (filter (lambda (x) (not (pred x))) l))
-(define (remove! pred l) (filter! (lambda (x) (not (pred x))) l))
-
-
-
-;;; Here's the taxonomy for the DELETE/ASSOC/MEMBER functions.
-;;; (I don't actually think these are the world's most important
-;;; functions -- the procedural FILTER/REMOVE/FIND/FIND-TAIL variants
-;;; are far more general.)
-;;;
-;;; Function Action
-;;; ---------------------------------------------------------------------------
-;;; remove pred lis Delete by general predicate
-;;; delete x lis [=] Delete by element comparison
-;;;
-;;; find pred lis Search by general predicate
-;;; find-tail pred lis Search by general predicate
-;;; member x lis [=] Search by element comparison
-;;;
-;;; assoc key lis [=] Search alist by key comparison
-;;; alist-delete key alist [=] Alist-delete by key comparison
-
-(define/opt (delete x lis (= equal?))
- (filter (lambda (y) (not (= x y))) lis))
-
-(define/opt (delete! x lis (= equal?))
- (filter! (lambda (y) (not (= x y))) lis))
-
-;;; Extended from R4RS to take an optional comparison argument.
-(define/opt (member x lis (= equal?))
- (find-tail (lambda (y) (= x y)) lis))
-
-;;; R4RS, hence we don't bother to define.
-;;; The MEMBER and then FIND-TAIL call should definitely
-;;; be inlined for MEMQ & MEMV.
-;(define (memq x lis) (member x lis eq?))
-;(define (memv x lis) (member x lis eqv?))
-
-
-;;; right-duplicate deletion
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; delete-duplicates delete-duplicates!
-;;;
-;;; Beware -- these are N^2 algorithms. To efficiently remove duplicates
-;;; in long lists, sort the list to bring duplicates together, then use a
-;;; linear-time algorithm to kill the dups. Or use an algorithm based on
-;;; element-marking. The former gives you O(n lg n), the latter is linear.
-
-(define/opt (delete-duplicates lis (elt= equal?))
- (check-arg procedure? elt= delete-duplicates)
- (let recur ((lis lis))
- (if (null-list? lis) lis
- (let* ((x (car lis))
- (tail (cdr lis))
- (new-tail (recur (delete x tail elt=))))
- (if (eq? tail new-tail) lis (cons x new-tail))))))
-
-(define/opt (delete-duplicates! lis (elt= equal?))
- (check-arg procedure? elt= delete-duplicates!)
- (let recur ((lis lis))
- (if (null-list? lis) lis
- (let* ((x (car lis))
- (tail (cdr lis))
- (new-tail (recur (delete! x tail elt=))))
- (if (eq? tail new-tail) lis (cons x new-tail))))))
-
-
-;;; alist stuff
-;;;;;;;;;;;;;;;
-
-;;; Extended from R4RS to take an optional comparison argument.
-(define/opt (assoc x lis (= equal?))
- (find (lambda (entry) (= x (car entry))) lis))
-
-(define (alist-cons key datum alist) (cons (cons key datum) alist))
-
-(define (alist-copy alist)
- (map (lambda (elt) (cons (car elt) (cdr elt)))
- alist))
-
-(define/opt (alist-delete key alist (= equal?))
- (filter (lambda (elt) (not (= key (car elt)))) alist))
-
-(define/opt (alist-delete! key alist (= equal?))
- (filter! (lambda (elt) (not (= key (car elt)))) alist))
-
-
-;;; find find-tail take-while drop-while span break any every list-index
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define (find pred list)
- (cond ((find-tail pred list) => car)
- (else #f)))
-
-(define (find-tail pred list)
- (check-arg procedure? pred find-tail)
- (let lp ((list list))
- (and (not (null-list? list))
- (if (pred (car list)) list
- (lp (cdr list))))))
-
-(define (take-while pred lis)
- (check-arg procedure? pred take-while)
- (let recur ((lis lis))
- (if (null-list? lis) '()
- (let ((x (car lis)))
- (if (pred x)
- (cons x (recur (cdr lis)))
- '())))))
-
-(define (drop-while pred lis)
- (check-arg procedure? pred drop-while)
- (let lp ((lis lis))
- (if (null-list? lis) '()
- (if (pred (car lis))
- (lp (cdr lis))
- lis))))
-
-(define (take-while! pred lis)
- (check-arg procedure? pred take-while!)
- (if (or (null-list? lis) (not (pred (car lis)))) '()
- (begin (let lp ((prev lis) (rest (cdr lis)))
- (if (pair? rest)
- (let ((x (car rest)))
- (if (pred x) (lp rest (cdr rest))
- (set-cdr! prev '())))))
- lis)))
-
-(define (span pred lis)
- (check-arg procedure? pred span)
- (let recur ((lis lis))
- (if (null-list? lis) (values '() '())
- (let ((x (car lis)))
- (if (pred x)
- (receive (prefix suffix) (recur (cdr lis))
- (values (cons x prefix) suffix))
- (values '() lis))))))
-
-(define (span! pred lis)
- (check-arg procedure? pred span!)
- (if (or (null-list? lis) (not (pred (car lis)))) (values '() lis)
- (let ((suffix (let lp ((prev lis) (rest (cdr lis)))
- (if (null-list? rest) rest
- (let ((x (car rest)))
- (if (pred x) (lp rest (cdr rest))
- (begin (set-cdr! prev '())
- rest)))))))
- (values lis suffix))))
-
-
-(define (break pred lis) (span (lambda (x) (not (pred x))) lis))
-(define (break! pred lis) (span! (lambda (x) (not (pred x))) lis))
-
-(define (any pred lis1 . lists)
- (check-arg procedure? pred any)
- (if (pair? lists)
-
- ;; N-ary case
- (receive (heads tails) (%cars+cdrs (cons lis1 lists))
- (and (pair? heads)
- (let lp ((heads heads) (tails tails))
- (receive (next-heads next-tails) (%cars+cdrs tails)
- (if (pair? next-heads)
- (or (apply pred heads) (lp next-heads next-tails))
- (apply pred heads)))))) ; Last PRED app is tail call.
-
- ;; Fast path
- (and (not (null-list? lis1))
- (let lp ((head (car lis1)) (tail (cdr lis1)))
- (if (null-list? tail)
- (pred head) ; Last PRED app is tail call.
- (or (pred head) (lp (car tail) (cdr tail))))))))
-
-
-;(define (every pred list) ; Simple definition.
-; (let lp ((list list)) ; Doesn't return the last PRED value.
-; (or (not (pair? list))
-; (and (pred (car list))
-; (lp (cdr list))))))
-
-(define (every pred lis1 . lists)
- (check-arg procedure? pred every)
- (if (pair? lists)
-
- ;; N-ary case
- (receive (heads tails) (%cars+cdrs (cons lis1 lists))
- (or (not (pair? heads))
- (let lp ((heads heads) (tails tails))
- (receive (next-heads next-tails) (%cars+cdrs tails)
- (if (pair? next-heads)
- (and (apply pred heads) (lp next-heads next-tails))
- (apply pred heads)))))) ; Last PRED app is tail call.
-
- ;; Fast path
- (or (null-list? lis1)
- (let lp ((head (car lis1)) (tail (cdr lis1)))
- (if (null-list? tail)
- (pred head) ; Last PRED app is tail call.
- (and (pred head) (lp (car tail) (cdr tail))))))))
-
-(define (list-index pred lis1 . lists)
- (check-arg procedure? pred list-index)
- (if (pair? lists)
-
- ;; N-ary case
- (let lp ((lists (cons lis1 lists)) (n 0))
- (receive (heads tails) (%cars+cdrs lists)
- (and (pair? heads)
- (if (apply pred heads) n
- (lp tails (+ n 1))))))
-
- ;; Fast path
- (let lp ((lis lis1) (n 0))
- (and (not (null-list? lis))
- (if (pred (car lis)) n (lp (cdr lis) (+ n 1)))))))
-
-;;; Reverse
-;;;;;;;;;;;
-
-;R4RS, so not defined here.
-;(define (reverse lis) (fold cons '() lis))
-
-;(define (reverse! lis)
-; (pair-fold (lambda (pair tail) (set-cdr! pair tail) pair) '() lis))
-
-(define (reverse! lis)
- (let lp ((lis lis) (ans '()))
- (if (null-list? lis) ans
- (let ((tail (cdr lis)))
- (set-cdr! lis ans)
- (lp tail lis)))))
-
-;;; Lists-as-sets
-;;;;;;;;;;;;;;;;;
-
-;;; This is carefully tuned code; do not modify casually.
-;;; - It is careful to share storage when possible;
-;;; - Side-effecting code tries not to perform redundant writes.
-;;; - It tries to avoid linear-time scans in special cases where constant-time
-;;; computations can be performed.
-;;; - It relies on similar properties from the other list-lib procs it calls.
-;;; For example, it uses the fact that the implementations of MEMBER and
-;;; FILTER in this source code share longest common tails between args
-;;; and results to get structure sharing in the lset procedures.
-
-(define (%lset2<= = lis1 lis2) (every (lambda (x) (member x lis2 =)) lis1))
-
-(define (lset<= = . lists)
- (check-arg procedure? = lset<=)
- (or (not (pair? lists)) ; 0-ary case
- (let lp ((s1 (car lists)) (rest (cdr lists)))
- (or (not (pair? rest))
- (let ((s2 (car rest)) (rest (cdr rest)))
- (and (or (eq? s2 s1) ; Fast path
- (%lset2<= = s1 s2)) ; Real test
- (lp s2 rest)))))))
-
-(define (lset= = . lists)
- (check-arg procedure? = lset=)
- (or (not (pair? lists)) ; 0-ary case
- (let lp ((s1 (car lists)) (rest (cdr lists)))
- (or (not (pair? rest))
- (let ((s2 (car rest))
- (rest (cdr rest)))
- (and (or (eq? s1 s2) ; Fast path
- (and (%lset2<= = s1 s2) (%lset2<= = s2 s1))) ; Real test
- (lp s2 rest)))))))
-
-
-(define (lset-adjoin = lis . elts)
- (check-arg procedure? = lset-adjoin)
- (fold (lambda (elt ans) (if (member elt ans =) ans (cons elt ans)))
- lis elts))
-
-
-(define (lset-union = . lists)
- (check-arg procedure? = lset-union)
- (reduce (lambda (lis ans) ; Compute ANS + LIS.
- (cond ((null? lis) ans) ; Don't copy any lists
- ((null? ans) lis) ; if we don't have to.
- ((eq? lis ans) ans)
- (else
- (fold (lambda (elt ans) (if (any (lambda (x) (= x elt)) ans)
- ans
- (cons elt ans)))
- ans lis))))
- '() lists))
-
-(define (lset-union! = . lists)
- (check-arg procedure? = lset-union!)
- (reduce (lambda (lis ans) ; Splice new elts of LIS onto the front of ANS.
- (cond ((null? lis) ans) ; Don't copy any lists
- ((null? ans) lis) ; if we don't have to.
- ((eq? lis ans) ans)
- (else
- (pair-fold (lambda (pair ans)
- (let ((elt (car pair)))
- (if (any (lambda (x) (= x elt)) ans)
- ans
- (begin (set-cdr! pair ans) pair))))
- ans lis))))
- '() lists))
-
-
-(define (lset-intersection = lis1 . lists)
- (check-arg procedure? = lset-intersection)
- (let ((lists (delete lis1 lists eq?))) ; Throw out any LIS1 vals.
- (cond ((any null-list? lists) '()) ; Short cut
- ((null? lists) lis1) ; Short cut
- (else (filter (lambda (x)
- (every (lambda (lis) (member x lis =)) lists))
- lis1)))))
-
-(define (lset-intersection! = lis1 . lists)
- (check-arg procedure? = lset-intersection!)
- (let ((lists (delete lis1 lists eq?))) ; Throw out any LIS1 vals.
- (cond ((any null-list? lists) '()) ; Short cut
- ((null? lists) lis1) ; Short cut
- (else (filter! (lambda (x)
- (every (lambda (lis) (member x lis =)) lists))
- lis1)))))
-
-
-(define (lset-difference = lis1 . lists)
- (check-arg procedure? = lset-difference)
- (let ((lists (filter pair? lists))) ; Throw out empty lists.
- (cond ((null? lists) lis1) ; Short cut
- ((memq lis1 lists) '()) ; Short cut
- (else (filter (lambda (x)
- (every (lambda (lis) (not (member x lis =)))
- lists))
- lis1)))))
-
-(define (lset-difference! = lis1 . lists)
- (check-arg procedure? = lset-difference!)
- (let ((lists (filter pair? lists))) ; Throw out empty lists.
- (cond ((null? lists) lis1) ; Short cut
- ((memq lis1 lists) '()) ; Short cut
- (else (filter! (lambda (x)
- (every (lambda (lis) (not (member x lis =)))
- lists))
- lis1)))))
-
-
-(define (lset-xor = . lists)
- (check-arg procedure? = lset-xor)
- (reduce (lambda (b a) ; Compute A xor B:
- ;; Note that this code relies on the constant-time
- ;; short-cuts provided by LSET-DIFF+INTERSECTION,
- ;; LSET-DIFFERENCE & APPEND to provide constant-time short
- ;; cuts for the cases A = (), B = (), and A eq? B. It takes
- ;; a careful case analysis to see it, but it's carefully
- ;; built in.
-
- ;; Compute a-b and a^b, then compute b-(a^b) and
- ;; cons it onto the front of a-b.
- (receive (a-b a-int-b) (lset-diff+intersection = a b)
- (cond ((null? a-b) (lset-difference = b a))
- ((null? a-int-b) (append b a))
- (else (fold (lambda (xb ans)
- (if (member xb a-int-b =) ans (cons xb ans)))
- a-b
- b)))))
- '() lists))
-
-
-(define (lset-xor! = . lists)
- (check-arg procedure? = lset-xor!)
- (reduce (lambda (b a) ; Compute A xor B:
- ;; Note that this code relies on the constant-time
- ;; short-cuts provided by LSET-DIFF+INTERSECTION,
- ;; LSET-DIFFERENCE & APPEND to provide constant-time short
- ;; cuts for the cases A = (), B = (), and A eq? B. It takes
- ;; a careful case analysis to see it, but it's carefully
- ;; built in.
-
- ;; Compute a-b and a^b, then compute b-(a^b) and
- ;; cons it onto the front of a-b.
- (receive (a-b a-int-b) (lset-diff+intersection! = a b)
- (cond ((null? a-b) (lset-difference! = b a))
- ((null? a-int-b) (append! b a))
- (else (pair-fold (lambda (b-pair ans)
- (if (member (car b-pair) a-int-b =) ans
- (begin (set-cdr! b-pair ans) b-pair)))
- a-b
- b)))))
- '() lists))
-
-
-(define (lset-diff+intersection = lis1 . lists)
- (check-arg procedure? = lset-diff+intersection)
- (cond ((every null-list? lists) (values lis1 '())) ; Short cut
- ((memq lis1 lists) (values '() lis1)) ; Short cut
- (else (partition (lambda (elt)
- (not (any (lambda (lis) (member elt lis =))
- lists)))
- lis1))))
-
-(define (lset-diff+intersection! = lis1 . lists)
- (check-arg procedure? = lset-diff+intersection!)
- (cond ((every null-list? lists) (values lis1 '())) ; Short cut
- ((memq lis1 lists) (values '() lis1)) ; Short cut
- (else (partition! (lambda (elt)
- (not (any (lambda (lis) (member elt lis =))
- lists)))
- lis1))))
-(define-library (srfi 1)
- (export
- xcons list-tabulate cons*
- proper-list? circular-list? dotted-list? not-pair? null-list? list=
- circular-list length+
- iota
- first second third fourth fifth sixth seventh eighth ninth tenth
- car+cdr
- take drop
- take-right drop-right
- take! drop-right!
- split-at split-at!
- last last-pair
- zip unzip1 unzip2 unzip3 unzip4 unzip5
- count
- append! append-reverse append-reverse! concatenate concatenate!
- unfold fold pair-fold reduce
- unfold-right fold-right pair-fold-right reduce-right
- append-map append-map! map! pair-for-each filter-map map-in-order
- filter partition remove
- filter! partition! remove!
- find find-tail any every list-index
- take-while drop-while take-while!
- span break span! break!
- delete delete!
- alist-cons alist-copy
- delete-duplicates delete-duplicates!
- alist-delete alist-delete!
- reverse!
- lset<= lset= lset-adjoin
- lset-union lset-intersection lset-difference lset-xor lset-diff+intersection
- lset-union! lset-intersection! lset-difference! lset-xor! lset-diff+intersection!
- )
- (import
- (except (scheme base) map member assoc)
- (scheme case-lambda)
- (scheme cxr)
- (srfi 8)
- (srfi aux))
- (begin
- (define-check-arg check-arg))
- (include "1.body.scm"))
-;;;;"array.scm" Arrays for Scheme
-; Copyright (C) 2001, 2003 Aubrey Jaffer
-;
-;Permission to copy this software, to modify it, to redistribute it,
-;to distribute modified versions, 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 warranty 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.
-
-;;@code{(require 'array)} or @code{(require 'srfi-63)}
-;;@ftindex array
-
-(define-record-type <array>
- (array:construct dimensions scales offset store)
- array:array?
- (dimensions dimensions)
- (scales scales)
- (offset offset)
- (store store))
-
-(define (array:dimensions array)
- (cond ((vector? array) (list (vector-length array)))
- ((string? array) (list (string-length array)))
- (else (dimensions array))))
-
-(define (array:scales array)
- (cond ((vector? array) '(1))
- ((string? array) '(1))
- (else (scales array))))
-
-(define (array:store array)
- (cond ((vector? array) array)
- ((string? array) array)
- (else (store array))))
-
-(define (array:offset array)
- (cond ((vector? array) 0)
- ((string? array) 0)
- (else (offset array))))
-
-;;@args obj
-;;Returns @code{#t} if the @1 is an array, and @code{#f} if not.
-(define (array? obj)
- (or (vector? obj) (string? obj) (array:array? obj)))
-
-;;@noindent
-;;@emph{Note:} Arrays are not disjoint from other Scheme types.
-;;Vectors and possibly strings also satisfy @code{array?}.
-;;A disjoint array predicate can be written:
-;;
-;;@example
-;;(define (strict-array? obj)
-;; (and (array? obj) (not (string? obj)) (not (vector? obj))))
-;;@end example
-
-;;@body
-;;Returns @code{#t} if @1 and @2 have the same rank and dimensions and the
-;;corresponding elements of @1 and @2 are @code{equal?}.
-
-;;@body
-;;@0 recursively compares the contents of pairs, vectors, strings, and
-;;@emph{arrays}, applying @code{eqv?} on other objects such as numbers
-;;and symbols. A rule of thumb is that objects are generally @0 if
-;;they print the same. @0 may fail to terminate if its arguments are
-;;circular data structures.
-;;
-;;@example
-;;(equal? 'a 'a) @result{} #t
-;;(equal? '(a) '(a)) @result{} #t
-;;(equal? '(a (b) c)
-;; '(a (b) c)) @result{} #t
-;;(equal? "abc" "abc") @result{} #t
-;;(equal? 2 2) @result{} #t
-;;(equal? (make-vector 5 'a)
-;; (make-vector 5 'a)) @result{} #t
-;;(equal? (make-array (a:fixN32b 4) 5 3)
-;; (make-array (a:fixN32b 4) 5 3)) @result{} #t
-;;(equal? (make-array '#(foo) 3 3)
-;; (make-array '#(foo) 3 3)) @result{} #t
-;;(equal? (lambda (x) x)
-;; (lambda (y) y)) @result{} @emph{unspecified}
-;;@end example
-(define (equal? obj1 obj2)
- (cond ((eqv? obj1 obj2) #t)
- ((or (pair? obj1) (pair? obj2))
- (and (pair? obj1) (pair? obj2)
- (equal? (car obj1) (car obj2))
- (equal? (cdr obj1) (cdr obj2))))
- ((or (string? obj1) (string? obj2))
- (and (string? obj1) (string? obj2)
- (string=? obj1 obj2)))
- ((or (vector? obj1) (vector? obj2))
- (and (vector? obj1) (vector? obj2)
- (equal? (vector-length obj1) (vector-length obj2))
- (do ((idx (+ -1 (vector-length obj1)) (+ -1 idx)))
- ((or (negative? idx)
- (not (equal? (vector-ref obj1 idx)
- (vector-ref obj2 idx))))
- (negative? idx)))))
- ((or (array? obj1) (array? obj2))
- (and (array? obj1) (array? obj2)
- (equal? (array:dimensions obj1) (array:dimensions obj2))
- (equal? (array:store obj1) (array:store obj2))))
- (else #f)))
-
-;;@body
-;;Returns the number of dimensions of @1. If @1 is not an array, 0 is
-;;returned.
-(define (array-rank obj)
- (if (array? obj) (length (array:dimensions obj)) 0))
-
-;;@args array
-;;Returns a list of dimensions.
-;;
-;;@example
-;;(array-dimensions (make-array '#() 3 5))
-;; @result{} (3 5)
-;;@end example
-(define array-dimensions array:dimensions)
-
-;;@args prototype k1 @dots{}
-;;
-;;Creates and returns an array of type @1 with dimensions @2, @dots{}
-;;and filled with elements from @1. @1 must be an array, vector, or
-;;string. The implementation-dependent type of the returned array
-;;will be the same as the type of @1; except if that would be a vector
-;;or string with rank not equal to one, in which case some variety of
-;;array will be returned.
-;;
-;;If the @1 has no elements, then the initial contents of the returned
-;;array are unspecified. Otherwise, the returned array will be filled
-;;with the element at the origin of @1.
-(define (make-array prototype . dimensions)
- (define tcnt (apply * dimensions))
- (let ((store
- (if (string? prototype)
- (case (string-length prototype)
- ((0) (make-string tcnt))
- (else (make-string tcnt
- (string-ref prototype 0))))
- (let ((pdims (array:dimensions prototype)))
- (case (apply * pdims)
- ((0) (make-vector tcnt))
- (else (make-vector tcnt
- (apply array-ref prototype
- (map (lambda (x) 0) pdims)))))))))
- (define (loop dims scales)
- (if (null? dims)
- (array:construct dimensions (cdr scales) 0 store)
- (loop (cdr dims) (cons (* (car dims) (car scales)) scales))))
- (loop (reverse dimensions) '(1))))
-;;@args prototype k1 @dots{}
-;;@0 is an alias for @code{make-array}.
-(define create-array make-array)
-
-;;@args array mapper k1 @dots{}
-;;@0 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:
-;;
-;;@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)
-;; @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 example
-(define (make-shared-array array mapper . dimensions)
- (define odl (array:scales array))
- (define rank (length dimensions))
- (define shape
- (map (lambda (dim) (if (list? dim) dim (list 0 (+ -1 dim)))) dimensions))
- (do ((idx (+ -1 rank) (+ -1 idx))
- (uvt (append (cdr (vector->list (make-vector rank 0))) '(1))
- (append (cdr uvt) '(0)))
- (uvts '() (cons uvt uvts)))
- ((negative? idx)
- (let ((ker0 (apply + (map * odl (apply mapper uvt)))))
- (array:construct
- (map (lambda (dim) (+ 1 (- (cadr dim) (car dim)))) shape)
- (map (lambda (uvt) (- (apply + (map * odl (apply mapper uvt))) ker0))
- uvts)
- (apply +
- (array:offset array)
- (map * odl (apply mapper (map car shape))))
- (array:store array))))))
-
-;;@args rank proto list
-;;@3 must be a rank-nested list consisting of all the elements, in
-;;row-major order, of the array to be created.
-;;
-;;@0 returns an array of rank @1 and type @2 consisting of all the
-;;elements, in row-major order, of @3. When @1 is 0, @3 is the lone
-;;array element; not necessarily a list.
-;;
-;;@example
-;;(list->array 2 '#() '((1 2) (3 4)))
-;; @result{} #2A((1 2) (3 4))
-;;(list->array 0 '#() 3)
-;; @result{} #0A 3
-;;@end example
-(define (list->array rank proto lst)
- (define dimensions
- (do ((shp '() (cons (length row) shp))
- (row lst (car lst))
- (rnk (+ -1 rank) (+ -1 rnk)))
- ((negative? rnk) (reverse shp))))
- (let ((nra (apply make-array proto dimensions)))
- (define (l2ra dims idxs row)
- (cond ((null? dims)
- (apply array-set! nra row (reverse idxs)))
- (else
- (if (not (eqv? (car dims) (length row)))
- (error "Array not rectangular:" dims dimensions))
- (do ((idx 0 (+ 1 idx))
- (row row (cdr row)))
- ((>= idx (car dims)))
- (l2ra (cdr dims) (cons idx idxs) (car row))))))
- (l2ra dimensions '() lst)
- nra))
-
-;;@args array
-;;Returns a rank-nested list consisting of all the elements, in
-;;row-major order, of @1. In the case of a rank-0 array, @0 returns
-;;the single element.
-;;
-;;@example
-;;(array->list #2A((ho ho ho) (ho oh oh)))
-;; @result{} ((ho ho ho) (ho oh oh))
-;;(array->list #0A ho)
-;; @result{} ho
-;;@end example
-(define (array->list ra)
- (define (ra2l dims idxs)
- (if (null? dims)
- (apply array-ref ra (reverse idxs))
- (do ((lst '() (cons (ra2l (cdr dims) (cons idx idxs)) lst))
- (idx (+ -1 (car dims)) (+ -1 idx)))
- ((negative? idx) lst))))
- (ra2l (array-dimensions ra) '()))
-
-;;@args vect proto dim1 @dots{}
-;;@1 must be a vector of length equal to the product of exact
-;;nonnegative integers @3, @dots{}.
-;;
-;;@0 returns an array of type @2 consisting of all the elements, in
-;;row-major order, of @1. In the case of a rank-0 array, @1 has a
-;;single element.
-;;
-;;@example
-;;(vector->array #(1 2 3 4) #() 2 2)
-;; @result{} #2A((1 2) (3 4))
-;;(vector->array '#(3) '#())
-;; @result{} #0A 3
-;;@end example
-(define (vector->array vect prototype . dimensions)
- (define vdx (vector-length vect))
- (if (not (eqv? vdx (apply * dimensions)))
- (error "Vector length does not equal product of dimensions:"
- vdx dimensions))
- (let ((ra (apply make-array prototype dimensions)))
- (define (v2ra dims idxs)
- (cond ((null? dims)
- (set! vdx (+ -1 vdx))
- (apply array-set! ra (vector-ref vect vdx) (reverse idxs)))
- (else
- (do ((idx (+ -1 (car dims)) (+ -1 idx)))
- ((negative? idx) vect)
- (v2ra (cdr dims) (cons idx idxs))))))
- (v2ra dimensions '())
- ra))
-
-;;@args array
-;;Returns a new vector consisting of all the elements of @1 in
-;;row-major order.
-;;
-;;@example
-;;(array->vector #2A ((1 2)( 3 4)))
-;; @result{} #(1 2 3 4)
-;;(array->vector #0A ho)
-;; @result{} #(ho)
-;;@end example
-(define (array->vector ra)
- (define dims (array-dimensions ra))
- (let* ((vdx (apply * dims))
- (vect (make-vector vdx)))
- (define (ra2v dims idxs)
- (if (null? dims)
- (let ((val (apply array-ref ra (reverse idxs))))
- (set! vdx (+ -1 vdx))
- (vector-set! vect vdx val)
- vect)
- (do ((idx (+ -1 (car dims)) (+ -1 idx)))
- ((negative? idx) vect)
- (ra2v (cdr dims) (cons idx idxs)))))
- (ra2v dims '())))
-
-(define (array:in-bounds? array indices)
- (do ((bnds (array:dimensions array) (cdr bnds))
- (idxs indices (cdr idxs)))
- ((or (null? bnds)
- (null? idxs)
- (not (integer? (car idxs)))
- (not (< -1 (car idxs) (car bnds))))
- (and (null? bnds) (null? idxs)))))
-
-;;@args array index1 @dots{}
-;;Returns @code{#t} if its arguments would be acceptable to
-;;@code{array-ref}.
-(define (array-in-bounds? array . indices)
- (array:in-bounds? array indices))
-
-;;@args array k1 @dots{}
-;;Returns the (@2, @dots{}) element of @1.
-(define (array-ref array . indices)
- (define store (array:store array))
- (or (array:in-bounds? array indices)
- (error "Bad indices:" indices))
- ((if (string? store) string-ref vector-ref)
- store (apply + (array:offset array) (map * (array:scales array) indices))))
-
-;;@args array obj k1 @dots{}
-;;Stores @2 in the (@3, @dots{}) element of @1. The value returned
-;;by @0 is unspecified.
-(define (array-set! array obj . indices)
- (define store (array:store array))
- (or (array:in-bounds? array indices)
- (error "Bad indices:" indices))
- ((if (string? store) string-set! vector-set!)
- store (apply + (array:offset array) (map * (array:scales array) indices))
- obj))
-
-;;@noindent
-;;These functions return a prototypical uniform-array enclosing the
-;;optional argument (which must be of the correct type). If the
-;;uniform-array type is supported by the implementation, then it is
-;;returned; defaulting to the next larger precision type; resorting
-;;finally to vector.
-
-(define (make-prototype-checker name pred? creator)
- (lambda args
- (case (length args)
- ((1) (if (pred? (car args))
- (creator (car args))
- (error "Incompatible type:" name (car args))))
- ((0) (creator))
- (else (error "Wrong number of arguments:" name args)))))
-
-(define (integer-bytes?? n)
- (lambda (obj)
- (and (integer? obj)
- (exact? obj)
- (or (negative? n) (not (negative? obj)))
- (do ((num obj (quotient num 256))
- (n (+ -1 (abs n)) (+ -1 n)))
- ((or (zero? num) (negative? n))
- (zero? num))))))
-
-;;@args z
-;;@args
-;;Returns an inexact 128.bit flonum complex uniform-array prototype.
-(define a:floc128b (make-prototype-checker 'a:floc128b complex? vector))
-;;@args z
-;;@args
-;;Returns an inexact 64.bit flonum complex uniform-array prototype.
-(define a:floc64b (make-prototype-checker 'a:floc64b complex? vector))
-;;@args z
-;;@args
-;;Returns an inexact 32.bit flonum complex uniform-array prototype.
-(define a:floc32b (make-prototype-checker 'a:floc32b complex? vector))
-;;@args z
-;;@args
-;;Returns an inexact 16.bit flonum complex uniform-array prototype.
-(define a:floc16b (make-prototype-checker 'a:floc16b complex? vector))
-
-;;@args z
-;;@args
-;;Returns an inexact 128.bit flonum real uniform-array prototype.
-(define a:flor128b (make-prototype-checker 'a:flor128b real? vector))
-;;@args z
-;;@args
-;;Returns an inexact 64.bit flonum real uniform-array prototype.
-(define a:flor64b (make-prototype-checker 'a:flor64b real? vector))
-;;@args z
-;;@args
-;;Returns an inexact 32.bit flonum real uniform-array prototype.
-(define a:flor32b (make-prototype-checker 'a:flor32b real? vector))
-;;@args z
-;;@args
-;;Returns an inexact 16.bit flonum real uniform-array prototype.
-(define a:flor16b (make-prototype-checker 'a:flor16b real? vector))
-
-;;@args z
-;;@args
-;;Returns an exact 128.bit decimal flonum rational uniform-array prototype.
-(define a:flor128b (make-prototype-checker 'a:flor128b real? vector))
-;;@args z
-;;@args
-;;Returns an exact 64.bit decimal flonum rational uniform-array prototype.
-(define a:flor64b (make-prototype-checker 'a:flor64b real? vector))
-;;@args z
-;;@args
-;;Returns an exact 32.bit decimal flonum rational uniform-array prototype.
-(define a:flor32b (make-prototype-checker 'a:flor32b real? vector))
-
-;;@args n
-;;@args
-;;Returns an exact binary fixnum uniform-array prototype with at least
-;;64 bits of precision.
-(define a:fixz64b (make-prototype-checker 'a:fixz64b (integer-bytes?? -8) vector))
-;;@args n
-;;@args
-;;Returns an exact binary fixnum uniform-array prototype with at least
-;;32 bits of precision.
-(define a:fixz32b (make-prototype-checker 'a:fixz32b (integer-bytes?? -4) vector))
-;;@args n
-;;@args
-;;Returns an exact binary fixnum uniform-array prototype with at least
-;;16 bits of precision.
-(define a:fixz16b (make-prototype-checker 'a:fixz16b (integer-bytes?? -2) vector))
-;;@args n
-;;@args
-;;Returns an exact binary fixnum uniform-array prototype with at least
-;;8 bits of precision.
-(define a:fixz8b (make-prototype-checker 'a:fixz8b (integer-bytes?? -1) vector))
-
-;;@args k
-;;@args
-;;Returns an exact non-negative binary fixnum uniform-array prototype with at
-;;least 64 bits of precision.
-(define a:fixn64b (make-prototype-checker 'a:fixn64b (integer-bytes?? 8) vector))
-;;@args k
-;;@args
-;;Returns an exact non-negative binary fixnum uniform-array prototype with at
-;;least 32 bits of precision.
-(define a:fixn32b (make-prototype-checker 'a:fixn32b (integer-bytes?? 4) vector))
-;;@args k
-;;@args
-;;Returns an exact non-negative binary fixnum uniform-array prototype with at
-;;least 16 bits of precision.
-(define a:fixn16b (make-prototype-checker 'a:fixn16b (integer-bytes?? 2) vector))
-;;@args k
-;;@args
-;;Returns an exact non-negative binary fixnum uniform-array prototype with at
-;;least 8 bits of precision.
-(define a:fixn8b (make-prototype-checker 'a:fixn8b (integer-bytes?? 1) vector))
-
-;;@args bool
-;;@args
-;;Returns a boolean uniform-array prototype.
-(define a:bool (make-prototype-checker 'a:bool boolean? vector))
-;;; SRFI-1 list-processing library -*- Scheme -*-
-;;; Reference implementation
-;;;
-;;; Copyright (c) 1998, 1999 by Olin Shivers. You may do as you please with
-;;; this code as long as you do not remove this copyright notice or
-;;; hold me liable for its use. Please send bug reports to shivers@ai.mit.edu.
-;;; -Olin
-
-;;; This is a library of list- and pair-processing functions. I wrote it after
-;;; carefully considering the functions provided by the libraries found in
-;;; R4RS/R5RS Scheme, MIT Scheme, Gambit, RScheme, MzScheme, slib, Common
-;;; Lisp, Bigloo, guile, T, APL and the SML standard basis. It is a pretty
-;;; rich toolkit, providing a superset of the functionality found in any of
-;;; the various Schemes I considered.
-
-;;; This implementation is intended as a portable reference implementation
-;;; for SRFI-1. See the porting notes below for more information.
-
-;;; Exported:
-;;; xcons tree-copy make-list list-tabulate cons* list-copy
-;;; proper-list? circular-list? dotted-list? not-pair? null-list? list=
-;;; circular-list length+
-;;; iota
-;;; first second third fourth fifth sixth seventh eighth ninth tenth
-;;; car+cdr
-;;; take drop
-;;; take-right drop-right
-;;; take! drop-right!
-;;; split-at split-at!
-;;; last last-pair
-;;; zip unzip1 unzip2 unzip3 unzip4 unzip5
-;;; count
-;;; append! append-reverse append-reverse! concatenate concatenate!
-;;; unfold fold pair-fold reduce
-;;; unfold-right fold-right pair-fold-right reduce-right
-;;; append-map append-map! map! pair-for-each filter-map map-in-order
-;;; filter partition remove
-;;; filter! partition! remove!
-;;; find find-tail any every list-index
-;;; take-while drop-while take-while!
-;;; span break span! break!
-;;; delete delete!
-;;; alist-cons alist-copy
-;;; delete-duplicates delete-duplicates!
-;;; alist-delete alist-delete!
-;;; reverse!
-;;; lset<= lset= lset-adjoin
-;;; lset-union lset-intersection lset-difference lset-xor lset-diff+intersection
-;;; lset-union! lset-intersection! lset-difference! lset-xor! lset-diff+intersection!
-;;;
-;;; In principle, the following R4RS list- and pair-processing procedures
-;;; are also part of this package's exports, although they are not defined
-;;; in this file:
-;;; Primitives: cons pair? null? car cdr set-car! set-cdr!
-;;; Non-primitives: list length append reverse cadr ... cddddr list-ref
-;;; memq memv assq assv
-;;; (The non-primitives are defined in this file, but commented out.)
-;;;
-;;; These R4RS procedures have extended definitions in SRFI-1 and are defined
-;;; in this file:
-;;; map for-each member assoc
-;;;
-;;; The remaining two R4RS list-processing procedures are not included:
-;;; list-tail (use drop)
-;;; list? (use proper-list?)
-
-
-;;; A note on recursion and iteration/reversal:
-;;; Many iterative list-processing algorithms naturally compute the elements
-;;; of the answer list in the wrong order (left-to-right or head-to-tail) from
-;;; the order needed to cons them into the proper answer (right-to-left, or
-;;; tail-then-head). One style or idiom of programming these algorithms, then,
-;;; loops, consing up the elements in reverse order, then destructively
-;;; reverses the list at the end of the loop. I do not do this. The natural
-;;; and efficient way to code these algorithms is recursively. This trades off
-;;; intermediate temporary list structure for intermediate temporary stack
-;;; structure. In a stack-based system, this improves cache locality and
-;;; lightens the load on the GC system. Don't stand on your head to iterate!
-;;; Recurse, where natural. Multiple-value returns make this even more
-;;; convenient, when the recursion/iteration has multiple state values.
-
-;;; Porting:
-;;; This is carefully tuned code; do not modify casually.
-;;; - It is careful to share storage when possible;
-;;; - Side-effecting code tries not to perform redundant writes.
-;;;
-;;; That said, a port of this library to a specific Scheme system might wish
-;;; to tune this code to exploit particulars of the implementation.
-;;; The single most important compiler-specific optimisation you could make
-;;; to this library would be to add rewrite rules or transforms to:
-;;; - transform applications of n-ary procedures (e.g. LIST=, CONS*, APPEND,
-;;; LSET-UNION) into multiple applications of a primitive two-argument
-;;; variant.
-;;; - transform applications of the mapping functions (MAP, FOR-EACH, FOLD,
-;;; ANY, EVERY) into open-coded loops. The killer here is that these
-;;; functions are n-ary. Handling the general case is quite inefficient,
-;;; requiring many intermediate data structures to be allocated and
-;;; discarded.
-;;; - transform applications of procedures that take optional arguments
-;;; into calls to variants that do not take optional arguments. This
-;;; eliminates unnecessary consing and parsing of the rest parameter.
-;;;
-;;; These transforms would provide BIG speedups. In particular, the n-ary
-;;; mapping functions are particularly slow and cons-intensive, and are good
-;;; candidates for tuning. I have coded fast paths for the single-list cases,
-;;; but what you really want to do is exploit the fact that the compiler
-;;; usually knows how many arguments are being passed to a particular
-;;; application of these functions -- they are usually explicitly called, not
-;;; passed around as higher-order values. If you can arrange to have your
-;;; compiler produce custom code or custom linkages based on the number of
-;;; arguments in the call, you can speed these functions up a *lot*. But this
-;;; kind of compiler technology no longer exists in the Scheme world as far as
-;;; I can see.
-;;;
-;;; Note that this code is, of course, dependent upon standard bindings for
-;;; the R5RS procedures -- i.e., it assumes that the variable CAR is bound
-;;; to the procedure that takes the car of a list. If your Scheme
-;;; implementation allows user code to alter the bindings of these procedures
-;;; in a manner that would be visible to these definitions, then there might
-;;; be trouble. You could consider horrible kludgery along the lines of
-;;; (define fact
-;;; (let ((= =) (- -) (* *))
-;;; (letrec ((real-fact (lambda (n)
-;;; (if (= n 0) 1 (* n (real-fact (- n 1)))))))
-;;; real-fact)))
-;;; Or you could consider shifting to a reasonable Scheme system that, say,
-;;; has a module system protecting code from this kind of lossage.
-;;;
-;;; This code does a fair amount of run-time argument checking. If your
-;;; Scheme system has a sophisticated compiler that can eliminate redundant
-;;; error checks, this is no problem. However, if not, these checks incur
-;;; some performance overhead -- and, in a safe Scheme implementation, they
-;;; are in some sense redundant: if we don't check to see that the PROC
-;;; parameter is a procedure, we'll find out anyway three lines later when
-;;; we try to call the value. It's pretty easy to rip all this argument
-;;; checking code out if it's inappropriate for your implementation -- just
-;;; nuke every call to CHECK-ARG.
-;;;
-;;; On the other hand, if you *do* have a sophisticated compiler that will
-;;; actually perform soft-typing and eliminate redundant checks (Rice's systems
-;;; being the only possible candidate of which I'm aware), leaving these checks
-;;; in can *help*, since their presence can be elided in redundant cases,
-;;; and in cases where they are needed, performing the checks early, at
-;;; procedure entry, can "lift" a check out of a loop.
-;;;
-;;; Finally, I have only checked the properties that can portably be checked
-;;; with R5RS Scheme -- and this is not complete. You may wish to alter
-;;; the CHECK-ARG parameter checks to perform extra, implementation-specific
-;;; checks, such as procedure arity for higher-order values.
-;;;
-;;; The code has only these non-R4RS dependencies:
-;;; A few calls to an ERROR procedure;
-;;; Uses of the R5RS multiple-value procedure VALUES and the m-v binding
-;;; RECEIVE macro (which isn't R5RS, but is a trivial macro).
-;;; Many calls to a parameter-checking procedure check-arg:
-;;; (define (check-arg pred val caller)
-;;; (let lp ((val val))
-;;; (if (pred val) val (lp (error "Bad argument" val pred caller)))))
-;;; A few uses of the LET-OPTIONAL and :OPTIONAL macros for parsing
-;;; optional arguments.
-;;;
-;;; Most of these procedures use the NULL-LIST? test to trigger the
-;;; base case in the inner loop or recursion. The NULL-LIST? function
-;;; is defined to be a careful one -- it raises an error if passed a
-;;; non-nil, non-pair value. The spec allows an implementation to use
-;;; a less-careful implementation that simply defines NULL-LIST? to
-;;; be NOT-PAIR?. This would speed up the inner loops of these procedures
-;;; at the expense of having them silently accept dotted lists.
-
-;;; A note on dotted lists:
-;;; I, personally, take the view that the only consistent view of lists
-;;; in Scheme is the view that *everything* is a list -- values such as
-;;; 3 or "foo" or 'bar are simply empty dotted lists. This is due to the
-;;; fact that Scheme actually has no true list type. It has a pair type,
-;;; and there is an *interpretation* of the trees built using this type
-;;; as lists.
-;;;
-;;; I lobbied to have these list-processing procedures hew to this
-;;; view, and accept any value as a list argument. I was overwhelmingly
-;;; overruled during the SRFI discussion phase. So I am inserting this
-;;; text in the reference lib and the SRFI spec as a sort of "minority
-;;; opinion" dissent.
-;;;
-;;; Many of the procedures in this library can be trivially redefined
-;;; to handle dotted lists, just by changing the NULL-LIST? base-case
-;;; check to NOT-PAIR?, meaning that any non-pair value is taken to be
-;;; an empty list. For most of these procedures, that's all that is
-;;; required.
-;;;
-;;; However, we have to do a little more work for some procedures that
-;;; *produce* lists from other lists. Were we to extend these procedures to
-;;; accept dotted lists, we would have to define how they terminate the lists
-;;; produced as results when passed a dotted list. I designed a coherent set
-;;; of termination rules for these cases; this was posted to the SRFI-1
-;;; discussion list. I additionally wrote an earlier version of this library
-;;; that implemented that spec. It has been discarded during later phases of
-;;; the definition and implementation of this library.
-;;;
-;;; The argument *against* defining these procedures to work on dotted
-;;; lists is that dotted lists are the rare, odd case, and that by
-;;; arranging for the procedures to handle them, we lose error checking
-;;; in the cases where a dotted list is passed by accident -- e.g., when
-;;; the programmer swaps a two arguments to a list-processing function,
-;;; one being a scalar and one being a list. For example,
-;;; (member '(1 3 5 7 9) 7)
-;;; This would quietly return #f if we extended MEMBER to accept dotted
-;;; lists.
-;;;
-;;; The SRFI discussion record contains more discussion on this topic.
-
-
-;;; Constructors
-;;;;;;;;;;;;;;;;
-
-;;; Occasionally useful as a value to be passed to a fold or other
-;;; higher-order procedure.
-(define (xcons d a) (cons a d))
-
-;;;; Recursively copy every cons.
-;(define (tree-copy x)
-; (let recur ((x x))
-; (if (not (pair? x)) x
-; (cons (recur (car x)) (recur (cdr x))))))
-
-;;; Make a list of length LEN.
-
-(define (make-list len . maybe-elt)
- (check-arg (lambda (n) (and (integer? n) (>= n 0))) len make-list)
- (let ((elt (cond ((null? maybe-elt) #f) ; Default value
- ((null? (cdr maybe-elt)) (car maybe-elt))
- (else (error "Too many arguments to MAKE-LIST"
- (cons len maybe-elt))))))
- (do ((i len (- i 1))
- (ans '() (cons elt ans)))
- ((<= i 0) ans))))
-
-
-;(define (list . ans) ans) ; R4RS
-
-
-;;; Make a list of length LEN. Elt i is (PROC i) for 0 <= i < LEN.
-
-(define (list-tabulate len proc)
- (check-arg (lambda (n) (and (integer? n) (>= n 0))) len list-tabulate)
- (check-arg procedure? proc list-tabulate)
- (do ((i (- len 1) (- i 1))
- (ans '() (cons (proc i) ans)))
- ((< i 0) ans)))
-
-;;; (cons* a1 a2 ... an) = (cons a1 (cons a2 (cons ... an)))
-;;; (cons* a1) = a1 (cons* a1 a2 ...) = (cons a1 (cons* a2 ...))
-;;;
-;;; (cons first (unfold not-pair? car cdr rest values))
-
-(define (cons* first . rest)
- (let recur ((x first) (rest rest))
- (if (pair? rest)
- (cons x (recur (car rest) (cdr rest)))
- x)))
-
-;;; (unfold not-pair? car cdr lis values)
-
-(define (list-copy lis)
- (let recur ((lis lis))
- (if (pair? lis)
- (cons (car lis) (recur (cdr lis)))
- lis)))
-
-;;; IOTA count [start step] (start start+step ... start+(count-1)*step)
-
-(define (iota count . maybe-start+step)
- (check-arg integer? count iota)
- (if (< count 0) (error "Negative step count" iota count))
- (let-optionals maybe-start+step ((start 0) (step 1))
- (check-arg number? start iota)
- (check-arg number? step iota)
- (let loop ((n 0) (r '()))
- (if (= n count)
- (reverse r)
- (loop (+ 1 n)
- (cons (+ start (* n step)) r))))))
-
-;;; I thought these were lovely, but the public at large did not share my
-;;; enthusiasm...
-;;; :IOTA to (0 ... to-1)
-;;; :IOTA from to (from ... to-1)
-;;; :IOTA from to step (from from+step ...)
-
-;;; IOTA: to (1 ... to)
-;;; IOTA: from to (from+1 ... to)
-;;; IOTA: from to step (from+step from+2step ...)
-
-;(define (%parse-iota-args arg1 rest-args proc)
-; (let ((check (lambda (n) (check-arg integer? n proc))))
-; (check arg1)
-; (if (pair? rest-args)
-; (let ((arg2 (check (car rest-args)))
-; (rest (cdr rest-args)))
-; (if (pair? rest)
-; (let ((arg3 (check (car rest)))
-; (rest (cdr rest)))
-; (if (pair? rest) (error "Too many parameters" proc arg1 rest-args)
-; (values arg1 arg2 arg3)))
-; (values arg1 arg2 1)))
-; (values 0 arg1 1))))
-;
-;(define (iota: arg1 . rest-args)
-; (receive (from to step) (%parse-iota-args arg1 rest-args iota:)
-; (let* ((numsteps (floor (/ (- to from) step)))
-; (last-val (+ from (* step numsteps))))
-; (if (< numsteps 0) (error "Negative step count" iota: from to step))
-; (do ((steps-left numsteps (- steps-left 1))
-; (val last-val (- val step))
-; (ans '() (cons val ans)))
-; ((<= steps-left 0) ans)))))
-;
-;
-;(define (\:iota arg1 . rest-args)
-; (receive (from to step) (%parse-iota-args arg1 rest-args :iota)
-; (let* ((numsteps (ceiling (/ (- to from) step)))
-; (last-val (+ from (* step (- numsteps 1)))))
-; (if (< numsteps 0) (error "Negative step count" :iota from to step))
-; (do ((steps-left numsteps (- steps-left 1))
-; (val last-val (- val step))
-; (ans '() (cons val ans)))
-; ((<= steps-left 0) ans)))))
-
-
-
-(define (circular-list val1 . vals)
- (let ((ans (cons val1 vals)))
- (set-cdr! (last-pair ans) ans)
- ans))
-
-;;; <proper-list> ::= () ; Empty proper list
-;;; | (cons <x> <proper-list>) ; Proper-list pair
-;;; Note that this definition rules out circular lists -- and this
-;;; function is required to detect this case and return false.
-
-(define (proper-list? x)
- (let lp ((x x) (lag x))
- (if (pair? x)
- (let ((x (cdr x)))
- (if (pair? x)
- (let ((x (cdr x))
- (lag (cdr lag)))
- (and (not (eq? x lag)) (lp x lag)))
- (null? x)))
- (null? x))))
-
-
-;;; A dotted list is a finite list (possibly of length 0) terminated
-;;; by a non-nil value. Any non-cons, non-nil value (e.g., "foo" or 5)
-;;; is a dotted list of length 0.
-;;;
-;;; <dotted-list> ::= <non-nil,non-pair> ; Empty dotted list
-;;; | (cons <x> <dotted-list>) ; Proper-list pair
-
-(define (dotted-list? x)
- (let lp ((x x) (lag x))
- (if (pair? x)
- (let ((x (cdr x)))
- (if (pair? x)
- (let ((x (cdr x))
- (lag (cdr lag)))
- (and (not (eq? x lag)) (lp x lag)))
- (not (null? x))))
- (not (null? x)))))
-
-(define (circular-list? x)
- (let lp ((x x) (lag x))
- (and (pair? x)
- (let ((x (cdr x)))
- (and (pair? x)
- (let ((x (cdr x))
- (lag (cdr lag)))
- (or (eq? x lag) (lp x lag))))))))
-
-(define (not-pair? x) (not (pair? x))) ; Inline me.
-
-;;; This is a legal definition which is fast and sloppy:
-;;; (define null-list? not-pair?)
-;;; but we'll provide a more careful one:
-(define (null-list? l)
- (cond ((pair? l) #f)
- ((null? l) #t)
- (else (error "null-list?: argument out of domain" l))))
-
-
-(define (list= = . lists)
- (or (null? lists) ; special case
-
- (let lp1 ((list-a (car lists)) (others (cdr lists)))
- (or (null? others)
- (let ((list-b (car others))
- (others (cdr others)))
- (if (eq? list-a list-b) ; EQ? => LIST=
- (lp1 list-b others)
- (let lp2 ((list-a list-a) (list-b list-b))
- (if (null-list? list-a)
- (and (null-list? list-b)
- (lp1 list-b others))
- (and (not (null-list? list-b))
- (= (car list-a) (car list-b))
- (lp2 (cdr list-a) (cdr list-b)))))))))))
-
-
-
-;;; R4RS, so commented out.
-;(define (length x) ; LENGTH may diverge or
-; (let lp ((x x) (len 0)) ; raise an error if X is
-; (if (pair? x) ; a circular list. This version
-; (lp (cdr x) (+ len 1)) ; diverges.
-; len)))
-
-(define (length+ x) ; Returns #f if X is circular.
- (let lp ((x x) (lag x) (len 0))
- (if (pair? x)
- (let ((x (cdr x))
- (len (+ len 1)))
- (if (pair? x)
- (let ((x (cdr x))
- (lag (cdr lag))
- (len (+ len 1)))
- (and (not (eq? x lag)) (lp x lag len)))
- len))
- len)))
-
-(define (zip list1 . more-lists) (apply map list list1 more-lists))
-
-
-;;; Selectors
-;;;;;;;;;;;;;
-
-;;; R4RS non-primitives:
-;(define (caar x) (car (car x)))
-;(define (cadr x) (car (cdr x)))
-;(define (cdar x) (cdr (car x)))
-;(define (cddr x) (cdr (cdr x)))
-;
-;(define (caaar x) (caar (car x)))
-;(define (caadr x) (caar (cdr x)))
-;(define (cadar x) (cadr (car x)))
-;(define (caddr x) (cadr (cdr x)))
-;(define (cdaar x) (cdar (car x)))
-;(define (cdadr x) (cdar (cdr x)))
-;(define (cddar x) (cddr (car x)))
-;(define (cdddr x) (cddr (cdr x)))
-;
-;(define (caaaar x) (caaar (car x)))
-;(define (caaadr x) (caaar (cdr x)))
-;(define (caadar x) (caadr (car x)))
-;(define (caaddr x) (caadr (cdr x)))
-;(define (cadaar x) (cadar (car x)))
-;(define (cadadr x) (cadar (cdr x)))
-;(define (caddar x) (caddr (car x)))
-;(define (cadddr x) (caddr (cdr x)))
-;(define (cdaaar x) (cdaar (car x)))
-;(define (cdaadr x) (cdaar (cdr x)))
-;(define (cdadar x) (cdadr (car x)))
-;(define (cdaddr x) (cdadr (cdr x)))
-;(define (cddaar x) (cddar (car x)))
-;(define (cddadr x) (cddar (cdr x)))
-;(define (cdddar x) (cdddr (car x)))
-;(define (cddddr x) (cdddr (cdr x)))
-
-
-(define first car)
-(define second cadr)
-(define third caddr)
-(define fourth cadddr)
-(define (fifth x) (car (cddddr x)))
-(define (sixth x) (cadr (cddddr x)))
-(define (seventh x) (caddr (cddddr x)))
-(define (eighth x) (cadddr (cddddr x)))
-(define (ninth x) (car (cddddr (cddddr x))))
-(define (tenth x) (cadr (cddddr (cddddr x))))
-
-(define (car+cdr pair) (values (car pair) (cdr pair)))
-
-;;; take & drop
-
-(define (take lis k)
- (check-arg integer? k take)
- (let recur ((lis lis) (k k))
- (if (zero? k) '()
- (cons (car lis)
- (recur (cdr lis) (- k 1))))))
-
-(define (drop lis k)
- (check-arg integer? k drop)
- (let iter ((lis lis) (k k))
- (if (zero? k) lis (iter (cdr lis) (- k 1)))))
-
-(define (take! lis k)
- (check-arg integer? k take!)
- (if (zero? k) '()
- (begin (set-cdr! (drop lis (- k 1)) '())
- lis)))
-
-;;; TAKE-RIGHT and DROP-RIGHT work by getting two pointers into the list,
-;;; off by K, then chasing down the list until the lead pointer falls off
-;;; the end.
-
-(define (take-right lis k)
- (check-arg integer? k take-right)
- (let lp ((lag lis) (lead (drop lis k)))
- (if (pair? lead)
- (lp (cdr lag) (cdr lead))
- lag)))
-
-(define (drop-right lis k)
- (check-arg integer? k drop-right)
- (let recur ((lag lis) (lead (drop lis k)))
- (if (pair? lead)
- (cons (car lag) (recur (cdr lag) (cdr lead)))
- '())))
-
-;;; In this function, LEAD is actually K+1 ahead of LAG. This lets
-;;; us stop LAG one step early, in time to smash its cdr to ().
-(define (drop-right! lis k)
- (check-arg integer? k drop-right!)
- (let ((lead (drop lis k)))
- (if (pair? lead)
-
- (let lp ((lag lis) (lead (cdr lead))) ; Standard case
- (if (pair? lead)
- (lp (cdr lag) (cdr lead))
- (begin (set-cdr! lag '())
- lis)))
-
- '()))) ; Special case dropping everything -- no cons to side-effect.
-
-;(define (list-ref lis i) (car (drop lis i))) ; R4RS
-
-;;; These use the APL convention, whereby negative indices mean
-;;; "from the right." I liked them, but they didn't win over the
-;;; SRFI reviewers.
-;;; K >= 0: Take and drop K elts from the front of the list.
-;;; K <= 0: Take and drop -K elts from the end of the list.
-
-;(define (take lis k)
-; (check-arg integer? k take)
-; (if (negative? k)
-; (list-tail lis (+ k (length lis)))
-; (let recur ((lis lis) (k k))
-; (if (zero? k) '()
-; (cons (car lis)
-; (recur (cdr lis) (- k 1)))))))
-;
-;(define (drop lis k)
-; (check-arg integer? k drop)
-; (if (negative? k)
-; (let recur ((lis lis) (nelts (+ k (length lis))))
-; (if (zero? nelts) '()
-; (cons (car lis)
-; (recur (cdr lis) (- nelts 1)))))
-; (list-tail lis k)))
-;
-;
-;(define (take! lis k)
-; (check-arg integer? k take!)
-; (cond ((zero? k) '())
-; ((positive? k)
-; (set-cdr! (list-tail lis (- k 1)) '())
-; lis)
-; (else (list-tail lis (+ k (length lis))))))
-;
-;(define (drop! lis k)
-; (check-arg integer? k drop!)
-; (if (negative? k)
-; (let ((nelts (+ k (length lis))))
-; (if (zero? nelts) '()
-; (begin (set-cdr! (list-tail lis (- nelts 1)) '())
-; lis)))
-; (list-tail lis k)))
-
-(define (split-at x k)
- (check-arg integer? k split-at)
- (let recur ((lis x) (k k))
- (if (zero? k) (values '() lis)
- (receive (prefix suffix) (recur (cdr lis) (- k 1))
- (values (cons (car lis) prefix) suffix)))))
-
-(define (split-at! x k)
- (check-arg integer? k split-at!)
- (if (zero? k) (values '() x)
- (let* ((prev (drop x (- k 1)))
- (suffix (cdr prev)))
- (set-cdr! prev '())
- (values x suffix))))
-
-
-(define (last lis) (car (last-pair lis)))
-
-(define (last-pair lis)
- (check-arg pair? lis last-pair)
- (let lp ((lis lis))
- (let ((tail (cdr lis)))
- (if (pair? tail) (lp tail) lis))))
-
-
-;;; Unzippers -- 1 through 5
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define (unzip1 lis) (map car lis))
-
-(define (unzip2 lis)
- (let recur ((lis lis))
- (if (null-list? lis) (values lis lis) ; Use NOT-PAIR? to handle
- (let ((elt (car lis))) ; dotted lists.
- (receive (a b) (recur (cdr lis))
- (values (cons (car elt) a)
- (cons (cadr elt) b)))))))
-
-(define (unzip3 lis)
- (let recur ((lis lis))
- (if (null-list? lis) (values lis lis lis)
- (let ((elt (car lis)))
- (receive (a b c) (recur (cdr lis))
- (values (cons (car elt) a)
- (cons (cadr elt) b)
- (cons (caddr elt) c)))))))
-
-(define (unzip4 lis)
- (let recur ((lis lis))
- (if (null-list? lis) (values lis lis lis lis)
- (let ((elt (car lis)))
- (receive (a b c d) (recur (cdr lis))
- (values (cons (car elt) a)
- (cons (cadr elt) b)
- (cons (caddr elt) c)
- (cons (cadddr elt) d)))))))
-
-(define (unzip5 lis)
- (let recur ((lis lis))
- (if (null-list? lis) (values lis lis lis lis lis)
- (let ((elt (car lis)))
- (receive (a b c d e) (recur (cdr lis))
- (values (cons (car elt) a)
- (cons (cadr elt) b)
- (cons (caddr elt) c)
- (cons (cadddr elt) d)
- (cons (car (cddddr elt)) e)))))))
-
-
-;;; append! append-reverse append-reverse! concatenate concatenate!
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define (append! . lists)
- ;; First, scan through lists looking for a non-empty one.
- (let lp ((lists lists) (prev '()))
- (if (not (pair? lists)) prev
- (let ((first (car lists))
- (rest (cdr lists)))
- (if (not (pair? first)) (lp rest first)
-
- ;; Now, do the splicing.
- (let lp2 ((tail-cons (last-pair first))
- (rest rest))
- (if (pair? rest)
- (let ((next (car rest))
- (rest (cdr rest)))
- (set-cdr! tail-cons next)
- (lp2 (if (pair? next) (last-pair next) tail-cons)
- rest))
- first)))))))
-
-;;; APPEND is R4RS.
-;(define (append . lists)
-; (if (pair? lists)
-; (let recur ((list1 (car lists)) (lists (cdr lists)))
-; (if (pair? lists)
-; (let ((tail (recur (car lists) (cdr lists))))
-; (fold-right cons tail list1)) ; Append LIST1 & TAIL.
-; list1))
-; '()))
-
-;(define (append-reverse rev-head tail) (fold cons tail rev-head))
-
-;(define (append-reverse! rev-head tail)
-; (pair-fold (lambda (pair tail) (set-cdr! pair tail) pair)
-; tail
-; rev-head))
-
-;;; Hand-inline the FOLD and PAIR-FOLD ops for speed.
-
-(define (append-reverse rev-head tail)
- (let lp ((rev-head rev-head) (tail tail))
- (if (null-list? rev-head) tail
- (lp (cdr rev-head) (cons (car rev-head) tail)))))
-
-(define (append-reverse! rev-head tail)
- (let lp ((rev-head rev-head) (tail tail))
- (if (null-list? rev-head) tail
- (let ((next-rev (cdr rev-head)))
- (set-cdr! rev-head tail)
- (lp next-rev rev-head)))))
-
-
-(define (concatenate lists) (reduce-right append '() lists))
-(define (concatenate! lists) (reduce-right append! '() lists))
-
-;;; Fold/map internal utilities
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; These little internal utilities are used by the general
-;;; fold & mapper funs for the n-ary cases . It'd be nice if they got inlined.
-;;; One the other hand, the n-ary cases are painfully inefficient as it is.
-;;; An aggressive implementation should simply re-write these functions
-;;; for raw efficiency; I have written them for as much clarity, portability,
-;;; and simplicity as can be achieved.
-;;;
-;;; I use the dreaded call/cc to do local aborts. A good compiler could
-;;; handle this with extreme efficiency. An implementation that provides
-;;; a one-shot, non-persistent continuation grabber could help the compiler
-;;; out by using that in place of the call/cc's in these routines.
-;;;
-;;; These functions have funky definitions that are precisely tuned to
-;;; the needs of the fold/map procs -- for example, to minimize the number
-;;; of times the argument lists need to be examined.
-
-;;; Return (map cdr lists).
-;;; However, if any element of LISTS is empty, just abort and return '().
-(define (%cdrs lists)
- (call-with-current-continuation
- (lambda (abort)
- (let recur ((lists lists))
- (if (pair? lists)
- (let ((lis (car lists)))
- (if (null-list? lis) (abort '())
- (cons (cdr lis) (recur (cdr lists)))))
- '())))))
-
-(define (%cars+ lists last-elt) ; (append! (map car lists) (list last-elt))
- (let recur ((lists lists))
- (if (pair? lists) (cons (caar lists) (recur (cdr lists))) (list last-elt))))
-
-;;; LISTS is a (not very long) non-empty list of lists.
-;;; Return two lists: the cars & the cdrs of the lists.
-;;; However, if any of the lists is empty, just abort and return [() ()].
-
-(define (%cars+cdrs lists)
- (call-with-current-continuation
- (lambda (abort)
- (let recur ((lists lists))
- (if (pair? lists)
- (receive (list other-lists) (car+cdr lists)
- (if (null-list? list) (abort '() '()) ; LIST is empty -- bail out
- (receive (a d) (car+cdr list)
- (receive (cars cdrs) (recur other-lists)
- (values (cons a cars) (cons d cdrs))))))
- (values '() '()))))))
-
-;;; Like %CARS+CDRS, but we pass in a final elt tacked onto the end of the
-;;; cars list. What a hack.
-(define (%cars+cdrs+ lists cars-final)
- (call-with-current-continuation
- (lambda (abort)
- (let recur ((lists lists))
- (if (pair? lists)
- (receive (list other-lists) (car+cdr lists)
- (if (null-list? list) (abort '() '()) ; LIST is empty -- bail out
- (receive (a d) (car+cdr list)
- (receive (cars cdrs) (recur other-lists)
- (values (cons a cars) (cons d cdrs))))))
- (values (list cars-final) '()))))))
-
-;;; Like %CARS+CDRS, but blow up if any list is empty.
-(define (%cars+cdrs/no-test lists)
- (let recur ((lists lists))
- (if (pair? lists)
- (receive (list other-lists) (car+cdr lists)
- (receive (a d) (car+cdr list)
- (receive (cars cdrs) (recur other-lists)
- (values (cons a cars) (cons d cdrs)))))
- (values '() '()))))
-
-
-;;; count
-;;;;;;;;;
-(define (count pred list1 . lists)
- (check-arg procedure? pred count)
- (if (pair? lists)
-
- ;; N-ary case
- (let lp ((list1 list1) (lists lists) (i 0))
- (if (null-list? list1) i
- (receive (as ds) (%cars+cdrs lists)
- (if (null? as) i
- (lp (cdr list1) ds
- (if (apply pred (car list1) as) (+ i 1) i))))))
-
- ;; Fast path
- (let lp ((lis list1) (i 0))
- (if (null-list? lis) i
- (lp (cdr lis) (if (pred (car lis)) (+ i 1) i))))))
-
-
-;;; fold/unfold
-;;;;;;;;;;;;;;;
-
-(define (unfold-right p f g seed . maybe-tail)
- (check-arg procedure? p unfold-right)
- (check-arg procedure? f unfold-right)
- (check-arg procedure? g unfold-right)
- (let lp ((seed seed) (ans (#\:optional maybe-tail '())))
- (if (p seed) ans
- (lp (g seed)
- (cons (f seed) ans)))))
-
-
-(define (unfold p f g seed . maybe-tail-gen)
- (check-arg procedure? p unfold)
- (check-arg procedure? f unfold)
- (check-arg procedure? g unfold)
- (if (pair? maybe-tail-gen)
-
- (let ((tail-gen (car maybe-tail-gen)))
- (if (pair? (cdr maybe-tail-gen))
- (apply error "Too many arguments" unfold p f g seed maybe-tail-gen)
-
- (let recur ((seed seed))
- (if (p seed) (tail-gen seed)
- (cons (f seed) (recur (g seed)))))))
-
- (let recur ((seed seed))
- (if (p seed) '()
- (cons (f seed) (recur (g seed)))))))
-
-
-(define (fold kons knil lis1 . lists)
- (check-arg procedure? kons fold)
- (if (pair? lists)
- (let lp ((lists (cons lis1 lists)) (ans knil)) ; N-ary case
- (receive (cars+ans cdrs) (%cars+cdrs+ lists ans)
- (if (null? cars+ans) ans ; Done.
- (lp cdrs (apply kons cars+ans)))))
-
- (let lp ((lis lis1) (ans knil)) ; Fast path
- (if (null-list? lis) ans
- (lp (cdr lis) (kons (car lis) ans))))))
-
-
-(define (fold-right kons knil lis1 . lists)
- (check-arg procedure? kons fold-right)
- (if (pair? lists)
- (let recur ((lists (cons lis1 lists))) ; N-ary case
- (let ((cdrs (%cdrs lists)))
- (if (null? cdrs) knil
- (apply kons (%cars+ lists (recur cdrs))))))
-
- (let recur ((lis lis1)) ; Fast path
- (if (null-list? lis) knil
- (let ((head (car lis)))
- (kons head (recur (cdr lis))))))))
-
-
-(define (pair-fold-right f zero lis1 . lists)
- (check-arg procedure? f pair-fold-right)
- (if (pair? lists)
- (let recur ((lists (cons lis1 lists))) ; N-ary case
- (let ((cdrs (%cdrs lists)))
- (if (null? cdrs) zero
- (apply f (append! lists (list (recur cdrs)))))))
-
- (let recur ((lis lis1)) ; Fast path
- (if (null-list? lis) zero (f lis (recur (cdr lis)))))))
-
-(define (pair-fold f zero lis1 . lists)
- (check-arg procedure? f pair-fold)
- (if (pair? lists)
- (let lp ((lists (cons lis1 lists)) (ans zero)) ; N-ary case
- (let ((tails (%cdrs lists)))
- (if (null? tails) ans
- (lp tails (apply f (append! lists (list ans)))))))
-
- (let lp ((lis lis1) (ans zero))
- (if (null-list? lis) ans
- (let ((tail (cdr lis))) ; Grab the cdr now,
- (lp tail (f lis ans))))))) ; in case F SET-CDR!s LIS.
-
-
-;;; REDUCE and REDUCE-RIGHT only use RIDENTITY in the empty-list case.
-;;; These cannot meaningfully be n-ary.
-
-(define (reduce f ridentity lis)
- (check-arg procedure? f reduce)
- (if (null-list? lis) ridentity
- (fold f (car lis) (cdr lis))))
-
-(define (reduce-right f ridentity lis)
- (check-arg procedure? f reduce-right)
- (if (null-list? lis) ridentity
- (let recur ((head (car lis)) (lis (cdr lis)))
- (if (pair? lis)
- (f head (recur (car lis) (cdr lis)))
- head))))
-
-
-
-;;; Mappers: append-map append-map! pair-for-each map! filter-map map-in-order
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define (append-map f lis1 . lists)
- (really-append-map append-map append f lis1 lists))
-(define (append-map! f lis1 . lists)
- (really-append-map append-map! append! f lis1 lists))
-
-(define (really-append-map who appender f lis1 lists)
- (check-arg procedure? f who)
- (if (pair? lists)
- (receive (cars cdrs) (%cars+cdrs (cons lis1 lists))
- (if (null? cars) '()
- (let recur ((cars cars) (cdrs cdrs))
- (let ((vals (apply f cars)))
- (receive (cars2 cdrs2) (%cars+cdrs cdrs)
- (if (null? cars2) vals
- (appender vals (recur cars2 cdrs2))))))))
-
- ;; Fast path
- (if (null-list? lis1) '()
- (let recur ((elt (car lis1)) (rest (cdr lis1)))
- (let ((vals (f elt)))
- (if (null-list? rest) vals
- (appender vals (recur (car rest) (cdr rest)))))))))
-
-
-(define (pair-for-each proc lis1 . lists)
- (check-arg procedure? proc pair-for-each)
- (if (pair? lists)
-
- (let lp ((lists (cons lis1 lists)))
- (let ((tails (%cdrs lists)))
- (if (pair? tails)
- (begin (apply proc lists)
- (lp tails)))))
-
- ;; Fast path.
- (let lp ((lis lis1))
- (if (not (null-list? lis))
- (let ((tail (cdr lis))) ; Grab the cdr now,
- (proc lis) ; in case PROC SET-CDR!s LIS.
- (lp tail))))))
-
-;;; We stop when LIS1 runs out, not when any list runs out.
-(define (map! f lis1 . lists)
- (check-arg procedure? f map!)
- (if (pair? lists)
- (let lp ((lis1 lis1) (lists lists))
- (if (not (null-list? lis1))
- (receive (heads tails) (%cars+cdrs/no-test lists)
- (set-car! lis1 (apply f (car lis1) heads))
- (lp (cdr lis1) tails))))
-
- ;; Fast path.
- (pair-for-each (lambda (pair) (set-car! pair (f (car pair)))) lis1))
- lis1)
-
-
-;;; Map F across L, and save up all the non-false results.
-(define (filter-map f lis1 . lists)
- (check-arg procedure? f filter-map)
- (if (pair? lists)
- (let recur ((lists (cons lis1 lists)))
- (receive (cars cdrs) (%cars+cdrs lists)
- (if (pair? cars)
- (cond ((apply f cars) => (lambda (x) (cons x (recur cdrs))))
- (else (recur cdrs))) ; Tail call in this arm.
- '())))
-
- ;; Fast path.
- (let recur ((lis lis1))
- (if (null-list? lis) lis
- (let ((tail (recur (cdr lis))))
- (cond ((f (car lis)) => (lambda (x) (cons x tail)))
- (else tail)))))))
-
-
-;;; Map F across lists, guaranteeing to go left-to-right.
-;;; NOTE: Some implementations of R5RS MAP are compliant with this spec;
-;;; in which case this procedure may simply be defined as a synonym for MAP.
-
-(define (map-in-order f lis1 . lists)
- (check-arg procedure? f map-in-order)
- (if (pair? lists)
- (let recur ((lists (cons lis1 lists)))
- (receive (cars cdrs) (%cars+cdrs lists)
- (if (pair? cars)
- (let ((x (apply f cars))) ; Do head first,
- (cons x (recur cdrs))) ; then tail.
- '())))
-
- ;; Fast path.
- (let recur ((lis lis1))
- (if (null-list? lis) lis
- (let ((tail (cdr lis))
- (x (f (car lis)))) ; Do head first,
- (cons x (recur tail))))))) ; then tail.
-
-
-;;; We extend MAP to handle arguments of unequal length.
-(define map map-in-order)
-
-
-;;; filter, remove, partition
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; FILTER, REMOVE, PARTITION and their destructive counterparts do not
-;;; disorder the elements of their argument.
-
-;; This FILTER shares the longest tail of L that has no deleted elements.
-;; If Scheme had multi-continuation calls, they could be made more efficient.
-
-(define (filter pred lis) ; Sleazing with EQ? makes this
- (check-arg procedure? pred filter) ; one faster.
- (let recur ((lis lis))
- (if (null-list? lis) lis ; Use NOT-PAIR? to handle dotted lists.
- (let ((head (car lis))
- (tail (cdr lis)))
- (if (pred head)
- (let ((new-tail (recur tail))) ; Replicate the RECUR call so
- (if (eq? tail new-tail) lis
- (cons head new-tail)))
- (recur tail)))))) ; this one can be a tail call.
-
-
-;;; Another version that shares longest tail.
-;(define (filter pred lis)
-; (receive (ans no-del?)
-; ;; (recur l) returns L with (pred x) values filtered.
-; ;; It also returns a flag NO-DEL? if the returned value
-; ;; is EQ? to L, i.e. if it didn't have to delete anything.
-; (let recur ((l l))
-; (if (null-list? l) (values l #t)
-; (let ((x (car l))
-; (tl (cdr l)))
-; (if (pred x)
-; (receive (ans no-del?) (recur tl)
-; (if no-del?
-; (values l #t)
-; (values (cons x ans) #f)))
-; (receive (ans no-del?) (recur tl) ; Delete X.
-; (values ans #f))))))
-; ans))
-
-
-
-;(define (filter! pred lis) ; Things are much simpler
-; (let recur ((lis lis)) ; if you are willing to
-; (if (pair? lis) ; push N stack frames & do N
-; (cond ((pred (car lis)) ; SET-CDR! writes, where N is
-; (set-cdr! lis (recur (cdr lis))); the length of the answer.
-; lis)
-; (else (recur (cdr lis))))
-; lis)))
-
-
-;;; This implementation of FILTER!
-;;; - doesn't cons, and uses no stack;
-;;; - is careful not to do redundant SET-CDR! writes, as writes to memory are
-;;; usually expensive on modern machines, and can be extremely expensive on
-;;; modern Schemes (e.g., ones that have generational GC's).
-;;; It just zips down contiguous runs of in and out elts in LIS doing the
-;;; minimal number of SET-CDR!s to splice the tail of one run of ins to the
-;;; beginning of the next.
-
-(define (filter! pred lis)
- (check-arg procedure? pred filter!)
- (let lp ((ans lis))
- (cond ((null-list? ans) ans) ; Scan looking for
- ((not (pred (car ans))) (lp (cdr ans))) ; first cons of result.
-
- ;; ANS is the eventual answer.
- ;; SCAN-IN: (CDR PREV) = LIS and (CAR PREV) satisfies PRED.
- ;; Scan over a contiguous segment of the list that
- ;; satisfies PRED.
- ;; SCAN-OUT: (CAR PREV) satisfies PRED. Scan over a contiguous
- ;; segment of the list that *doesn't* satisfy PRED.
- ;; When the segment ends, patch in a link from PREV
- ;; to the start of the next good segment, and jump to
- ;; SCAN-IN.
- (else (letrec ((scan-in (lambda (prev lis)
- (if (pair? lis)
- (if (pred (car lis))
- (scan-in lis (cdr lis))
- (scan-out prev (cdr lis))))))
- (scan-out (lambda (prev lis)
- (let lp ((lis lis))
- (if (pair? lis)
- (if (pred (car lis))
- (begin (set-cdr! prev lis)
- (scan-in lis (cdr lis)))
- (lp (cdr lis)))
- (set-cdr! prev lis))))))
- (scan-in ans (cdr ans))
- ans)))))
-
-
-
-;;; Answers share common tail with LIS where possible;
-;;; the technique is slightly subtle.
-
-(define (partition pred lis)
- (check-arg procedure? pred partition)
- (let recur ((lis lis))
- (if (null-list? lis) (values lis lis) ; Use NOT-PAIR? to handle dotted lists.
- (let ((elt (car lis))
- (tail (cdr lis)))
- (receive (in out) (recur tail)
- (if (pred elt)
- (values (if (pair? out) (cons elt in) lis) out)
- (values in (if (pair? in) (cons elt out) lis))))))))
-
-
-
-;(define (partition! pred lis) ; Things are much simpler
-; (let recur ((lis lis)) ; if you are willing to
-; (if (null-list? lis) (values lis lis) ; push N stack frames & do N
-; (let ((elt (car lis))) ; SET-CDR! writes, where N is
-; (receive (in out) (recur (cdr lis)) ; the length of LIS.
-; (cond ((pred elt)
-; (set-cdr! lis in)
-; (values lis out))
-; (else (set-cdr! lis out)
-; (values in lis))))))))
-
-
-;;; This implementation of PARTITION!
-;;; - doesn't cons, and uses no stack;
-;;; - is careful not to do redundant SET-CDR! writes, as writes to memory are
-;;; usually expensive on modern machines, and can be extremely expensive on
-;;; modern Schemes (e.g., ones that have generational GC's).
-;;; It just zips down contiguous runs of in and out elts in LIS doing the
-;;; minimal number of SET-CDR!s to splice these runs together into the result
-;;; lists.
-
-(define (partition! pred lis)
- (check-arg procedure? pred partition!)
- (if (null-list? lis) (values lis lis)
-
- ;; This pair of loops zips down contiguous in & out runs of the
- ;; list, splicing the runs together. The invariants are
- ;; SCAN-IN: (cdr in-prev) = LIS.
- ;; SCAN-OUT: (cdr out-prev) = LIS.
- (letrec ((scan-in (lambda (in-prev out-prev lis)
- (let lp ((in-prev in-prev) (lis lis))
- (if (pair? lis)
- (if (pred (car lis))
- (lp lis (cdr lis))
- (begin (set-cdr! out-prev lis)
- (scan-out in-prev lis (cdr lis))))
- (set-cdr! out-prev lis))))) ; Done.
-
- (scan-out (lambda (in-prev out-prev lis)
- (let lp ((out-prev out-prev) (lis lis))
- (if (pair? lis)
- (if (pred (car lis))
- (begin (set-cdr! in-prev lis)
- (scan-in lis out-prev (cdr lis)))
- (lp lis (cdr lis)))
- (set-cdr! in-prev lis)))))) ; Done.
-
- ;; Crank up the scan&splice loops.
- (if (pred (car lis))
- ;; LIS begins in-list. Search for out-list's first pair.
- (let lp ((prev-l lis) (l (cdr lis)))
- (cond ((not (pair? l)) (values lis l))
- ((pred (car l)) (lp l (cdr l)))
- (else (scan-out prev-l l (cdr l))
- (values lis l)))) ; Done.
-
- ;; LIS begins out-list. Search for in-list's first pair.
- (let lp ((prev-l lis) (l (cdr lis)))
- (cond ((not (pair? l)) (values l lis))
- ((pred (car l))
- (scan-in l prev-l (cdr l))
- (values l lis)) ; Done.
- (else (lp l (cdr l)))))))))
-
-
-;;; Inline us, please.
-(define (remove pred l) (filter (lambda (x) (not (pred x))) l))
-(define (remove! pred l) (filter! (lambda (x) (not (pred x))) l))
-
-
-
-;;; Here's the taxonomy for the DELETE/ASSOC/MEMBER functions.
-;;; (I don't actually think these are the world's most important
-;;; functions -- the procedural FILTER/REMOVE/FIND/FIND-TAIL variants
-;;; are far more general.)
-;;;
-;;; Function Action
-;;; ---------------------------------------------------------------------------
-;;; remove pred lis Delete by general predicate
-;;; delete x lis [=] Delete by element comparison
-;;;
-;;; find pred lis Search by general predicate
-;;; find-tail pred lis Search by general predicate
-;;; member x lis [=] Search by element comparison
-;;;
-;;; assoc key lis [=] Search alist by key comparison
-;;; alist-delete key alist [=] Alist-delete by key comparison
-
-(define (delete x lis . maybe-=)
- (let ((= (#\:optional maybe-= equal?)))
- (filter (lambda (y) (not (= x y))) lis)))
-
-(define (delete! x lis . maybe-=)
- (let ((= (#\:optional maybe-= equal?)))
- (filter! (lambda (y) (not (= x y))) lis)))
-
-;;; Extended from R4RS to take an optional comparison argument.
-(define (member x lis . maybe-=)
- (let ((= (#\:optional maybe-= equal?)))
- (find-tail (lambda (y) (= x y)) lis)))
-
-;;; R4RS, hence we don't bother to define.
-;;; The MEMBER and then FIND-TAIL call should definitely
-;;; be inlined for MEMQ & MEMV.
-;(define (memq x lis) (member x lis eq?))
-;(define (memv x lis) (member x lis eqv?))
-
-
-;;; right-duplicate deletion
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; delete-duplicates delete-duplicates!
-;;;
-;;; Beware -- these are N^2 algorithms. To efficiently remove duplicates
-;;; in long lists, sort the list to bring duplicates together, then use a
-;;; linear-time algorithm to kill the dups. Or use an algorithm based on
-;;; element-marking. The former gives you O(n lg n), the latter is linear.
-
-(define (delete-duplicates lis . maybe-=)
- (let ((elt= (#\:optional maybe-= equal?)))
- (check-arg procedure? elt= delete-duplicates)
- (let recur ((lis lis))
- (if (null-list? lis) lis
- (let* ((x (car lis))
- (tail (cdr lis))
- (new-tail (recur (delete x tail elt=))))
- (if (eq? tail new-tail) lis (cons x new-tail)))))))
-
-(define (delete-duplicates! lis maybe-=)
- (let ((elt= (#\:optional maybe-= equal?)))
- (check-arg procedure? elt= delete-duplicates!)
- (let recur ((lis lis))
- (if (null-list? lis) lis
- (let* ((x (car lis))
- (tail (cdr lis))
- (new-tail (recur (delete! x tail elt=))))
- (if (eq? tail new-tail) lis (cons x new-tail)))))))
-
-
-;;; alist stuff
-;;;;;;;;;;;;;;;
-
-;;; Extended from R4RS to take an optional comparison argument.
-(define (assoc x lis . maybe-=)
- (let ((= (#\:optional maybe-= equal?)))
- (find (lambda (entry) (= x (car entry))) lis)))
-
-(define (alist-cons key datum alist) (cons (cons key datum) alist))
-
-(define (alist-copy alist)
- (map (lambda (elt) (cons (car elt) (cdr elt)))
- alist))
-
-(define (alist-delete key alist . maybe-=)
- (let ((= (#\:optional maybe-= equal?)))
- (filter (lambda (elt) (not (= key (car elt)))) alist)))
-
-(define (alist-delete! key alist . maybe-=)
- (let ((= (#\:optional maybe-= equal?)))
- (filter! (lambda (elt) (not (= key (car elt)))) alist)))
-
-
-;;; find find-tail take-while drop-while span break any every list-index
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define (find pred list)
- (cond ((find-tail pred list) => car)
- (else #f)))
-
-(define (find-tail pred list)
- (check-arg procedure? pred find-tail)
- (let lp ((list list))
- (and (not (null-list? list))
- (if (pred (car list)) list
- (lp (cdr list))))))
-
-(define (take-while pred lis)
- (check-arg procedure? pred take-while)
- (let recur ((lis lis))
- (if (null-list? lis) '()
- (let ((x (car lis)))
- (if (pred x)
- (cons x (recur (cdr lis)))
- '())))))
-
-(define (drop-while pred lis)
- (check-arg procedure? pred drop-while)
- (let lp ((lis lis))
- (if (null-list? lis) '()
- (if (pred (car lis))
- (lp (cdr lis))
- lis))))
-
-(define (take-while! pred lis)
- (check-arg procedure? pred take-while!)
- (if (or (null-list? lis) (not (pred (car lis)))) '()
- (begin (let lp ((prev lis) (rest (cdr lis)))
- (if (pair? rest)
- (let ((x (car rest)))
- (if (pred x) (lp rest (cdr rest))
- (set-cdr! prev '())))))
- lis)))
-
-(define (span pred lis)
- (check-arg procedure? pred span)
- (let recur ((lis lis))
- (if (null-list? lis) (values '() '())
- (let ((x (car lis)))
- (if (pred x)
- (receive (prefix suffix) (recur (cdr lis))
- (values (cons x prefix) suffix))
- (values '() lis))))))
-
-(define (span! pred lis)
- (check-arg procedure? pred span!)
- (if (or (null-list? lis) (not (pred (car lis)))) (values '() lis)
- (let ((suffix (let lp ((prev lis) (rest (cdr lis)))
- (if (null-list? rest) rest
- (let ((x (car rest)))
- (if (pred x) (lp rest (cdr rest))
- (begin (set-cdr! prev '())
- rest)))))))
- (values lis suffix))))
-
-
-(define (break pred lis) (span (lambda (x) (not (pred x))) lis))
-(define (break! pred lis) (span! (lambda (x) (not (pred x))) lis))
-
-(define (any pred lis1 . lists)
- (check-arg procedure? pred any)
- (if (pair? lists)
-
- ;; N-ary case
- (receive (heads tails) (%cars+cdrs (cons lis1 lists))
- (and (pair? heads)
- (let lp ((heads heads) (tails tails))
- (receive (next-heads next-tails) (%cars+cdrs tails)
- (if (pair? next-heads)
- (or (apply pred heads) (lp next-heads next-tails))
- (apply pred heads)))))) ; Last PRED app is tail call.
-
- ;; Fast path
- (and (not (null-list? lis1))
- (let lp ((head (car lis1)) (tail (cdr lis1)))
- (if (null-list? tail)
- (pred head) ; Last PRED app is tail call.
- (or (pred head) (lp (car tail) (cdr tail))))))))
-
-
-;(define (every pred list) ; Simple definition.
-; (let lp ((list list)) ; Doesn't return the last PRED value.
-; (or (not (pair? list))
-; (and (pred (car list))
-; (lp (cdr list))))))
-
-(define (every pred lis1 . lists)
- (check-arg procedure? pred every)
- (if (pair? lists)
-
- ;; N-ary case
- (receive (heads tails) (%cars+cdrs (cons lis1 lists))
- (or (not (pair? heads))
- (let lp ((heads heads) (tails tails))
- (receive (next-heads next-tails) (%cars+cdrs tails)
- (if (pair? next-heads)
- (and (apply pred heads) (lp next-heads next-tails))
- (apply pred heads)))))) ; Last PRED app is tail call.
-
- ;; Fast path
- (or (null-list? lis1)
- (let lp ((head (car lis1)) (tail (cdr lis1)))
- (if (null-list? tail)
- (pred head) ; Last PRED app is tail call.
- (and (pred head) (lp (car tail) (cdr tail))))))))
-
-(define (list-index pred lis1 . lists)
- (check-arg procedure? pred list-index)
- (if (pair? lists)
-
- ;; N-ary case
- (let lp ((lists (cons lis1 lists)) (n 0))
- (receive (heads tails) (%cars+cdrs lists)
- (and (pair? heads)
- (if (apply pred heads) n
- (lp tails (+ n 1))))))
-
- ;; Fast path
- (let lp ((lis lis1) (n 0))
- (and (not (null-list? lis))
- (if (pred (car lis)) n (lp (cdr lis) (+ n 1)))))))
-
-;;; Reverse
-;;;;;;;;;;;
-
-;R4RS, so not defined here.
-;(define (reverse lis) (fold cons '() lis))
-
-;(define (reverse! lis)
-; (pair-fold (lambda (pair tail) (set-cdr! pair tail) pair) '() lis))
-
-(define (reverse! lis)
- (let lp ((lis lis) (ans '()))
- (if (null-list? lis) ans
- (let ((tail (cdr lis)))
- (set-cdr! lis ans)
- (lp tail lis)))))
-
-;;; Lists-as-sets
-;;;;;;;;;;;;;;;;;
-
-;;; This is carefully tuned code; do not modify casually.
-;;; - It is careful to share storage when possible;
-;;; - Side-effecting code tries not to perform redundant writes.
-;;; - It tries to avoid linear-time scans in special cases where constant-time
-;;; computations can be performed.
-;;; - It relies on similar properties from the other list-lib procs it calls.
-;;; For example, it uses the fact that the implementations of MEMBER and
-;;; FILTER in this source code share longest common tails between args
-;;; and results to get structure sharing in the lset procedures.
-
-(define (%lset2<= = lis1 lis2) (every (lambda (x) (member x lis2 =)) lis1))
-
-(define (lset<= = . lists)
- (check-arg procedure? = lset<=)
- (or (not (pair? lists)) ; 0-ary case
- (let lp ((s1 (car lists)) (rest (cdr lists)))
- (or (not (pair? rest))
- (let ((s2 (car rest)) (rest (cdr rest)))
- (and (or (eq? s2 s1) ; Fast path
- (%lset2<= = s1 s2)) ; Real test
- (lp s2 rest)))))))
-
-(define (lset= = . lists)
- (check-arg procedure? = lset=)
- (or (not (pair? lists)) ; 0-ary case
- (let lp ((s1 (car lists)) (rest (cdr lists)))
- (or (not (pair? rest))
- (let ((s2 (car rest))
- (rest (cdr rest)))
- (and (or (eq? s1 s2) ; Fast path
- (and (%lset2<= = s1 s2) (%lset2<= = s2 s1))) ; Real test
- (lp s2 rest)))))))
-
-
-(define (lset-adjoin = lis . elts)
- (check-arg procedure? = lset-adjoin)
- (fold (lambda (elt ans) (if (member elt ans =) ans (cons elt ans)))
- lis elts))
-
-
-(define (lset-union = . lists)
- (check-arg procedure? = lset-union)
- (reduce (lambda (lis ans) ; Compute ANS + LIS.
- (cond ((null? lis) ans) ; Don't copy any lists
- ((null? ans) lis) ; if we don't have to.
- ((eq? lis ans) ans)
- (else
- (fold (lambda (elt ans) (if (any (lambda (x) (= x elt)) ans)
- ans
- (cons elt ans)))
- ans lis))))
- '() lists))
-
-(define (lset-union! = . lists)
- (check-arg procedure? = lset-union!)
- (reduce (lambda (lis ans) ; Splice new elts of LIS onto the front of ANS.
- (cond ((null? lis) ans) ; Don't copy any lists
- ((null? ans) lis) ; if we don't have to.
- ((eq? lis ans) ans)
- (else
- (pair-fold (lambda (pair ans)
- (let ((elt (car pair)))
- (if (any (lambda (x) (= x elt)) ans)
- ans
- (begin (set-cdr! pair ans) pair))))
- ans lis))))
- '() lists))
-
-
-(define (lset-intersection = lis1 . lists)
- (check-arg procedure? = lset-intersection)
- (let ((lists (delete lis1 lists eq?))) ; Throw out any LIS1 vals.
- (cond ((any null-list? lists) '()) ; Short cut
- ((null? lists) lis1) ; Short cut
- (else (filter (lambda (x)
- (every (lambda (lis) (member x lis =)) lists))
- lis1)))))
-
-(define (lset-intersection! = lis1 . lists)
- (check-arg procedure? = lset-intersection!)
- (let ((lists (delete lis1 lists eq?))) ; Throw out any LIS1 vals.
- (cond ((any null-list? lists) '()) ; Short cut
- ((null? lists) lis1) ; Short cut
- (else (filter! (lambda (x)
- (every (lambda (lis) (member x lis =)) lists))
- lis1)))))
-
-
-(define (lset-difference = lis1 . lists)
- (check-arg procedure? = lset-difference)
- (let ((lists (filter pair? lists))) ; Throw out empty lists.
- (cond ((null? lists) lis1) ; Short cut
- ((memq lis1 lists) '()) ; Short cut
- (else (filter (lambda (x)
- (every (lambda (lis) (not (member x lis =)))
- lists))
- lis1)))))
-
-(define (lset-difference! = lis1 . lists)
- (check-arg procedure? = lset-difference!)
- (let ((lists (filter pair? lists))) ; Throw out empty lists.
- (cond ((null? lists) lis1) ; Short cut
- ((memq lis1 lists) '()) ; Short cut
- (else (filter! (lambda (x)
- (every (lambda (lis) (not (member x lis =)))
- lists))
- lis1)))))
-
-
-(define (lset-xor = . lists)
- (check-arg procedure? = lset-xor)
- (reduce (lambda (b a) ; Compute A xor B:
- ;; Note that this code relies on the constant-time
- ;; short-cuts provided by LSET-DIFF+INTERSECTION,
- ;; LSET-DIFFERENCE & APPEND to provide constant-time short
- ;; cuts for the cases A = (), B = (), and A eq? B. It takes
- ;; a careful case analysis to see it, but it's carefully
- ;; built in.
-
- ;; Compute a-b and a^b, then compute b-(a^b) and
- ;; cons it onto the front of a-b.
- (receive (a-b a-int-b) (lset-diff+intersection = a b)
- (cond ((null? a-b) (lset-difference = b a))
- ((null? a-int-b) (append b a))
- (else (fold (lambda (xb ans)
- (if (member xb a-int-b =) ans (cons xb ans)))
- a-b
- b)))))
- '() lists))
-
-
-(define (lset-xor! = . lists)
- (check-arg procedure? = lset-xor!)
- (reduce (lambda (b a) ; Compute A xor B:
- ;; Note that this code relies on the constant-time
- ;; short-cuts provided by LSET-DIFF+INTERSECTION,
- ;; LSET-DIFFERENCE & APPEND to provide constant-time short
- ;; cuts for the cases A = (), B = (), and A eq? B. It takes
- ;; a careful case analysis to see it, but it's carefully
- ;; built in.
-
- ;; Compute a-b and a^b, then compute b-(a^b) and
- ;; cons it onto the front of a-b.
- (receive (a-b a-int-b) (lset-diff+intersection! = a b)
- (cond ((null? a-b) (lset-difference! = b a))
- ((null? a-int-b) (append! b a))
- (else (pair-fold (lambda (b-pair ans)
- (if (member (car b-pair) a-int-b =) ans
- (begin (set-cdr! b-pair ans) b-pair)))
- a-b
- b)))))
- '() lists))
-
-
-(define (lset-diff+intersection = lis1 . lists)
- (check-arg procedure? = lset-diff+intersection)
- (cond ((every null-list? lists) (values lis1 '())) ; Short cut
- ((memq lis1 lists) (values '() lis1)) ; Short cut
- (else (partition (lambda (elt)
- (not (any (lambda (lis) (member elt lis =))
- lists)))
- lis1))))
-
-(define (lset-diff+intersection! = lis1 . lists)
- (check-arg procedure? = lset-diff+intersection!)
- (cond ((every null-list? lists) (values lis1 '())) ; Short cut
- ((memq lis1 lists) (values '() lis1)) ; Short cut
- (else (partition! (lambda (elt)
- (not (any (lambda (lis) (member elt lis =))
- lists)))
- lis1))))
-;;; Copyright (C) John Cowan 2013. All Rights Reserved.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-(define-library (srfi 111)
- (export box box? unbox set-box!)
- (import (scheme base))
- (begin
- (define-record-type <box>
- (box value)
- box?
- (value unbox set-box!))))
-;; Copyright (C) Taylan Ulrich Bayırlı/Kammer (2015). All Rights Reserved.
-
-;; Permission is hereby granted, free of charge, to any person obtaining
-;; a copy of this software and associated documentation files (the
-;; "Software"), to deal in the Software without restriction, including
-;; without limitation the rights to use, copy, modify, merge, publish,
-;; distribute, sublicense, and/or sell copies of the Software, and to
-;; permit persons to whom the Software is furnished to do so, subject to
-;; the following conditions:
-
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-;; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-(define-library (srfi 17)
- (export set! setter getter-with-setter)
- (import
- (rename (scheme base) (set! %set!))
- (srfi 1))
- (begin
-
- (define-syntax set!
- (syntax-rules ()
- ((_ (getter arg ...) val)
- ((setter getter) arg ... val))
- ((_ var val)
- (%set! var val))))
-
- (define setter
- (let ((setters `((,car . ,set-car!)
- (,cdr . ,set-cdr!)
- (,caar . ,(lambda (p v) (set-car! (car p) v)))
- (,cadr . ,(lambda (p v) (set-car! (cdr p) v)))
- (,cdar . ,(lambda (p v) (set-cdr! (car p) v)))
- (,cddr . ,(lambda (p v) (set-cdr! (cdr p) v)))
- (,list-ref . ,list-set!)
- (,vector-ref . ,vector-set!)
- (,string-ref . ,string-set!)
- (,bytevector-u8-ref . ,bytevector-u8-set!))))
- (letrec ((setter
- (lambda (proc)
- (let ((probe (assv proc setters)))
- (if probe
- (cdr probe)
- (error "No setter for " proc)))))
- (set-setter!
- (lambda (proc setter)
- (set! setters (cons (cons proc setter) setters)))))
- (set-setter! setter set-setter!)
- setter)))
-
- (define (getter-with-setter get set)
- (let ((proc (lambda args (apply get args))))
- (set! (setter proc) set)
- proc))
-
- ))
-;; Copyright (C) Taylan Ulrich Bayırlı/Kammer (2015). All Rights Reserved.
-
-;; Permission is hereby granted, free of charge, to any person obtaining
-;; a copy of this software and associated documentation files (the
-;; "Software"), to deal in the Software without restriction, including
-;; without limitation the rights to use, copy, modify, merge, publish,
-;; distribute, sublicense, and/or sell copies of the Software, and to
-;; permit persons to whom the Software is furnished to do so, subject to
-;; the following conditions:
-
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-;; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-;;; The SRFI claims that having the same variable appear multiple times is an
-;;; error in let* and so also in and-let*. In fact let* allows rebinding the
-;;; same variable, so we also allow it here.
-
-(define-library (srfi 2)
- (export and-let*)
- (import (scheme base))
- (begin
- (define-syntax and-let*
- (syntax-rules ()
-
- ;; Handle zero-clauses special-case.
- ((_ () . body)
- (begin #t . body))
-
- ;; Reduce clauses down to one regardless of body.
- ((_ ((var expr) rest . rest*) . body)
- (let ((var expr))
- (and var (and-let* (rest . rest*) . body))))
- ((_ ((expr) rest . rest*) . body)
- (and expr (and-let* (rest . rest*) . body)))
- ((_ (var rest . rest*) . body)
- (begin
- (let ((var #f)) #f) ;(identifier? var)
- (and var (and-let* (rest . rest*) . body))))
-
- ;; Handle 1-clause cases without a body.
- ((_ ((var expr)))
- expr)
- ((_ ((expr)))
- expr)
- ((_ (var))
- (begin
- (let ((var #f)) #f) ;(identifier? var)
- var))
-
- ;; Handle 1-clause cases with a body.
- ((_ ((var expr)) . body)
- (let ((var expr))
- (and var (begin . body))))
- ((_ ((expr)) . body)
- (and expr (begin . body)))
- ((_ (var) . body)
- (begin
- (let ((var #f)) #f) ;(identifier? var)
- (and var (begin . body))))))))
-;;; Copyright (C) André van Tonder (2004). All Rights Reserved.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-;============================================================================================
-; IMPLEMENTATION:
-;
-; Andre van Tonder, 2004.
-;
-;============================================================================================
-
-(define-syntax define-record-type
- (syntax-rules ()
- ((define-record-type . body)
- (parse-declaration #f . body))))
-
-(define-syntax define-record-scheme
- (syntax-rules ()
- ((define-record-scheme . body)
- (parse-declaration #t . body))))
-
-(define-syntax parse-declaration
- (syntax-rules ()
- ((parse-declaration is-scheme? (name super ...) constructor-clause predicate field-clause ...)
- (build-record 0 constructor-clause (super ...) (field-clause ...) name predicate is-scheme?))
- ((parse-declaration is-scheme? (name super ...) constructor-clause)
- (parse-declaration is-scheme? (name super ...) constructor-clause #f))
- ((parse-declaration is-scheme? (name super ...))
- (parse-declaration is-scheme? (name super ...) #f #f))
- ((parse-declaration is-scheme? name . rest)
- (parse-declaration is-scheme? (name) . rest))))
-
-(define-syntax record-update!
- (syntax-rules ()
- ((record-update! record name (label exp) ...)
- (meta
- `(let ((r record))
- ((meta ,(name ("setter") label)) r exp)
- ...
- r)))))
-
-(define-syntax record-update
- (syntax-rules ()
- ((record-update record name (label exp) ...)
- (name ("is-scheme?")
- (meta
- `(let ((new ((meta ,(name ("copier"))) record)))
- (record-update! new name (label exp) ...)))
- (record-compose (name record) (name (label exp) ...))))))
-
-(define-syntax record-compose
- (syntax-rules ()
- ((record-compose (export-name (label exp) ...))
- (export-name (label exp) ...))
- ((record-compose (import-name record) ... (export-name (label exp) ...))
- (help-compose 1 (import-name record) ... (export-name (label exp) ...)))))
-
-(define-syntax help-compose
- (syntax-rules ()
- ((help-compose 1 (import-name record) import ... (export-name (label exp) ...))
- (meta
- `(help-compose 2
- (meta ,(intersection
- (meta ,(export-name ("labels")))
- (meta ,(remove-from (meta ,(import-name ("labels")))
- (label ...)
- if-free=))
- if-free=))
- (import-name record)
- import ...
- (export-name (label exp) ...))))
- ((help-compose 2 (copy-label ...) (import-name record) import ... (export-name . bindings))
- (meta
- `(let ((r record))
- (record-compose import ...
- (export-name (copy-label ((meta ,(import-name ("getter") copy-label)) r))
- ...
- . bindings)))))))
-
-(define-syntax build-record
- (syntax-rules ()
- ((build-record 0 (constructor . pos-labels) . rest) ; extract positional labels from constructor clause
- (build-record 1 (constructor . pos-labels) pos-labels . rest)) ;
- ((build-record 0 constructor . rest) ;
- (build-record 1 (constructor . #f) () . rest)) ;
- ((build-record 1 constructor-clause (pos-label ...) (super ...)
- ((label . accessors) ...) . rest)
- (meta
- `(build-record 2
- constructor-clause
- (meta ,(union (meta ,(super ("labels"))) ; compute union of labels from supers,
- ... ; constructor clause and field clauses
- (pos-label ...)
- (label ...)
- top:if-free=))
- ((label . accessors) ...)
- (meta ,(union (meta ,(super ("supers"))) ; compute transitive union of supers
- ...
- top:if-free=))
- . rest)))
- ((build-record 2 (constructor . pos-labels) labels . rest) ; insert default constructor labels if not given
- (syntax-if pos-labels
- (build-record 3 (constructor . pos-labels) labels . rest)
- (build-record 3 (constructor . labels) labels . rest)))
- ((build-record 3 constructor-clause labels ((label . accessors) ...) . rest)
- (meta
- `(build-record 4
- (meta ,(remove-from labels ; separate the labels that do not appear in a
- (label ...) ; field clause for next step
- top:if-free=))
- ((label . accessors) ...)
- constructor-clause
- labels
- . rest)))
- ((build-record 4
- (undeclared-label ...)
- (field-clause ...)
- (constructor . pos-labels)
- labels
- supers
- name
- predicate
- is-scheme?)
- (meta
- `(build-record 5 ; generate identifiers for constructor, predicate
- is-scheme? ; getters and setters as needed
- name
- supers
- supers
- labels
- (meta ,(to-identifier constructor))
- (meta ,(add-temporaries pos-labels)) ; needed for constructor below
- (meta ,(to-identifier predicate))
- (meta ,(augment-field field-clause))
- ...
- (undeclared-label (meta ,(generate-identifier))
- (meta ,(generate-identifier)))
- ...)))
- ((build-record 5
- is-scheme?
- name
- (super ...)
- supers
- (label ...)
- constructor
- ((pos-label pos-temp) ...)
- predicate
- (field-label getter setter)
- ...)
-
- (begin
- (syntax-if is-scheme?
-
- (begin
- (define-generic (predicate x) (lambda (x) #f))
- (define-generic (getter x))
- ...
- (define-generic (setter x v))
- ...
- (define-generic (copy x)))
-
- (begin
- (srfi-9:define-record-type internal-name
- (maker field-label ...)
- predicate
- (field-label getter setter) ...)
-
- (define constructor
- (lambda (pos-temp ...)
- (populate 1 maker (field-label ...) (pos-label pos-temp) ...)))
-
- (extend-predicates supers predicate)
- (extend-accessors supers field-label predicate getter setter)
- ...
-
- (define (copy x)
- (maker (getter x) ...))
- (extend-copiers supers copy predicate)
-
- (define-method (show (r predicate))
- (list 'name
- (list 'field-label (getter r))
- ...))))
-
- (define-syntax name
- (syntax-rules (field-label ...)
- ((name ("is-scheme?") sk fk) (syntax-if is-scheme? sk fk))
- ((name ("predicate") k) (syntax-apply k predicate))
- ((name ("supers") k) (syntax-apply k (super ... name)))
- ((name ("labels") k) (syntax-apply k (label ...)))
- ((name ("pos-labels") k) (syntax-apply k (pos-label ...)))
- ((name ("getter") field-label k) (syntax-apply k getter))
- ...
- ((name ("getter") other k) (syntax-apply k #f))
- ((name ("setter") field-label k) (syntax-apply k setter))
- ...
- ((name ("setter") other k) (syntax-apply k #f))
- ((name ("copier") k) (syntax-apply k copy))
- ((name . bindings) (populate 1 maker (field-label ...) . bindings))))))))
-
-
-(define-syntax to-identifier
- (syntax-rules ()
- ((to-identifier #f k) (syntax-apply k generated-identifier))
- ((to-identifier id k) (syntax-apply k id))))
-
-(define-syntax augment-field
- (syntax-rules ()
- ((augment-field (label) k) (syntax-apply k (label generated-getter generated-setter)))
- ((augment-field (label getter) k) (meta `(label (meta ,(to-identifier getter)) generated-setter) k))
- ((augment-field (label getter setter) k) (meta `(label (meta ,(to-identifier getter))
- (meta ,(to-identifier setter))) k))))
-
-(define-syntax extend-predicates
- (syntax-rules ()
- ((extend-predicates (super ...) predicate)
- (begin
- (meta
- `(define-method (meta ,(super ("predicate")))
- (predicate)
- (x)
- any?))
- ...))))
-
-(define-syntax extend-copiers
- (syntax-rules ()
- ((extend-copiers (super ...) copy predicate)
- (begin
- (meta
- `(define-method (meta ,(super ("copier")))
- (predicate)
- (x)
- copy))
- ...))))
-
-(define-syntax extend-accessors
- (syntax-rules ()
- ((extend-accessors (super ...) label predicate selector modifier)
- (meta
- `(begin
- (syntax-if (meta ,(super ("getter") label))
- (define-method (meta ,(super ("getter") label))
- (predicate)
- (x)
- selector)
- (begin))
- ...
- (syntax-if (meta ,(super ("setter") label))
- (define-method (meta ,(super ("setter") label))
- (predicate any?)
- (x v)
- modifier)
- (begin))
- ...)))))
-
-(define-syntax populate
- (syntax-rules ()
- ((populate 1 maker labels . bindings)
- (meta
- `(populate 2 maker
- (meta ,(order labels bindings ('<undefined>))))))
- ((populate 2 maker ((label exp) ...))
- (maker exp ...))))
-
-(define-syntax order
- (syntax-rules ()
- ((order (label ...) ((label* . binding) ...) default k)
- (meta
- `(if-empty? (meta ,(remove-from (label* ...)
- (label ...)
- if-free=))
- (order "emit" (label ...) ((label* . binding) ...) default k)
- (syntax-error "Illegal labels in" ((label* . binding) ...)
- "Legal labels are" (label ...)))))
- ((order "emit" (label ...) bindings default k)
- (meta
- `((label . (meta ,(syntax-lookup label
- bindings
- if-free=
- default)))
- ...)
- k))))
-
-
-;============================================================================================
-; Simple generic functions:
-
-(define-syntax define-generic
- (syntax-rules ()
- ((define-generic (name arg ...))
- (define-generic (name arg ...)
- (lambda (arg ...) (error "Inapplicable method:" 'name
- "Arguments:" (show arg) ... ))))
- ((define-generic (name arg ...) proc)
- (define name (make-generic (arg ...) proc)))))
-
-(define-syntax define-method
- (syntax-rules ()
- ((define-method (generic (arg pred?) ...) . body)
- (define-method generic (pred? ...) (arg ...) (lambda (arg ...) . body)))
- ((define-method generic (pred? ...) (arg ...) procedure)
- (let ((next ((generic) 'get-proc))
- (proc procedure))
- (((generic) 'set-proc)
- (lambda (arg ...)
- (if (and (pred? arg) ...)
- (proc arg ...)
- (next arg ...))))))))
-
-(define-syntax make-generic
- (syntax-rules ()
- ((make-generic (arg arg+ ...) default-proc)
- (let ((proc default-proc))
- (case-lambda
- ((arg arg+ ...)
- (proc arg arg+ ...))
- (()
- (lambda (msg)
- (case msg
- ((get-proc) proc)
- ((set-proc) (lambda (new)
- (set! proc new)))))))))))
-
-(define-generic (show x)
- (lambda (x) x))
-
-(define (any? x) #t)
-
-
-;============================================================================================
-; Syntax utilities:
-
-(define-syntax syntax-error
- (syntax-rules ()))
-
-(define-syntax syntax-apply
- (syntax-rules ()
- ((syntax-apply (f . args) exp ...)
- (f exp ... . args))))
-
-(define-syntax syntax-cons
- (syntax-rules ()
- ((syntax-cons x rest k)
- (syntax-apply k (x . rest)))))
-
-(define-syntax syntax-cons-after
- (syntax-rules ()
- ((syntax-cons-after rest x k)
- (syntax-apply k (x . rest)))))
-
-(define-syntax if-empty?
- (syntax-rules ()
- ((if-empty? () sk fk) sk)
- ((if-empty? (h . t) sk fk) fk)))
-
-(define-syntax add-temporaries
- (syntax-rules ()
- ((add-temporaries lst k) (add-temporaries lst () k))
- ((add-temporaries () lst-temps k) (syntax-apply k lst-temps))
- ((add-temporaries (h . t) (done ...) k) (add-temporaries t (done ... (h temp)) k))))
-
-(define-syntax if-free=
- (syntax-rules ()
- ((if-free= x y kt kf)
- (let-syntax
- ((test (syntax-rules (x)
- ((test x kt* kf*) kt*)
- ((test z kt* kf*) kf*))))
- (test y kt kf)))))
-
-(define-syntax top:if-free=
- (syntax-rules ()
- ((top:if-free= x y kt kf)
- (begin
- (define-syntax if-free=:test
- (syntax-rules (x)
- ((if-free=:test x kt* kf*) kt*)
- ((if-free=:test z kt* kf*) kf*)))
- (if-free=:test y kt kf)))))
-
-(define-syntax meta
- (syntax-rules (meta quasiquote unquote)
- ((meta `(meta ,(function argument ...)) k)
- (meta `(argument ...) (syntax-apply-to function k)))
- ((meta `(a . b) k)
- (meta `a (descend-right b k)))
- ((meta `whatever k) (syntax-apply k whatever))
- ((meta `arg)
- (meta `arg (syntax-id)))))
-
-(define-syntax syntax-apply-to
- (syntax-rules ()
- ((syntax-apply-to (argument ...) function k)
- (function argument ... k))))
-
-(define-syntax descend-right
- (syntax-rules ()
- ((descend-right evaled b k)
- (meta `b (syntax-cons-after evaled k)))))
-
-(define-syntax syntax-id
- (syntax-rules ()
- ((syntax-id arg) arg)))
-
-(define-syntax remove-duplicates
- (syntax-rules ()
- ((remove-duplicates lst compare? k)
- (remove-duplicates lst () compare? k))
- ((remove-duplicates () done compare? k)
- (syntax-apply k done))
- ((remove-duplicates (h . t) (d ...) compare? k)
- (if-member? h (d ...) compare?
- (remove-duplicates t (d ...) compare? k)
- (remove-duplicates t (d ... h) compare? k)))))
-
-(define-syntax syntax-filter
- (syntax-rules ()
- ((syntax-filter () (if-p? arg ...) k)
- (syntax-apply k ()))
- ((syntax-filter (h . t) (if-p? arg ...) k)
- (if-p? h arg ...
- (syntax-filter t (if-p? arg ...) (syntax-cons-after h k))
- (syntax-filter t (if-p? arg ...) k)))))
-
-(define-syntax if-member?
- (syntax-rules ()
- ((if-member? x () compare? sk fk)
- fk)
- ((if-member? x (h . t) compare? sk fk)
- (compare? x h
- sk
- (if-member? x t compare? sk fk)))))
-
-(define-syntax union
- (syntax-rules ()
- ((union (x ...) ... compare? k)
- (remove-duplicates (x ... ...) compare? k))))
-
-(define-syntax intersection
- (syntax-rules ()
- ((intersection list1 list2 compare? k)
- (syntax-filter list1 (if-member? list2 compare?) k))))
-
-(define-syntax remove-from
- (syntax-rules ()
- ((remove-from list1 list2 compare? k)
- (syntax-filter list1 (if-not-member? list2 compare?) k))))
-
-(define-syntax if-not-member?
- (syntax-rules ()
- ((if-not-member? x list compare? sk fk)
- (if-member? x list compare? fk sk))))
-
-(define-syntax generate-identifier
- (syntax-rules ()
- ((generate-identifier k) (syntax-apply k generated-identifier))))
-
-(define-syntax syntax-if
- (syntax-rules ()
- ((syntax-if #f sk fk) fk)
- ((syntax-if other sk fk) sk)))
-
-(define-syntax syntax-lookup
- (syntax-rules ()
- ((syntax-lookup label () compare fail k)
- (syntax-apply k fail))
- ((syntax-lookup label ((label* . value) . bindings) compare fail k)
- (compare label label*
- (syntax-apply k value)
- (syntax-lookup label bindings compare fail k)))))
-;;; array as-srfi-9-record
-;;; 2001 Jussi Piitulainen
-
-;;; Untested.
-
-(define-record-type
- array:srfi-9-record-type-descriptor
- (array:make vec ind shp)
- array:array?
- (vec array:vector)
- (ind array:index)
- (shp array:shape))
-(define-library (srfi 60)
- (export
- ;; Bitwise Operations
- logand
- bitwise-and
- logior
- bitwise-ior
- logxor
- bitwise-xor
- lognot
- bitwise-not
- bitwise-if
- bitwise-merge
- logtest
- any-bits-set?
-
- ;; Integer Properties
- logcount
- bit-count
- integer-length
- log2-binary-factors
- first-set-bit
-
- ;; Bit Within Word
- logbit?
- bit-set?
- copy-bit
-
- ;; Field of Bits
- bit-field
- copy-bit-field
- ash
- arithmetic-shift
- rotate-bit-field
- reverse-bit-field
-
- ;; Bits as Booleans
- integer->list
- list->integer
- booleans->integer
- )
- (import (scheme base))
- (include "60.upstream.scm"))
-;;; Copyright (C) Jussi Piitulainen (2001). All Rights Reserved.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-(define (array-ref a . xs)
- (or (array:array? a)
- (error "not an array"))
- (let ((shape (array:shape a)))
- (if (null? xs)
- (array:check-indices "array-ref" xs shape)
- (let ((x (car xs)))
- (if (vector? x)
- (array:check-index-vector "array-ref" x shape)
- (if (integer? x)
- (array:check-indices "array-ref" xs shape)
- (if (array:array? x)
- (array:check-index-actor "array-ref" x shape)
- (error "not an index object"))))))
- (vector-ref
- (array:vector a)
- (if (null? xs)
- (vector-ref (array:index a) 0)
- (let ((x (car xs)))
- (if (vector? x)
- (array:index/vector
- (quotient (vector-length shape) 2)
- (array:index a)
- x)
- (if (integer? x)
- (array:vector-index (array:index a) xs)
- (if (array:array? x)
- (array:index/array
- (quotient (vector-length shape) 2)
- (array:index a)
- (array:vector x)
- (array:index x))
- (error "array-ref: bad index object")))))))))
-
-(define (array-set! a x . xs)
- (or (array:array? a)
- (error "array-set!: not an array"))
- (let ((shape (array:shape a)))
- (if (null? xs)
- (array:check-indices "array-set!" '() shape)
- (if (vector? x)
- (array:check-index-vector "array-set!" x shape)
- (if (integer? x)
- (array:check-indices.o "array-set!" (cons x xs) shape)
- (if (array:array? x)
- (array:check-index-actor "array-set!" x shape)
- (error "not an index object")))))
- (if (null? xs)
- (vector-set! (array:vector a) (vector-ref (array:index a) 0) x)
- (if (vector? x)
- (vector-set! (array:vector a)
- (array:index/vector
- (quotient (vector-length shape) 2)
- (array:index a)
- x)
- (car xs))
- (if (integer? x)
- (let ((v (array:vector a))
- (i (array:index a))
- (r (quotient (vector-length shape) 2)))
- (do ((sum (* (vector-ref i 0) x)
- (+ sum (* (vector-ref i k) (car ks))))
- (ks xs (cdr ks))
- (k 1 (+ k 1)))
- ((= k r)
- (vector-set! v (+ sum (vector-ref i k)) (car ks)))))
- (if (array:array? x)
- (vector-set! (array:vector a)
- (array:index/array
- (quotient (vector-length shape) 2)
- (array:index a)
- (array:vector x)
- (array:index x))
- (car xs))
- (error (string-append
- "array-set!: bad index object: "
- (array:thing->string x)))))))))
-(define-library (srfi 63)
- (export
- array?
- equal?
- array-rank
- array-dimensions
- make-array
- make-shared-array
- list->array
- array->list
- vector->array
- array->vector
- array-in-bounds?
- array-ref
- array-set!
- a:floc128b
- a:floc64b
- a:floc32b
- a:floc16b
- a:flor128b
- a:flor64b
- a:flor32b
- a:flor16b
- a:fixz64b
- a:fixz32b
- a:fixz16b
- a:fixz8b
- a:fixn64b
- a:fixn32b
- a:fixn16b
- a:fixn8b
- a:bool
- )
- (import (except (scheme base) equal?))
- (include "63.body.scm"))
-;;; array
-;;; 1997 - 2001 Jussi Piitulainen
-
-;;; --- Intro ---
-
-;;; This interface to arrays is based on Alan Bawden's array.scm of
-;;; 1993 (earlier version in the Internet Repository and another
-;;; version in SLIB). This is a complete rewrite, to be consistent
-;;; with the rest of Scheme and to make arrays independent of lists.
-
-;;; Some modifications are due to discussion in srfi-25 mailing list.
-
-;;; (array? obj)
-;;; (make-array shape [obj]) changed arguments
-;;; (shape bound ...) new
-;;; (array shape obj ...) new
-;;; (array-rank array) changed name back
-;;; (array-start array dimension) new
-;;; (array-end array dimension) new
-;;; (array-ref array k ...)
-;;; (array-ref array index) new variant
-;;; (array-set! array k ... obj) changed argument order
-;;; (array-set! array index obj) new variant
-;;; (share-array array shape proc) changed arguments
-
-;;; All other variables in this file have names in "array:".
-
-;;; Should there be a way to make arrays with initial values mapped
-;;; from indices? Sure. The current "initial object" is lame.
-;;;
-;;; Removed (array-shape array) from here. There is a new version
-;;; in arlib though.
-
-;;; --- Representation type dependencies ---
-
-;;; The mapping from array indices to the index to the underlying vector
-;;; is whatever array:optimize returns. The file "opt" provides three
-;;; representations:
-;;;
-;;; mbda) mapping is a procedure that allows an optional argument
-;;; tter) mapping is two procedures that takes exactly the indices
-;;; ctor) mapping is a vector of a constant term and coefficients
-;;;
-;;; Choose one in "opt" to make the optimizer. Then choose the matching
-;;; implementation of array-ref and array-set!.
-;;;
-;;; These should be made macros to inline them. Or have a good compiler
-;;; and plant the package as a module.
-
-;;; 1. Pick an optimizer.
-;;; 2. Pick matching index representation.
-;;; 3. Pick a record implementation; as-procedure is generic; syntax inlines.
-;;; 3. This file is otherwise portable.
-
-;;; --- Portable R5RS (R4RS and multiple values) ---
-
-;;; (array? obj)
-;;; returns #t if `obj' is an array and #t or #f otherwise.
-
-(define (array? obj)
- (array:array? obj))
-
-;;; (make-array shape)
-;;; (make-array shape obj)
-;;; makes array of `shape' with each cell containing `obj' initially.
-
-(define (make-array shape . rest)
- (or (array:good-shape? shape)
- (error "make-array: shape is not a shape"))
- (apply array:make-array shape rest))
-
-(define (array:make-array shape . rest)
- (let ((size (array:size shape)))
- (array:make
- (if (pair? rest)
- (apply (lambda (o) (make-vector size o)) rest)
- (make-vector size))
- (if (= size 0)
- (array:optimize-empty
- (vector-ref (array:shape shape) 1))
- (array:optimize
- (array:make-index shape)
- (vector-ref (array:shape shape) 1)))
- (array:shape->vector shape))))
-
-;;; (shape bound ...)
-;;; makes a shape. Bounds must be an even number of exact, pairwise
-;;; non-decreasing integers. Note that any such array can be a shape.
-
-(define (shape . bounds)
- (let ((v (list->vector bounds)))
- (or (even? (vector-length v))
- (error (string-append "shape: uneven number of bounds: "
- (array:list->string bounds))))
- (let ((shp (array:make
- v
- (if (pair? bounds)
- (array:shape-index)
- (array:empty-shape-index))
- (vector 0 (quotient (vector-length v) 2)
- 0 2))))
- (or (array:good-shape? shp)
- (error (string-append "shape: bounds are not pairwise "
- "non-decreasing exact integers: "
- (array:list->string bounds))))
- shp)))
-
-;;; (array shape obj ...)
-;;; is analogous to `vector'.
-
-(define (array shape . elts)
- (or (array:good-shape? shape)
- (error (string-append "array: shape " (array:thing->string shape)
- " is not a shape")))
- (let ((size (array:size shape)))
- (let ((vector (list->vector elts)))
- (or (= (vector-length vector) size)
- (error (string-append "array: an array of shape "
- (array:shape-vector->string
- (array:vector shape))
- " has "
- (number->string size)
- " elements but got "
- (number->string (vector-length vector))
- " values: "
- (array:list->string elts))))
- (array:make
- vector
- (if (= size 0)
- (array:optimize-empty
- (vector-ref (array:shape shape) 1))
- (array:optimize
- (array:make-index shape)
- (vector-ref (array:shape shape) 1)))
- (array:shape->vector shape)))))
-
-;;; (array-rank array)
-;;; returns the number of dimensions of `array'.
-
-(define (array-rank array)
- (quotient (vector-length (array:shape array)) 2))
-
-;;; (array-start array k)
-;;; returns the lower bound index of array along dimension k. This is
-;;; the least valid index along that dimension if the dimension is not
-;;; empty.
-
-(define (array-start array d)
- (vector-ref (array:shape array) (+ d d)))
-
-;;; (array-end array k)
-;;; returns the upper bound index of array along dimension k. This is
-;;; not a valid index. If the dimension is empty, this is the same as
-;;; the lower bound along it.
-
-(define (array-end array d)
- (vector-ref (array:shape array) (+ d d 1)))
-
-;;; (share-array array shape proc)
-;;; makes an array that shares elements of `array' at shape `shape'.
-;;; The arguments to `proc' are indices of the result. The values of
-;;; `proc' are indices of `array'.
-
-;;; Todo: in the error message, should recognise the mapping and show it.
-
-(define (share-array array subshape f)
- (or (array:good-shape? subshape)
- (error (string-append "share-array: shape "
- (array:thing->string subshape)
- " is not a shape")))
- (let ((subsize (array:size subshape)))
- (or (array:good-share? subshape subsize f (array:shape array))
- (error (string-append "share-array: subshape "
- (array:shape-vector->string
- (array:vector subshape))
- " does not map into supershape "
- (array:shape-vector->string
- (array:shape array))
- " under mapping "
- (array:map->string
- f
- (vector-ref (array:shape subshape) 1)))))
- (let ((g (array:index array)))
- (array:make
- (array:vector array)
- (if (= subsize 0)
- (array:optimize-empty
- (vector-ref (array:shape subshape) 1))
- (array:optimize
- (lambda ks
- (call-with-values
- (lambda () (apply f ks))
- (lambda ks (array:vector-index g ks))))
- (vector-ref (array:shape subshape) 1)))
- (array:shape->vector subshape)))))
-
-;;; --- Hrmph ---
-
-;;; (array:share/index! ...)
-;;; reuses a user supplied index object when recognising the
-;;; mapping. The mind balks at the very nasty side effect that
-;;; exposes the implementation. So this is not in the spec.
-;;; But letting index objects in at all creates a pressure
-;;; to go the whole hog. Arf.
-
-;;; Use array:optimize-empty for an empty array to get a
-;;; clearly invalid vector index.
-
-;;; Surely it's perverse to use an actor for index here? But
-;;; the possibility is provided for completeness.
-
-(define (array:share/index! array subshape proc index)
- (array:make
- (array:vector array)
- (if (= (array:size subshape) 0)
- (array:optimize-empty
- (quotient (vector-length (array:shape array)) 2))
- ((if (vector? index)
- array:optimize/vector
- array:optimize/actor)
- (lambda (subindex)
- (let ((superindex (proc subindex)))
- (if (vector? superindex)
- (array:index/vector
- (quotient (vector-length (array:shape array)) 2)
- (array:index array)
- superindex)
- (array:index/array
- (quotient (vector-length (array:shape array)) 2)
- (array:index array)
- (array:vector superindex)
- (array:index superindex)))))
- index))
- (array:shape->vector subshape)))
-
-(define (array:optimize/vector f v)
- (let ((r (vector-length v)))
- (do ((k 0 (+ k 1)))
- ((= k r))
- (vector-set! v k 0))
- (let ((n0 (f v))
- (cs (make-vector (+ r 1)))
- (apply (array:applier-to-vector (+ r 1))))
- (vector-set! cs 0 n0)
- (let wok ((k 0))
- (if (< k r)
- (let ((k1 (+ k 1)))
- (vector-set! v k 1)
- (let ((nk (- (f v) n0)))
- (vector-set! v k 0)
- (vector-set! cs k1 nk)
- (wok k1)))))
- (apply (array:maker r) cs))))
-
-(define (array:optimize/actor f a)
- (let ((r (array-end a 0))
- (v (array:vector a))
- (i (array:index a)))
- (do ((k 0 (+ k 1)))
- ((= k r))
- (vector-set! v (array:actor-index i k) 0))
- (let ((n0 (f a))
- (cs (make-vector (+ r 1)))
- (apply (array:applier-to-vector (+ r 1))))
- (vector-set! cs 0 n0)
- (let wok ((k 0))
- (if (< k r)
- (let ((k1 (+ k 1))
- (t (array:actor-index i k)))
- (vector-set! v t 1)
- (let ((nk (- (f a) n0)))
- (vector-set! v t 0)
- (vector-set! cs k1 nk)
- (wok k1)))))
- (apply (array:maker r) cs))))
-
-;;; --- Internals ---
-
-(define (array:shape->vector shape)
- (let ((idx (array:index shape))
- (shv (array:vector shape))
- (rnk (vector-ref (array:shape shape) 1)))
- (let ((vec (make-vector (* rnk 2))))
- (do ((k 0 (+ k 1)))
- ((= k rnk)
- vec)
- (vector-set! vec (+ k k)
- (vector-ref shv (array:shape-vector-index idx k 0)))
- (vector-set! vec (+ k k 1)
- (vector-ref shv (array:shape-vector-index idx k 1)))))))
-
-;;; (array:size shape)
-;;; returns the number of elements in arrays of shape `shape'.
-
-(define (array:size shape)
- (let ((idx (array:index shape))
- (shv (array:vector shape))
- (rnk (vector-ref (array:shape shape) 1)))
- (do ((k 0 (+ k 1))
- (s 1 (* s
- (- (vector-ref shv (array:shape-vector-index idx k 1))
- (vector-ref shv (array:shape-vector-index idx k 0))))))
- ((= k rnk) s))))
-
-;;; (array:make-index shape)
-;;; returns an index function for arrays of shape `shape'. This is a
-;;; runtime composition of several variable arity procedures, to be
-;;; passed to array:optimize for recognition as an affine function of
-;;; as many variables as there are dimensions in arrays of this shape.
-
-(define (array:make-index shape)
- (let ((idx (array:index shape))
- (shv (array:vector shape))
- (rnk (vector-ref (array:shape shape) 1)))
- (do ((f (lambda () 0)
- (lambda (k . ks)
- (+ (* s (- k (vector-ref
- shv
- (array:shape-vector-index idx (- j 1) 0))))
- (apply f ks))))
- (s 1 (* s (- (vector-ref
- shv
- (array:shape-vector-index idx (- j 1) 1))
- (vector-ref
- shv
- (array:shape-vector-index idx (- j 1) 0)))))
- (j rnk (- j 1)))
- ((= j 0)
- f))))
-
-
-;;; --- Error checking ---
-
-;;; (array:good-shape? shape)
-;;; returns true if `shape' is an array of the right shape and its
-;;; elements are exact integers that pairwise bound intervals `[lo..hi)´.
-
-(define (array:good-shape? shape)
- (and (array:array? shape)
- (let ((u (array:shape shape))
- (v (array:vector shape))
- (x (array:index shape)))
- (and (= (vector-length u) 4)
- (= (vector-ref u 0) 0)
- (= (vector-ref u 2) 0)
- (= (vector-ref u 3) 2))
- (let ((p (vector-ref u 1)))
- (do ((k 0 (+ k 1))
- (true #t (let ((lo (vector-ref
- v
- (array:shape-vector-index x k 0)))
- (hi (vector-ref
- v
- (array:shape-vector-index x k 1))))
- (and true
- (integer? lo)
- (exact? lo)
- (integer? hi)
- (exact? hi)
- (<= lo hi)))))
- ((= k p) true))))))
-
-;;; (array:good-share? subv subsize mapping superv)
-;;; returns true if the extreme indices in the subshape vector map
-;;; into the bounds in the supershape vector.
-
-;;; If some interval in `subv' is empty, then `subv' is empty and its
-;;; image under `f' is empty and it is trivially alright. One must
-;;; not call `f', though.
-
-(define (array:good-share? subshape subsize f super)
- (or (zero? subsize)
- (letrec
- ((sub (array:vector subshape))
- (dex (array:index subshape))
- (ck (lambda (k ks)
- (if (zero? k)
- (call-with-values
- (lambda () (apply f ks))
- (lambda qs (array:good-indices? qs super)))
- (and (ck (- k 1)
- (cons (vector-ref
- sub
- (array:shape-vector-index
- dex
- (- k 1)
- 0))
- ks))
- (ck (- k 1)
- (cons (- (vector-ref
- sub
- (array:shape-vector-index
- dex
- (- k 1)
- 1))
- 1)
- ks)))))))
- (let ((rnk (vector-ref (array:shape subshape) 1)))
- (or (array:unchecked-share-depth? rnk)
- (ck rnk '()))))))
-
-;;; Check good-share on 10 dimensions at most. The trouble is,
-;;; the cost of this check is exponential in the number of dimensions.
-
-(define (array:unchecked-share-depth? rank)
- (if (> rank 10)
- (begin
- (display `(warning unchecked depth in share
- ,rank subdimensions))
- (newline)
- #t)
- #f))
-
-;;; (array:check-indices caller indices shape-vector)
-;;; (array:check-indices.o caller indices shape-vector)
-;;; (array:check-index-vector caller index-vector shape-vector)
-;;; return if the index is in bounds, else signal error.
-;;;
-;;; Shape-vector is the internal representation, with
-;;; b and e for dimension k at 2k and 2k + 1.
-
-(define (array:check-indices who ks shv)
- (or (array:good-indices? ks shv)
- (error (array:not-in who ks shv))))
-
-(define (array:check-indices.o who ks shv)
- (or (array:good-indices.o? ks shv)
- (error (array:not-in who (reverse (cdr (reverse ks))) shv))))
-
-(define (array:check-index-vector who ks shv)
- (or (array:good-index-vector? ks shv)
- (error (array:not-in who (vector->list ks) shv))))
-
-(define (array:check-index-actor who ks shv)
- (let ((shape (array:shape ks)))
- (or (and (= (vector-length shape) 2)
- (= (vector-ref shape 0) 0))
- (error "not an actor"))
- (or (array:good-index-actor?
- (vector-ref shape 1)
- (array:vector ks)
- (array:index ks)
- shv)
- (array:not-in who (do ((k (vector-ref shape 1) (- k 1))
- (m '() (cons (vector-ref
- (array:vector ks)
- (array:actor-index
- (array:index ks)
- (- k 1)))
- m)))
- ((= k 0) m))
- shv))))
-
-(define (array:good-indices? ks shv)
- (let ((d2 (vector-length shv)))
- (do ((kp ks (if (pair? kp)
- (cdr kp)))
- (k 0 (+ k 2))
- (true #t (and true (pair? kp)
- (array:good-index? (car kp) shv k))))
- ((= k d2)
- (and true (null? kp))))))
-
-(define (array:good-indices.o? ks.o shv)
- (let ((d2 (vector-length shv)))
- (do ((kp ks.o (if (pair? kp)
- (cdr kp)))
- (k 0 (+ k 2))
- (true #t (and true (pair? kp)
- (array:good-index? (car kp) shv k))))
- ((= k d2)
- (and true (pair? kp) (null? (cdr kp)))))))
-
-(define (array:good-index-vector? ks shv)
- (let ((r2 (vector-length shv)))
- (and (= (* 2 (vector-length ks)) r2)
- (do ((j 0 (+ j 1))
- (k 0 (+ k 2))
- (true #t (and true
- (array:good-index? (vector-ref ks j) shv k))))
- ((= k r2) true)))))
-
-(define (array:good-index-actor? r v i shv)
- (and (= (* 2 r) (vector-length shv))
- (do ((j 0 (+ j 1))
- (k 0 (+ k 2))
- (true #t (and true
- (array:good-index? (vector-ref
- v
- (array:actor-index i j))
- shv
- k))))
- ((= j r) true))))
-
-;;; (array:good-index? index shape-vector 2d)
-;;; returns true if index is within bounds for dimension 2d/2.
-
-(define (array:good-index? w shv k)
- (and (integer? w)
- (exact? w)
- (<= (vector-ref shv k) w)
- (< w (vector-ref shv (+ k 1)))))
-
-(define (array:not-in who ks shv)
- (let ((index (array:list->string ks))
- (bounds (array:shape-vector->string shv)))
- (error (string-append who
- ": index " index
- " not in bounds " bounds))))
-
-(define (array:list->string ks)
- (do ((index "" (string-append index (array:thing->string (car ks)) " "))
- (ks ks (cdr ks)))
- ((null? ks) index)))
-
-(define (array:shape-vector->string shv)
- (do ((bounds "" (string-append bounds
- "["
- (number->string (vector-ref shv t))
- ".."
- (number->string (vector-ref shv (+ t 1)))
- ")"
- " "))
- (t 0 (+ t 2)))
- ((= t (vector-length shv)) bounds)))
-
-(define (array:thing->string thing)
- (cond
- ((number? thing) (number->string thing))
- ((symbol? thing) (string-append "#<symbol>" (symbol->string thing)))
- ((char? thing) "#<char>")
- ((string? thing) "#<string>")
- ((list? thing) (string-append "#" (number->string (length thing))
- "<list>"))
-
- ((pair? thing) "#<pair>")
- ((array? thing) "#<array>")
- ((vector? thing) (string-append "#" (number->string
- (vector-length thing))
- "<vector>"))
- ((procedure? thing) "#<procedure>")
- (else
- (case thing
- ((()) "()")
- ((#t) "#t")
- ((#f) "#f")
- (else
- "#<whatsit>")))))
-
-;;; And to grok an affine map, vector->vector type. Column k of arr
-;;; will contain coefficients n0 ... nm of 1 k1 ... km for kth value.
-;;;
-;;; These are for the error message when share fails.
-
-(define (array:index-ref ind k)
- (if (vector? ind)
- (vector-ref ind k)
- (vector-ref
- (array:vector ind)
- (array:actor-index (array:index ind) k))))
-
-(define (array:index-set! ind k o)
- (if (vector? ind)
- (vector-set! ind k o)
- (vector-set!
- (array:vector ind)
- (array:actor-index (array:index ind) k)
- o)))
-
-(define (array:index-length ind)
- (if (vector? ind)
- (vector-length ind)
- (vector-ref (array:shape ind) 1)))
-
-(define (array:map->string proc r)
- (let* ((m (array:grok/arguments proc r))
- (s (vector-ref (array:shape m) 3)))
- (do ((i "" (string-append i c "k" (number->string k)))
- (c "" ", ")
- (k 1 (+ k 1)))
- ((< r k)
- (do ((o "" (string-append o c (array:map-column->string m r k)))
- (c "" ", ")
- (k 0 (+ k 1)))
- ((= k s)
- (string-append i " => " o)))))))
-
-(define (array:map-column->string m r k)
- (let ((v (array:vector m))
- (i (array:index m)))
- (let ((n0 (vector-ref v (array:vector-index i (list 0 k)))))
- (let wok ((j 1)
- (e (if (= n0 0) "" (number->string n0))))
- (if (<= j r)
- (let ((nj (vector-ref v (array:vector-index i (list j k)))))
- (if (= nj 0)
- (wok (+ j 1) e)
- (let* ((nj (if (= nj 1) ""
- (if (= nj -1) "-"
- (string-append (number->string nj)
- " "))))
- (njkj (string-append nj "k" (number->string j))))
- (if (string=? e "")
- (wok (+ j 1) njkj)
- (wok (+ j 1) (string-append e " + " njkj))))))
- (if (string=? e "") "0" e))))))
-
-(define (array:grok/arguments proc r)
- (array:grok/index!
- (lambda (vec)
- (call-with-values
- (lambda ()
- (array:apply-to-vector r proc vec))
- vector))
- (make-vector r)))
-
-(define (array:grok/index! proc in)
- (let ((m (array:index-length in)))
- (do ((k 0 (+ k 1)))
- ((= k m))
- (array:index-set! in k 0))
- (let* ((n0 (proc in))
- (n (array:index-length n0)))
- (let ((arr (make-array (shape 0 (+ m 1) 0 n)))) ; (*)
- (do ((k 0 (+ k 1)))
- ((= k n))
- (array-set! arr 0 k (array:index-ref n0 k))) ; (**)
- (do ((j 0 (+ j 1)))
- ((= j m))
- (array:index-set! in j 1)
- (let ((nj (proc in)))
- (array:index-set! in j 0)
- (do ((k 0 (+ k 1)))
- ((= k n))
- (array-set! arr (+ j 1) k (- (array:index-ref nj k) ; (**)
- (array:index-ref n0 k))))))
- arr))))
-;; (*) Should not use `make-array' and `shape' here
-;; (**) Should not use `array-set!' here
-;; Should use something internal to the library instead: either lower
-;; level code (preferable but complex) or alternative names to these same.
-;; Copyright (C) John David Stone (1999). All Rights Reserved.
-
-;; Made an R7RS library by Taylan Ulrich Bayırlı/Kammer, Copyright (C) 2014.
-
-;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;; of this software and associated documentation files (the "Software"), to deal
-;; in the Software without restriction, including without limitation the rights
-;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-;; copies of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-
-;; The above copyright notice and this permission notice shall be included in
-;; all copies or substantial portions of the Software.
-
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-(define-library (srfi 8)
- (export receive)
- (import (scheme base))
- (begin
- (define-syntax receive
- (syntax-rules ()
- ((receive formals expression body ...)
- (call-with-values (lambda () expression)
- (lambda formals body ...)))))))
-;;; Copyright (C) Jussi Piitulainen (2001). All Rights Reserved.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-(begin
- (define array:opt-args '(ctor (4)))
- (define (array:optimize f r)
- (case r
- ((0) (let ((n0 (f))) (array:0 n0)))
- ((1) (let ((n0 (f 0))) (array:1 n0 (- (f 1) n0))))
- ((2)
- (let ((n0 (f 0 0)))
- (array:2 n0 (- (f 1 0) n0) (- (f 0 1) n0))))
- ((3)
- (let ((n0 (f 0 0 0)))
- (array:3
- n0
- (- (f 1 0 0) n0)
- (- (f 0 1 0) n0)
- (- (f 0 0 1) n0))))
- (else
- (let ((v
- (do ((k 0 (+ k 1)) (v '() (cons 0 v)))
- ((= k r) v))))
- (let ((n0 (apply f v)))
- (apply
- array:n
- n0
- (array:coefficients f n0 v v)))))))
- (define (array:optimize-empty r)
- (let ((x (make-vector (+ r 1) 0)))
- (vector-set! x r -1)
- x))
- (define (array:coefficients f n0 vs vp)
- (case vp
- ((()) '())
- (else
- (set-car! vp 1)
- (let ((n (- (apply f vs) n0)))
- (set-car! vp 0)
- (cons n (array:coefficients f n0 vs (cdr vp)))))))
- (define (array:vector-index x ks)
- (do ((sum 0 (+ sum (* (vector-ref x k) (car ks))))
- (ks ks (cdr ks))
- (k 0 (+ k 1)))
- ((null? ks) (+ sum (vector-ref x k)))))
- (define (array:shape-index) '#(2 1 0))
- (define (array:empty-shape-index) '#(0 0 -1))
- (define (array:shape-vector-index x r k)
- (+
- (* (vector-ref x 0) r)
- (* (vector-ref x 1) k)
- (vector-ref x 2)))
- (define (array:actor-index x k)
- (+ (* (vector-ref x 0) k) (vector-ref x 1)))
- (define (array:0 n0) (vector n0))
- (define (array:1 n0 n1) (vector n1 n0))
- (define (array:2 n0 n1 n2) (vector n1 n2 n0))
- (define (array:3 n0 n1 n2 n3) (vector n1 n2 n3 n0))
- (define (array:n n0 n1 n2 n3 n4 . ns)
- (apply vector n1 n2 n3 n4 (append ns (list n0))))
- (define (array:maker r)
- (case r
- ((0) array:0)
- ((1) array:1)
- ((2) array:2)
- ((3) array:3)
- (else array:n)))
- (define array:indexer/vector
- (let ((em
- (vector
- (lambda (x i) (+ (vector-ref x 0)))
- (lambda (x i)
- (+
- (* (vector-ref x 0) (vector-ref i 0))
- (vector-ref x 1)))
- (lambda (x i)
- (+
- (* (vector-ref x 0) (vector-ref i 0))
- (* (vector-ref x 1) (vector-ref i 1))
- (vector-ref x 2)))
- (lambda (x i)
- (+
- (* (vector-ref x 0) (vector-ref i 0))
- (* (vector-ref x 1) (vector-ref i 1))
- (* (vector-ref x 2) (vector-ref i 2))
- (vector-ref x 3)))
- (lambda (x i)
- (+
- (* (vector-ref x 0) (vector-ref i 0))
- (* (vector-ref x 1) (vector-ref i 1))
- (* (vector-ref x 2) (vector-ref i 2))
- (* (vector-ref x 3) (vector-ref i 3))
- (vector-ref x 4)))
- (lambda (x i)
- (+
- (* (vector-ref x 0) (vector-ref i 0))
- (* (vector-ref x 1) (vector-ref i 1))
- (* (vector-ref x 2) (vector-ref i 2))
- (* (vector-ref x 3) (vector-ref i 3))
- (* (vector-ref x 4) (vector-ref i 4))
- (vector-ref x 5)))
- (lambda (x i)
- (+
- (* (vector-ref x 0) (vector-ref i 0))
- (* (vector-ref x 1) (vector-ref i 1))
- (* (vector-ref x 2) (vector-ref i 2))
- (* (vector-ref x 3) (vector-ref i 3))
- (* (vector-ref x 4) (vector-ref i 4))
- (* (vector-ref x 5) (vector-ref i 5))
- (vector-ref x 6)))
- (lambda (x i)
- (+
- (* (vector-ref x 0) (vector-ref i 0))
- (* (vector-ref x 1) (vector-ref i 1))
- (* (vector-ref x 2) (vector-ref i 2))
- (* (vector-ref x 3) (vector-ref i 3))
- (* (vector-ref x 4) (vector-ref i 4))
- (* (vector-ref x 5) (vector-ref i 5))
- (* (vector-ref x 6) (vector-ref i 6))
- (vector-ref x 7)))
- (lambda (x i)
- (+
- (* (vector-ref x 0) (vector-ref i 0))
- (* (vector-ref x 1) (vector-ref i 1))
- (* (vector-ref x 2) (vector-ref i 2))
- (* (vector-ref x 3) (vector-ref i 3))
- (* (vector-ref x 4) (vector-ref i 4))
- (* (vector-ref x 5) (vector-ref i 5))
- (* (vector-ref x 6) (vector-ref i 6))
- (* (vector-ref x 7) (vector-ref i 7))
- (vector-ref x 8)))
- (lambda (x i)
- (+
- (* (vector-ref x 0) (vector-ref i 0))
- (* (vector-ref x 1) (vector-ref i 1))
- (* (vector-ref x 2) (vector-ref i 2))
- (* (vector-ref x 3) (vector-ref i 3))
- (* (vector-ref x 4) (vector-ref i 4))
- (* (vector-ref x 5) (vector-ref i 5))
- (* (vector-ref x 6) (vector-ref i 6))
- (* (vector-ref x 7) (vector-ref i 7))
- (* (vector-ref x 8) (vector-ref i 8))
- (vector-ref x 9)))))
- (it
- (lambda (w)
- (lambda (x i)
- (+
- (* (vector-ref x 0) (vector-ref i 0))
- (* (vector-ref x 1) (vector-ref i 1))
- (* (vector-ref x 2) (vector-ref i 2))
- (* (vector-ref x 3) (vector-ref i 3))
- (* (vector-ref x 4) (vector-ref i 4))
- (* (vector-ref x 5) (vector-ref i 5))
- (* (vector-ref x 6) (vector-ref i 6))
- (* (vector-ref x 7) (vector-ref i 7))
- (* (vector-ref x 8) (vector-ref i 8))
- (* (vector-ref x 9) (vector-ref i 9))
- (do ((xi
- 0
- (+
- (* (vector-ref x u) (vector-ref i u))
- xi))
- (u (- w 1) (- u 1)))
- ((< u 10) xi))
- (vector-ref x w))))))
- (lambda (r) (if (< r 10) (vector-ref em r) (it r)))))
- (define array:indexer/array
- (let ((em
- (vector
- (lambda (x v i) (+ (vector-ref x 0)))
- (lambda (x v i)
- (+
- (*
- (vector-ref x 0)
- (vector-ref v (array:actor-index i 0)))
- (vector-ref x 1)))
- (lambda (x v i)
- (+
- (*
- (vector-ref x 0)
- (vector-ref v (array:actor-index i 0)))
- (*
- (vector-ref x 1)
- (vector-ref v (array:actor-index i 1)))
- (vector-ref x 2)))
- (lambda (x v i)
- (+
- (*
- (vector-ref x 0)
- (vector-ref v (array:actor-index i 0)))
- (*
- (vector-ref x 1)
- (vector-ref v (array:actor-index i 1)))
- (*
- (vector-ref x 2)
- (vector-ref v (array:actor-index i 2)))
- (vector-ref x 3)))
- (lambda (x v i)
- (+
- (*
- (vector-ref x 0)
- (vector-ref v (array:actor-index i 0)))
- (*
- (vector-ref x 1)
- (vector-ref v (array:actor-index i 1)))
- (*
- (vector-ref x 2)
- (vector-ref v (array:actor-index i 2)))
- (*
- (vector-ref x 3)
- (vector-ref v (array:actor-index i 3)))
- (vector-ref x 4)))
- (lambda (x v i)
- (+
- (*
- (vector-ref x 0)
- (vector-ref v (array:actor-index i 0)))
- (*
- (vector-ref x 1)
- (vector-ref v (array:actor-index i 1)))
- (*
- (vector-ref x 2)
- (vector-ref v (array:actor-index i 2)))
- (*
- (vector-ref x 3)
- (vector-ref v (array:actor-index i 3)))
- (*
- (vector-ref x 4)
- (vector-ref v (array:actor-index i 4)))
- (vector-ref x 5)))
- (lambda (x v i)
- (+
- (*
- (vector-ref x 0)
- (vector-ref v (array:actor-index i 0)))
- (*
- (vector-ref x 1)
- (vector-ref v (array:actor-index i 1)))
- (*
- (vector-ref x 2)
- (vector-ref v (array:actor-index i 2)))
- (*
- (vector-ref x 3)
- (vector-ref v (array:actor-index i 3)))
- (*
- (vector-ref x 4)
- (vector-ref v (array:actor-index i 4)))
- (*
- (vector-ref x 5)
- (vector-ref v (array:actor-index i 5)))
- (vector-ref x 6)))
- (lambda (x v i)
- (+
- (*
- (vector-ref x 0)
- (vector-ref v (array:actor-index i 0)))
- (*
- (vector-ref x 1)
- (vector-ref v (array:actor-index i 1)))
- (*
- (vector-ref x 2)
- (vector-ref v (array:actor-index i 2)))
- (*
- (vector-ref x 3)
- (vector-ref v (array:actor-index i 3)))
- (*
- (vector-ref x 4)
- (vector-ref v (array:actor-index i 4)))
- (*
- (vector-ref x 5)
- (vector-ref v (array:actor-index i 5)))
- (*
- (vector-ref x 6)
- (vector-ref v (array:actor-index i 6)))
- (vector-ref x 7)))
- (lambda (x v i)
- (+
- (*
- (vector-ref x 0)
- (vector-ref v (array:actor-index i 0)))
- (*
- (vector-ref x 1)
- (vector-ref v (array:actor-index i 1)))
- (*
- (vector-ref x 2)
- (vector-ref v (array:actor-index i 2)))
- (*
- (vector-ref x 3)
- (vector-ref v (array:actor-index i 3)))
- (*
- (vector-ref x 4)
- (vector-ref v (array:actor-index i 4)))
- (*
- (vector-ref x 5)
- (vector-ref v (array:actor-index i 5)))
- (*
- (vector-ref x 6)
- (vector-ref v (array:actor-index i 6)))
- (*
- (vector-ref x 7)
- (vector-ref v (array:actor-index i 7)))
- (vector-ref x 8)))
- (lambda (x v i)
- (+
- (*
- (vector-ref x 0)
- (vector-ref v (array:actor-index i 0)))
- (*
- (vector-ref x 1)
- (vector-ref v (array:actor-index i 1)))
- (*
- (vector-ref x 2)
- (vector-ref v (array:actor-index i 2)))
- (*
- (vector-ref x 3)
- (vector-ref v (array:actor-index i 3)))
- (*
- (vector-ref x 4)
- (vector-ref v (array:actor-index i 4)))
- (*
- (vector-ref x 5)
- (vector-ref v (array:actor-index i 5)))
- (*
- (vector-ref x 6)
- (vector-ref v (array:actor-index i 6)))
- (*
- (vector-ref x 7)
- (vector-ref v (array:actor-index i 7)))
- (*
- (vector-ref x 8)
- (vector-ref v (array:actor-index i 8)))
- (vector-ref x 9)))))
- (it
- (lambda (w)
- (lambda (x v i)
- (+
- (*
- (vector-ref x 0)
- (vector-ref v (array:actor-index i 0)))
- (*
- (vector-ref x 1)
- (vector-ref v (array:actor-index i 1)))
- (*
- (vector-ref x 2)
- (vector-ref v (array:actor-index i 2)))
- (*
- (vector-ref x 3)
- (vector-ref v (array:actor-index i 3)))
- (*
- (vector-ref x 4)
- (vector-ref v (array:actor-index i 4)))
- (*
- (vector-ref x 5)
- (vector-ref v (array:actor-index i 5)))
- (*
- (vector-ref x 6)
- (vector-ref v (array:actor-index i 6)))
- (*
- (vector-ref x 7)
- (vector-ref v (array:actor-index i 7)))
- (*
- (vector-ref x 8)
- (vector-ref v (array:actor-index i 8)))
- (*
- (vector-ref x 9)
- (vector-ref v (array:actor-index i 9)))
- (do ((xi
- 0
- (+
- (*
- (vector-ref x u)
- (vector-ref
- v
- (array:actor-index i u)))
- xi))
- (u (- w 1) (- u 1)))
- ((< u 10) xi))
- (vector-ref x w))))))
- (lambda (r) (if (< r 10) (vector-ref em r) (it r)))))
- (define array:applier-to-vector
- (let ((em
- (vector
- (lambda (p v) (p))
- (lambda (p v) (p (vector-ref v 0)))
- (lambda (p v)
- (p (vector-ref v 0) (vector-ref v 1)))
- (lambda (p v)
- (p
- (vector-ref v 0)
- (vector-ref v 1)
- (vector-ref v 2)))
- (lambda (p v)
- (p
- (vector-ref v 0)
- (vector-ref v 1)
- (vector-ref v 2)
- (vector-ref v 3)))
- (lambda (p v)
- (p
- (vector-ref v 0)
- (vector-ref v 1)
- (vector-ref v 2)
- (vector-ref v 3)
- (vector-ref v 4)))
- (lambda (p v)
- (p
- (vector-ref v 0)
- (vector-ref v 1)
- (vector-ref v 2)
- (vector-ref v 3)
- (vector-ref v 4)
- (vector-ref v 5)))
- (lambda (p v)
- (p
- (vector-ref v 0)
- (vector-ref v 1)
- (vector-ref v 2)
- (vector-ref v 3)
- (vector-ref v 4)
- (vector-ref v 5)
- (vector-ref v 6)))
- (lambda (p v)
- (p
- (vector-ref v 0)
- (vector-ref v 1)
- (vector-ref v 2)
- (vector-ref v 3)
- (vector-ref v 4)
- (vector-ref v 5)
- (vector-ref v 6)
- (vector-ref v 7)))
- (lambda (p v)
- (p
- (vector-ref v 0)
- (vector-ref v 1)
- (vector-ref v 2)
- (vector-ref v 3)
- (vector-ref v 4)
- (vector-ref v 5)
- (vector-ref v 6)
- (vector-ref v 7)
- (vector-ref v 8)))))
- (it
- (lambda (r)
- (lambda (p v)
- (apply
- p
- (vector-ref v 0)
- (vector-ref v 1)
- (vector-ref v 2)
- (vector-ref v 3)
- (vector-ref v 4)
- (vector-ref v 5)
- (vector-ref v 6)
- (vector-ref v 7)
- (vector-ref v 8)
- (vector-ref v 9)
- (do ((k r (- k 1))
- (r
- '()
- (cons (vector-ref v (- k 1)) r)))
- ((= k 10) r)))))))
- (lambda (r) (if (< r 10) (vector-ref em r) (it r)))))
- (define array:applier-to-actor
- (let ((em
- (vector
- (lambda (p a) (p))
- (lambda (p a) (p (array-ref a 0)))
- (lambda (p a)
- (p (array-ref a 0) (array-ref a 1)))
- (lambda (p a)
- (p
- (array-ref a 0)
- (array-ref a 1)
- (array-ref a 2)))
- (lambda (p a)
- (p
- (array-ref a 0)
- (array-ref a 1)
- (array-ref a 2)
- (array-ref a 3)))
- (lambda (p a)
- (p
- (array-ref a 0)
- (array-ref a 1)
- (array-ref a 2)
- (array-ref a 3)
- (array-ref a 4)))
- (lambda (p a)
- (p
- (array-ref a 0)
- (array-ref a 1)
- (array-ref a 2)
- (array-ref a 3)
- (array-ref a 4)
- (array-ref a 5)))
- (lambda (p a)
- (p
- (array-ref a 0)
- (array-ref a 1)
- (array-ref a 2)
- (array-ref a 3)
- (array-ref a 4)
- (array-ref a 5)
- (array-ref a 6)))
- (lambda (p a)
- (p
- (array-ref a 0)
- (array-ref a 1)
- (array-ref a 2)
- (array-ref a 3)
- (array-ref a 4)
- (array-ref a 5)
- (array-ref a 6)
- (array-ref a 7)))
- (lambda (p a)
- (p
- (array-ref a 0)
- (array-ref a 1)
- (array-ref a 2)
- (array-ref a 3)
- (array-ref a 4)
- (array-ref a 5)
- (array-ref a 6)
- (array-ref a 7)
- (array-ref a 8)))))
- (it
- (lambda (r)
- (lambda (p a)
- (apply
- a
- (array-ref a 0)
- (array-ref a 1)
- (array-ref a 2)
- (array-ref a 3)
- (array-ref a 4)
- (array-ref a 5)
- (array-ref a 6)
- (array-ref a 7)
- (array-ref a 8)
- (array-ref a 9)
- (do ((k r (- k 1))
- (r '() (cons (array-ref a (- k 1)) r)))
- ((= k 10) r)))))))
- (lambda (r)
- "These are high level, hiding implementation at call site."
- (if (< r 10) (vector-ref em r) (it r)))))
- (define array:applier-to-backing-vector
- (let ((em
- (vector
- (lambda (p ai av) (p))
- (lambda (p ai av)
- (p (vector-ref av (array:actor-index ai 0))))
- (lambda (p ai av)
- (p
- (vector-ref av (array:actor-index ai 0))
- (vector-ref av (array:actor-index ai 1))))
- (lambda (p ai av)
- (p
- (vector-ref av (array:actor-index ai 0))
- (vector-ref av (array:actor-index ai 1))
- (vector-ref av (array:actor-index ai 2))))
- (lambda (p ai av)
- (p
- (vector-ref av (array:actor-index ai 0))
- (vector-ref av (array:actor-index ai 1))
- (vector-ref av (array:actor-index ai 2))
- (vector-ref av (array:actor-index ai 3))))
- (lambda (p ai av)
- (p
- (vector-ref av (array:actor-index ai 0))
- (vector-ref av (array:actor-index ai 1))
- (vector-ref av (array:actor-index ai 2))
- (vector-ref av (array:actor-index ai 3))
- (vector-ref av (array:actor-index ai 4))))
- (lambda (p ai av)
- (p
- (vector-ref av (array:actor-index ai 0))
- (vector-ref av (array:actor-index ai 1))
- (vector-ref av (array:actor-index ai 2))
- (vector-ref av (array:actor-index ai 3))
- (vector-ref av (array:actor-index ai 4))
- (vector-ref av (array:actor-index ai 5))))
- (lambda (p ai av)
- (p
- (vector-ref av (array:actor-index ai 0))
- (vector-ref av (array:actor-index ai 1))
- (vector-ref av (array:actor-index ai 2))
- (vector-ref av (array:actor-index ai 3))
- (vector-ref av (array:actor-index ai 4))
- (vector-ref av (array:actor-index ai 5))
- (vector-ref av (array:actor-index ai 6))))
- (lambda (p ai av)
- (p
- (vector-ref av (array:actor-index ai 0))
- (vector-ref av (array:actor-index ai 1))
- (vector-ref av (array:actor-index ai 2))
- (vector-ref av (array:actor-index ai 3))
- (vector-ref av (array:actor-index ai 4))
- (vector-ref av (array:actor-index ai 5))
- (vector-ref av (array:actor-index ai 6))
- (vector-ref av (array:actor-index ai 7))))
- (lambda (p ai av)
- (p
- (vector-ref av (array:actor-index ai 0))
- (vector-ref av (array:actor-index ai 1))
- (vector-ref av (array:actor-index ai 2))
- (vector-ref av (array:actor-index ai 3))
- (vector-ref av (array:actor-index ai 4))
- (vector-ref av (array:actor-index ai 5))
- (vector-ref av (array:actor-index ai 6))
- (vector-ref av (array:actor-index ai 7))
- (vector-ref av (array:actor-index ai 8))))))
- (it
- (lambda (r)
- (lambda (p ai av)
- (apply
- p
- (vector-ref av (array:actor-index ai 0))
- (vector-ref av (array:actor-index ai 1))
- (vector-ref av (array:actor-index ai 2))
- (vector-ref av (array:actor-index ai 3))
- (vector-ref av (array:actor-index ai 4))
- (vector-ref av (array:actor-index ai 5))
- (vector-ref av (array:actor-index ai 6))
- (vector-ref av (array:actor-index ai 7))
- (vector-ref av (array:actor-index ai 8))
- (vector-ref av (array:actor-index ai 9))
- (do ((k r (- k 1))
- (r
- '()
- (cons
- (vector-ref
- av
- (array:actor-index ai (- k 1)))
- r)))
- ((= k 10) r)))))))
- (lambda (r)
- "These are low level, exposing implementation at call site."
- (if (< r 10) (vector-ref em r) (it r)))))
- (define (array:index/vector r x v)
- ((array:indexer/vector r) x v))
- (define (array:index/array r x av ai)
- ((array:indexer/array r) x av ai))
- (define (array:apply-to-vector r p v)
- ((array:applier-to-vector r) p v))
- (define (array:apply-to-actor r p a)
- ((array:applier-to-actor r) p a)))
-(define-library (srfi 25)
- (export
- array?
- make-array
- shape
- array
- array-rank
- array-start
- array-end
- array-ref
- array-set!
- share-array
- )
- (import
- (scheme base)
- (scheme write))
- (include "25.as-srfi-9-record.upstream.scm")
- (include "25.ix-ctor.upstream.scm")
- (include "25.op-ctor.upstream.scm")
- (include "25.main.upstream.scm"))
-(define-library (srfi 26)
- (export cut cute)
- (import (scheme base))
- (include "26.upstream.scm"))
-;;;;"array.scm" Arrays for Scheme
-; Copyright (C) 2001, 2003 Aubrey Jaffer
-;
-;Permission to copy this software, to modify it, to redistribute it,
-;to distribute modified versions, 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 warranty 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.
-
-;;@code{(require 'array)} or @code{(require 'srfi-63)}
-;;@ftindex array
-
-(require 'record)
-
-(define array:rtd
- (make-record-type "array"
- '(dimensions
- scales ;list of dimension scales
- offset ;exact integer
- store ;data
- )))
-
-(define array:dimensions
- (let ((dimensions (record-accessor array:rtd 'dimensions)))
- (lambda (array)
- (cond ((vector? array) (list (vector-length array)))
- ((string? array) (list (string-length array)))
- (else (dimensions array))))))
-
-(define array:scales
- (let ((scales (record-accessor array:rtd 'scales)))
- (lambda (obj)
- (cond ((string? obj) '(1))
- ((vector? obj) '(1))
- (else (scales obj))))))
-
-(define array:store
- (let ((store (record-accessor array:rtd 'store)))
- (lambda (obj)
- (cond ((string? obj) obj)
- ((vector? obj) obj)
- (else (store obj))))))
-
-(define array:offset
- (let ((offset (record-accessor array:rtd 'offset)))
- (lambda (obj)
- (cond ((string? obj) 0)
- ((vector? obj) 0)
- (else (offset obj))))))
-
-(define array:construct
- (record-constructor array:rtd '(dimensions scales offset store)))
-
-;;@args obj
-;;Returns @code{#t} if the @1 is an array, and @code{#f} if not.
-(define array?
- (let ((array:array? (record-predicate array:rtd)))
- (lambda (obj) (or (string? obj) (vector? obj) (array:array? obj)))))
-
-;;@noindent
-;;@emph{Note:} Arrays are not disjoint from other Scheme types.
-;;Vectors and possibly strings also satisfy @code{array?}.
-;;A disjoint array predicate can be written:
-;;
-;;@example
-;;(define (strict-array? obj)
-;; (and (array? obj) (not (string? obj)) (not (vector? obj))))
-;;@end example
-
-;;@body
-;;Returns @code{#t} if @1 and @2 have the same rank and dimensions and the
-;;corresponding elements of @1 and @2 are @code{equal?}.
-
-;;@body
-;;@0 recursively compares the contents of pairs, vectors, strings, and
-;;@emph{arrays}, applying @code{eqv?} on other objects such as numbers
-;;and symbols. A rule of thumb is that objects are generally @0 if
-;;they print the same. @0 may fail to terminate if its arguments are
-;;circular data structures.
-;;
-;;@example
-;;(equal? 'a 'a) @result{} #t
-;;(equal? '(a) '(a)) @result{} #t
-;;(equal? '(a (b) c)
-;; '(a (b) c)) @result{} #t
-;;(equal? "abc" "abc") @result{} #t
-;;(equal? 2 2) @result{} #t
-;;(equal? (make-vector 5 'a)
-;; (make-vector 5 'a)) @result{} #t
-;;(equal? (make-array (A:fixN32b 4) 5 3)
-;; (make-array (A:fixN32b 4) 5 3)) @result{} #t
-;;(equal? (make-array '#(foo) 3 3)
-;; (make-array '#(foo) 3 3)) @result{} #t
-;;(equal? (lambda (x) x)
-;; (lambda (y) y)) @result{} @emph{unspecified}
-;;@end example
-(define (equal? obj1 obj2)
- (cond ((eqv? obj1 obj2) #t)
- ((or (pair? obj1) (pair? obj2))
- (and (pair? obj1) (pair? obj2)
- (equal? (car obj1) (car obj2))
- (equal? (cdr obj1) (cdr obj2))))
- ((or (string? obj1) (string? obj2))
- (and (string? obj1) (string? obj2)
- (string=? obj1 obj2)))
- ((or (vector? obj1) (vector? obj2))
- (and (vector? obj1) (vector? obj2)
- (equal? (vector-length obj1) (vector-length obj2))
- (do ((idx (+ -1 (vector-length obj1)) (+ -1 idx)))
- ((or (negative? idx)
- (not (equal? (vector-ref obj1 idx)
- (vector-ref obj2 idx))))
- (negative? idx)))))
- ((or (array? obj1) (array? obj2))
- (and (array? obj1) (array? obj2)
- (equal? (array:dimensions obj1) (array:dimensions obj2))
- (equal? (array:store obj1) (array:store obj2))))
- (else #f)))
-
-;;@body
-;;Returns the number of dimensions of @1. If @1 is not an array, 0 is
-;;returned.
-(define (array-rank obj)
- (if (array? obj) (length (array:dimensions obj)) 0))
-
-;;@args array
-;;Returns a list of dimensions.
-;;
-;;@example
-;;(array-dimensions (make-array '#() 3 5))
-;; @result{} (3 5)
-;;@end example
-(define array-dimensions array:dimensions)
-
-;;@args prototype k1 @dots{}
-;;
-;;Creates and returns an array of type @1 with dimensions @2, @dots{}
-;;and filled with elements from @1. @1 must be an array, vector, or
-;;string. The implementation-dependent type of the returned array
-;;will be the same as the type of @1; except if that would be a vector
-;;or string with rank not equal to one, in which case some variety of
-;;array will be returned.
-;;
-;;If the @1 has no elements, then the initial contents of the returned
-;;array are unspecified. Otherwise, the returned array will be filled
-;;with the element at the origin of @1.
-(define (make-array prototype . dimensions)
- (define tcnt (apply * dimensions))
- (let ((store
- (if (string? prototype)
- (case (string-length prototype)
- ((0) (make-string tcnt))
- (else (make-string tcnt
- (string-ref prototype 0))))
- (let ((pdims (array:dimensions prototype)))
- (case (apply * pdims)
- ((0) (make-vector tcnt))
- (else (make-vector tcnt
- (apply array-ref prototype
- (map (lambda (x) 0) pdims)))))))))
- (define (loop dims scales)
- (if (null? dims)
- (array:construct dimensions (cdr scales) 0 store)
- (loop (cdr dims) (cons (* (car dims) (car scales)) scales))))
- (loop (reverse dimensions) '(1))))
-;;@args prototype k1 @dots{}
-;;@0 is an alias for @code{make-array}.
-(define create-array make-array)
-
-;;@args array mapper k1 @dots{}
-;;@0 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:
-;;
-;;@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)
-;; @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 example
-(define (make-shared-array array mapper . dimensions)
- (define odl (array:scales array))
- (define rank (length dimensions))
- (define shape
- (map (lambda (dim) (if (list? dim) dim (list 0 (+ -1 dim)))) dimensions))
- (do ((idx (+ -1 rank) (+ -1 idx))
- (uvt (append (cdr (vector->list (make-vector rank 0))) '(1))
- (append (cdr uvt) '(0)))
- (uvts '() (cons uvt uvts)))
- ((negative? idx)
- (let ((ker0 (apply + (map * odl (apply mapper uvt)))))
- (array:construct
- (map (lambda (dim) (+ 1 (- (cadr dim) (car dim)))) shape)
- (map (lambda (uvt) (- (apply + (map * odl (apply mapper uvt))) ker0))
- uvts)
- (apply +
- (array:offset array)
- (map * odl (apply mapper (map car shape))))
- (array:store array))))))
-
-;;@args rank proto list
-;;@3 must be a rank-nested list consisting of all the elements, in
-;;row-major order, of the array to be created.
-;;
-;;@0 returns an array of rank @1 and type @2 consisting of all the
-;;elements, in row-major order, of @3. When @1 is 0, @3 is the lone
-;;array element; not necessarily a list.
-;;
-;;@example
-;;(list->array 2 '#() '((1 2) (3 4)))
-;; @result{} #2A((1 2) (3 4))
-;;(list->array 0 '#() 3)
-;; @result{} #0A 3
-;;@end example
-(define (list->array rank proto lst)
- (define dimensions
- (do ((shp '() (cons (length row) shp))
- (row lst (car lst))
- (rnk (+ -1 rank) (+ -1 rnk)))
- ((negative? rnk) (reverse shp))))
- (let ((nra (apply make-array proto dimensions)))
- (define (l2ra dims idxs row)
- (cond ((null? dims)
- (apply array-set! nra row (reverse idxs)))
- ((if (not (eqv? (car dims) (length row)))
- (slib:error 'list->array
- 'non-rectangular 'array dims dimensions))
- (do ((idx 0 (+ 1 idx))
- (row row (cdr row)))
- ((>= idx (car dims)))
- (l2ra (cdr dims) (cons idx idxs) (car row))))))
- (l2ra dimensions '() lst)
- nra))
-
-;;@args array
-;;Returns a rank-nested list consisting of all the elements, in
-;;row-major order, of @1. In the case of a rank-0 array, @0 returns
-;;the single element.
-;;
-;;@example
-;;(array->list #2A((ho ho ho) (ho oh oh)))
-;; @result{} ((ho ho ho) (ho oh oh))
-;;(array->list #0A ho)
-;; @result{} ho
-;;@end example
-(define (array->list ra)
- (define (ra2l dims idxs)
- (if (null? dims)
- (apply array-ref ra (reverse idxs))
- (do ((lst '() (cons (ra2l (cdr dims) (cons idx idxs)) lst))
- (idx (+ -1 (car dims)) (+ -1 idx)))
- ((negative? idx) lst))))
- (ra2l (array-dimensions ra) '()))
-
-;;@args vect proto dim1 @dots{}
-;;@1 must be a vector of length equal to the product of exact
-;;nonnegative integers @3, @dots{}.
-;;
-;;@0 returns an array of type @2 consisting of all the elements, in
-;;row-major order, of @1. In the case of a rank-0 array, @1 has a
-;;single element.
-;;
-;;@example
-;;(vector->array #(1 2 3 4) #() 2 2)
-;; @result{} #2A((1 2) (3 4))
-;;(vector->array '#(3) '#())
-;; @result{} #0A 3
-;;@end example
-(define (vector->array vect prototype . dimensions)
- (define vdx (vector-length vect))
- (if (not (eqv? vdx (apply * dimensions)))
- (slib:error 'vector->array vdx '<> (cons '* dimensions)))
- (let ((ra (apply make-array prototype dimensions)))
- (define (v2ra dims idxs)
- (cond ((null? dims)
- (set! vdx (+ -1 vdx))
- (apply array-set! ra (vector-ref vect vdx) (reverse idxs)))
- (else
- (do ((idx (+ -1 (car dims)) (+ -1 idx)))
- ((negative? idx) vect)
- (v2ra (cdr dims) (cons idx idxs))))))
- (v2ra dimensions '())
- ra))
-
-;;@args array
-;;Returns a new vector consisting of all the elements of @1 in
-;;row-major order.
-;;
-;;@example
-;;(array->vector #2A ((1 2)( 3 4)))
-;; @result{} #(1 2 3 4)
-;;(array->vector #0A ho)
-;; @result{} #(ho)
-;;@end example
-(define (array->vector ra)
- (define dims (array-dimensions ra))
- (let* ((vdx (apply * dims))
- (vect (make-vector vdx)))
- (define (ra2v dims idxs)
- (if (null? dims)
- (let ((val (apply array-ref ra (reverse idxs))))
- (set! vdx (+ -1 vdx))
- (vector-set! vect vdx val)
- vect)
- (do ((idx (+ -1 (car dims)) (+ -1 idx)))
- ((negative? idx) vect)
- (ra2v (cdr dims) (cons idx idxs)))))
- (ra2v dims '())))
-
-(define (array:in-bounds? array indices)
- (do ((bnds (array:dimensions array) (cdr bnds))
- (idxs indices (cdr idxs)))
- ((or (null? bnds)
- (null? idxs)
- (not (integer? (car idxs)))
- (not (< -1 (car idxs) (car bnds))))
- (and (null? bnds) (null? idxs)))))
-
-;;@args array index1 @dots{}
-;;Returns @code{#t} if its arguments would be acceptable to
-;;@code{array-ref}.
-(define (array-in-bounds? array . indices)
- (array:in-bounds? array indices))
-
-;;@args array k1 @dots{}
-;;Returns the (@2, @dots{}) element of @1.
-(define (array-ref array . indices)
- (define store (array:store array))
- (or (array:in-bounds? array indices)
- (slib:error 'array-ref 'bad-indices indices))
- ((if (string? store) string-ref vector-ref)
- store (apply + (array:offset array) (map * (array:scales array) indices))))
-
-;;@args array obj k1 @dots{}
-;;Stores @2 in the (@3, @dots{}) element of @1. The value returned
-;;by @0 is unspecified.
-(define (array-set! array obj . indices)
- (define store (array:store array))
- (or (array:in-bounds? array indices)
- (slib:error 'array-set! 'bad-indices indices))
- ((if (string? store) string-set! vector-set!)
- store (apply + (array:offset array) (map * (array:scales array) indices))
- obj))
-
-;;@noindent
-;;These functions return a prototypical uniform-array enclosing the
-;;optional argument (which must be of the correct type). If the
-;;uniform-array type is supported by the implementation, then it is
-;;returned; defaulting to the next larger precision type; resorting
-;;finally to vector.
-
-(define (make-prototype-checker name pred? creator)
- (lambda args
- (case (length args)
- ((1) (if (pred? (car args))
- (creator (car args))
- (slib:error name 'incompatible 'type (car args))))
- ((0) (creator))
- (else (slib:error name 'wrong 'number 'of 'args args)))))
-
-(define (integer-bytes?? n)
- (lambda (obj)
- (and (integer? obj)
- (exact? obj)
- (or (negative? n) (not (negative? obj)))
- (do ((num obj (quotient num 256))
- (n (+ -1 (abs n)) (+ -1 n)))
- ((or (zero? num) (negative? n))
- (zero? num))))))
-
-;;@args z
-;;@args
-;;Returns an inexact 128.bit flonum complex uniform-array prototype.
-(define A:floC128b (make-prototype-checker 'A:floC128b complex? vector))
-;;@args z
-;;@args
-;;Returns an inexact 64.bit flonum complex uniform-array prototype.
-(define A:floC64b (make-prototype-checker 'A:floC64b complex? vector))
-;;@args z
-;;@args
-;;Returns an inexact 32.bit flonum complex uniform-array prototype.
-(define A:floC32b (make-prototype-checker 'A:floC32b complex? vector))
-;;@args z
-;;@args
-;;Returns an inexact 16.bit flonum complex uniform-array prototype.
-(define A:floC16b (make-prototype-checker 'A:floC16b complex? vector))
-
-;;@args z
-;;@args
-;;Returns an inexact 128.bit flonum real uniform-array prototype.
-(define A:floR128b (make-prototype-checker 'A:floR128b real? vector))
-;;@args z
-;;@args
-;;Returns an inexact 64.bit flonum real uniform-array prototype.
-(define A:floR64b (make-prototype-checker 'A:floR64b real? vector))
-;;@args z
-;;@args
-;;Returns an inexact 32.bit flonum real uniform-array prototype.
-(define A:floR32b (make-prototype-checker 'A:floR32b real? vector))
-;;@args z
-;;@args
-;;Returns an inexact 16.bit flonum real uniform-array prototype.
-(define A:floR16b (make-prototype-checker 'A:floR16b real? vector))
-
-;;@args z
-;;@args
-;;Returns an exact 128.bit decimal flonum rational uniform-array prototype.
-(define A:floR128b (make-prototype-checker 'A:floR128b real? vector))
-;;@args z
-;;@args
-;;Returns an exact 64.bit decimal flonum rational uniform-array prototype.
-(define A:floR64b (make-prototype-checker 'A:floR64b real? vector))
-;;@args z
-;;@args
-;;Returns an exact 32.bit decimal flonum rational uniform-array prototype.
-(define A:floR32b (make-prototype-checker 'A:floR32b real? vector))
-
-;;@args n
-;;@args
-;;Returns an exact binary fixnum uniform-array prototype with at least
-;;64 bits of precision.
-(define A:fixZ64b (make-prototype-checker 'A:fixZ64b (integer-bytes?? -8) vector))
-;;@args n
-;;@args
-;;Returns an exact binary fixnum uniform-array prototype with at least
-;;32 bits of precision.
-(define A:fixZ32b (make-prototype-checker 'A:fixZ32b (integer-bytes?? -4) vector))
-;;@args n
-;;@args
-;;Returns an exact binary fixnum uniform-array prototype with at least
-;;16 bits of precision.
-(define A:fixZ16b (make-prototype-checker 'A:fixZ16b (integer-bytes?? -2) vector))
-;;@args n
-;;@args
-;;Returns an exact binary fixnum uniform-array prototype with at least
-;;8 bits of precision.
-(define A:fixZ8b (make-prototype-checker 'A:fixZ8b (integer-bytes?? -1) vector))
-
-;;@args k
-;;@args
-;;Returns an exact non-negative binary fixnum uniform-array prototype with at
-;;least 64 bits of precision.
-(define A:fixN64b (make-prototype-checker 'A:fixN64b (integer-bytes?? 8) vector))
-;;@args k
-;;@args
-;;Returns an exact non-negative binary fixnum uniform-array prototype with at
-;;least 32 bits of precision.
-(define A:fixN32b (make-prototype-checker 'A:fixN32b (integer-bytes?? 4) vector))
-;;@args k
-;;@args
-;;Returns an exact non-negative binary fixnum uniform-array prototype with at
-;;least 16 bits of precision.
-(define A:fixN16b (make-prototype-checker 'A:fixN16b (integer-bytes?? 2) vector))
-;;@args k
-;;@args
-;;Returns an exact non-negative binary fixnum uniform-array prototype with at
-;;least 8 bits of precision.
-(define A:fixN8b (make-prototype-checker 'A:fixN8b (integer-bytes?? 1) vector))
-
-;;@args bool
-;;@args
-;;Returns a boolean uniform-array prototype.
-(define A:bool (make-prototype-checker 'A:bool boolean? vector))
-; REFERENCE IMPLEMENTATION FOR SRFI-26 "CUT"
-; ==========================================
-;
-; Sebastian.Egner@philips.com, 5-Jun-2002.
-; adapted from the posting by Al Petrofsky <al@petrofsky.org>
-; placed in the public domain
-;
-; The code to handle the variable argument case was originally
-; proposed by Michael Sperber and has been adapted to the new
-; syntax of the macro using an explicit rest-slot symbol. The
-; code to evaluate the non-slots for cute has been proposed by
-; Dale Jordan. The code to allow a slot for the procedure position
-; and to process the macro using an internal macro is based on
-; a suggestion by Al Petrofsky. The code found below is, with
-; exception of this header and some changes in variable names,
-; entirely written by Al Petrofsky.
-;
-; compliance:
-; Scheme R5RS (including macros).
-;
-; loading this file into Scheme 48 0.57:
-; ,load cut.scm
-;
-; history of this file:
-; SE, 6-Feb-2002: initial version as 'curry' with ". <>" notation
-; SE, 14-Feb-2002: revised for <___>
-; SE, 27-Feb-2002: revised for 'cut'
-; SE, 03-Jun-2002: revised for proc-slot, cute
-; SE, 04-Jun-2002: rewritten with internal transformer (no "loop" pattern)
-; SE, 05-Jun-2002: replace my code by Al's; substituted "constant" etc.
-; to match the convention in the SRFI-document
-
-; (srfi-26-internal-cut slot-names combination . se)
-; transformer used internally
-; slot-names : the internal names of the slots
-; combination : procedure being specialized, followed by its arguments
-; se : slots-or-exprs, the qualifiers of the macro
-
-(define-syntax srfi-26-internal-cut
- (syntax-rules (<> <___>)
-
- ;; construct fixed- or variable-arity procedure:
- ;; (begin proc) throws an error if proc is not an <expression>
- ((srfi-26-internal-cut (slot-name ...) (proc arg ...))
- (lambda (slot-name ...) ((begin proc) arg ...)))
- ((srfi-26-internal-cut (slot-name ...) (proc arg ...) <___>)
- (lambda (slot-name ... . rest-slot) (apply proc arg ... rest-slot)))
-
- ;; process one slot-or-expr
- ((srfi-26-internal-cut (slot-name ...) (position ...) <> . se)
- (srfi-26-internal-cut (slot-name ... x) (position ... x) . se))
- ((srfi-26-internal-cut (slot-name ...) (position ...) nse . se)
- (srfi-26-internal-cut (slot-name ...) (position ... nse) . se))))
-
-; (srfi-26-internal-cute slot-names nse-bindings combination . se)
-; transformer used internally
-; slot-names : the internal names of the slots
-; nse-bindings : let-style bindings for the non-slot expressions.
-; combination : procedure being specialized, followed by its arguments
-; se : slots-or-exprs, the qualifiers of the macro
-
-(define-syntax srfi-26-internal-cute
- (syntax-rules (<> <___>)
-
- ;; If there are no slot-or-exprs to process, then:
- ;; construct a fixed-arity procedure,
- ((srfi-26-internal-cute
- (slot-name ...) nse-bindings (proc arg ...))
- (let nse-bindings (lambda (slot-name ...) (proc arg ...))))
- ;; or a variable-arity procedure
- ((srfi-26-internal-cute
- (slot-name ...) nse-bindings (proc arg ...) <___>)
- (let nse-bindings (lambda (slot-name ... . x) (apply proc arg ... x))))
-
- ;; otherwise, process one slot:
- ((srfi-26-internal-cute
- (slot-name ...) nse-bindings (position ...) <> . se)
- (srfi-26-internal-cute
- (slot-name ... x) nse-bindings (position ... x) . se))
- ;; or one non-slot expression
- ((srfi-26-internal-cute
- slot-names nse-bindings (position ...) nse . se)
- (srfi-26-internal-cute
- slot-names ((x nse) . nse-bindings) (position ... x) . se))))
-
-; exported syntax
-
-(define-syntax cut
- (syntax-rules ()
- ((cut . slots-or-exprs)
- (srfi-26-internal-cut () () . slots-or-exprs))))
-
-(define-syntax cute
- (syntax-rules ()
- ((cute . slots-or-exprs)
- (srfi-26-internal-cute () () () . slots-or-exprs))))
-;;;; "logical.scm", bit access and operations for integers for Scheme
-;;; Copyright (C) 1991, 1993, 2001, 2003, 2005 Aubrey Jaffer
-;
-;Permission to copy this software, to modify it, to redistribute it,
-;to distribute modified versions, 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 warranty 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: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 (logical:ash-4 x)
- (if (negative? x)
- (+ -1 (quotient (+ 1 x) 16))
- (quotient x 16)))
-
-(define (logical:reduce op4 ident)
- (lambda args
- (do ((res ident (op4 res (car rgs) 1 0))
- (rgs args (cdr rgs)))
- ((null? rgs) res))))
-
-;@
-(define logand
- (letrec
- ((lgand
- (lambda (n2 n1 scl acc)
- (cond ((= n1 n2) (+ acc (* scl n1)))
- ((zero? n2) acc)
- ((zero? n1) acc)
- (else (lgand (logical:ash-4 n2)
- (logical:ash-4 n1)
- (* 16 scl)
- (+ (* (vector-ref (vector-ref logical:boole-and
- (modulo n1 16))
- (modulo n2 16))
- scl)
- acc)))))))
- (logical:reduce lgand -1)))
-;@
-(define logior
- (letrec
- ((lgior
- (lambda (n2 n1 scl acc)
- (cond ((= n1 n2) (+ acc (* scl n1)))
- ((zero? n2) (+ acc (* scl n1)))
- ((zero? n1) (+ acc (* scl n2)))
- (else (lgior (logical:ash-4 n2)
- (logical:ash-4 n1)
- (* 16 scl)
- (+ (* (- 15 (vector-ref
- (vector-ref logical:boole-and
- (- 15 (modulo n1 16)))
- (- 15 (modulo n2 16))))
- scl)
- acc)))))))
- (logical:reduce lgior 0)))
-;@
-(define logxor
- (letrec
- ((lgxor
- (lambda (n2 n1 scl acc)
- (cond ((= n1 n2) acc)
- ((zero? n2) (+ acc (* scl n1)))
- ((zero? n1) (+ acc (* scl n2)))
- (else (lgxor (logical:ash-4 n2)
- (logical:ash-4 n1)
- (* 16 scl)
- (+ (* (vector-ref (vector-ref logical:boole-xor
- (modulo n1 16))
- (modulo n2 16))
- scl)
- acc)))))))
- (logical:reduce lgxor 0)))
-;@
-(define (lognot n) (- -1 n))
-;@
-(define (logtest n1 n2)
- (not (zero? (logand n1 n2))))
-;@
-(define (logbit? index n)
- (logtest (expt 2 index) n))
-;@
-(define (copy-bit index to bool)
- (if bool
- (logior to (arithmetic-shift 1 index))
- (logand to (lognot (arithmetic-shift 1 index)))))
-;@
-(define (bitwise-if mask n0 n1)
- (logior (logand mask n0)
- (logand (lognot mask) n1)))
-;@
-(define (bit-field n start end)
- (logand (lognot (ash -1 (- end start)))
- (arithmetic-shift n (- start))))
-;@
-(define (copy-bit-field to from start end)
- (bitwise-if (arithmetic-shift (lognot (ash -1 (- end start))) start)
- (arithmetic-shift from start)
- to))
-;@
-(define (rotate-bit-field n count start end)
- (define width (- end start))
- (set! count (modulo count width))
- (let ((mask (lognot (ash -1 width))))
- (define zn (logand mask (arithmetic-shift n (- start))))
- (logior (arithmetic-shift
- (logior (logand mask (arithmetic-shift zn count))
- (arithmetic-shift zn (- count width)))
- start)
- (logand (lognot (ash mask start)) n))))
-;@
-(define (arithmetic-shift n count)
- (if (negative? count)
- (let ((k (expt 2 (- count))))
- (if (negative? n)
- (+ -1 (quotient (+ 1 n) k))
- (quotient n k)))
- (* (expt 2 count) n)))
-;@
-(define integer-length
- (letrec ((intlen (lambda (n tot)
- (case n
- ((0 -1) (+ 0 tot))
- ((1 -2) (+ 1 tot))
- ((2 3 -3 -4) (+ 2 tot))
- ((4 5 6 7 -5 -6 -7 -8) (+ 3 tot))
- (else (intlen (logical:ash-4 n) (+ 4 tot)))))))
- (lambda (n) (intlen n 0))))
-;@
-(define logcount
- (letrec ((logcnt (lambda (n tot)
- (if (zero? n)
- tot
- (logcnt (quotient n 16)
- (+ (vector-ref
- '#(0 1 1 2 1 2 2 3 1 2 2 3 2 3 3 4)
- (modulo n 16))
- tot))))))
- (lambda (n)
- (cond ((negative? n) (logcnt (lognot n) 0))
- ((positive? n) (logcnt n 0))
- (else 0)))))
-;@
-(define (log2-binary-factors n)
- (+ -1 (integer-length (logand n (- n)))))
-
-(define (bit-reverse k n)
- (do ((m (if (negative? n) (lognot n) n) (arithmetic-shift m -1))
- (k (+ -1 k) (+ -1 k))
- (rvs 0 (logior (arithmetic-shift rvs 1) (logand 1 m))))
- ((negative? k) (if (negative? n) (lognot rvs) rvs))))
-;@
-(define (reverse-bit-field n start end)
- (define width (- end start))
- (let ((mask (lognot (ash -1 width))))
- (define zn (logand mask (arithmetic-shift n (- start))))
- (logior (arithmetic-shift (bit-reverse width zn) start)
- (logand (lognot (ash mask start)) n))))
-;@
-(define (integer->list k . len)
- (if (null? len)
- (do ((k k (arithmetic-shift k -1))
- (lst '() (cons (odd? k) lst)))
- ((<= k 0) lst))
- (do ((idx (+ -1 (car len)) (+ -1 idx))
- (k k (arithmetic-shift k -1))
- (lst '() (cons (odd? k) lst)))
- ((negative? idx) lst))))
-;@
-(define (list->integer bools)
- (do ((bs bools (cdr bs))
- (acc 0 (+ acc acc (if (car bs) 1 0))))
- ((null? bs) acc)))
-(define (booleans->integer . bools)
- (list->integer bools))
-
-;;;;@ SRFI-60 aliases
-(define ash arithmetic-shift)
-(define bitwise-ior logior)
-(define bitwise-xor logxor)
-(define bitwise-and logand)
-(define bitwise-not lognot)
-(define bit-count logcount)
-(define bit-set? logbit?)
-(define any-bits-set? logtest)
-(define first-set-bit log2-binary-factors)
-(define bitwise-merge bitwise-if)
-
-;;; Legacy
-;;(define (logical:rotate k count len) (rotate-bit-field k count 0 len))
-;;(define (logical:ones deg) (lognot (ash -1 deg)))
-;;(define integer-expt expt) ; legacy name
-; 54-BIT INTEGER IMPLEMENTATION OF THE "MRG32K3A"-GENERATOR
-; =========================================================
-;
-; Sebastian.Egner@philips.com, Mar-2002.
-;
-; This file is an implementation of Pierre L'Ecuyer's MRG32k3a
-; pseudo random number generator. Please refer to 'mrg32k3a.scm'
-; for more information.
-;
-; compliance:
-; Scheme R5RS with integers covering at least {-2^53..2^53-1}.
-;
-; history of this file:
-; SE, 18-Mar-2002: initial version
-; SE, 22-Mar-2002: comments adjusted, range added
-; SE, 25-Mar-2002: pack/unpack just return their argument
-
-; the actual generator
-
-(define (mrg32k3a-random-m1 state)
- (let ((x11 (vector-ref state 0))
- (x12 (vector-ref state 1))
- (x13 (vector-ref state 2))
- (x21 (vector-ref state 3))
- (x22 (vector-ref state 4))
- (x23 (vector-ref state 5)))
- (let ((x10 (modulo (- (* 1403580 x12) (* 810728 x13)) 4294967087))
- (x20 (modulo (- (* 527612 x21) (* 1370589 x23)) 4294944443)))
- (vector-set! state 0 x10)
- (vector-set! state 1 x11)
- (vector-set! state 2 x12)
- (vector-set! state 3 x20)
- (vector-set! state 4 x21)
- (vector-set! state 5 x22)
- (modulo (- x10 x20) 4294967087))))
-
-; interface to the generic parts of the generator
-
-(define (mrg32k3a-pack-state unpacked-state)
- unpacked-state)
-
-(define (mrg32k3a-unpack-state state)
- state)
-
-(define (mrg32k3a-random-range) ; m1
- 4294967087)
-
-(define (mrg32k3a-random-integer state range) ; rejection method
- (let* ((q (quotient 4294967087 range))
- (qn (* q range)))
- (do ((x (mrg32k3a-random-m1 state) (mrg32k3a-random-m1 state)))
- ((< x qn) (quotient x q)))))
-
-(define (mrg32k3a-random-real state) ; normalization is 1/(m1+1)
- (* 0.0000000002328306549295728 (+ 1.0 (mrg32k3a-random-m1 state))))
-
-;;; Copyright (C) 2004 Taylor Campbell. All rights reserved.
-
-;;; Made an R7RS library by Taylan Ulrich Bayırlı/Kammer, Copyright (C) 2014.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-(define-library (srfi 61)
- (export cond)
- (import (except (scheme base) cond))
- (begin
-
- (define-syntax cond
- (syntax-rules (=> else)
-
- ((cond (else else1 else2 ...))
- ;; The (if #t (begin ...)) wrapper ensures that there may be no
- ;; internal definitions in the body of the clause. R5RS mandates
- ;; this in text (by referring to each subform of the clauses as
- ;; <expression>) but not in its reference implementation of `cond',
- ;; which just expands to (begin ...) with no (if #t ...) wrapper.
- (if #t (begin else1 else2 ...)))
-
- ((cond (test => receiver) more-clause ...)
- (let ((t test))
- (cond/maybe-more t
- (receiver t)
- more-clause ...)))
-
- ((cond (generator guard => receiver) more-clause ...)
- (call-with-values (lambda () generator)
- (lambda t
- (cond/maybe-more (apply guard t)
- (apply receiver t)
- more-clause ...))))
-
- ((cond (test) more-clause ...)
- (let ((t test))
- (cond/maybe-more t t more-clause ...)))
-
- ((cond (test body1 body2 ...) more-clause ...)
- (cond/maybe-more test
- (begin body1 body2 ...)
- more-clause ...))))
-
- (define-syntax cond/maybe-more
- (syntax-rules ()
- ((cond/maybe-more test consequent)
- (if test
- consequent))
- ((cond/maybe-more test consequent clause ...)
- (if test
- consequent
- (cond clause ...)))))
-
- ))
-; GENERIC PART OF MRG32k3a-GENERATOR FOR SRFI-27
-; ==============================================
-;
-; Sebastian.Egner@philips.com, 2002.
-;
-; This is the generic R5RS-part of the implementation of the MRG32k3a
-; generator to be used in SRFI-27. It is based on a separate implementation
-; of the core generator (presumably in native code) and on code to
-; provide essential functionality not available in R5RS (see below).
-;
-; compliance:
-; Scheme R5RS with integer covering at least {-2^53..2^53-1}.
-; In addition,
-; SRFI-23: error
-;
-; history of this file:
-; SE, 22-Mar-2002: refactored from earlier versions
-; SE, 25-Mar-2002: pack/unpack need not allocate
-; SE, 27-Mar-2002: changed interface to core generator
-; SE, 10-Apr-2002: updated spec of mrg32k3a-random-integer
-
-; Generator
-; =========
-;
-; Pierre L'Ecuyer's MRG32k3a generator is a Combined Multiple Recursive
-; Generator. It produces the sequence {(x[1,n] - x[2,n]) mod m1 : n}
-; defined by the two recursive generators
-;
-; x[1,n] = ( a12 x[1,n-2] + a13 x[1,n-3]) mod m1,
-; x[2,n] = (a21 x[2,n-1] + a23 x[2,n-3]) mod m2,
-;
-; where the constants are
-; m1 = 4294967087 = 2^32 - 209 modulus of 1st component
-; m2 = 4294944443 = 2^32 - 22853 modulus of 2nd component
-; a12 = 1403580 recursion coefficients
-; a13 = -810728
-; a21 = 527612
-; a23 = -1370589
-;
-; The generator passes all tests of G. Marsaglia's Diehard testsuite.
-; Its period is (m1^3 - 1)(m2^3 - 1)/2 which is nearly 2^191.
-; L'Ecuyer reports: "This generator is well-behaved in all dimensions
-; up to at least 45: ..." [with respect to the spectral test, SE].
-;
-; The period is maximal for all values of the seed as long as the
-; state of both recursive generators is not entirely zero.
-;
-; As the successor state is a linear combination of previous
-; states, it is possible to advance the generator by more than one
-; iteration by applying a linear transformation. The following
-; publication provides detailed information on how to do that:
-;
-; [1] P. L'Ecuyer, R. Simard, E. J. Chen, W. D. Kelton:
-; An Object-Oriented Random-Number Package With Many Long
-; Streams and Substreams. 2001.
-; To appear in Operations Research.
-;
-; Arithmetics
-; ===========
-;
-; The MRG32k3a generator produces values in {0..2^32-209-1}. All
-; subexpressions of the actual generator fit into {-2^53..2^53-1}.
-; The code below assumes that Scheme's "integer" covers this range.
-; In addition, it is assumed that floating point literals can be
-; read and there is some arithmetics with inexact numbers.
-;
-; However, for advancing the state of the generator by more than
-; one step at a time, the full range {0..2^32-209-1} is needed.
-
-
-; Required: Backbone Generator
-; ============================
-;
-; At this point in the code, the following procedures are assumed
-; to be defined to execute the core generator:
-;
-; (mrg32k3a-pack-state unpacked-state) -> packed-state
-; (mrg32k3a-unpack-state packed-state) -> unpacked-state
-; pack/unpack a state of the generator. The core generator works
-; on packed states, passed as an explicit argument, only. This
-; allows native code implementations to store their state in a
-; suitable form. Unpacked states are #(x10 x11 x12 x20 x21 x22)
-; with integer x_ij. Pack/unpack need not allocate new objects
-; in case packed and unpacked states are identical.
-;
-; (mrg32k3a-random-range) -> m-max
-; (mrg32k3a-random-integer packed-state range) -> x in {0..range-1}
-; advance the state of the generator and return the next random
-; range-limited integer.
-; Note that the state is not necessarily advanced by just one
-; step because we use the rejection method to avoid any problems
-; with distribution anomalies.
-; The range argument must be an exact integer in {1..m-max}.
-; It can be assumed that range is a fixnum if the Scheme system
-; has such a number representation.
-;
-; (mrg32k3a-random-real packed-state) -> x in (0,1)
-; advance the state of the generator and return the next random
-; real number between zero and one (both excluded). The type of
-; the result should be a flonum if possible.
-
-; Required: Record Data Type
-; ==========================
-;
-; At this point in the code, the following procedures are assumed
-; to be defined to create and access a new record data type:
-;
-; (\:random-source-make a0 a1 a2 a3 a4 a5) -> s
-; constructs a new random source object s consisting of the
-; objects a0 .. a5 in this order.
-;
-; (\:random-source? obj) -> bool
-; tests if a Scheme object is a :random-source.
-;
-; (\:random-source-state-ref s) -> a0
-; (\:random-source-state-set! s) -> a1
-; (\:random-source-randomize! s) -> a2
-; (\:random-source-pseudo-randomize! s) -> a3
-; (\:random-source-make-integers s) -> a4
-; (\:random-source-make-reals s) -> a5
-; retrieve the values in the fields of the object s.
-
-; Required: Current Time as an Integer
-; ====================================
-;
-; At this point in the code, the following procedure is assumed
-; to be defined to obtain a value that is likely to be different
-; for each invokation of the Scheme system:
-;
-; (\:random-source-current-time) -> x
-; an integer that depends on the system clock. It is desired
-; that the integer changes as fast as possible.
-
-
-; Accessing the State
-; ===================
-
-(define (mrg32k3a-state-ref packed-state)
- (cons 'lecuyer-mrg32k3a
- (vector->list (mrg32k3a-unpack-state packed-state))))
-
-(define (mrg32k3a-state-set external-state)
-
- (define (check-value x m)
- (if (and (integer? x)
- (exact? x)
- (<= 0 x (- m 1)))
- #t
- (error "illegal value" x)))
-
- (if (and (list? external-state)
- (= (length external-state) 7)
- (eq? (car external-state) 'lecuyer-mrg32k3a))
- (let ((s (cdr external-state)))
- (check-value (list-ref s 0) mrg32k3a-m1)
- (check-value (list-ref s 1) mrg32k3a-m1)
- (check-value (list-ref s 2) mrg32k3a-m1)
- (check-value (list-ref s 3) mrg32k3a-m2)
- (check-value (list-ref s 4) mrg32k3a-m2)
- (check-value (list-ref s 5) mrg32k3a-m2)
- (if (or (zero? (+ (list-ref s 0) (list-ref s 1) (list-ref s 2)))
- (zero? (+ (list-ref s 3) (list-ref s 4) (list-ref s 5))))
- (error "illegal degenerate state" external-state))
- (mrg32k3a-pack-state (list->vector s)))
- (error "malformed state" external-state)))
-
-
-; Pseudo-Randomization
-; ====================
-;
-; Reference [1] above shows how to obtain many long streams and
-; substream from the backbone generator.
-;
-; The idea is that the generator is a linear operation on the state.
-; Hence, we can express this operation as a 3x3-matrix acting on the
-; three most recent states. Raising the matrix to the k-th power, we
-; obtain the operation to advance the state by k steps at once. The
-; virtual streams and substreams are now simply parts of the entire
-; periodic sequence (which has period around 2^191).
-;
-; For the implementation it is necessary to compute with matrices in
-; the ring (Z/(m1*m1)*Z)^(3x3). By the Chinese-Remainder Theorem, this
-; is isomorphic to ((Z/m1*Z) x (Z/m2*Z))^(3x3). We represent such a pair
-; of matrices
-; [ [[x00 x01 x02],
-; [x10 x11 x12],
-; [x20 x21 x22]], mod m1
-; [[y00 y01 y02],
-; [y10 y11 y12],
-; [y20 y21 y22]] mod m2]
-; as a vector of length 18 of the integers as writen above:
-; #(x00 x01 x02 x10 x11 x12 x20 x21 x22
-; y00 y01 y02 y10 y11 y12 y20 y21 y22)
-;
-; As the implementation should only use the range {-2^53..2^53-1}, the
-; fundamental operation (x*y) mod m, where x, y, m are nearly 2^32,
-; is computed by breaking up x and y as x = x1*w + x0 and y = y1*w + y0
-; where w = 2^16. In this case, all operations fit the range because
-; w^2 mod m is a small number. If proper multiprecision integers are
-; available this is not necessary, but pseudo-randomize! is an expected
-; to be called only occasionally so we do not provide this implementation.
-
-(define mrg32k3a-m1 4294967087) ; modulus of component 1
-(define mrg32k3a-m2 4294944443) ; modulus of component 2
-
-(define mrg32k3a-initial-state ; 0 3 6 9 12 15 of A^16, see below
- '#( 1062452522
- 2961816100
- 342112271
- 2854655037
- 3321940838
- 3542344109))
-
-(define mrg32k3a-generators #f) ; computed when needed
-
-(define (mrg32k3a-pseudo-randomize-state i j)
-
- (define (product A B) ; A*B in ((Z/m1*Z) x (Z/m2*Z))^(3x3)
-
- (define w 65536) ; wordsize to split {0..2^32-1}
- (define w-sqr1 209) ; w^2 mod m1
- (define w-sqr2 22853) ; w^2 mod m2
-
- (define (lc i0 i1 i2 j0 j1 j2 m w-sqr) ; linear combination
- (let ((a0h (quotient (vector-ref A i0) w))
- (a0l (modulo (vector-ref A i0) w))
- (a1h (quotient (vector-ref A i1) w))
- (a1l (modulo (vector-ref A i1) w))
- (a2h (quotient (vector-ref A i2) w))
- (a2l (modulo (vector-ref A i2) w))
- (b0h (quotient (vector-ref B j0) w))
- (b0l (modulo (vector-ref B j0) w))
- (b1h (quotient (vector-ref B j1) w))
- (b1l (modulo (vector-ref B j1) w))
- (b2h (quotient (vector-ref B j2) w))
- (b2l (modulo (vector-ref B j2) w)))
- (modulo
- (+ (* (+ (* a0h b0h)
- (* a1h b1h)
- (* a2h b2h))
- w-sqr)
- (* (+ (* a0h b0l)
- (* a0l b0h)
- (* a1h b1l)
- (* a1l b1h)
- (* a2h b2l)
- (* a2l b2h))
- w)
- (* a0l b0l)
- (* a1l b1l)
- (* a2l b2l))
- m)))
-
- (vector
- (lc 0 1 2 0 3 6 mrg32k3a-m1 w-sqr1) ; (A*B)_00 mod m1
- (lc 0 1 2 1 4 7 mrg32k3a-m1 w-sqr1) ; (A*B)_01
- (lc 0 1 2 2 5 8 mrg32k3a-m1 w-sqr1)
- (lc 3 4 5 0 3 6 mrg32k3a-m1 w-sqr1) ; (A*B)_10
- (lc 3 4 5 1 4 7 mrg32k3a-m1 w-sqr1)
- (lc 3 4 5 2 5 8 mrg32k3a-m1 w-sqr1)
- (lc 6 7 8 0 3 6 mrg32k3a-m1 w-sqr1)
- (lc 6 7 8 1 4 7 mrg32k3a-m1 w-sqr1)
- (lc 6 7 8 2 5 8 mrg32k3a-m1 w-sqr1)
- (lc 9 10 11 9 12 15 mrg32k3a-m2 w-sqr2) ; (A*B)_00 mod m2
- (lc 9 10 11 10 13 16 mrg32k3a-m2 w-sqr2)
- (lc 9 10 11 11 14 17 mrg32k3a-m2 w-sqr2)
- (lc 12 13 14 9 12 15 mrg32k3a-m2 w-sqr2)
- (lc 12 13 14 10 13 16 mrg32k3a-m2 w-sqr2)
- (lc 12 13 14 11 14 17 mrg32k3a-m2 w-sqr2)
- (lc 15 16 17 9 12 15 mrg32k3a-m2 w-sqr2)
- (lc 15 16 17 10 13 16 mrg32k3a-m2 w-sqr2)
- (lc 15 16 17 11 14 17 mrg32k3a-m2 w-sqr2)))
-
- (define (power A e) ; A^e
- (cond
- ((zero? e)
- '#(1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1))
- ((= e 1)
- A)
- ((even? e)
- (power (product A A) (quotient e 2)))
- (else
- (product (power A (- e 1)) A))))
-
- (define (power-power A b) ; A^(2^b)
- (if (zero? b)
- A
- (power-power (product A A) (- b 1))))
-
- (define A ; the MRG32k3a recursion
- '#( 0 1403580 4294156359
- 1 0 0
- 0 1 0
- 527612 0 4293573854
- 1 0 0
- 0 1 0))
-
- ; check arguments
- (if (not (and (integer? i)
- (exact? i)
- (integer? j)
- (exact? j)))
- (error "i j must be exact integer" i j))
-
- ; precompute A^(2^127) and A^(2^76) only once
-
- (if (not mrg32k3a-generators)
- (set! mrg32k3a-generators
- (list (power-power A 127)
- (power-power A 76)
- (power A 16))))
-
- ; compute M = A^(16 + i*2^127 + j*2^76)
- (let ((M (product
- (list-ref mrg32k3a-generators 2)
- (product
- (power (list-ref mrg32k3a-generators 0)
- (modulo i (expt 2 28)))
- (power (list-ref mrg32k3a-generators 1)
- (modulo j (expt 2 28)))))))
- (mrg32k3a-pack-state
- (vector
- (vector-ref M 0)
- (vector-ref M 3)
- (vector-ref M 6)
- (vector-ref M 9)
- (vector-ref M 12)
- (vector-ref M 15)))))
-
-; True Randomization
-; ==================
-;
-; The value obtained from the system time is feed into a very
-; simple pseudo random number generator. This in turn is used
-; to obtain numbers to randomize the state of the MRG32k3a
-; generator, avoiding period degeneration.
-
-(define (mrg32k3a-randomize-state state)
- ;; G. Marsaglia's simple 16-bit generator with carry
- (let* ((m 65536)
- (x (modulo (random-source-current-time) m)))
- (define (random-m)
- (let ((y (modulo x m)))
- (set! x (+ (* 30903 y) (quotient x m)))
- y))
- (define (random n) ; m < n < m^2
- (modulo (+ (* (random-m) m) (random-m)) n))
-
- ; modify the state
- (let ((m1 mrg32k3a-m1)
- (m2 mrg32k3a-m2)
- (s (mrg32k3a-unpack-state state)))
- (mrg32k3a-pack-state
- (vector
- (+ 1 (modulo (+ (vector-ref s 0) (random (- m1 1))) (- m1 1)))
- (modulo (+ (vector-ref s 1) (random m1)) m1)
- (modulo (+ (vector-ref s 2) (random m1)) m1)
- (+ 1 (modulo (+ (vector-ref s 3) (random (- m2 1))) (- m2 1)))
- (modulo (+ (vector-ref s 4) (random m2)) m2)
- (modulo (+ (vector-ref s 5) (random m2)) m2))))))
-
-
-; Large Integers
-; ==============
-;
-; To produce large integer random deviates, for n > m-max, we first
-; construct large random numbers in the range {0..m-max^k-1} for some
-; k such that m-max^k >= n and then use the rejection method to choose
-; uniformly from the range {0..n-1}.
-
-(define mrg32k3a-m-max
- (mrg32k3a-random-range))
-
-(define (mrg32k3a-random-power state k) ; n = m-max^k, k >= 1
- (if (= k 1)
- (mrg32k3a-random-integer state mrg32k3a-m-max)
- (+ (* (mrg32k3a-random-power state (- k 1)) mrg32k3a-m-max)
- (mrg32k3a-random-integer state mrg32k3a-m-max))))
-
-(define (mrg32k3a-random-large state n) ; n > m-max
- (do ((k 2 (+ k 1))
- (mk (* mrg32k3a-m-max mrg32k3a-m-max) (* mk mrg32k3a-m-max)))
- ((>= mk n)
- (let* ((mk-by-n (quotient mk n))
- (a (* mk-by-n n)))
- (do ((x (mrg32k3a-random-power state k)
- (mrg32k3a-random-power state k)))
- ((< x a) (quotient x mk-by-n)))))))
-
-
-; Multiple Precision Reals
-; ========================
-;
-; To produce multiple precision reals we produce a large integer value
-; and convert it into a real value. This value is then normalized.
-; The precision goal is unit <= 1/(m^k + 1), or 1/unit - 1 <= m^k.
-; If you know more about the floating point number types of the
-; Scheme system, this can be improved.
-
-(define (mrg32k3a-random-real-mp state unit)
- (do ((k 1 (+ k 1))
- (u (- (/ 1 unit) 1) (/ u mrg32k3a-m1)))
- ((<= u 1)
- (/ (exact->inexact (+ (mrg32k3a-random-power state k) 1))
- (exact->inexact (+ (expt mrg32k3a-m-max k) 1))))))
-
-
-; Provide the Interface as Specified in the SRFI
-; ==============================================
-;
-; An object of type random-source is a record containing the procedures
-; as components. The actual state of the generator is stored in the
-; binding-time environment of make-random-source.
-
-(define (make-random-source)
- (let ((state (mrg32k3a-pack-state ; make a new copy
- (list->vector (vector->list mrg32k3a-initial-state)))))
- (\:random-source-make
- (lambda ()
- (mrg32k3a-state-ref state))
- (lambda (new-state)
- (set! state (mrg32k3a-state-set new-state)))
- (lambda ()
- (set! state (mrg32k3a-randomize-state state)))
- (lambda (i j)
- (set! state (mrg32k3a-pseudo-randomize-state i j)))
- (lambda ()
- (lambda (n)
- (cond
- ((not (and (integer? n) (exact? n) (positive? n)))
- (error "range must be exact positive integer" n))
- ((<= n mrg32k3a-m-max)
- (mrg32k3a-random-integer state n))
- (else
- (mrg32k3a-random-large state n)))))
- (lambda args
- (cond
- ((null? args)
- (lambda ()
- (mrg32k3a-random-real state)))
- ((null? (cdr args))
- (let ((unit (car args)))
- (cond
- ((not (and (real? unit) (< 0 unit 1)))
- (error "unit must be real in (0,1)" unit))
- ((<= (- (/ 1 unit) 1) mrg32k3a-m1)
- (lambda ()
- (mrg32k3a-random-real state)))
- (else
- (lambda ()
- (mrg32k3a-random-real-mp state unit))))))
- (else
- (error "illegal arguments" args)))))))
-
-(define random-source?
- \:random-source?)
-
-(define (random-source-state-ref s)
- ((\:random-source-state-ref s)))
-
-(define (random-source-state-set! s state)
- ((\:random-source-state-set! s) state))
-
-(define (random-source-randomize! s)
- ((\:random-source-randomize! s)))
-
-(define (random-source-pseudo-randomize! s i j)
- ((\:random-source-pseudo-randomize! s) i j))
-
-; ---
-
-(define (random-source-make-integers s)
- ((\:random-source-make-integers s)))
-
-(define (random-source-make-reals s . unit)
- (apply (\:random-source-make-reals s) unit))
-
-; ---
-
-(define default-random-source
- (make-random-source))
-
-(define random-integer
- (random-source-make-integers default-random-source))
-
-(define random-real
- (random-source-make-reals default-random-source))
-(define-library (srfi 27)
- (export
- random-integer
- random-real
- default-random-source
- make-random-source
- random-source?
- random-source-state-ref
- random-source-state-set!
- random-source-randomize!
- random-source-pseudo-randomize!
- random-source-make-integers
- random-source-make-reals
- )
- (import
- (scheme base)
- (scheme time))
- (begin
-
- (define-record-type \:random-source
- (\\:random-source-make
- state-ref
- state-set!
- randomize!
- pseudo-randomize!
- make-integers
- make-reals)
- \:random-source?
- (state-ref \:random-source-state-ref)
- (state-set! \:random-source-state-set!)
- (randomize! \:random-source-randomize!)
- (pseudo-randomize! \:random-source-pseudo-randomize!)
- (make-integers \:random-source-make-integers)
- (make-reals \:random-source-make-reals))
-
- (define (\\:random-source-current-time)
- (current-jiffy))
-
- (define exact->inexact inexact)
-
- )
- (include "27.mrg32k3a-a.upstream.scm")
- (include "27.mrg32k3a.upstream.scm"))
-;; Copyright (C) Scott G. Miller (2002). All Rights Reserved.
-
-;; Made an R7RS library by Taylan Ulrich Bayırlı/Kammer, Copyright (C) 2014.
-
-;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;; of this software and associated documentation files (the "Software"), to deal
-;; in the Software without restriction, including without limitation the rights
-;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-;; copies of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-
-;; The above copyright notice and this permission notice shall be included in
-;; all copies or substantial portions of the Software.
-
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-(define-library (srfi 28)
- (export format)
- (import
- (scheme base)
- (scheme write))
- (begin
- (define format
- (lambda (format-string . objects)
- (let ((buffer (open-output-string)))
- (let loop ((format-list (string->list format-string))
- (objects objects))
- (cond ((null? format-list) (get-output-string buffer))
- ((char=? (car format-list) #\~)
- (if (null? (cdr format-list))
- (error 'format "Incomplete escape sequence")
- (case (cadr format-list)
- ((#\a)
- (if (null? objects)
- (error 'format "No value for escape sequence")
- (begin
- (display (car objects) buffer)
- (loop (cddr format-list) (cdr objects)))))
- ((#\s)
- (if (null? objects)
- (error 'format "No value for escape sequence")
- (begin
- (write (car objects) buffer)
- (loop (cddr format-list) (cdr objects)))))
- ((#\%)
- (newline buffer)
- (loop (cddr format-list) objects))
- ((#\~)
- (write-char #\~ buffer)
- (loop (cddr format-list) objects))
- (else
- (error 'format "Unrecognized escape sequence")))))
- (else (write-char (car format-list) buffer)
- (loop (cdr format-list) objects)))))))))
-;; Copyright (C) Taylan Ulrich Bayırlı/Kammer (2015). All Rights Reserved.
-
-;; Permission is hereby granted, free of charge, to any person obtaining
-;; a copy of this software and associated documentation files (the
-;; "Software"), to deal in the Software without restriction, including
-;; without limitation the rights to use, copy, modify, merge, publish,
-;; distribute, sublicense, and/or sell copies of the Software, and to
-;; permit persons to whom the Software is furnished to do so, subject to
-;; the following conditions:
-
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-;; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-(define-library (srfi 31)
- (export rec)
- (import (scheme base))
- (begin
- (define-syntax rec
- (syntax-rules ()
- ((rec (name . args) body ...)
- (letrec ((name (lambda args body ...)))
- name))
- ((rec name expr)
- (letrec ((name expr))
- name))))))
-;; Copyright (C) Richard Kelsey, Michael Sperber (2002). All Rights Reserved.
-;;
-;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;; of this software and associated documentation files (the "Software"), to deal
-;; in the Software without restriction, including without limitation the rights
-;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-;; copies of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-;;
-;; The above copyright notice and this permission notice shall be included in
-;; all copies or substantial portions of the Software.
-;;
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-(define-record-type <condition-type>
- (really-make-condition-type name supertype fields all-fields)
- condition-type?
- (name condition-type-name)
- (supertype condition-type-supertype)
- (fields condition-type-fields)
- (all-fields condition-type-all-fields))
-
-(define (make-condition-type name supertype fields)
- (if (not (symbol? name))
- (error "make-condition-type: name is not a symbol"
- name))
- (if (not (condition-type? supertype))
- (error "make-condition-type: supertype is not a condition type"
- supertype))
- (if (not
- (null? (lset-intersection eq?
- (condition-type-all-fields supertype)
- fields)))
- (error "duplicate field name" ))
- (really-make-condition-type name
- supertype
- fields
- (append (condition-type-all-fields supertype)
- fields)))
-
-(define-syntax define-condition-type
- (syntax-rules ()
- ((define-condition-type ?name ?supertype ?predicate
- (?field1 ?accessor1) ...)
- (begin
- (define ?name
- (make-condition-type '?name
- ?supertype
- '(?field1 ...)))
- (define (?predicate thing)
- (and (condition? thing)
- (condition-has-type? thing ?name)))
- (define (?accessor1 condition)
- (condition-ref (extract-condition condition ?name)
- '?field1))
- ...))))
-
-(define (condition-subtype? subtype supertype)
- (let recur ((subtype subtype))
- (cond ((not subtype) #f)
- ((eq? subtype supertype) #t)
- (else
- (recur (condition-type-supertype subtype))))))
-
-(define (condition-type-field-supertype condition-type field)
- (let loop ((condition-type condition-type))
- (cond ((not condition-type) #f)
- ((memq field (condition-type-fields condition-type))
- condition-type)
- (else
- (loop (condition-type-supertype condition-type))))))
-
-; The type-field-alist is of the form
-; ((<type> (<field-name> . <value>) ...) ...)
-(define-record-type <condition>
- (really-make-condition type-field-alist)
- condition?
- (type-field-alist condition-type-field-alist))
-
-(define (make-condition type . field-plist)
- (let ((alist (let label ((plist field-plist))
- (if (null? plist)
- '()
- (cons (cons (car plist)
- (cadr plist))
- (label (cddr plist)))))))
- (if (not (lset= eq?
- (condition-type-all-fields type)
- (map car alist)))
- (error "condition fields don't match condition type"))
- (really-make-condition (list (cons type alist)))))
-
-(define (condition-has-type? condition type)
- (any (lambda (has-type)
- (condition-subtype? has-type type))
- (condition-types condition)))
-
-(define (condition-ref condition field)
- (type-field-alist-ref (condition-type-field-alist condition)
- field))
-
-(define (type-field-alist-ref type-field-alist field)
- (let loop ((type-field-alist type-field-alist))
- (cond ((null? type-field-alist)
- (error "type-field-alist-ref: field not found"
- type-field-alist field))
- ((assq field (cdr (car type-field-alist)))
- => cdr)
- (else
- (loop (cdr type-field-alist))))))
-
-(define (make-compound-condition condition-1 . conditions)
- (really-make-condition
- (apply append (map condition-type-field-alist
- (cons condition-1 conditions)))))
-
-(define (extract-condition condition type)
- (let ((entry (find (lambda (entry)
- (condition-subtype? (car entry) type))
- (condition-type-field-alist condition))))
- (if (not entry)
- (error "extract-condition: invalid condition type"
- condition type))
- (really-make-condition
- (list (cons type
- (map (lambda (field)
- (assq field (cdr entry)))
- (condition-type-all-fields type)))))))
-
-(define-syntax condition
- (syntax-rules ()
- ((condition (?type1 (?field1 ?value1) ...) ...)
- (type-field-alist->condition
- (list
- (cons ?type1
- (list (cons '?field1 ?value1) ...))
- ...)))))
-
-(define (type-field-alist->condition type-field-alist)
- (really-make-condition
- (map (lambda (entry)
- (cons (car entry)
- (map (lambda (field)
- (or (assq field (cdr entry))
- (cons field
- (type-field-alist-ref type-field-alist field))))
- (condition-type-all-fields (car entry)))))
- type-field-alist)))
-
-(define (condition-types condition)
- (map car (condition-type-field-alist condition)))
-
-(define (check-condition-type-field-alist the-type-field-alist)
- (let loop ((type-field-alist the-type-field-alist))
- (if (not (null? type-field-alist))
- (let* ((entry (car type-field-alist))
- (type (car entry))
- (field-alist (cdr entry))
- (fields (map car field-alist))
- (all-fields (condition-type-all-fields type)))
- (for-each (lambda (missing-field)
- (let ((supertype
- (condition-type-field-supertype type missing-field)))
- (if (not
- (any (lambda (entry)
- (let ((type (car entry)))
- (condition-subtype? type supertype)))
- the-type-field-alist))
- (error "missing field in condition construction"
- type
- missing-field))))
- (lset-difference eq? all-fields fields))
- (loop (cdr type-field-alist))))))
-
-(define &condition (really-make-condition-type '&condition
- #f
- '()
- '()))
-
-(define-condition-type &message &condition
- message-condition?
- (message condition-message))
-
-(define-condition-type &serious &condition
- serious-condition?)
-
-(define-condition-type &error &serious
- error?)
-(define-library (srfi 35)
- (export
- make-condition-type
- condition-type?
- make-condition
- condition?
- condition-has-type?
- condition-ref
- make-compound-condition
- extract-condition
- define-condition-type
- condition
- &condition
- &message
- &serious
- &error
- )
- (import
- (scheme base)
- (srfi 1))
- (include "35.body.scm"))
-(define-library (srfi 64)
- (import
- (srfi 64 test-runner)
- (srfi 64 test-runner-simple)
- (srfi 64 execution))
- (export
- ;; Execution
- test-begin test-end test-group test-group-with-cleanup
-
- test-skip test-expect-fail
- test-match-name test-match-nth
- test-match-all test-match-any
-
- test-assert test-eqv test-eq test-equal test-approximate
- test-error test-read-eval-string
-
- test-apply test-with-runner
-
- test-exit
-
- ;; Test runner
- test-runner-null test-runner? test-runner-reset
-
- test-result-alist test-result-alist!
- test-result-ref test-result-set!
- test-result-remove test-result-clear
-
- test-runner-pass-count
- test-runner-fail-count
- test-runner-xpass-count
- test-runner-xfail-count
- test-runner-skip-count
-
- test-runner-test-name
-
- test-runner-group-path
- test-runner-group-stack
-
- test-runner-aux-value test-runner-aux-value!
-
- test-result-kind test-passed?
-
- test-runner-on-test-begin test-runner-on-test-begin!
- test-runner-on-test-end test-runner-on-test-end!
- test-runner-on-group-begin test-runner-on-group-begin!
- test-runner-on-group-end test-runner-on-group-end!
- test-runner-on-final test-runner-on-final!
- test-runner-on-bad-count test-runner-on-bad-count!
- test-runner-on-bad-end-name test-runner-on-bad-end-name!
-
- test-runner-factory test-runner-create
- test-runner-current test-runner-get
-
- ;; Simple test runner
- test-runner-simple
- test-on-group-begin-simple test-on-group-end-simple test-on-final-simple
- test-on-test-begin-simple test-on-test-end-simple
- test-on-bad-count-simple test-on-bad-end-name-simple
- ))
-;; Copyright (C) Richard Kelsey, Michael Sperber (2002). All Rights Reserved.
-;;
-;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;; of this software and associated documentation files (the "Software"), to deal
-;; in the Software without restriction, including without limitation the rights
-;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-;; copies of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-;;
-;; The above copyright notice and this permission notice shall be included in
-;; all copies or substantial portions of the Software.
-;;
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-(define-record-type condition-type
- (really-make-condition-type name supertype fields all-fields)
- condition-type?
- (name condition-type-name)
- (supertype condition-type-supertype)
- (fields condition-type-fields)
- (all-fields condition-type-all-fields))
-
-(define (make-condition-type name supertype fields)
- (if (not (symbol? name))
- (error "make-condition-type: name is not a symbol"
- name))
- (if (not (condition-type? supertype))
- (error "make-condition-type: supertype is not a condition type"
- supertype))
- (if (not
- (null? (lset-intersection eq?
- (condition-type-all-fields supertype)
- fields)))
- (error "duplicate field name" ))
- (really-make-condition-type name
- supertype
- fields
- (append (condition-type-all-fields supertype)
- fields)))
-
-(define-syntax define-condition-type
- (syntax-rules ()
- ((define-condition-type ?name ?supertype ?predicate
- (?field1 ?accessor1) ...)
- (begin
- (define ?name
- (make-condition-type '?name
- ?supertype
- '(?field1 ...)))
- (define (?predicate thing)
- (and (condition? thing)
- (condition-has-type? thing ?name)))
- (define (?accessor1 condition)
- (condition-ref (extract-condition condition ?name)
- '?field1))
- ...))))
-
-(define (condition-subtype? subtype supertype)
- (let recur ((subtype subtype))
- (cond ((not subtype) #f)
- ((eq? subtype supertype) #t)
- (else
- (recur (condition-type-supertype subtype))))))
-
-(define (condition-type-field-supertype condition-type field)
- (let loop ((condition-type condition-type))
- (cond ((not condition-type) #f)
- ((memq field (condition-type-fields condition-type))
- condition-type)
- (else
- (loop (condition-type-supertype condition-type))))))
-
-; The type-field-alist is of the form
-; ((<type> (<field-name> . <value>) ...) ...)
-(define-record-type condition
- (really-make-condition type-field-alist)
- condition?
- (type-field-alist condition-type-field-alist))
-
-(define (make-condition type . field-plist)
- (let ((alist (let label ((plist field-plist))
- (if (null? plist)
- '()
- (cons (cons (car plist)
- (cadr plist))
- (label (cddr plist)))))))
- (if (not (lset= eq?
- (condition-type-all-fields type)
- (map car alist)))
- (error "condition fields don't match condition type"))
- (really-make-condition (list (cons type alist)))))
-
-(define (condition-has-type? condition type)
- (any (lambda (has-type)
- (condition-subtype? has-type type))
- (condition-types condition)))
-
-(define (condition-ref condition field)
- (type-field-alist-ref (condition-type-field-alist condition)
- field))
-
-(define (type-field-alist-ref type-field-alist field)
- (let loop ((type-field-alist type-field-alist))
- (cond ((null? type-field-alist)
- (error "type-field-alist-ref: field not found"
- type-field-alist field))
- ((assq field (cdr (car type-field-alist)))
- => cdr)
- (else
- (loop (cdr type-field-alist))))))
-
-(define (make-compound-condition condition-1 . conditions)
- (really-make-condition
- (apply append (map condition-type-field-alist
- (cons condition-1 conditions)))))
-
-(define (extract-condition condition type)
- (let ((entry (find (lambda (entry)
- (condition-subtype? (car entry) type))
- (condition-type-field-alist condition))))
- (if (not entry)
- (error "extract-condition: invalid condition type"
- condition type))
- (really-make-condition
- (list (cons type
- (map (lambda (field)
- (assq field (cdr entry)))
- (condition-type-all-fields type)))))))
-
-(define-syntax condition
- (syntax-rules ()
- ((condition (?type1 (?field1 ?value1) ...) ...)
- (type-field-alist->condition
- (list
- (cons ?type1
- (list (cons '?field1 ?value1) ...))
- ...)))))
-
-(define (type-field-alist->condition type-field-alist)
- (really-make-condition
- (map (lambda (entry)
- (cons (car entry)
- (map (lambda (field)
- (or (assq field (cdr entry))
- (cons field
- (type-field-alist-ref type-field-alist field))))
- (condition-type-all-fields (car entry)))))
- type-field-alist)))
-
-(define (condition-types condition)
- (map car (condition-type-field-alist condition)))
-
-(define (check-condition-type-field-alist the-type-field-alist)
- (let loop ((type-field-alist the-type-field-alist))
- (if (not (null? type-field-alist))
- (let* ((entry (car type-field-alist))
- (type (car entry))
- (field-alist (cdr entry))
- (fields (map car field-alist))
- (all-fields (condition-type-all-fields type)))
- (for-each (lambda (missing-field)
- (let ((supertype
- (condition-type-field-supertype type missing-field)))
- (if (not
- (any (lambda (entry)
- (let ((type (car entry)))
- (condition-subtype? type supertype)))
- the-type-field-alist))
- (error "missing field in condition construction"
- type
- missing-field))))
- (lset-difference eq? all-fields fields))
- (loop (cdr type-field-alist))))))
-
-(define &condition (really-make-condition-type '&condition
- #f
- '()
- '()))
-
-(define-condition-type &message &condition
- message-condition?
- (message condition-message))
-
-(define-condition-type &serious &condition
- serious-condition?)
-
-(define-condition-type &error &serious
- error?)
-;;; args-fold.scm - a program argument processor
-;;;
-;;; Copyright (c) 2002 Anthony Carrico
-;;; Copyright (c) 2014 Taylan Ulrich Bayırlı/Kammer
-;;;
-;;; All rights reserved.
-;;;
-;;; Redistribution and use in source and binary forms, with or without
-;;; modification, are permitted provided that the following conditions
-;;; are met:
-;;; 1. Redistributions of source code must retain the above copyright
-;;; notice, this list of conditions and the following disclaimer.
-;;; 2. Redistributions in binary form must reproduce the above copyright
-;;; notice, this list of conditions and the following disclaimer in the
-;;; documentation and/or other materials provided with the distribution.
-;;; 3. The name of the authors may not be used to endorse or promote products
-;;; derived from this software without specific prior written permission.
-;;;
-;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
-;;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-;;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-;;; IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-;;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-;;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-;;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-;;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-;;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-(define-record-type <option>
- (option names required-arg? optional-arg? processor)
- option?
- (names option-names)
- (required-arg? option-required-arg?)
- (optional-arg? option-optional-arg?)
- (processor option-processor))
-
-(define (args-fold args options unrecognized-option-proc operand-proc . seeds)
-
- (define (find-option name)
- ;; ISSUE: This is a brute force search. Could use a table.
- (find (lambda (option)
- (find (lambda (test-name)
- (equal? name test-name))
- (option-names option)))
- options))
-
- (define (scan-short-options index shorts args seeds)
- (if (= index (string-length shorts))
- (scan-args args seeds)
- (let* ((name (string-ref shorts index))
- (option (or (find-option name)
- (option (list name)
- #f
- #f
- unrecognized-option-proc))))
- (cond
- ((and (< (+ index 1) (string-length shorts))
- (or (option-required-arg? option)
- (option-optional-arg? option)))
- (let-values
- ((seeds (apply (option-processor option)
- option
- name
- (substring
- shorts
- (+ index 1)
- (string-length shorts))
- seeds)))
- (scan-args args seeds)))
- ((and (option-required-arg? option)
- (pair? args))
- (let-values
- ((seeds (apply (option-processor option)
- option
- name
- (car args)
- seeds)))
- (scan-args (cdr args) seeds)))
- (else
- (let-values
- ((seeds (apply (option-processor option)
- option
- name
- #f
- seeds)))
- (scan-short-options
- (+ index 1)
- shorts
- args
- seeds)))))))
-
- (define (scan-operands operands seeds)
- (if (null? operands)
- (apply values seeds)
- (let-values ((seeds (apply operand-proc
- (car operands)
- seeds)))
- (scan-operands (cdr operands) seeds))))
-
- (define (scan-args args seeds)
- (if (null? args)
- (apply values seeds)
- (let ((arg (car args))
- (args (cdr args)))
- ;; NOTE: This string matching code would be simpler
- ;; using a regular expression matcher.
- (cond
- ((string=? "--" arg)
- ;; End option scanning:
- (scan-operands args seeds))
- ((and (> (string-length arg) 4)
- (char=? #\- (string-ref arg 0))
- (char=? #\- (string-ref arg 1))
- (not (char=? #\= (string-ref arg 2)))
- (let loop ((index 3))
- (cond ((= index (string-length arg))
- #f)
- ((char=? #\= (string-ref arg index))
- index)
- (else
- (loop (+ 1 index))))))
- ;; Found long option with arg:
- => (lambda (=-index)
- (let*-values
- (((name)
- (substring arg 2 =-index))
- ((option-arg)
- (substring arg
- (+ =-index 1)
- (string-length arg)))
- ((option)
- (or (find-option name)
- (option (list name)
- #t
- #f
- unrecognized-option-proc)))
- (seeds
- (apply (option-processor option)
- option
- name
- option-arg
- seeds)))
- (scan-args args seeds))))
- ((and (> (string-length arg) 3)
- (char=? #\- (string-ref arg 0))
- (char=? #\- (string-ref arg 1)))
- ;; Found long option:
- (let* ((name (substring arg 2 (string-length arg)))
- (option (or (find-option name)
- (option
- (list name)
- #f
- #f
- unrecognized-option-proc))))
- (if (and (option-required-arg? option)
- (pair? args))
- (let-values
- ((seeds (apply (option-processor option)
- option
- name
- (car args)
- seeds)))
- (scan-args (cdr args) seeds))
- (let-values
- ((seeds (apply (option-processor option)
- option
- name
- #f
- seeds)))
- (scan-args args seeds)))))
- ((and (> (string-length arg) 1)
- (char=? #\- (string-ref arg 0)))
- ;; Found short options
- (let ((shorts (substring arg 1 (string-length arg))))
- (scan-short-options 0 shorts args seeds)))
- (else
- (let-values ((seeds (apply operand-proc arg seeds)))
- (scan-args args seeds)))))))
-
- (scan-args args seeds))
-(define-library (srfi 37)
- (export
- args-fold
- option
- option-names
- option-required-arg?
- option-optional-arg?
- option-processor
- )
- (import
- (scheme base)
- (srfi 1))
- (include "37.body.scm"))
-;;; Copyright (C) 2006 Chongkai Zhu. All Rights Reserved.
-
-;;; Made an R7RS library by Taylan Ulrich Bayırlı/Kammer, Copyright (C) 2014.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-(define-library (srfi 87)
- (export case)
- (import (except (scheme base) case))
- (begin
- (define-syntax case
- (syntax-rules (else =>)
- ((case (key ...)
- clauses ...)
- (let ((atom-key (key ...)))
- (case atom-key clauses ...)))
- ((case key
- (else => result))
- (result key))
- ((case key
- ((atoms ...) => result))
- (if (memv key '(atoms ...))
- (result key)))
- ((case key
- ((atoms ...) => result)
- clause clauses ...)
- (if (memv key '(atoms ...))
- (result key)
- (case key clause clauses ...)))
- ((case key
- (else result1 result2 ...))
- (begin result1 result2 ...))
- ((case key
- ((atoms ...) result1 result2 ...))
- (if (memv key '(atoms ...))
- (begin result1 result2 ...)))
- ((case key
- ((atoms ...) result1 result2 ...)
- clause clauses ...)
- (if (memv key '(atoms ...))
- (begin result1 result2 ...)
- (case key clause clauses ...)))))))
-;;; args-fold.scm - a program argument processor
-;;;
-;;; Copyright (c) 2002 Anthony Carrico
-;;;
-;;; All rights reserved.
-;;;
-;;; Redistribution and use in source and binary forms, with or without
-;;; modification, are permitted provided that the following conditions
-;;; are met:
-;;; 1. Redistributions of source code must retain the above copyright
-;;; notice, this list of conditions and the following disclaimer.
-;;; 2. Redistributions in binary form must reproduce the above copyright
-;;; notice, this list of conditions and the following disclaimer in the
-;;; documentation and/or other materials provided with the distribution.
-;;; 3. The name of the authors may not be used to endorse or promote products
-;;; derived from this software without specific prior written permission.
-;;;
-;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
-;;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-;;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-;;; IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-;;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-;;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-;;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-;;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-;;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-;;; NOTE: This implementation uses the following SRFIs:
-;;; "SRFI 9: Defining Record Types"
-;;; "SRFI 11: Syntax for receiving multiple values"
-;;;
-;;; NOTE: The scsh-utils and Chicken implementations use regular
-;;; expressions. These might be easier to read and understand.
-
-(define option #f)
-(define option-names #f)
-(define option-required-arg? #f)
-(define option-optional-arg? #f)
-(define option-processor #f)
-(define option? #f)
-
-(let ()
- (define-record-type option-type
- ($option names required-arg? optional-arg? processor)
- $option?
- (names $option-names)
- (required-arg? $option-required-arg?)
- (optional-arg? $option-optional-arg?)
- (processor $option-processor))
- (set! option $option)
- (set! option-names $option-names)
- (set! option-required-arg? $option-required-arg?)
- (set! option-optional-arg? $option-optional-arg?)
- (set! option-processor $option-processor)
- (set! option? $option?))
-
-(define args-fold
- (lambda (args
- options
- unrecognized-option-proc
- operand-proc
- . seeds)
- (letrec
- ((find
- (lambda (l ?)
- (cond ((null? l) #f)
- ((? (car l)) (car l))
- (else (find (cdr l) ?)))))
- (find-option
- ;; ISSUE: This is a brute force search. Could use a table.
- (lambda (name)
- (find
- options
- (lambda (option)
- (find
- (option-names option)
- (lambda (test-name)
- (equal? name test-name)))))))
- (scan-short-options
- (lambda (index shorts args seeds)
- (if (= index (string-length shorts))
- (scan-args args seeds)
- (let* ((name (string-ref shorts index))
- (option (or (find-option name)
- (option (list name)
- #f
- #f
- unrecognized-option-proc))))
- (cond ((and (< (+ index 1) (string-length shorts))
- (or (option-required-arg? option)
- (option-optional-arg? option)))
- (let-values
- ((seeds (apply (option-processor option)
- option
- name
- (substring
- shorts
- (+ index 1)
- (string-length shorts))
- seeds)))
- (scan-args args seeds)))
- ((and (option-required-arg? option)
- (pair? args))
- (let-values
- ((seeds (apply (option-processor option)
- option
- name
- (car args)
- seeds)))
- (scan-args (cdr args) seeds)))
- (else
- (let-values
- ((seeds (apply (option-processor option)
- option
- name
- #f
- seeds)))
- (scan-short-options
- (+ index 1)
- shorts
- args
- seeds))))))))
- (scan-operands
- (lambda (operands seeds)
- (if (null? operands)
- (apply values seeds)
- (let-values ((seeds (apply operand-proc
- (car operands)
- seeds)))
- (scan-operands (cdr operands) seeds)))))
- (scan-args
- (lambda (args seeds)
- (if (null? args)
- (apply values seeds)
- (let ((arg (car args))
- (args (cdr args)))
- ;; NOTE: This string matching code would be simpler
- ;; using a regular expression matcher.
- (cond
- (;; (rx bos "--" eos)
- (string=? "--" arg)
- ;; End option scanning:
- (scan-operands args seeds))
- (;;(rx bos
- ;; "--"
- ;; (submatch (+ (~ "=")))
- ;; "="
- ;; (submatch (* any)))
- (and (> (string-length arg) 4)
- (char=? #\- (string-ref arg 0))
- (char=? #\- (string-ref arg 1))
- (not (char=? #\= (string-ref arg 2)))
- (let loop ((index 3))
- (cond ((= index (string-length arg))
- #f)
- ((char=? #\= (string-ref arg index))
- index)
- (else
- (loop (+ 1 index))))))
- ;; Found long option with arg:
- => (lambda (=-index)
- (let*-values
- (((name)
- (substring arg 2 =-index))
- ((option-arg)
- (substring arg
- (+ =-index 1)
- (string-length arg)))
- ((option)
- (or (find-option name)
- (option (list name)
- #t
- #f
- unrecognized-option-proc)))
- (seeds
- (apply (option-processor option)
- option
- name
- option-arg
- seeds)))
- (scan-args args seeds))))
- (;;(rx bos "--" (submatch (+ any)))
- (and (> (string-length arg) 3)
- (char=? #\- (string-ref arg 0))
- (char=? #\- (string-ref arg 1)))
- ;; Found long option:
- (let* ((name (substring arg 2 (string-length arg)))
- (option (or (find-option name)
- (option
- (list name)
- #f
- #f
- unrecognized-option-proc))))
- (if (and (option-required-arg? option)
- (pair? args))
- (let-values
- ((seeds (apply (option-processor option)
- option
- name
- (car args)
- seeds)))
- (scan-args (cdr args) seeds))
- (let-values
- ((seeds (apply (option-processor option)
- option
- name
- #f
- seeds)))
- (scan-args args seeds)))))
- (;; (rx bos "-" (submatch (+ any)))
- (and (> (string-length arg) 1)
- (char=? #\- (string-ref arg 0)))
- ;; Found short options
- (let ((shorts (substring arg 1 (string-length arg))))
- (scan-short-options 0 shorts args seeds)))
- (else
- (let-values ((seeds (apply operand-proc arg seeds)))
- (scan-args args seeds)))))))))
- (scan-args args seeds))))
-(define-library (srfi 41)
- (export
- stream-null stream-cons stream? stream-null? stream-pair? stream-car
- stream-cdr stream-lambda define-stream list->stream port->stream stream
- stream->list stream-append stream-concat stream-constant stream-drop
- stream-drop-while stream-filter stream-fold stream-for-each stream-from
- stream-iterate stream-length stream-let stream-map stream-match _
- stream-of stream-range stream-ref stream-reverse stream-scan stream-take
- stream-take-while stream-unfold stream-unfolds stream-zip
- )
- (import
- (srfi 41 primitive)
- (srfi 41 derived)))
-; <PLAINTEXT>
-; Eager Comprehensions in [outer..inner|expr]-Convention
-; ======================================================
-;
-; sebastian.egner@philips.com, Eindhoven, The Netherlands, 26-Dec-2007
-; Scheme R5RS (incl. macros), SRFI-23 (error).
-;
-; Loading the implementation into Scheme48 0.57:
-; ,open srfi-23
-; ,load ec.scm
-;
-; Loading the implementation into PLT/DrScheme 317:
-; ; File > Open ... "ec.scm", click Execute
-;
-; Loading the implementation into SCM 5d7:
-; (require 'macro) (require 'record)
-; (load "ec.scm")
-;
-; Implementation comments:
-; * All local (not exported) identifiers are named ec-<something>.
-; * This implementation focuses on portability, performance,
-; readability, and simplicity roughly in this order. Design
-; decisions related to performance are taken for Scheme48.
-; * Alternative implementations, Comments and Warnings are
-; mentioned after the definition with a heading.
-
-
-; ==========================================================================
-; The fundamental comprehension do-ec
-; ==========================================================================
-;
-; All eager comprehensions are reduced into do-ec and
-; all generators are reduced to :do.
-;
-; We use the following short names for syntactic variables
-; q - qualifier
-; cc - current continuation, thing to call at the end;
-; the CPS is (m (cc ...) arg ...) -> (cc ... expr ...)
-; cmd - an expression being evaluated for its side-effects
-; expr - an expression
-; gen - a generator of an eager comprehension
-; ob - outer binding
-; oc - outer command
-; lb - loop binding
-; ne1? - not-end1? (before the payload)
-; ib - inner binding
-; ic - inner command
-; ne2? - not-end2? (after the payload)
-; ls - loop step
-; etc - more arguments of mixed type
-
-
-; (do-ec q ... cmd)
-; handles nested, if/not/and/or, begin, :let, and calls generator
-; macros in CPS to transform them into fully decorated :do.
-; The code generation for a :do is delegated to do-ec:do.
-
-(define-syntax do-ec
- (syntax-rules (nested if not and or begin do let)
-
- ; explicit nesting -> implicit nesting
- ((do-ec (nested q ...) etc ...)
- (do-ec q ... etc ...) )
-
- ; implicit nesting -> fold do-ec
- ((do-ec q1 q2 etc1 etc ...)
- (do-ec q1 (do-ec q2 etc1 etc ...)) )
-
- ; no qualifiers at all -> evaluate cmd once
- ((do-ec cmd)
- (begin cmd (if #f #f)) )
-
-; now (do-ec q cmd) remains
-
- ; filter -> make conditional
- ((do-ec (if test) cmd)
- (if test (do-ec cmd)) )
- ((do-ec (not test) cmd)
- (if (not test) (do-ec cmd)) )
- ((do-ec (and test ...) cmd)
- (if (and test ...) (do-ec cmd)) )
- ((do-ec (or test ...) cmd)
- (if (or test ...) (do-ec cmd)) )
-
- ; begin -> make a sequence
- ((do-ec (begin etc ...) cmd)
- (begin etc ... (do-ec cmd)) )
-
- ; fully decorated :do-generator -> delegate to do-ec:do
- ((do-ec (#\:do olet lbs ne1? ilet ne2? lss) cmd)
- (do-ec:do cmd (#\:do olet lbs ne1? ilet ne2? lss)) )
-
-; anything else -> call generator-macro in CPS; reentry at (*)
-
- ((do-ec (g arg1 arg ...) cmd)
- (g (do-ec:do cmd) arg1 arg ...) )))
-
-
-; (do-ec:do cmd (#\:do olet lbs ne1? ilet ne2? lss))
-; generates code for a single fully decorated :do-generator
-; with cmd as payload, taking care of special cases.
-
-(define-syntax do-ec:do
- (syntax-rules (#\:do let)
-
- ; reentry point (*) -> generate code
- ((do-ec:do cmd
- (#\:do (let obs oc ...)
- lbs
- ne1?
- (let ibs ic ...)
- ne2?
- (ls ...) ))
- (ec-simplify
- (let obs
- oc ...
- (let loop lbs
- (ec-simplify
- (if ne1?
- (ec-simplify
- (let ibs
- ic ...
- cmd
- (ec-simplify
- (if ne2?
- (loop ls ...) )))))))))) ))
-
-
-; (ec-simplify <expression>)
-; generates potentially more efficient code for <expression>.
-; The macro handles if, (begin <command>*), and (let () <command>*)
-; and takes care of special cases.
-
-(define-syntax ec-simplify
- (syntax-rules (if not let begin)
-
-; one- and two-sided if
-
- ; literal <test>
- ((ec-simplify (if #t consequent))
- consequent )
- ((ec-simplify (if #f consequent))
- (if #f #f) )
- ((ec-simplify (if #t consequent alternate))
- consequent )
- ((ec-simplify (if #f consequent alternate))
- alternate )
-
- ; (not (not <test>))
- ((ec-simplify (if (not (not test)) consequent))
- (ec-simplify (if test consequent)) )
- ((ec-simplify (if (not (not test)) consequent alternate))
- (ec-simplify (if test consequent alternate)) )
-
-; (let () <command>*)
-
- ; empty <binding spec>*
- ((ec-simplify (let () command ...))
- (ec-simplify (begin command ...)) )
-
-; begin
-
- ; flatten use helper (ec-simplify 1 done to-do)
- ((ec-simplify (begin command ...))
- (ec-simplify 1 () (command ...)) )
- ((ec-simplify 1 done ((begin to-do1 ...) to-do2 ...))
- (ec-simplify 1 done (to-do1 ... to-do2 ...)) )
- ((ec-simplify 1 (done ...) (to-do1 to-do ...))
- (ec-simplify 1 (done ... to-do1) (to-do ...)) )
-
- ; exit helper
- ((ec-simplify 1 () ())
- (if #f #f) )
- ((ec-simplify 1 (command) ())
- command )
- ((ec-simplify 1 (command1 command ...) ())
- (begin command1 command ...) )
-
-; anything else
-
- ((ec-simplify expression)
- expression )))
-
-
-; ==========================================================================
-; The special generators :do, :let, :parallel, :while, and :until
-; ==========================================================================
-
-(define-syntax \:do
- (syntax-rules ()
-
- ; full decorated -> continue with cc, reentry at (*)
- ((#\:do (cc ...) olet lbs ne1? ilet ne2? lss)
- (cc ... (#\:do olet lbs ne1? ilet ne2? lss)) )
-
- ; short form -> fill in default values
- ((#\:do cc lbs ne1? lss)
- (#\:do cc (let ()) lbs ne1? (let ()) #t lss) )))
-
-
-(define-syntax \:let
- (syntax-rules (index)
- ((\:let cc var (index i) expression)
- (#\:do cc (let ((var expression) (i 0))) () #t (let ()) #f ()) )
- ((\:let cc var expression)
- (#\:do cc (let ((var expression))) () #t (let ()) #f ()) )))
-
-
-(define-syntax \:parallel
- (syntax-rules (#\:do)
- ((\:parallel cc)
- cc )
- ((\:parallel cc (g arg1 arg ...) gen ...)
- (g (\:parallel-1 cc (gen ...)) arg1 arg ...) )))
-
-; (\:parallel-1 cc (to-do ...) result [ next ] )
-; iterates over to-do by converting the first generator into
-; the :do-generator next and merging next into result.
-
-(define-syntax \:parallel-1 ; used as
- (syntax-rules (#\:do let)
-
- ; process next element of to-do, reentry at (**)
- ((\:parallel-1 cc ((g arg1 arg ...) gen ...) result)
- (g (\:parallel-1 cc (gen ...) result) arg1 arg ...) )
-
- ; reentry point (**) -> merge next into result
- ((\:parallel-1
- cc
- gens
- (#\:do (let (ob1 ...) oc1 ...)
- (lb1 ...)
- ne1?1
- (let (ib1 ...) ic1 ...)
- ne2?1
- (ls1 ...) )
- (#\:do (let (ob2 ...) oc2 ...)
- (lb2 ...)
- ne1?2
- (let (ib2 ...) ic2 ...)
- ne2?2
- (ls2 ...) ))
- (\:parallel-1
- cc
- gens
- (#\:do (let (ob1 ... ob2 ...) oc1 ... oc2 ...)
- (lb1 ... lb2 ...)
- (and ne1?1 ne1?2)
- (let (ib1 ... ib2 ...) ic1 ... ic2 ...)
- (and ne2?1 ne2?2)
- (ls1 ... ls2 ...) )))
-
- ; no more gens -> continue with cc, reentry at (*)
- ((\:parallel-1 (cc ...) () result)
- (cc ... result) )))
-
-(define-syntax \:while
- (syntax-rules ()
- ((\:while cc (g arg1 arg ...) test)
- (g (\:while-1 cc test) arg1 arg ...) )))
-
-; (\:while-1 cc test (#\:do ...))
-; modifies the fully decorated :do-generator such that it
-; runs while test is a true value.
-; The original implementation just replaced ne1? by
-; (and ne1? test) as follows:
-;
-; (define-syntax \:while-1
-; (syntax-rules (#\:do)
-; ((\:while-1 cc test (#\:do olet lbs ne1? ilet ne2? lss))
-; (#\:do cc olet lbs (and ne1? test) ilet ne2? lss) )))
-;
-; Bug #1:
-; Unfortunately, this code is wrong because ne1? may depend
-; in the inner bindings introduced in ilet, but ne1? is evaluated
-; outside of the inner bindings. (Refer to the specification of
-; :do to see the structure.)
-; The problem manifests itself (as sunnan@handgranat.org
-; observed, 25-Apr-2005) when the :list-generator is modified:
-;
-; (do-ec (\:while (\:list x '(1 2)) (= x 1)) (display x)).
-;
-; In order to generate proper code, we introduce temporary
-; variables saving the values of the inner bindings. The inner
-; bindings are executed in a new ne1?, which also evaluates ne1?
-; outside the scope of the inner bindings, then the inner commands
-; are executed (possibly changing the variables), and then the
-; values of the inner bindings are saved and (and ne1? test) is
-; returned. In the new ilet, the inner variables are bound and
-; initialized and their values are restored. So we construct:
-;
-; (let (ob .. (ib-tmp #f) ...)
-; oc ...
-; (let loop (lb ...)
-; (if (let (ne1?-value ne1?)
-; (let ((ib-var ib-rhs) ...)
-; ic ...
-; (set! ib-tmp ib-var) ...)
-; (and ne1?-value test))
-; (let ((ib-var ib-tmp) ...)
-; /payload/
-; (if ne2?
-; (loop ls ...) )))))
-;
-; Bug #2:
-; Unfortunately, the above expansion is still incorrect (as Jens-Axel
-; Soegaard pointed out, 4-Jun-2007) because ib-rhs are evaluated even
-; if ne1?-value is #f, indicating that the loop has ended.
-; The problem manifests itself in the following example:
-;
-; (do-ec (\:while (\:list x '(1)) #t) (display x))
-;
-; Which iterates :list beyond exhausting the list '(1).
-;
-; For the fix, we follow Jens-Axel's approach of guarding the evaluation
-; of ib-rhs with a check on ne1?-value.
-
-(define-syntax \:while-1
- (syntax-rules (#\:do let)
- ((\:while-1 cc test (#\:do olet lbs ne1? ilet ne2? lss))
- (\:while-2 cc test () () () (#\:do olet lbs ne1? ilet ne2? lss)))))
-
-(define-syntax \:while-2
- (syntax-rules (#\:do let)
- ((\:while-2 cc
- test
- (ib-let ...)
- (ib-save ...)
- (ib-restore ...)
- (#\:do olet
- lbs
- ne1?
- (let ((ib-var ib-rhs) ib ...) ic ...)
- ne2?
- lss))
- (\:while-2 cc
- test
- (ib-let ... (ib-tmp #f))
- (ib-save ... (ib-var ib-rhs))
- (ib-restore ... (ib-var ib-tmp))
- (#\:do olet
- lbs
- ne1?
- (let (ib ...) ic ... (set! ib-tmp ib-var))
- ne2?
- lss)))
- ((\:while-2 cc
- test
- (ib-let ...)
- (ib-save ...)
- (ib-restore ...)
- (#\:do (let (ob ...) oc ...) lbs ne1? (let () ic ...) ne2? lss))
- (#\:do cc
- (let (ob ... ib-let ...) oc ...)
- lbs
- (let ((ne1?-value ne1?))
- (and ne1?-value
- (let (ib-save ...)
- ic ...
- test)))
- (let (ib-restore ...))
- ne2?
- lss))))
-
-
-(define-syntax \:until
- (syntax-rules ()
- ((\:until cc (g arg1 arg ...) test)
- (g (\:until-1 cc test) arg1 arg ...) )))
-
-(define-syntax \:until-1
- (syntax-rules (#\:do)
- ((\:until-1 cc test (#\:do olet lbs ne1? ilet ne2? lss))
- (#\:do cc olet lbs ne1? ilet (and ne2? (not test)) lss) )))
-
-
-; ==========================================================================
-; The typed generators :list :string :vector etc.
-; ==========================================================================
-
-(define-syntax \:list
- (syntax-rules (index)
- ((\:list cc var (index i) arg ...)
- (\:parallel cc (\:list var arg ...) (\:integers i)) )
- ((\:list cc var arg1 arg2 arg ...)
- (\:list cc var (append arg1 arg2 arg ...)) )
- ((\:list cc var arg)
- (#\:do cc
- (let ())
- ((t arg))
- (not (null? t))
- (let ((var (car t))))
- #t
- ((cdr t)) ))))
-
-
-(define-syntax \:string
- (syntax-rules (index)
- ((\:string cc var (index i) arg)
- (#\:do cc
- (let ((str arg) (len 0))
- (set! len (string-length str)))
- ((i 0))
- (< i len)
- (let ((var (string-ref str i))))
- #t
- ((+ i 1)) ))
- ((\:string cc var (index i) arg1 arg2 arg ...)
- (\:string cc var (index i) (string-append arg1 arg2 arg ...)) )
- ((\:string cc var arg1 arg ...)
- (\:string cc var (index i) arg1 arg ...) )))
-
-; Alternative: An implementation in the style of :vector can also
-; be used for :string. However, it is less interesting as the
-; overhead of string-append is much less than for 'vector-append'.
-
-
-(define-syntax \:vector
- (syntax-rules (index)
- ((\:vector cc var arg)
- (\:vector cc var (index i) arg) )
- ((\:vector cc var (index i) arg)
- (#\:do cc
- (let ((vec arg) (len 0))
- (set! len (vector-length vec)))
- ((i 0))
- (< i len)
- (let ((var (vector-ref vec i))))
- #t
- ((+ i 1)) ))
-
- ((\:vector cc var (index i) arg1 arg2 arg ...)
- (\:parallel cc (\:vector cc var arg1 arg2 arg ...) (\:integers i)) )
- ((\:vector cc var arg1 arg2 arg ...)
- (#\:do cc
- (let ((vec #f)
- (len 0)
- (vecs (ec-:vector-filter (list arg1 arg2 arg ...))) ))
- ((k 0))
- (if (< k len)
- #t
- (if (null? vecs)
- #f
- (begin (set! vec (car vecs))
- (set! vecs (cdr vecs))
- (set! len (vector-length vec))
- (set! k 0)
- #t )))
- (let ((var (vector-ref vec k))))
- #t
- ((+ k 1)) ))))
-
-(define (ec-:vector-filter vecs)
- (if (null? vecs)
- '()
- (if (zero? (vector-length (car vecs)))
- (ec-:vector-filter (cdr vecs))
- (cons (car vecs) (ec-:vector-filter (cdr vecs))) )))
-
-; Alternative: A simpler implementation for :vector uses vector->list
-; append and :list in the multi-argument case. Please refer to the
-; 'design.scm' for more details.
-
-
-(define-syntax \:integers
- (syntax-rules (index)
- ((\:integers cc var (index i))
- (#\:do cc ((var 0) (i 0)) #t ((+ var 1) (+ i 1))) )
- ((\:integers cc var)
- (#\:do cc ((var 0)) #t ((+ var 1))) )))
-
-
-(define-syntax \:range
- (syntax-rules (index)
-
- ; handle index variable and add optional args
- ((\:range cc var (index i) arg1 arg ...)
- (\:parallel cc (\:range var arg1 arg ...) (\:integers i)) )
- ((\:range cc var arg1)
- (\:range cc var 0 arg1 1) )
- ((\:range cc var arg1 arg2)
- (\:range cc var arg1 arg2 1) )
-
-; special cases (partially evaluated by hand from general case)
-
- ((\:range cc var 0 arg2 1)
- (#\:do cc
- (let ((b arg2))
- (if (not (and (integer? b) (exact? b)))
- (error
- "arguments of :range are not exact integer "
- "(use :real-range?)" 0 b 1 )))
- ((var 0))
- (< var b)
- (let ())
- #t
- ((+ var 1)) ))
-
- ((\:range cc var 0 arg2 -1)
- (#\:do cc
- (let ((b arg2))
- (if (not (and (integer? b) (exact? b)))
- (error
- "arguments of :range are not exact integer "
- "(use :real-range?)" 0 b 1 )))
- ((var 0))
- (> var b)
- (let ())
- #t
- ((- var 1)) ))
-
- ((\:range cc var arg1 arg2 1)
- (#\:do cc
- (let ((a arg1) (b arg2))
- (if (not (and (integer? a) (exact? a)
- (integer? b) (exact? b) ))
- (error
- "arguments of :range are not exact integer "
- "(use :real-range?)" a b 1 )) )
- ((var a))
- (< var b)
- (let ())
- #t
- ((+ var 1)) ))
-
- ((\:range cc var arg1 arg2 -1)
- (#\:do cc
- (let ((a arg1) (b arg2) (s -1) (stop 0))
- (if (not (and (integer? a) (exact? a)
- (integer? b) (exact? b) ))
- (error
- "arguments of :range are not exact integer "
- "(use :real-range?)" a b -1 )) )
- ((var a))
- (> var b)
- (let ())
- #t
- ((- var 1)) ))
-
-; the general case
-
- ((\:range cc var arg1 arg2 arg3)
- (#\:do cc
- (let ((a arg1) (b arg2) (s arg3) (stop 0))
- (if (not (and (integer? a) (exact? a)
- (integer? b) (exact? b)
- (integer? s) (exact? s) ))
- (error
- "arguments of :range are not exact integer "
- "(use :real-range?)" a b s ))
- (if (zero? s)
- (error "step size must not be zero in :range") )
- (set! stop (+ a (* (max 0 (ceiling (/ (- b a) s))) s))) )
- ((var a))
- (not (= var stop))
- (let ())
- #t
- ((+ var s)) ))))
-
-; Comment: The macro :range inserts some code to make sure the values
-; are exact integers. This overhead has proven very helpful for
-; saving users from themselves.
-
-
-(define-syntax \:real-range
- (syntax-rules (index)
-
- ; add optional args and index variable
- ((\:real-range cc var arg1)
- (\:real-range cc var (index i) 0 arg1 1) )
- ((\:real-range cc var (index i) arg1)
- (\:real-range cc var (index i) 0 arg1 1) )
- ((\:real-range cc var arg1 arg2)
- (\:real-range cc var (index i) arg1 arg2 1) )
- ((\:real-range cc var (index i) arg1 arg2)
- (\:real-range cc var (index i) arg1 arg2 1) )
- ((\:real-range cc var arg1 arg2 arg3)
- (\:real-range cc var (index i) arg1 arg2 arg3) )
-
- ; the fully qualified case
- ((\:real-range cc var (index i) arg1 arg2 arg3)
- (#\:do cc
- (let ((a arg1) (b arg2) (s arg3) (istop 0))
- (if (not (and (real? a) (real? b) (real? s)))
- (error "arguments of :real-range are not real" a b s) )
- (if (and (exact? a) (or (not (exact? b)) (not (exact? s))))
- (set! a (inexact a)) )
- (set! istop (/ (- b a) s)) )
- ((i 0))
- (< i istop)
- (let ((var (+ a (* s i)))))
- #t
- ((+ i 1)) ))))
-
-; Comment: The macro :real-range adapts the exactness of the start
-; value in case any of the other values is inexact. This is a
-; precaution to avoid (list-ec (\: x 0 3.0) x) => '(0 1.0 2.0).
-
-
-(define-syntax \:char-range
- (syntax-rules (index)
- ((\:char-range cc var (index i) arg1 arg2)
- (\:parallel cc (\:char-range var arg1 arg2) (\:integers i)) )
- ((\:char-range cc var arg1 arg2)
- (#\:do cc
- (let ((imax (char->integer arg2))))
- ((i (char->integer arg1)))
- (<= i imax)
- (let ((var (integer->char i))))
- #t
- ((+ i 1)) ))))
-
-; Warning: There is no R5RS-way to implement the :char-range generator
-; because the integers obtained by char->integer are not necessarily
-; consecutive. We simply assume this anyhow for illustration.
-
-
-(define-syntax \:port
- (syntax-rules (index)
- ((\:port cc var (index i) arg1 arg ...)
- (\:parallel cc (\:port var arg1 arg ...) (\:integers i)) )
- ((\:port cc var arg)
- (\:port cc var arg read) )
- ((\:port cc var arg1 arg2)
- (#\:do cc
- (let ((port arg1) (read-proc arg2)))
- ((var (read-proc port)))
- (not (eof-object? var))
- (let ())
- #t
- ((read-proc port)) ))))
-
-
-; ==========================================================================
-; The typed generator :dispatched and utilities for constructing dispatchers
-; ==========================================================================
-
-(define-syntax \:dispatched
- (syntax-rules (index)
- ((\:dispatched cc var (index i) dispatch arg1 arg ...)
- (\:parallel cc
- (\:integers i)
- (\:dispatched var dispatch arg1 arg ...) ))
- ((\:dispatched cc var dispatch arg1 arg ...)
- (#\:do cc
- (let ((d dispatch)
- (args (list arg1 arg ...))
- (g #f)
- (empty (list #f)) )
- (set! g (d args))
- (if (not (procedure? g))
- (error "unrecognized arguments in dispatching"
- args
- (d '()) )))
- ((var (g empty)))
- (not (eq? var empty))
- (let ())
- #t
- ((g empty)) ))))
-
-; Comment: The unique object empty is created as a newly allocated
-; non-empty list. It is compared using eq? which distinguishes
-; the object from any other object, according to R5RS 6.1.
-
-
-(define-syntax \:generator-proc
- (syntax-rules (#\:do let)
-
- ; call g with a variable, reentry at (**)
- ((\:generator-proc (g arg ...))
- (g (\:generator-proc var) var arg ...) )
-
- ; reentry point (**) -> make the code from a single :do
- ((\:generator-proc
- var
- (#\:do (let obs oc ...)
- ((lv li) ...)
- ne1?
- (let ((i v) ...) ic ...)
- ne2?
- (ls ...)) )
- (ec-simplify
- (let obs
- oc ...
- (let ((lv li) ... (ne2 #t))
- (ec-simplify
- (let ((i #f) ...) ; v not yet valid
- (lambda (empty)
- (if (and ne1? ne2)
- (ec-simplify
- (begin
- (set! i v) ...
- ic ...
- (let ((value var))
- (ec-simplify
- (if ne2?
- (ec-simplify
- (begin (set! lv ls) ...) )
- (set! ne2 #f) ))
- value )))
- empty ))))))))
-
- ; silence warnings of some macro expanders
- ((\:generator-proc var)
- (error "illegal macro call") )))
-
-
-(define (dispatch-union d1 d2)
- (lambda (args)
- (let ((g1 (d1 args)) (g2 (d2 args)))
- (if g1
- (if g2
- (if (null? args)
- (append (if (list? g1) g1 (list g1))
- (if (list? g2) g2 (list g2)) )
- (error "dispatching conflict" args (d1 '()) (d2 '())) )
- g1 )
- (if g2 g2 #f) ))))
-
-
-; ==========================================================================
-; The dispatching generator :
-; ==========================================================================
-
-(define (make-initial-:-dispatch)
- (lambda (args)
- (case (length args)
- ((0) 'SRFI42)
- ((1) (let ((a1 (car args)))
- (cond
- ((list? a1)
- (\:generator-proc (\:list a1)) )
- ((string? a1)
- (\:generator-proc (\:string a1)) )
- ((vector? a1)
- (\:generator-proc (\:vector a1)) )
- ((and (integer? a1) (exact? a1))
- (\:generator-proc (\:range a1)) )
- ((real? a1)
- (\:generator-proc (\:real-range a1)) )
- ((input-port? a1)
- (\:generator-proc (\:port a1)) )
- (else
- #f ))))
- ((2) (let ((a1 (car args)) (a2 (cadr args)))
- (cond
- ((and (list? a1) (list? a2))
- (\:generator-proc (\:list a1 a2)) )
- ((and (string? a1) (string? a1))
- (\:generator-proc (\:string a1 a2)) )
- ((and (vector? a1) (vector? a2))
- (\:generator-proc (\:vector a1 a2)) )
- ((and (integer? a1) (exact? a1) (integer? a2) (exact? a2))
- (\:generator-proc (\:range a1 a2)) )
- ((and (real? a1) (real? a2))
- (\:generator-proc (\:real-range a1 a2)) )
- ((and (char? a1) (char? a2))
- (\:generator-proc (\:char-range a1 a2)) )
- ((and (input-port? a1) (procedure? a2))
- (\:generator-proc (\:port a1 a2)) )
- (else
- #f ))))
- ((3) (let ((a1 (car args)) (a2 (cadr args)) (a3 (caddr args)))
- (cond
- ((and (list? a1) (list? a2) (list? a3))
- (\:generator-proc (\:list a1 a2 a3)) )
- ((and (string? a1) (string? a1) (string? a3))
- (\:generator-proc (\:string a1 a2 a3)) )
- ((and (vector? a1) (vector? a2) (vector? a3))
- (\:generator-proc (\:vector a1 a2 a3)) )
- ((and (integer? a1) (exact? a1)
- (integer? a2) (exact? a2)
- (integer? a3) (exact? a3))
- (\:generator-proc (\:range a1 a2 a3)) )
- ((and (real? a1) (real? a2) (real? a3))
- (\:generator-proc (\:real-range a1 a2 a3)) )
- (else
- #f ))))
- (else
- (letrec ((every?
- (lambda (pred args)
- (if (null? args)
- #t
- (and (pred (car args))
- (every? pred (cdr args)) )))))
- (cond
- ((every? list? args)
- (\:generator-proc (\:list (apply append args))) )
- ((every? string? args)
- (\:generator-proc (\:string (apply string-append args))) )
- ((every? vector? args)
- (\:generator-proc (\:list (apply append (map vector->list args)))) )
- (else
- #f )))))))
-
-(define \:-dispatch
- (make-initial-:-dispatch) )
-
-(define (\:-dispatch-ref)
- \:-dispatch )
-
-(define (\:-dispatch-set! dispatch)
- (if (not (procedure? dispatch))
- (error "not a procedure" dispatch) )
- (set! \:-dispatch dispatch) )
-
-(define-syntax \:
- (syntax-rules (index)
- ((\: cc var (index i) arg1 arg ...)
- (\:dispatched cc var (index i) \:-dispatch arg1 arg ...) )
- ((\: cc var arg1 arg ...)
- (\:dispatched cc var \:-dispatch arg1 arg ...) )))
-
-
-; ==========================================================================
-; The utility comprehensions fold-ec, fold3-ec
-; ==========================================================================
-
-(define-syntax fold3-ec
- (syntax-rules (nested)
- ((fold3-ec x0 (nested q1 ...) q etc1 etc2 etc3 etc ...)
- (fold3-ec x0 (nested q1 ... q) etc1 etc2 etc3 etc ...) )
- ((fold3-ec x0 q1 q2 etc1 etc2 etc3 etc ...)
- (fold3-ec x0 (nested q1 q2) etc1 etc2 etc3 etc ...) )
- ((fold3-ec x0 expression f1 f2)
- (fold3-ec x0 (nested) expression f1 f2) )
-
- ((fold3-ec x0 qualifier expression f1 f2)
- (let ((result #f) (empty #t))
- (do-ec qualifier
- (let ((value expression)) ; don't duplicate
- (if empty
- (begin (set! result (f1 value))
- (set! empty #f) )
- (set! result (f2 value result)) )))
- (if empty x0 result) ))))
-
-
-(define-syntax fold-ec
- (syntax-rules (nested)
- ((fold-ec x0 (nested q1 ...) q etc1 etc2 etc ...)
- (fold-ec x0 (nested q1 ... q) etc1 etc2 etc ...) )
- ((fold-ec x0 q1 q2 etc1 etc2 etc ...)
- (fold-ec x0 (nested q1 q2) etc1 etc2 etc ...) )
- ((fold-ec x0 expression f2)
- (fold-ec x0 (nested) expression f2) )
-
- ((fold-ec x0 qualifier expression f2)
- (let ((result x0))
- (do-ec qualifier (set! result (f2 expression result)))
- result ))))
-
-
-; ==========================================================================
-; The comprehensions list-ec string-ec vector-ec etc.
-; ==========================================================================
-
-(define-syntax list-ec
- (syntax-rules ()
- ((list-ec etc1 etc ...)
- (reverse (fold-ec '() etc1 etc ... cons)) )))
-
-; Alternative: Reverse can safely be replaced by reverse! if you have it.
-;
-; Alternative: It is possible to construct the result in the correct order
-; using set-cdr! to add at the tail. This removes the overhead of copying
-; at the end, at the cost of more book-keeping.
-
-
-(define-syntax append-ec
- (syntax-rules ()
- ((append-ec etc1 etc ...)
- (apply append (list-ec etc1 etc ...)) )))
-
-(define-syntax string-ec
- (syntax-rules ()
- ((string-ec etc1 etc ...)
- (list->string (list-ec etc1 etc ...)) )))
-
-; Alternative: For very long strings, the intermediate list may be a
-; problem. A more space-aware implementation collect the characters
-; in an intermediate list and when this list becomes too large it is
-; converted into an intermediate string. At the end, the intermediate
-; strings are concatenated with string-append.
-
-
-(define-syntax string-append-ec
- (syntax-rules ()
- ((string-append-ec etc1 etc ...)
- (apply string-append (list-ec etc1 etc ...)) )))
-
-(define-syntax vector-ec
- (syntax-rules ()
- ((vector-ec etc1 etc ...)
- (list->vector (list-ec etc1 etc ...)) )))
-
-; Comment: A similar approach as for string-ec can be used for vector-ec.
-; However, the space overhead for the intermediate list is much lower
-; than for string-ec and as there is no vector-append, the intermediate
-; vectors must be copied explicitly.
-
-(define-syntax vector-of-length-ec
- (syntax-rules (nested)
- ((vector-of-length-ec k (nested q1 ...) q etc1 etc ...)
- (vector-of-length-ec k (nested q1 ... q) etc1 etc ...) )
- ((vector-of-length-ec k q1 q2 etc1 etc ...)
- (vector-of-length-ec k (nested q1 q2) etc1 etc ...) )
- ((vector-of-length-ec k expression)
- (vector-of-length-ec k (nested) expression) )
-
- ((vector-of-length-ec k qualifier expression)
- (let ((len k))
- (let ((vec (make-vector len))
- (i 0) )
- (do-ec qualifier
- (if (< i len)
- (begin (vector-set! vec i expression)
- (set! i (+ i 1)) )
- (error "vector is too short for the comprehension") ))
- (if (= i len)
- vec
- (error "vector is too long for the comprehension") ))))))
-
-
-(define-syntax sum-ec
- (syntax-rules ()
- ((sum-ec etc1 etc ...)
- (fold-ec (+) etc1 etc ... +) )))
-
-(define-syntax product-ec
- (syntax-rules ()
- ((product-ec etc1 etc ...)
- (fold-ec (*) etc1 etc ... *) )))
-
-(define-syntax min-ec
- (syntax-rules ()
- ((min-ec etc1 etc ...)
- (fold3-ec (min) etc1 etc ... min min) )))
-
-(define-syntax max-ec
- (syntax-rules ()
- ((max-ec etc1 etc ...)
- (fold3-ec (max) etc1 etc ... max max) )))
-
-(define-syntax last-ec
- (syntax-rules (nested)
- ((last-ec default (nested q1 ...) q etc1 etc ...)
- (last-ec default (nested q1 ... q) etc1 etc ...) )
- ((last-ec default q1 q2 etc1 etc ...)
- (last-ec default (nested q1 q2) etc1 etc ...) )
- ((last-ec default expression)
- (last-ec default (nested) expression) )
-
- ((last-ec default qualifier expression)
- (let ((result default))
- (do-ec qualifier (set! result expression))
- result ))))
-
-
-; ==========================================================================
-; The fundamental early-stopping comprehension first-ec
-; ==========================================================================
-
-(define-syntax first-ec
- (syntax-rules (nested)
- ((first-ec default (nested q1 ...) q etc1 etc ...)
- (first-ec default (nested q1 ... q) etc1 etc ...) )
- ((first-ec default q1 q2 etc1 etc ...)
- (first-ec default (nested q1 q2) etc1 etc ...) )
- ((first-ec default expression)
- (first-ec default (nested) expression) )
-
- ((first-ec default qualifier expression)
- (let ((result default) (stop #f))
- (ec-guarded-do-ec
- stop
- (nested qualifier)
- (begin (set! result expression)
- (set! stop #t) ))
- result ))))
-
-; (ec-guarded-do-ec stop (nested q ...) cmd)
-; constructs (do-ec q ... cmd) where the generators gen in q ... are
-; replaced by (\:until gen stop).
-
-(define-syntax ec-guarded-do-ec
- (syntax-rules (nested if not and or begin)
-
- ((ec-guarded-do-ec stop (nested (nested q1 ...) q2 ...) cmd)
- (ec-guarded-do-ec stop (nested q1 ... q2 ...) cmd) )
-
- ((ec-guarded-do-ec stop (nested (if test) q ...) cmd)
- (if test (ec-guarded-do-ec stop (nested q ...) cmd)) )
- ((ec-guarded-do-ec stop (nested (not test) q ...) cmd)
- (if (not test) (ec-guarded-do-ec stop (nested q ...) cmd)) )
- ((ec-guarded-do-ec stop (nested (and test ...) q ...) cmd)
- (if (and test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) )
- ((ec-guarded-do-ec stop (nested (or test ...) q ...) cmd)
- (if (or test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) )
-
- ((ec-guarded-do-ec stop (nested (begin etc ...) q ...) cmd)
- (begin etc ... (ec-guarded-do-ec stop (nested q ...) cmd)) )
-
- ((ec-guarded-do-ec stop (nested gen q ...) cmd)
- (do-ec
- (\:until gen stop)
- (ec-guarded-do-ec stop (nested q ...) cmd) ))
-
- ((ec-guarded-do-ec stop (nested) cmd)
- (do-ec cmd) )))
-
-; Alternative: Instead of modifying the generator with :until, it is
-; possible to use call-with-current-continuation:
-;
-; (define-synatx first-ec
-; ...same as above...
-; ((first-ec default qualifier expression)
-; (call-with-current-continuation
-; (lambda (cc)
-; (do-ec qualifier (cc expression))
-; default ))) ))
-;
-; This is much simpler but not necessarily as efficient.
-
-
-; ==========================================================================
-; The early-stopping comprehensions any?-ec every?-ec
-; ==========================================================================
-
-(define-syntax any?-ec
- (syntax-rules (nested)
- ((any?-ec (nested q1 ...) q etc1 etc ...)
- (any?-ec (nested q1 ... q) etc1 etc ...) )
- ((any?-ec q1 q2 etc1 etc ...)
- (any?-ec (nested q1 q2) etc1 etc ...) )
- ((any?-ec expression)
- (any?-ec (nested) expression) )
-
- ((any?-ec qualifier expression)
- (first-ec #f qualifier (if expression) #t) )))
-
-(define-syntax every?-ec
- (syntax-rules (nested)
- ((every?-ec (nested q1 ...) q etc1 etc ...)
- (every?-ec (nested q1 ... q) etc1 etc ...) )
- ((every?-ec q1 q2 etc1 etc ...)
- (every?-ec (nested q1 q2) etc1 etc ...) )
- ((every?-ec expression)
- (every?-ec (nested) expression) )
-
- ((every?-ec qualifier expression)
- (first-ec #t qualifier (if (not expression)) #f) )))
-
-(define-library (srfi 42)
- (export
- \:
- \:-dispatch-ref
- \:-dispatch-set!
- \:char-range
- \:dispatched
- \:do
- \:generator-proc
- \:integers
- \:let
- \:list
- \:parallel
- \:port
- \:range
- \:real-range
- \:string
- \:until
- \:vector
- \:while
- any?-ec
- append-ec
- dispatch-union
- do-ec
- every?-ec
- first-ec
- fold-ec
- fold3-ec
- last-ec
- list-ec
- make-initial-\:-dispatch
- max-ec
- min-ec
- product-ec
- string-append-ec
- string-ec
- sum-ec
- vector-ec
- vector-of-length-ec
- )
- (import
- (scheme base)
- (scheme cxr)
- (scheme read))
- (include "42.body.scm"))
-;;; "sort.scm" Defines: sorted?, merge, merge!, sort, sort!
-;;; Author : Richard A. O'Keefe (based on Prolog code by D.H.D.Warren)
-;;;
-;;; Made an R7RS library by Taylan Ulrich Bayırlı/Kammer, Copyright (C) 2014.
-
-;;; Copyright (C) Aubrey Jaffer 2006. All Rights Reserved.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-;;; Updated: 11 June 1991
-;;; Modified for scheme library: Aubrey Jaffer 19 Sept. 1991
-;;; Updated: 19 June 1995
-;;; (sort, sort!, sorted?): Generalized to strings by jaffer: 2003-09-09
-;;; (sort, sort!, sorted?): Generalized to arrays by jaffer: 2003-10-04
-;;; jaffer: 2006-10-08:
-;;; (sort, sort!, sorted?, merge, merge!): Added optional KEY argument.
-;;; jaffer: 2006-11-05:
-;;; (sorted?, merge, merge!, sort, sort!): Call KEY arg at most once
-;;; per element.
-
-;;; (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 (sorted? seq less? . opt-key)
- (define key (if (null? opt-key) values (car opt-key)))
- (cond ((null? seq) #t)
- ((array? seq)
- (let ((dimax (+ -1 (car (array-dimensions seq)))))
- (or (<= dimax 1)
- (let loop ((idx (+ -1 dimax))
- (last (key (array-ref seq dimax))))
- (or (negative? idx)
- (let ((nxt (key (array-ref seq idx))))
- (and (less? nxt last)
- (loop (+ -1 idx) nxt))))))))
- ((null? (cdr seq)) #t)
- (else
- (let loop ((last (key (car seq)))
- (next (cdr seq)))
- (or (null? next)
- (let ((nxt (key (car next))))
- (and (not (less? nxt last))
- (loop nxt (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 arrays. See below.
-;@
-(define (merge a b less? . opt-key)
- (define key (if (null? opt-key) values (car opt-key)))
- (cond ((null? a) b)
- ((null? b) a)
- (else
- (let loop ((x (car a)) (kx (key (car a))) (a (cdr a))
- (y (car b)) (ky (key (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? ky kx)
- (if (null? b)
- (cons y (cons x a))
- (cons y (loop x kx a (car b) (key (car b)) (cdr b))))
- ;; x <= y
- (if (null? a)
- (cons x (cons y b))
- (cons x (loop (car a) (key (car a)) (cdr a) y ky b))))))))
-
-(define (sort:merge! a b less? key)
- (define (loop r a kcara b kcarb)
- (cond ((less? kcarb kcara)
- (set-cdr! r b)
- (if (null? (cdr b))
- (set-cdr! b a)
- (loop b a kcara (cdr b) (key (cadr b)))))
- (else ; (car a) <= (car b)
- (set-cdr! r a)
- (if (null? (cdr a))
- (set-cdr! a b)
- (loop a (cdr a) (key (cadr a)) b kcarb)))))
- (cond ((null? a) b)
- ((null? b) a)
- (else
- (let ((kcara (key (car a)))
- (kcarb (key (car b))))
- (cond
- ((less? kcarb kcara)
- (if (null? (cdr b))
- (set-cdr! b a)
- (loop b a kcara (cdr b) (key (cadr b))))
- b)
- (else ; (car a) <= (car b)
- (if (null? (cdr a))
- (set-cdr! a b)
- (loop a (cdr a) (key (cadr a)) b kcarb))
- a))))))
-
-;;; 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 arrays.
-;@
-(define (merge! a b less? . opt-key)
- (sort:merge! a b less? (if (null? opt-key) values (car opt-key))))
-
-(define (sort:sort-list! seq less? key)
- (define keyer (if key car values))
- (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? keyer)))
- ((= n 2) (let ((x (car seq))
- (y (cadr seq))
- (p seq))
- (set! seq (cddr seq))
- (cond ((less? (keyer y) (keyer x))
- (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 '())))
- (define (key-wrap! lst)
- (cond ((null? lst))
- (else (set-car! lst (cons (key (car lst)) (car lst)))
- (key-wrap! (cdr lst)))))
- (define (key-unwrap! lst)
- (cond ((null? lst))
- (else (set-car! lst (cdar lst))
- (key-unwrap! (cdr lst)))))
- (cond (key
- (key-wrap! seq)
- (set! seq (step (length seq)))
- (key-unwrap! seq)
- seq)
- (else
- (step (length seq)))))
-
-(define (rank-1-array->list array)
- (define dimensions (array-dimensions array))
- (do ((idx (+ -1 (car dimensions)) (+ -1 idx))
- (lst '() (cons (array-ref array idx) lst)))
- ((< idx 0) lst)))
-
-;;; (sort! sequence less?)
-;;; sorts the list, array, or string 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.
-;;; A. Jaffer modified to always return the original list.
-;@
-(define (sort! seq less? . opt-key)
- (define key (if (null? opt-key) #f (car opt-key)))
- (cond ((array? seq)
- (let ((dims (array-dimensions seq)))
- (do ((sorted (sort:sort-list! (rank-1-array->list seq) less? key)
- (cdr sorted))
- (i 0 (+ i 1)))
- ((null? sorted) seq)
- (array-set! seq (car sorted) i))))
- (else ; otherwise, assume it is a list
- (let ((ret (sort:sort-list! seq less? key)))
- (if (not (eq? ret seq))
- (do ((crt ret (cdr crt)))
- ((eq? (cdr crt) seq)
- (set-cdr! crt ret)
- (let ((scar (car seq)) (scdr (cdr seq)))
- (set-car! seq (car ret)) (set-cdr! seq (cdr ret))
- (set-car! ret scar) (set-cdr! ret scdr)))))
- seq))))
-
-;;; (sort sequence less?)
-;;; sorts a array, string, 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 seq less? . opt-key)
- (define key (if (null? opt-key) #f (car opt-key)))
- (cond ((array? seq)
- (let ((dims (array-dimensions seq)))
- (define newra (apply make-array seq dims))
- (do ((sorted (sort:sort-list! (rank-1-array->list seq) less? key)
- (cdr sorted))
- (i 0 (+ i 1)))
- ((null? sorted) newra)
- (array-set! newra (car sorted) i))))
- (else (sort:sort-list! (append seq '()) less? key))))
-; <PLAINTEXT>
-; Eager Comprehensions in [outer..inner|expr]-Convention
-; ======================================================
-;
-; sebastian.egner@philips.com, Eindhoven, The Netherlands, 26-Dec-2007
-; Scheme R5RS (incl. macros), SRFI-23 (error).
-;
-; Loading the implementation into Scheme48 0.57:
-; ,open srfi-23
-; ,load ec.scm
-;
-; Loading the implementation into PLT/DrScheme 317:
-; ; File > Open ... "ec.scm", click Execute
-;
-; Loading the implementation into SCM 5d7:
-; (require 'macro) (require 'record)
-; (load "ec.scm")
-;
-; Implementation comments:
-; * All local (not exported) identifiers are named ec-<something>.
-; * This implementation focuses on portability, performance,
-; readability, and simplicity roughly in this order. Design
-; decisions related to performance are taken for Scheme48.
-; * Alternative implementations, Comments and Warnings are
-; mentioned after the definition with a heading.
-
-
-; ==========================================================================
-; The fundamental comprehension do-ec
-; ==========================================================================
-;
-; All eager comprehensions are reduced into do-ec and
-; all generators are reduced to :do.
-;
-; We use the following short names for syntactic variables
-; q - qualifier
-; cc - current continuation, thing to call at the end;
-; the CPS is (m (cc ...) arg ...) -> (cc ... expr ...)
-; cmd - an expression being evaluated for its side-effects
-; expr - an expression
-; gen - a generator of an eager comprehension
-; ob - outer binding
-; oc - outer command
-; lb - loop binding
-; ne1? - not-end1? (before the payload)
-; ib - inner binding
-; ic - inner command
-; ne2? - not-end2? (after the payload)
-; ls - loop step
-; etc - more arguments of mixed type
-
-
-; (do-ec q ... cmd)
-; handles nested, if/not/and/or, begin, :let, and calls generator
-; macros in CPS to transform them into fully decorated :do.
-; The code generation for a :do is delegated to do-ec:do.
-
-(define-syntax do-ec
- (syntax-rules (nested if not and or begin \:do let)
-
- ; explicit nesting -> implicit nesting
- ((do-ec (nested q ...) etc ...)
- (do-ec q ... etc ...) )
-
- ; implicit nesting -> fold do-ec
- ((do-ec q1 q2 etc1 etc ...)
- (do-ec q1 (do-ec q2 etc1 etc ...)) )
-
- ; no qualifiers at all -> evaluate cmd once
- ((do-ec cmd)
- (begin cmd (if #f #f)) )
-
-; now (do-ec q cmd) remains
-
- ; filter -> make conditional
- ((do-ec (if test) cmd)
- (if test (do-ec cmd)) )
- ((do-ec (not test) cmd)
- (if (not test) (do-ec cmd)) )
- ((do-ec (and test ...) cmd)
- (if (and test ...) (do-ec cmd)) )
- ((do-ec (or test ...) cmd)
- (if (or test ...) (do-ec cmd)) )
-
- ; begin -> make a sequence
- ((do-ec (begin etc ...) cmd)
- (begin etc ... (do-ec cmd)) )
-
- ; fully decorated :do-generator -> delegate to do-ec:do
- ((do-ec (#\:do olet lbs ne1? ilet ne2? lss) cmd)
- (do-ec:do cmd (#\:do olet lbs ne1? ilet ne2? lss)) )
-
-; anything else -> call generator-macro in CPS; reentry at (*)
-
- ((do-ec (g arg1 arg ...) cmd)
- (g (do-ec:do cmd) arg1 arg ...) )))
-
-
-; (do-ec:do cmd (#\:do olet lbs ne1? ilet ne2? lss))
-; generates code for a single fully decorated :do-generator
-; with cmd as payload, taking care of special cases.
-
-(define-syntax do-ec:do
- (syntax-rules (#\:do let)
-
- ; reentry point (*) -> generate code
- ((do-ec:do cmd
- (#\:do (let obs oc ...)
- lbs
- ne1?
- (let ibs ic ...)
- ne2?
- (ls ...) ))
- (ec-simplify
- (let obs
- oc ...
- (let loop lbs
- (ec-simplify
- (if ne1?
- (ec-simplify
- (let ibs
- ic ...
- cmd
- (ec-simplify
- (if ne2?
- (loop ls ...) )))))))))) ))
-
-
-; (ec-simplify <expression>)
-; generates potentially more efficient code for <expression>.
-; The macro handles if, (begin <command>*), and (let () <command>*)
-; and takes care of special cases.
-
-(define-syntax ec-simplify
- (syntax-rules (if not let begin)
-
-; one- and two-sided if
-
- ; literal <test>
- ((ec-simplify (if #t consequent))
- consequent )
- ((ec-simplify (if #f consequent))
- (if #f #f) )
- ((ec-simplify (if #t consequent alternate))
- consequent )
- ((ec-simplify (if #f consequent alternate))
- alternate )
-
- ; (not (not <test>))
- ((ec-simplify (if (not (not test)) consequent))
- (ec-simplify (if test consequent)) )
- ((ec-simplify (if (not (not test)) consequent alternate))
- (ec-simplify (if test consequent alternate)) )
-
-; (let () <command>*)
-
- ; empty <binding spec>*
- ((ec-simplify (let () command ...))
- (ec-simplify (begin command ...)) )
-
-; begin
-
- ; flatten use helper (ec-simplify 1 done to-do)
- ((ec-simplify (begin command ...))
- (ec-simplify 1 () (command ...)) )
- ((ec-simplify 1 done ((begin to-do1 ...) to-do2 ...))
- (ec-simplify 1 done (to-do1 ... to-do2 ...)) )
- ((ec-simplify 1 (done ...) (to-do1 to-do ...))
- (ec-simplify 1 (done ... to-do1) (to-do ...)) )
-
- ; exit helper
- ((ec-simplify 1 () ())
- (if #f #f) )
- ((ec-simplify 1 (command) ())
- command )
- ((ec-simplify 1 (command1 command ...) ())
- (begin command1 command ...) )
-
-; anything else
-
- ((ec-simplify expression)
- expression )))
-
-
-; ==========================================================================
-; The special generators :do, :let, :parallel, :while, and :until
-; ==========================================================================
-
-(define-syntax \:do
- (syntax-rules ()
-
- ; full decorated -> continue with cc, reentry at (*)
- ((#\:do (cc ...) olet lbs ne1? ilet ne2? lss)
- (cc ... (#\:do olet lbs ne1? ilet ne2? lss)) )
-
- ; short form -> fill in default values
- ((#\:do cc lbs ne1? lss)
- (#\:do cc (let ()) lbs ne1? (let ()) #t lss) )))
-
-
-(define-syntax \:let
- (syntax-rules (index)
- ((\:let cc var (index i) expression)
- (#\:do cc (let ((var expression) (i 0))) () #t (let ()) #f ()) )
- ((\:let cc var expression)
- (#\:do cc (let ((var expression))) () #t (let ()) #f ()) )))
-
-
-(define-syntax \:parallel
- (syntax-rules (#\:do)
- ((\:parallel cc)
- cc )
- ((\:parallel cc (g arg1 arg ...) gen ...)
- (g (\:parallel-1 cc (gen ...)) arg1 arg ...) )))
-
-; (\:parallel-1 cc (to-do ...) result [ next ] )
-; iterates over to-do by converting the first generator into
-; the :do-generator next and merging next into result.
-
-(define-syntax \:parallel-1 ; used as
- (syntax-rules (#\:do let)
-
- ; process next element of to-do, reentry at (**)
- ((\:parallel-1 cc ((g arg1 arg ...) gen ...) result)
- (g (\:parallel-1 cc (gen ...) result) arg1 arg ...) )
-
- ; reentry point (**) -> merge next into result
- ((\:parallel-1
- cc
- gens
- (#\:do (let (ob1 ...) oc1 ...)
- (lb1 ...)
- ne1?1
- (let (ib1 ...) ic1 ...)
- ne2?1
- (ls1 ...) )
- (#\:do (let (ob2 ...) oc2 ...)
- (lb2 ...)
- ne1?2
- (let (ib2 ...) ic2 ...)
- ne2?2
- (ls2 ...) ))
- (\:parallel-1
- cc
- gens
- (#\:do (let (ob1 ... ob2 ...) oc1 ... oc2 ...)
- (lb1 ... lb2 ...)
- (and ne1?1 ne1?2)
- (let (ib1 ... ib2 ...) ic1 ... ic2 ...)
- (and ne2?1 ne2?2)
- (ls1 ... ls2 ...) )))
-
- ; no more gens -> continue with cc, reentry at (*)
- ((\:parallel-1 (cc ...) () result)
- (cc ... result) )))
-
-(define-syntax \:while
- (syntax-rules ()
- ((\:while cc (g arg1 arg ...) test)
- (g (\:while-1 cc test) arg1 arg ...) )))
-
-; (\:while-1 cc test (#\:do ...))
-; modifies the fully decorated :do-generator such that it
-; runs while test is a true value.
-; The original implementation just replaced ne1? by
-; (and ne1? test) as follows:
-;
-; (define-syntax \:while-1
-; (syntax-rules (#\:do)
-; ((\:while-1 cc test (#\:do olet lbs ne1? ilet ne2? lss))
-; (#\:do cc olet lbs (and ne1? test) ilet ne2? lss) )))
-;
-; Bug #1:
-; Unfortunately, this code is wrong because ne1? may depend
-; in the inner bindings introduced in ilet, but ne1? is evaluated
-; outside of the inner bindings. (Refer to the specification of
-; :do to see the structure.)
-; The problem manifests itself (as sunnan@handgranat.org
-; observed, 25-Apr-2005) when the :list-generator is modified:
-;
-; (do-ec (\:while (\:list x '(1 2)) (= x 1)) (display x)).
-;
-; In order to generate proper code, we introduce temporary
-; variables saving the values of the inner bindings. The inner
-; bindings are executed in a new ne1?, which also evaluates ne1?
-; outside the scope of the inner bindings, then the inner commands
-; are executed (possibly changing the variables), and then the
-; values of the inner bindings are saved and (and ne1? test) is
-; returned. In the new ilet, the inner variables are bound and
-; initialized and their values are restored. So we construct:
-;
-; (let (ob .. (ib-tmp #f) ...)
-; oc ...
-; (let loop (lb ...)
-; (if (let (ne1?-value ne1?)
-; (let ((ib-var ib-rhs) ...)
-; ic ...
-; (set! ib-tmp ib-var) ...)
-; (and ne1?-value test))
-; (let ((ib-var ib-tmp) ...)
-; /payload/
-; (if ne2?
-; (loop ls ...) )))))
-;
-; Bug #2:
-; Unfortunately, the above expansion is still incorrect (as Jens-Axel
-; Soegaard pointed out, 4-Jun-2007) because ib-rhs are evaluated even
-; if ne1?-value is #f, indicating that the loop has ended.
-; The problem manifests itself in the following example:
-;
-; (do-ec (\:while (\:list x '(1)) #t) (display x))
-;
-; Which iterates :list beyond exhausting the list '(1).
-;
-; For the fix, we follow Jens-Axel's approach of guarding the evaluation
-; of ib-rhs with a check on ne1?-value.
-
-(define-syntax \:while-1
- (syntax-rules (#\:do let)
- ((\:while-1 cc test (#\:do olet lbs ne1? ilet ne2? lss))
- (\:while-2 cc test () () () (#\:do olet lbs ne1? ilet ne2? lss)))))
-
-(define-syntax \:while-2
- (syntax-rules (#\:do let)
- ((\:while-2 cc
- test
- (ib-let ...)
- (ib-save ...)
- (ib-restore ...)
- (#\:do olet
- lbs
- ne1?
- (let ((ib-var ib-rhs) ib ...) ic ...)
- ne2?
- lss))
- (\:while-2 cc
- test
- (ib-let ... (ib-tmp #f))
- (ib-save ... (ib-var ib-rhs))
- (ib-restore ... (ib-var ib-tmp))
- (#\:do olet
- lbs
- ne1?
- (let (ib ...) ic ... (set! ib-tmp ib-var))
- ne2?
- lss)))
- ((\:while-2 cc
- test
- (ib-let ...)
- (ib-save ...)
- (ib-restore ...)
- (#\:do (let (ob ...) oc ...) lbs ne1? (let () ic ...) ne2? lss))
- (#\:do cc
- (let (ob ... ib-let ...) oc ...)
- lbs
- (let ((ne1?-value ne1?))
- (and ne1?-value
- (let (ib-save ...)
- ic ...
- test)))
- (let (ib-restore ...))
- ne2?
- lss))))
-
-
-(define-syntax \:until
- (syntax-rules ()
- ((\:until cc (g arg1 arg ...) test)
- (g (\:until-1 cc test) arg1 arg ...) )))
-
-(define-syntax \:until-1
- (syntax-rules (#\:do)
- ((\:until-1 cc test (#\:do olet lbs ne1? ilet ne2? lss))
- (#\:do cc olet lbs ne1? ilet (and ne2? (not test)) lss) )))
-
-
-; ==========================================================================
-; The typed generators :list :string :vector etc.
-; ==========================================================================
-
-(define-syntax \:list
- (syntax-rules (index)
- ((\:list cc var (index i) arg ...)
- (\:parallel cc (\:list var arg ...) (\:integers i)) )
- ((\:list cc var arg1 arg2 arg ...)
- (\:list cc var (append arg1 arg2 arg ...)) )
- ((\:list cc var arg)
- (#\:do cc
- (let ())
- ((t arg))
- (not (null? t))
- (let ((var (car t))))
- #t
- ((cdr t)) ))))
-
-
-(define-syntax \:string
- (syntax-rules (index)
- ((\:string cc var (index i) arg)
- (#\:do cc
- (let ((str arg) (len 0))
- (set! len (string-length str)))
- ((i 0))
- (< i len)
- (let ((var (string-ref str i))))
- #t
- ((+ i 1)) ))
- ((\:string cc var (index i) arg1 arg2 arg ...)
- (\:string cc var (index i) (string-append arg1 arg2 arg ...)) )
- ((\:string cc var arg1 arg ...)
- (\:string cc var (index i) arg1 arg ...) )))
-
-; Alternative: An implementation in the style of :vector can also
-; be used for :string. However, it is less interesting as the
-; overhead of string-append is much less than for 'vector-append'.
-
-
-(define-syntax \:vector
- (syntax-rules (index)
- ((\:vector cc var arg)
- (\:vector cc var (index i) arg) )
- ((\:vector cc var (index i) arg)
- (#\:do cc
- (let ((vec arg) (len 0))
- (set! len (vector-length vec)))
- ((i 0))
- (< i len)
- (let ((var (vector-ref vec i))))
- #t
- ((+ i 1)) ))
-
- ((\:vector cc var (index i) arg1 arg2 arg ...)
- (\:parallel cc (\:vector cc var arg1 arg2 arg ...) (\:integers i)) )
- ((\:vector cc var arg1 arg2 arg ...)
- (#\:do cc
- (let ((vec #f)
- (len 0)
- (vecs (ec-:vector-filter (list arg1 arg2 arg ...))) ))
- ((k 0))
- (if (< k len)
- #t
- (if (null? vecs)
- #f
- (begin (set! vec (car vecs))
- (set! vecs (cdr vecs))
- (set! len (vector-length vec))
- (set! k 0)
- #t )))
- (let ((var (vector-ref vec k))))
- #t
- ((+ k 1)) ))))
-
-(define (ec-:vector-filter vecs)
- (if (null? vecs)
- '()
- (if (zero? (vector-length (car vecs)))
- (ec-:vector-filter (cdr vecs))
- (cons (car vecs) (ec-:vector-filter (cdr vecs))) )))
-
-; Alternative: A simpler implementation for :vector uses vector->list
-; append and :list in the multi-argument case. Please refer to the
-; 'design.scm' for more details.
-
-
-(define-syntax \:integers
- (syntax-rules (index)
- ((\:integers cc var (index i))
- (#\:do cc ((var 0) (i 0)) #t ((+ var 1) (+ i 1))) )
- ((\:integers cc var)
- (#\:do cc ((var 0)) #t ((+ var 1))) )))
-
-
-(define-syntax \:range
- (syntax-rules (index)
-
- ; handle index variable and add optional args
- ((\:range cc var (index i) arg1 arg ...)
- (\:parallel cc (\:range var arg1 arg ...) (\:integers i)) )
- ((\:range cc var arg1)
- (\:range cc var 0 arg1 1) )
- ((\:range cc var arg1 arg2)
- (\:range cc var arg1 arg2 1) )
-
-; special cases (partially evaluated by hand from general case)
-
- ((\:range cc var 0 arg2 1)
- (#\:do cc
- (let ((b arg2))
- (if (not (and (integer? b) (exact? b)))
- (error
- "arguments of :range are not exact integer "
- "(use :real-range?)" 0 b 1 )))
- ((var 0))
- (< var b)
- (let ())
- #t
- ((+ var 1)) ))
-
- ((\:range cc var 0 arg2 -1)
- (#\:do cc
- (let ((b arg2))
- (if (not (and (integer? b) (exact? b)))
- (error
- "arguments of :range are not exact integer "
- "(use :real-range?)" 0 b 1 )))
- ((var 0))
- (> var b)
- (let ())
- #t
- ((- var 1)) ))
-
- ((\:range cc var arg1 arg2 1)
- (#\:do cc
- (let ((a arg1) (b arg2))
- (if (not (and (integer? a) (exact? a)
- (integer? b) (exact? b) ))
- (error
- "arguments of :range are not exact integer "
- "(use :real-range?)" a b 1 )) )
- ((var a))
- (< var b)
- (let ())
- #t
- ((+ var 1)) ))
-
- ((\:range cc var arg1 arg2 -1)
- (#\:do cc
- (let ((a arg1) (b arg2) (s -1) (stop 0))
- (if (not (and (integer? a) (exact? a)
- (integer? b) (exact? b) ))
- (error
- "arguments of :range are not exact integer "
- "(use :real-range?)" a b -1 )) )
- ((var a))
- (> var b)
- (let ())
- #t
- ((- var 1)) ))
-
-; the general case
-
- ((\:range cc var arg1 arg2 arg3)
- (#\:do cc
- (let ((a arg1) (b arg2) (s arg3) (stop 0))
- (if (not (and (integer? a) (exact? a)
- (integer? b) (exact? b)
- (integer? s) (exact? s) ))
- (error
- "arguments of :range are not exact integer "
- "(use :real-range?)" a b s ))
- (if (zero? s)
- (error "step size must not be zero in :range") )
- (set! stop (+ a (* (max 0 (ceiling (/ (- b a) s))) s))) )
- ((var a))
- (not (= var stop))
- (let ())
- #t
- ((+ var s)) ))))
-
-; Comment: The macro :range inserts some code to make sure the values
-; are exact integers. This overhead has proven very helpful for
-; saving users from themselves.
-
-
-(define-syntax \:real-range
- (syntax-rules (index)
-
- ; add optional args and index variable
- ((\:real-range cc var arg1)
- (\:real-range cc var (index i) 0 arg1 1) )
- ((\:real-range cc var (index i) arg1)
- (\:real-range cc var (index i) 0 arg1 1) )
- ((\:real-range cc var arg1 arg2)
- (\:real-range cc var (index i) arg1 arg2 1) )
- ((\:real-range cc var (index i) arg1 arg2)
- (\:real-range cc var (index i) arg1 arg2 1) )
- ((\:real-range cc var arg1 arg2 arg3)
- (\:real-range cc var (index i) arg1 arg2 arg3) )
-
- ; the fully qualified case
- ((\:real-range cc var (index i) arg1 arg2 arg3)
- (#\:do cc
- (let ((a arg1) (b arg2) (s arg3) (istop 0))
- (if (not (and (real? a) (real? b) (real? s)))
- (error "arguments of :real-range are not real" a b s) )
- (if (and (exact? a) (or (not (exact? b)) (not (exact? s))))
- (set! a (exact->inexact a)) )
- (set! istop (/ (- b a) s)) )
- ((i 0))
- (< i istop)
- (let ((var (+ a (* s i)))))
- #t
- ((+ i 1)) ))))
-
-; Comment: The macro :real-range adapts the exactness of the start
-; value in case any of the other values is inexact. This is a
-; precaution to avoid (list-ec (\: x 0 3.0) x) => '(0 1.0 2.0).
-
-
-(define-syntax \:char-range
- (syntax-rules (index)
- ((\:char-range cc var (index i) arg1 arg2)
- (\:parallel cc (\:char-range var arg1 arg2) (\:integers i)) )
- ((\:char-range cc var arg1 arg2)
- (#\:do cc
- (let ((imax (char->integer arg2))))
- ((i (char->integer arg1)))
- (<= i imax)
- (let ((var (integer->char i))))
- #t
- ((+ i 1)) ))))
-
-; Warning: There is no R5RS-way to implement the :char-range generator
-; because the integers obtained by char->integer are not necessarily
-; consecutive. We simply assume this anyhow for illustration.
-
-
-(define-syntax \:port
- (syntax-rules (index)
- ((\:port cc var (index i) arg1 arg ...)
- (\:parallel cc (\:port var arg1 arg ...) (\:integers i)) )
- ((\:port cc var arg)
- (\:port cc var arg read) )
- ((\:port cc var arg1 arg2)
- (#\:do cc
- (let ((port arg1) (read-proc arg2)))
- ((var (read-proc port)))
- (not (eof-object? var))
- (let ())
- #t
- ((read-proc port)) ))))
-
-
-; ==========================================================================
-; The typed generator :dispatched and utilities for constructing dispatchers
-; ==========================================================================
-
-(define-syntax \:dispatched
- (syntax-rules (index)
- ((\:dispatched cc var (index i) dispatch arg1 arg ...)
- (\:parallel cc
- (\:integers i)
- (\:dispatched var dispatch arg1 arg ...) ))
- ((\:dispatched cc var dispatch arg1 arg ...)
- (#\:do cc
- (let ((d dispatch)
- (args (list arg1 arg ...))
- (g #f)
- (empty (list #f)) )
- (set! g (d args))
- (if (not (procedure? g))
- (error "unrecognized arguments in dispatching"
- args
- (d '()) )))
- ((var (g empty)))
- (not (eq? var empty))
- (let ())
- #t
- ((g empty)) ))))
-
-; Comment: The unique object empty is created as a newly allocated
-; non-empty list. It is compared using eq? which distinguishes
-; the object from any other object, according to R5RS 6.1.
-
-
-(define-syntax \:generator-proc
- (syntax-rules (#\:do let)
-
- ; call g with a variable, reentry at (**)
- ((\:generator-proc (g arg ...))
- (g (\:generator-proc var) var arg ...) )
-
- ; reentry point (**) -> make the code from a single :do
- ((\:generator-proc
- var
- (#\:do (let obs oc ...)
- ((lv li) ...)
- ne1?
- (let ((i v) ...) ic ...)
- ne2?
- (ls ...)) )
- (ec-simplify
- (let obs
- oc ...
- (let ((lv li) ... (ne2 #t))
- (ec-simplify
- (let ((i #f) ...) ; v not yet valid
- (lambda (empty)
- (if (and ne1? ne2)
- (ec-simplify
- (begin
- (set! i v) ...
- ic ...
- (let ((value var))
- (ec-simplify
- (if ne2?
- (ec-simplify
- (begin (set! lv ls) ...) )
- (set! ne2 #f) ))
- value )))
- empty ))))))))
-
- ; silence warnings of some macro expanders
- ((\:generator-proc var)
- (error "illegal macro call") )))
-
-
-(define (dispatch-union d1 d2)
- (lambda (args)
- (let ((g1 (d1 args)) (g2 (d2 args)))
- (if g1
- (if g2
- (if (null? args)
- (append (if (list? g1) g1 (list g1))
- (if (list? g2) g2 (list g2)) )
- (error "dispatching conflict" args (d1 '()) (d2 '())) )
- g1 )
- (if g2 g2 #f) ))))
-
-
-; ==========================================================================
-; The dispatching generator :
-; ==========================================================================
-
-(define (make-initial-\:-dispatch)
- (lambda (args)
- (case (length args)
- ((0) 'SRFI42)
- ((1) (let ((a1 (car args)))
- (cond
- ((list? a1)
- (\:generator-proc (\:list a1)) )
- ((string? a1)
- (\:generator-proc (\:string a1)) )
- ((vector? a1)
- (\:generator-proc (\:vector a1)) )
- ((and (integer? a1) (exact? a1))
- (\:generator-proc (\:range a1)) )
- ((real? a1)
- (\:generator-proc (\:real-range a1)) )
- ((input-port? a1)
- (\:generator-proc (\:port a1)) )
- (else
- #f ))))
- ((2) (let ((a1 (car args)) (a2 (cadr args)))
- (cond
- ((and (list? a1) (list? a2))
- (\:generator-proc (\:list a1 a2)) )
- ((and (string? a1) (string? a1))
- (\:generator-proc (\:string a1 a2)) )
- ((and (vector? a1) (vector? a2))
- (\:generator-proc (\:vector a1 a2)) )
- ((and (integer? a1) (exact? a1) (integer? a2) (exact? a2))
- (\:generator-proc (\:range a1 a2)) )
- ((and (real? a1) (real? a2))
- (\:generator-proc (\:real-range a1 a2)) )
- ((and (char? a1) (char? a2))
- (\:generator-proc (\:char-range a1 a2)) )
- ((and (input-port? a1) (procedure? a2))
- (\:generator-proc (\:port a1 a2)) )
- (else
- #f ))))
- ((3) (let ((a1 (car args)) (a2 (cadr args)) (a3 (caddr args)))
- (cond
- ((and (list? a1) (list? a2) (list? a3))
- (\:generator-proc (\:list a1 a2 a3)) )
- ((and (string? a1) (string? a1) (string? a3))
- (\:generator-proc (\:string a1 a2 a3)) )
- ((and (vector? a1) (vector? a2) (vector? a3))
- (\:generator-proc (\:vector a1 a2 a3)) )
- ((and (integer? a1) (exact? a1)
- (integer? a2) (exact? a2)
- (integer? a3) (exact? a3))
- (\:generator-proc (\:range a1 a2 a3)) )
- ((and (real? a1) (real? a2) (real? a3))
- (\:generator-proc (\:real-range a1 a2 a3)) )
- (else
- #f ))))
- (else
- (letrec ((every?
- (lambda (pred args)
- (if (null? args)
- #t
- (and (pred (car args))
- (every? pred (cdr args)) )))))
- (cond
- ((every? list? args)
- (\:generator-proc (\:list (apply append args))) )
- ((every? string? args)
- (\:generator-proc (\:string (apply string-append args))) )
- ((every? vector? args)
- (\:generator-proc (\:list (apply append (map vector->list args)))) )
- (else
- #f )))))))
-
-(define \\:-dispatch
- (make-initial-\:-dispatch) )
-
-(define (\\:-dispatch-ref)
- \:-dispatch )
-
-(define (\\:-dispatch-set! dispatch)
- (if (not (procedure? dispatch))
- (error "not a procedure" dispatch) )
- (set! \:-dispatch dispatch) )
-
-(define-syntax \:
- (syntax-rules (index)
- ((\: cc var (index i) arg1 arg ...)
- (\:dispatched cc var (index i) \:-dispatch arg1 arg ...) )
- ((\: cc var arg1 arg ...)
- (\:dispatched cc var \:-dispatch arg1 arg ...) )))
-
-
-; ==========================================================================
-; The utility comprehensions fold-ec, fold3-ec
-; ==========================================================================
-
-(define-syntax fold3-ec
- (syntax-rules (nested)
- ((fold3-ec x0 (nested q1 ...) q etc1 etc2 etc3 etc ...)
- (fold3-ec x0 (nested q1 ... q) etc1 etc2 etc3 etc ...) )
- ((fold3-ec x0 q1 q2 etc1 etc2 etc3 etc ...)
- (fold3-ec x0 (nested q1 q2) etc1 etc2 etc3 etc ...) )
- ((fold3-ec x0 expression f1 f2)
- (fold3-ec x0 (nested) expression f1 f2) )
-
- ((fold3-ec x0 qualifier expression f1 f2)
- (let ((result #f) (empty #t))
- (do-ec qualifier
- (let ((value expression)) ; don't duplicate
- (if empty
- (begin (set! result (f1 value))
- (set! empty #f) )
- (set! result (f2 value result)) )))
- (if empty x0 result) ))))
-
-
-(define-syntax fold-ec
- (syntax-rules (nested)
- ((fold-ec x0 (nested q1 ...) q etc1 etc2 etc ...)
- (fold-ec x0 (nested q1 ... q) etc1 etc2 etc ...) )
- ((fold-ec x0 q1 q2 etc1 etc2 etc ...)
- (fold-ec x0 (nested q1 q2) etc1 etc2 etc ...) )
- ((fold-ec x0 expression f2)
- (fold-ec x0 (nested) expression f2) )
-
- ((fold-ec x0 qualifier expression f2)
- (let ((result x0))
- (do-ec qualifier (set! result (f2 expression result)))
- result ))))
-
-
-; ==========================================================================
-; The comprehensions list-ec string-ec vector-ec etc.
-; ==========================================================================
-
-(define-syntax list-ec
- (syntax-rules ()
- ((list-ec etc1 etc ...)
- (reverse (fold-ec '() etc1 etc ... cons)) )))
-
-; Alternative: Reverse can safely be replaced by reverse! if you have it.
-;
-; Alternative: It is possible to construct the result in the correct order
-; using set-cdr! to add at the tail. This removes the overhead of copying
-; at the end, at the cost of more book-keeping.
-
-
-(define-syntax append-ec
- (syntax-rules ()
- ((append-ec etc1 etc ...)
- (apply append (list-ec etc1 etc ...)) )))
-
-(define-syntax string-ec
- (syntax-rules ()
- ((string-ec etc1 etc ...)
- (list->string (list-ec etc1 etc ...)) )))
-
-; Alternative: For very long strings, the intermediate list may be a
-; problem. A more space-aware implementation collect the characters
-; in an intermediate list and when this list becomes too large it is
-; converted into an intermediate string. At the end, the intermediate
-; strings are concatenated with string-append.
-
-
-(define-syntax string-append-ec
- (syntax-rules ()
- ((string-append-ec etc1 etc ...)
- (apply string-append (list-ec etc1 etc ...)) )))
-
-(define-syntax vector-ec
- (syntax-rules ()
- ((vector-ec etc1 etc ...)
- (list->vector (list-ec etc1 etc ...)) )))
-
-; Comment: A similar approach as for string-ec can be used for vector-ec.
-; However, the space overhead for the intermediate list is much lower
-; than for string-ec and as there is no vector-append, the intermediate
-; vectors must be copied explicitly.
-
-(define-syntax vector-of-length-ec
- (syntax-rules (nested)
- ((vector-of-length-ec k (nested q1 ...) q etc1 etc ...)
- (vector-of-length-ec k (nested q1 ... q) etc1 etc ...) )
- ((vector-of-length-ec k q1 q2 etc1 etc ...)
- (vector-of-length-ec k (nested q1 q2) etc1 etc ...) )
- ((vector-of-length-ec k expression)
- (vector-of-length-ec k (nested) expression) )
-
- ((vector-of-length-ec k qualifier expression)
- (let ((len k))
- (let ((vec (make-vector len))
- (i 0) )
- (do-ec qualifier
- (if (< i len)
- (begin (vector-set! vec i expression)
- (set! i (+ i 1)) )
- (error "vector is too short for the comprehension") ))
- (if (= i len)
- vec
- (error "vector is too long for the comprehension") ))))))
-
-
-(define-syntax sum-ec
- (syntax-rules ()
- ((sum-ec etc1 etc ...)
- (fold-ec (+) etc1 etc ... +) )))
-
-(define-syntax product-ec
- (syntax-rules ()
- ((product-ec etc1 etc ...)
- (fold-ec (*) etc1 etc ... *) )))
-
-(define-syntax min-ec
- (syntax-rules ()
- ((min-ec etc1 etc ...)
- (fold3-ec (min) etc1 etc ... min min) )))
-
-(define-syntax max-ec
- (syntax-rules ()
- ((max-ec etc1 etc ...)
- (fold3-ec (max) etc1 etc ... max max) )))
-
-(define-syntax last-ec
- (syntax-rules (nested)
- ((last-ec default (nested q1 ...) q etc1 etc ...)
- (last-ec default (nested q1 ... q) etc1 etc ...) )
- ((last-ec default q1 q2 etc1 etc ...)
- (last-ec default (nested q1 q2) etc1 etc ...) )
- ((last-ec default expression)
- (last-ec default (nested) expression) )
-
- ((last-ec default qualifier expression)
- (let ((result default))
- (do-ec qualifier (set! result expression))
- result ))))
-
-
-; ==========================================================================
-; The fundamental early-stopping comprehension first-ec
-; ==========================================================================
-
-(define-syntax first-ec
- (syntax-rules (nested)
- ((first-ec default (nested q1 ...) q etc1 etc ...)
- (first-ec default (nested q1 ... q) etc1 etc ...) )
- ((first-ec default q1 q2 etc1 etc ...)
- (first-ec default (nested q1 q2) etc1 etc ...) )
- ((first-ec default expression)
- (first-ec default (nested) expression) )
-
- ((first-ec default qualifier expression)
- (let ((result default) (stop #f))
- (ec-guarded-do-ec
- stop
- (nested qualifier)
- (begin (set! result expression)
- (set! stop #t) ))
- result ))))
-
-; (ec-guarded-do-ec stop (nested q ...) cmd)
-; constructs (do-ec q ... cmd) where the generators gen in q ... are
-; replaced by (\:until gen stop).
-
-(define-syntax ec-guarded-do-ec
- (syntax-rules (nested if not and or begin)
-
- ((ec-guarded-do-ec stop (nested (nested q1 ...) q2 ...) cmd)
- (ec-guarded-do-ec stop (nested q1 ... q2 ...) cmd) )
-
- ((ec-guarded-do-ec stop (nested (if test) q ...) cmd)
- (if test (ec-guarded-do-ec stop (nested q ...) cmd)) )
- ((ec-guarded-do-ec stop (nested (not test) q ...) cmd)
- (if (not test) (ec-guarded-do-ec stop (nested q ...) cmd)) )
- ((ec-guarded-do-ec stop (nested (and test ...) q ...) cmd)
- (if (and test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) )
- ((ec-guarded-do-ec stop (nested (or test ...) q ...) cmd)
- (if (or test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) )
-
- ((ec-guarded-do-ec stop (nested (begin etc ...) q ...) cmd)
- (begin etc ... (ec-guarded-do-ec stop (nested q ...) cmd)) )
-
- ((ec-guarded-do-ec stop (nested gen q ...) cmd)
- (do-ec
- (\:until gen stop)
- (ec-guarded-do-ec stop (nested q ...) cmd) ))
-
- ((ec-guarded-do-ec stop (nested) cmd)
- (do-ec cmd) )))
-
-; Alternative: Instead of modifying the generator with :until, it is
-; possible to use call-with-current-continuation:
-;
-; (define-synatx first-ec
-; ...same as above...
-; ((first-ec default qualifier expression)
-; (call-with-current-continuation
-; (lambda (cc)
-; (do-ec qualifier (cc expression))
-; default ))) ))
-;
-; This is much simpler but not necessarily as efficient.
-
-
-; ==========================================================================
-; The early-stopping comprehensions any?-ec every?-ec
-; ==========================================================================
-
-(define-syntax any?-ec
- (syntax-rules (nested)
- ((any?-ec (nested q1 ...) q etc1 etc ...)
- (any?-ec (nested q1 ... q) etc1 etc ...) )
- ((any?-ec q1 q2 etc1 etc ...)
- (any?-ec (nested q1 q2) etc1 etc ...) )
- ((any?-ec expression)
- (any?-ec (nested) expression) )
-
- ((any?-ec qualifier expression)
- (first-ec #f qualifier (if expression) #t) )))
-
-(define-syntax every?-ec
- (syntax-rules (nested)
- ((every?-ec (nested q1 ...) q etc1 etc ...)
- (every?-ec (nested q1 ... q) etc1 etc ...) )
- ((every?-ec q1 q2 etc1 etc ...)
- (every?-ec (nested q1 q2) etc1 etc ...) )
- ((every?-ec expression)
- (every?-ec (nested) expression) )
-
- ((every?-ec qualifier expression)
- (first-ec #t qualifier (if (not expression)) #f) )))
-
-;;;;;; SRFI 43: Vector library -*- Scheme -*-
-;;;
-;;; $Id: vector-lib.scm,v 1.7 2009/03/29 09:46:03 sperber Exp $
-;;;
-;;; Taylor Campbell wrote this code; he places it in the public domain.
-;;; Will Clinger [wdc] made some corrections, also in the public domain.
-
-;;; Copyright (C) Taylor Campbell (2003). All rights reserved.
-
-;;; Made an R7RS library by Taylan Ulrich Bayırlı/Kammer, Copyright (C) 2014.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-;;; --------------------
-;;; Exported procedure index
-;;;
-;;; * Constructors
-;;; make-vector vector
-;;; vector-unfold vector-unfold-right
-;;; vector-copy vector-reverse-copy
-;;; vector-append vector-concatenate
-;;;
-;;; * Predicates
-;;; vector?
-;;; vector-empty?
-;;; vector=
-;;;
-;;; * Selectors
-;;; vector-ref
-;;; vector-length
-;;;
-;;; * Iteration
-;;; vector-fold vector-fold-right
-;;; vector-map vector-map!
-;;; vector-for-each
-;;; vector-count
-;;;
-;;; * Searching
-;;; vector-index vector-skip
-;;; vector-index-right vector-skip-right
-;;; vector-binary-search
-;;; vector-any vector-every
-;;;
-;;; * Mutators
-;;; vector-set!
-;;; vector-swap!
-;;; vector-fill!
-;;; vector-reverse!
-;;; vector-copy! vector-reverse-copy!
-;;; vector-reverse!
-;;;
-;;; * Conversion
-;;; vector->list reverse-vector->list
-;;; list->vector reverse-list->vector
-
-
-
-;;; --------------------
-;;; Commentary on efficiency of the code
-
-;;; This code is somewhat tuned for efficiency. There are several
-;;; internal routines that can be optimized greatly to greatly improve
-;;; the performance of much of the library. These internal procedures
-;;; are already carefully tuned for performance, and lambda-lifted by
-;;; hand. Some other routines are lambda-lifted by hand, but only the
-;;; loops are lambda-lifted, and only if some routine has two possible
-;;; loops -- a fast path and an n-ary case --, whereas _all_ of the
-;;; internal routines' loops are lambda-lifted so as to never cons a
-;;; closure in their body (VECTOR-PARSE-START+END doesn't have a loop),
-;;; even in Scheme systems that perform no loop optimization (which is
-;;; most of them, unfortunately).
-;;;
-;;; Fast paths are provided for common cases in most of the loops in
-;;; this library.
-;;;
-;;; All calls to primitive vector operations are protected by a prior
-;;; type check; they can be safely converted to use unsafe equivalents
-;;; of the operations, if available. Ideally, the compiler should be
-;;; able to determine this, but the state of Scheme compilers today is
-;;; not a happy one.
-;;;
-;;; Efficiency of the actual algorithms is a rather mundane point to
-;;; mention; vector operations are rarely beyond being straightforward.
-
-
-
-;;; --------------------
-;;; Utilities
-
-(define (nonneg-int? x)
- (and (integer? x)
- (not (negative? x))))
-
-(define (between? x y z)
- (and (< x y)
- (<= y z)))
-
-(define (unspecified-value) (if #f #f))
-
-;++ This should be implemented more efficiently. It shouldn't cons a
-;++ closure, and the cons cells used in the loops when using this could
-;++ be reused.
-(define (vectors-ref vectors i)
- (map (lambda (v) (vector-ref v i)) vectors))
-
-
-
-;;; --------------------
-;;; Internal routines
-
-;;; These should all be integrated, native, or otherwise optimized --
-;;; they're used a _lot_ --. All of the loops and LETs inside loops
-;;; are lambda-lifted by hand, just so as not to cons closures in the
-;;; loops. (If your compiler can do better than that if they're not
-;;; lambda-lifted, then lambda-drop (?) them.)
-
-;;; (VECTOR-PARSE-START+END <vector> <arguments>
-;;; <start-name> <end-name>
-;;; <callee>)
-;;; -> [start end]
-;;; Return two values, composing a valid range within VECTOR, as
-;;; extracted from ARGUMENTS or defaulted from VECTOR -- 0 for START
-;;; and the length of VECTOR for END --; START-NAME and END-NAME are
-;;; purely for error checking.
-(define (vector-parse-start+end vec args start-name end-name callee)
- (let ((len (vector-length vec)))
- (cond ((null? args)
- (values 0 len))
- ((null? (cdr args))
- (check-indices vec
- (car args) start-name
- len end-name
- callee))
- ((null? (cddr args))
- (check-indices vec
- (car args) start-name
- (cadr args) end-name
- callee))
- (else
- (error "too many arguments"
- `(extra args were ,(cddr args))
- `(while calling ,callee))))))
-
-(define-syntax let-vector-start+end
- (syntax-rules ()
- ((let-vector-start+end ?callee ?vec ?args (?start ?end)
- ?body1 ?body2 ...)
- (let ((?vec (check-type vector? ?vec ?callee)))
- (receive (?start ?end)
- (vector-parse-start+end ?vec ?args '?start '?end
- ?callee)
- ?body1 ?body2 ...)))))
-
-;;; (%SMALLEST-LENGTH <vector-list> <default-length> <callee>)
-;;; -> exact, nonnegative integer
-;;; Compute the smallest length of VECTOR-LIST. DEFAULT-LENGTH is
-;;; the length that is returned if VECTOR-LIST is empty. Common use
-;;; of this is in n-ary vector routines:
-;;; (define (f vec . vectors)
-;;; (let ((vec (check-type vector? vec f)))
-;;; ...(%smallest-length vectors (vector-length vec) f)...))
-;;; %SMALLEST-LENGTH takes care of the type checking -- which is what
-;;; the CALLEE argument is for --; thus, the design is tuned for
-;;; avoiding redundant type checks.
-(define %smallest-length
- (letrec ((loop (lambda (vector-list length callee)
- (if (null? vector-list)
- length
- (loop (cdr vector-list)
- (min (vector-length
- (check-type vector?
- (car vector-list)
- callee))
- length)
- callee)))))
- loop))
-
-;;; (%VECTOR-REVERSE-COPY! <target> <tstart> <source> <sstart> <send>)
-;;; Copy elements from SSTART to SEND from SOURCE to TARGET, in the
-;;; reverse order.
-(define %vector-reverse-copy!
- (letrec ((loop (lambda (target source sstart i j)
- (cond ((>= i sstart)
- (vector-set! target j (vector-ref source i))
- (loop target source sstart
- (- i 1)
- (+ j 1)))))))
- (lambda (target tstart source sstart send)
- (loop target source sstart
- (- send 1)
- tstart))))
-
-;;; (%VECTOR-REVERSE! <vector>)
-(define %vector-reverse!
- (letrec ((loop (lambda (vec i j)
- (cond ((<= i j)
- (let ((v (vector-ref vec i)))
- (vector-set! vec i (vector-ref vec j))
- (vector-set! vec j v)
- (loop vec (+ i 1) (- j 1))))))))
- (lambda (vec start end)
- (loop vec start (- end 1)))))
-
-;;; (%VECTOR-FOLD1 <kons> <knil> <vector>) -> knil'
-;;; (KONS <index> <knil> <elt>) -> knil'
-(define %vector-fold1
- (letrec ((loop (lambda (kons knil len vec i)
- (if (= i len)
- knil
- (loop kons
- (kons i knil (vector-ref vec i))
- len vec (+ i 1))))))
- (lambda (kons knil len vec)
- (loop kons knil len vec 0))))
-
-;;; (%VECTOR-FOLD2+ <kons> <knil> <vector> ...) -> knil'
-;;; (KONS <index> <knil> <elt> ...) -> knil'
-(define %vector-fold2+
- (letrec ((loop (lambda (kons knil len vectors i)
- (if (= i len)
- knil
- (loop kons
- (apply kons i knil
- (vectors-ref vectors i))
- len vectors (+ i 1))))))
- (lambda (kons knil len vectors)
- (loop kons knil len vectors 0))))
-
-;;; (%VECTOR-MAP! <f> <target> <length> <vector>) -> target
-;;; (F <index> <elt>) -> elt'
-(define %vector-map1!
- (letrec ((loop (lambda (f target vec i)
- (if (zero? i)
- target
- (let ((j (- i 1)))
- (vector-set! target j
- (f j (vector-ref vec j)))
- (loop f target vec j))))))
- (lambda (f target vec len)
- (loop f target vec len))))
-
-;;; (%VECTOR-MAP2+! <f> <target> <vectors> <len>) -> target
-;;; (F <index> <elt> ...) -> elt'
-(define %vector-map2+!
- (letrec ((loop (lambda (f target vectors i)
- (if (zero? i)
- target
- (let ((j (- i 1)))
- (vector-set! target j
- (apply f j (vectors-ref vectors j)))
- (loop f target vectors j))))))
- (lambda (f target vectors len)
- (loop f target vectors len))))
-
-
-
-;;;;;;;;;;;;;;;;;;;;;;;; ***** vector-lib ***** ;;;;;;;;;;;;;;;;;;;;;;;
-
-;;; --------------------
-;;; Constructors
-
-;;; (VECTOR-UNFOLD <f> <length> <initial-seed> ...) -> vector
-;;; (F <index> <seed> ...) -> [elt seed' ...]
-;;; The fundamental vector constructor. Creates a vector whose
-;;; length is LENGTH and iterates across each index K between 0 and
-;;; LENGTH, applying F at each iteration to the current index and the
-;;; current seeds to receive N+1 values: first, the element to put in
-;;; the Kth slot and then N new seeds for the next iteration.
-(define vector-unfold
- (letrec ((tabulate! ; Special zero-seed case.
- (lambda (f vec i len)
- (cond ((< i len)
- (vector-set! vec i (f i))
- (tabulate! f vec (+ i 1) len)))))
- (unfold1! ; Fast path for one seed.
- (lambda (f vec i len seed)
- (if (< i len)
- (receive (elt new-seed)
- (f i seed)
- (vector-set! vec i elt)
- (unfold1! f vec (+ i 1) len new-seed)))))
- (unfold2+! ; Slower variant for N seeds.
- (lambda (f vec i len seeds)
- (if (< i len)
- (receive (elt . new-seeds)
- (apply f i seeds)
- (vector-set! vec i elt)
- (unfold2+! f vec (+ i 1) len new-seeds))))))
- (lambda (f len . initial-seeds)
- (let ((f (check-type procedure? f vector-unfold))
- (len (check-type nonneg-int? len vector-unfold)))
- (let ((vec (make-vector len)))
- (cond ((null? initial-seeds)
- (tabulate! f vec 0 len))
- ((null? (cdr initial-seeds))
- (unfold1! f vec 0 len (car initial-seeds)))
- (else
- (unfold2+! f vec 0 len initial-seeds)))
- vec)))))
-
-;;; (VECTOR-UNFOLD-RIGHT <f> <length> <initial-seed> ...) -> vector
-;;; (F <seed> ...) -> [seed' ...]
-;;; Like VECTOR-UNFOLD, but it generates elements from LENGTH to 0
-;;; (still exclusive with LENGTH and inclusive with 0), not 0 to
-;;; LENGTH as with VECTOR-UNFOLD.
-(define vector-unfold-right
- (letrec ((tabulate!
- (lambda (f vec i)
- (cond ((>= i 0)
- (vector-set! vec i (f i))
- (tabulate! f vec (- i 1))))))
- (unfold1!
- (lambda (f vec i seed)
- (if (>= i 0)
- (receive (elt new-seed)
- (f i seed)
- (vector-set! vec i elt)
- (unfold1! f vec (- i 1) new-seed)))))
- (unfold2+!
- (lambda (f vec i seeds)
- (if (>= i 0)
- (receive (elt . new-seeds)
- (apply f i seeds)
- (vector-set! vec i elt)
- (unfold2+! f vec (- i 1) new-seeds))))))
- (lambda (f len . initial-seeds)
- (let ((f (check-type procedure? f vector-unfold-right))
- (len (check-type nonneg-int? len vector-unfold-right)))
- (let ((vec (make-vector len))
- (i (- len 1)))
- (cond ((null? initial-seeds)
- (tabulate! f vec i))
- ((null? (cdr initial-seeds))
- (unfold1! f vec i (car initial-seeds)))
- (else
- (unfold2+! f vec i initial-seeds)))
- vec)))))
-
-;;; (VECTOR-REVERSE-COPY <vector> [<start> <end>]) -> vector
-;;; Create a newly allocated vector whose elements are the reversed
-;;; sequence of elements between START and END in VECTOR. START's
-;;; default is 0; END's default is the length of VECTOR.
-(define (vector-reverse-copy vec . maybe-start+end)
- (let-vector-start+end vector-reverse-copy vec maybe-start+end
- (start end)
- (let ((new (make-vector (- end start))))
- (%vector-reverse-copy! new 0 vec start end)
- new)))
-
-;;; (VECTOR-CONCATENATE <vector-list>) -> vector
-;;; Concatenate the vectors in VECTOR-LIST. This is equivalent to
-;;; (apply vector-append VECTOR-LIST)
-;;; but VECTOR-APPEND tends to be implemented in terms of
-;;; VECTOR-CONCATENATE, and some Schemes bork when the list to apply
-;;; a function to is too long.
-;;;
-;;; Actually, they're both implemented in terms of an internal routine.
-(define (vector-concatenate vector-list)
- (vector-concatenate:aux vector-list vector-concatenate))
-
-;;; Auxiliary for VECTOR-APPEND and VECTOR-CONCATENATE
-(define vector-concatenate:aux
- (letrec ((compute-length
- (lambda (vectors len callee)
- (if (null? vectors)
- len
- (let ((vec (check-type vector? (car vectors)
- callee)))
- (compute-length (cdr vectors)
- (+ (vector-length vec) len)
- callee)))))
- (concatenate!
- (lambda (vectors target to)
- (if (null? vectors)
- target
- (let* ((vec1 (car vectors))
- (len (vector-length vec1)))
- (vector-copy! target to vec1 0 len)
- (concatenate! (cdr vectors) target
- (+ to len)))))))
- (lambda (vectors callee)
- (cond ((null? vectors) ;+++
- (make-vector 0))
- ((null? (cdr vectors)) ;+++
- ;; Blech, we still have to allocate a new one.
- (let* ((vec (check-type vector? (car vectors) callee))
- (len (vector-length vec))
- (new (make-vector len)))
- (vector-copy! new 0 vec 0 len)
- new))
- (else
- (let ((new-vector
- (make-vector (compute-length vectors 0 callee))))
- (concatenate! vectors new-vector 0)
- new-vector))))))
-
-
-
-;;; --------------------
-;;; Predicates
-
-;;; (VECTOR-EMPTY? <vector>) -> boolean
-;;; Return #T if VECTOR has zero elements in it, i.e. VECTOR's length
-;;; is 0, and #F if not.
-(define (vector-empty? vec)
- (let ((vec (check-type vector? vec vector-empty?)))
- (zero? (vector-length vec))))
-
-;;; (VECTOR= <elt=?> <vector> ...) -> boolean
-;;; (ELT=? <value> <value>) -> boolean
-;;; Determine vector equality generalized across element comparators.
-;;; Vectors A and B are equal iff their lengths are the same and for
-;;; each respective elements E_a and E_b (element=? E_a E_b) returns
-;;; a true value. ELT=? is always applied to two arguments. Element
-;;; comparison must be consistent wtih EQ?; that is, if (eq? E_a E_b)
-;;; results in a true value, then (ELEMENT=? E_a E_b) must result in a
-;;; true value. This may be exploited to avoid multiple unnecessary
-;;; element comparisons. (This implementation does, but does not deal
-;;; with the situation that ELEMENT=? is EQ? to avoid more unnecessary
-;;; comparisons, but I believe this optimization is probably fairly
-;;; insignificant.)
-;;;
-;;; If the number of vector arguments is zero or one, then #T is
-;;; automatically returned. If there are N vector arguments,
-;;; VECTOR_1 VECTOR_2 ... VECTOR_N, then VECTOR_1 & VECTOR_2 are
-;;; compared; if they are equal, the vectors VECTOR_2 ... VECTOR_N
-;;; are compared. The precise order in which ELT=? is applied is not
-;;; specified.
-(define (vector= elt=? . vectors)
- (let ((elt=? (check-type procedure? elt=? vector=)))
- (cond ((null? vectors)
- #t)
- ((null? (cdr vectors))
- (check-type vector? (car vectors) vector=)
- #t)
- (else
- (let loop ((vecs vectors))
- (let ((vec1 (check-type vector? (car vecs) vector=))
- (vec2+ (cdr vecs)))
- (or (null? vec2+)
- (and (binary-vector= elt=? vec1 (car vec2+))
- (loop vec2+)))))))))
-(define (binary-vector= elt=? vector-a vector-b)
- (or (eq? vector-a vector-b) ;+++
- (let ((length-a (vector-length vector-a))
- (length-b (vector-length vector-b)))
- (letrec ((loop (lambda (i)
- (or (= i length-a)
- (and (< i length-b)
- (test (vector-ref vector-a i)
- (vector-ref vector-b i)
- i)))))
- (test (lambda (elt-a elt-b i)
- (and (or (eq? elt-a elt-b) ;+++
- (elt=? elt-a elt-b))
- (loop (+ i 1))))))
- (and (= length-a length-b)
- (loop 0))))))
-
-
-
-;;; --------------------
-;;; Iteration
-
-;;; (VECTOR-FOLD <kons> <initial-knil> <vector> ...) -> knil
-;;; (KONS <knil> <elt> ...) -> knil' ; N vectors -> N+1 args
-;;; The fundamental vector iterator. KONS is iterated over each
-;;; index in all of the vectors in parallel, stopping at the end of
-;;; the shortest; KONS is applied to an argument list of (list I
-;;; STATE (vector-ref VEC I) ...), where STATE is the current state
-;;; value -- the state value begins with KNIL and becomes whatever
-;;; KONS returned at the respective iteration --, and I is the
-;;; current index in the iteration. The iteration is strictly left-
-;;; to-right.
-;;; (vector-fold KONS KNIL (vector E_1 E_2 ... E_N))
-;;; <=>
-;;; (KONS (... (KONS (KONS KNIL E_1) E_2) ... E_N-1) E_N)
-(define (vector-fold kons knil vec . vectors)
- (let ((kons (check-type procedure? kons vector-fold))
- (vec (check-type vector? vec vector-fold)))
- (if (null? vectors)
- (%vector-fold1 kons knil (vector-length vec) vec)
- (%vector-fold2+ kons knil
- (%smallest-length vectors
- (vector-length vec)
- vector-fold)
- (cons vec vectors)))))
-
-;;; (VECTOR-FOLD-RIGHT <kons> <initial-knil> <vector> ...) -> knil
-;;; (KONS <knil> <elt> ...) -> knil' ; N vectors => N+1 args
-;;; The fundamental vector recursor. Iterates in parallel across
-;;; VECTOR ... right to left, applying KONS to the elements and the
-;;; current state value; the state value becomes what KONS returns
-;;; at each next iteration. KNIL is the initial state value.
-;;; (vector-fold-right KONS KNIL (vector E_1 E_2 ... E_N))
-;;; <=>
-;;; (KONS (... (KONS (KONS KNIL E_N) E_N-1) ... E_2) E_1)
-;;;
-;;; Not implemented in terms of a more primitive operations that might
-;;; called %VECTOR-FOLD-RIGHT due to the fact that it wouldn't be very
-;;; useful elsewhere.
-(define vector-fold-right
- (letrec ((loop1 (lambda (kons knil vec i)
- (if (negative? i)
- knil
- (loop1 kons (kons i knil (vector-ref vec i))
- vec
- (- i 1)))))
- (loop2+ (lambda (kons knil vectors i)
- (if (negative? i)
- knil
- (loop2+ kons
- (apply kons i knil
- (vectors-ref vectors i))
- vectors
- (- i 1))))))
- (lambda (kons knil vec . vectors)
- (let ((kons (check-type procedure? kons vector-fold-right))
- (vec (check-type vector? vec vector-fold-right)))
- (if (null? vectors)
- (loop1 kons knil vec (- (vector-length vec) 1))
- (loop2+ kons knil (cons vec vectors)
- (- (%smallest-length vectors
- (vector-length vec)
- vector-fold-right)
- 1)))))))
-
-;;; (VECTOR-MAP <f> <vector> ...) -> vector
-;;; (F <elt> ...) -> value ; N vectors -> N args
-;;; Constructs a new vector of the shortest length of the vector
-;;; arguments. Each element at index I of the new vector is mapped
-;;; from the old vectors by (F I (vector-ref VECTOR I) ...). The
-;;; dynamic order of application of F is unspecified.
-(define (vector-map f vec . vectors)
- (let ((f (check-type procedure? f vector-map))
- (vec (check-type vector? vec vector-map)))
- (if (null? vectors)
- (let ((len (vector-length vec)))
- (%vector-map1! f (make-vector len) vec len))
- (let ((len (%smallest-length vectors
- (vector-length vec)
- vector-map)))
- (%vector-map2+! f (make-vector len) (cons vec vectors)
- len)))))
-
-;;; (VECTOR-MAP! <f> <vector> ...) -> unspecified
-;;; (F <elt> ...) -> element' ; N vectors -> N args
-;;; Similar to VECTOR-MAP, but rather than mapping the new elements
-;;; into a new vector, the new mapped elements are destructively
-;;; inserted into the first vector. Again, the dynamic order of
-;;; application of F is unspecified, so it is dangerous for F to
-;;; manipulate the first VECTOR.
-(define (vector-map! f vec . vectors)
- (let ((f (check-type procedure? f vector-map!))
- (vec (check-type vector? vec vector-map!)))
- (if (null? vectors)
- (%vector-map1! f vec vec (vector-length vec))
- (%vector-map2+! f vec (cons vec vectors)
- (%smallest-length vectors
- (vector-length vec)
- vector-map!)))
- (unspecified-value)))
-
-;;; (VECTOR-FOR-EACH <f> <vector> ...) -> unspecified
-;;; (F <elt> ...) ; N vectors -> N args
-;;; Simple vector iterator: applies F to each index in the range [0,
-;;; LENGTH), where LENGTH is the length of the smallest vector
-;;; argument passed, and the respective element at that index. In
-;;; contrast with VECTOR-MAP, F is reliably applied to each
-;;; subsequent elements, starting at index 0 from left to right, in
-;;; the vectors.
-(define vector-for-each
- (letrec ((for-each1
- (lambda (f vec i len)
- (cond ((< i len)
- (f i (vector-ref vec i))
- (for-each1 f vec (+ i 1) len)))))
- (for-each2+
- (lambda (f vecs i len)
- (cond ((< i len)
- (apply f i (vectors-ref vecs i))
- (for-each2+ f vecs (+ i 1) len))))))
- (lambda (f vec . vectors)
- (let ((f (check-type procedure? f vector-for-each))
- (vec (check-type vector? vec vector-for-each)))
- (if (null? vectors)
- (for-each1 f vec 0 (vector-length vec))
- (for-each2+ f (cons vec vectors) 0
- (%smallest-length vectors
- (vector-length vec)
- vector-for-each)))))))
-
-;;; (VECTOR-COUNT <predicate?> <vector> ...)
-;;; -> exact, nonnegative integer
-;;; (PREDICATE? <index> <value> ...) ; N vectors -> N+1 args
-;;; PREDICATE? is applied element-wise to the elements of VECTOR ...,
-;;; and a count is tallied of the number of elements for which a
-;;; true value is produced by PREDICATE?. This count is returned.
-(define (vector-count pred? vec . vectors)
- (let ((pred? (check-type procedure? pred? vector-count))
- (vec (check-type vector? vec vector-count)))
- (if (null? vectors)
- (%vector-fold1 (lambda (index count elt)
- (if (pred? index elt)
- (+ count 1)
- count))
- 0
- (vector-length vec)
- vec)
- (%vector-fold2+ (lambda (index count . elts)
- (if (apply pred? index elts)
- (+ count 1)
- count))
- 0
- (%smallest-length vectors
- (vector-length vec)
- vector-count)
- (cons vec vectors)))))
-
-
-
-;;; --------------------
-;;; Searching
-
-;;; (VECTOR-INDEX <predicate?> <vector> ...)
-;;; -> exact, nonnegative integer or #F
-;;; (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args
-;;; Search left-to-right across VECTOR ... in parallel, returning the
-;;; index of the first set of values VALUE ... such that (PREDICATE?
-;;; VALUE ...) returns a true value; if no such set of elements is
-;;; reached, return #F.
-(define (vector-index pred? vec . vectors)
- (vector-index/skip pred? vec vectors vector-index))
-
-;;; (VECTOR-SKIP <predicate?> <vector> ...)
-;;; -> exact, nonnegative integer or #F
-;;; (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args
-;;; (vector-index (lambda elts (not (apply PREDICATE? elts)))
-;;; VECTOR ...)
-;;; Like VECTOR-INDEX, but find the index of the first set of values
-;;; that do _not_ satisfy PREDICATE?.
-(define (vector-skip pred? vec . vectors)
- (vector-index/skip (lambda elts (not (apply pred? elts)))
- vec vectors
- vector-skip))
-
-;;; Auxiliary for VECTOR-INDEX & VECTOR-SKIP
-(define vector-index/skip
- (letrec ((loop1 (lambda (pred? vec len i)
- (cond ((= i len) #f)
- ((pred? (vector-ref vec i)) i)
- (else (loop1 pred? vec len (+ i 1))))))
- (loop2+ (lambda (pred? vectors len i)
- (cond ((= i len) #f)
- ((apply pred? (vectors-ref vectors i)) i)
- (else (loop2+ pred? vectors len
- (+ i 1)))))))
- (lambda (pred? vec vectors callee)
- (let ((pred? (check-type procedure? pred? callee))
- (vec (check-type vector? vec callee)))
- (if (null? vectors)
- (loop1 pred? vec (vector-length vec) 0)
- (loop2+ pred? (cons vec vectors)
- (%smallest-length vectors
- (vector-length vec)
- callee)
- 0))))))
-
-;;; (VECTOR-INDEX-RIGHT <predicate?> <vector> ...)
-;;; -> exact, nonnegative integer or #F
-;;; (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args
-;;; Right-to-left variant of VECTOR-INDEX.
-(define (vector-index-right pred? vec . vectors)
- (vector-index/skip-right pred? vec vectors vector-index-right))
-
-;;; (VECTOR-SKIP-RIGHT <predicate?> <vector> ...)
-;;; -> exact, nonnegative integer or #F
-;;; (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args
-;;; Right-to-left variant of VECTOR-SKIP.
-(define (vector-skip-right pred? vec . vectors)
- (vector-index/skip-right (lambda elts (not (apply pred? elts)))
- vec vectors
- vector-index-right))
-
-(define vector-index/skip-right
- (letrec ((loop1 (lambda (pred? vec i)
- (cond ((negative? i) #f)
- ((pred? (vector-ref vec i)) i)
- (else (loop1 pred? vec (- i 1))))))
- (loop2+ (lambda (pred? vectors i)
- (cond ((negative? i) #f)
- ((apply pred? (vectors-ref vectors i)) i)
- (else (loop2+ pred? vectors (- i 1)))))))
- (lambda (pred? vec vectors callee)
- (let ((pred? (check-type procedure? pred? callee))
- (vec (check-type vector? vec callee)))
- (if (null? vectors)
- (loop1 pred? vec (- (vector-length vec) 1))
- (loop2+ pred? (cons vec vectors)
- (- (%smallest-length vectors
- (vector-length vec)
- callee)
- 1)))))))
-
-;;; (VECTOR-BINARY-SEARCH <vector> <value> <cmp> [<start> <end>])
-;;; -> exact, nonnegative integer or #F
-;;; (CMP <value1> <value2>) -> integer
-;;; positive -> VALUE1 > VALUE2
-;;; zero -> VALUE1 = VALUE2
-;;; negative -> VALUE1 < VALUE2
-;;; Perform a binary search through VECTOR for VALUE, comparing each
-;;; element to VALUE with CMP.
-(define (vector-binary-search vec value cmp . maybe-start+end)
- (let ((cmp (check-type procedure? cmp vector-binary-search)))
- (let-vector-start+end vector-binary-search vec maybe-start+end
- (start end)
- (let loop ((start start) (end end) (j #f))
- (let ((i (quotient (+ start end) 2)))
- (if (or (= start end) (and j (= i j)))
- #f
- (let ((comparison
- (check-type integer?
- (cmp (vector-ref vec i) value)
- `(,cmp for ,vector-binary-search))))
- (cond ((zero? comparison) i)
- ((positive? comparison) (loop start i i))
- (else (loop i end i))))))))))
-
-;;; (VECTOR-ANY <pred?> <vector> ...) -> value
-;;; Apply PRED? to each parallel element in each VECTOR ...; if PRED?
-;;; should ever return a true value, immediately stop and return that
-;;; value; otherwise, when the shortest vector runs out, return #F.
-;;; The iteration and order of application of PRED? across elements
-;;; is of the vectors is strictly left-to-right.
-(define vector-any
- (letrec ((loop1 (lambda (pred? vec i len len-1)
- (and (not (= i len))
- (if (= i len-1)
- (pred? (vector-ref vec i))
- (or (pred? (vector-ref vec i))
- (loop1 pred? vec (+ i 1)
- len len-1))))))
- (loop2+ (lambda (pred? vectors i len len-1)
- (and (not (= i len))
- (if (= i len-1)
- (apply pred? (vectors-ref vectors i))
- (or (apply pred? (vectors-ref vectors i))
- (loop2+ pred? vectors (+ i 1)
- len len-1)))))))
- (lambda (pred? vec . vectors)
- (let ((pred? (check-type procedure? pred? vector-any))
- (vec (check-type vector? vec vector-any)))
- (if (null? vectors)
- (let ((len (vector-length vec)))
- (loop1 pred? vec 0 len (- len 1)))
- (let ((len (%smallest-length vectors
- (vector-length vec)
- vector-any)))
- (loop2+ pred? (cons vec vectors) 0 len (- len 1))))))))
-
-;;; (VECTOR-EVERY <pred?> <vector> ...) -> value
-;;; Apply PRED? to each parallel value in each VECTOR ...; if PRED?
-;;; should ever return #F, immediately stop and return #F; otherwise,
-;;; if PRED? should return a true value for each element, stopping at
-;;; the end of the shortest vector, return the last value that PRED?
-;;; returned. In the case that there is an empty vector, return #T.
-;;; The iteration and order of application of PRED? across elements
-;;; is of the vectors is strictly left-to-right.
-(define vector-every
- (letrec ((loop1 (lambda (pred? vec i len len-1)
- (or (= i len)
- (if (= i len-1)
- (pred? (vector-ref vec i))
- (and (pred? (vector-ref vec i))
- (loop1 pred? vec (+ i 1)
- len len-1))))))
- (loop2+ (lambda (pred? vectors i len len-1)
- (or (= i len)
- (if (= i len-1)
- (apply pred? (vectors-ref vectors i))
- (and (apply pred? (vectors-ref vectors i))
- (loop2+ pred? vectors (+ i 1)
- len len-1)))))))
- (lambda (pred? vec . vectors)
- (let ((pred? (check-type procedure? pred? vector-every))
- (vec (check-type vector? vec vector-every)))
- (if (null? vectors)
- (let ((len (vector-length vec)))
- (loop1 pred? vec 0 len (- len 1)))
- (let ((len (%smallest-length vectors
- (vector-length vec)
- vector-every)))
- (loop2+ pred? (cons vec vectors) 0 len (- len 1))))))))
-
-
-
-;;; --------------------
-;;; Mutators
-
-;;; (VECTOR-SWAP! <vector> <index1> <index2>) -> unspecified
-;;; Swap the values in the locations at INDEX1 and INDEX2.
-(define (vector-swap! vec i j)
- (let ((vec (check-type vector? vec vector-swap!)))
- (let ((i (check-index vec i vector-swap!))
- (j (check-index vec j vector-swap!)))
- (let ((x (vector-ref vec i)))
- (vector-set! vec i (vector-ref vec j))
- (vector-set! vec j x)))))
-
-;;; (VECTOR-REVERSE-COPY! <target> <tstart> <source> [<sstart> <send>])
-;;; [wdc] Corrected to allow 0 <= sstart <= send <= (vector-length source).
-(define (vector-reverse-copy! target tstart source . maybe-sstart+send)
- (define (doit! sstart send source-length)
- (let ((tstart (check-type nonneg-int? tstart vector-reverse-copy!))
- (sstart (check-type nonneg-int? sstart vector-reverse-copy!))
- (send (check-type nonneg-int? send vector-reverse-copy!)))
- (cond ((and (eq? target source)
- (or (between? sstart tstart send)
- (between? tstart sstart
- (+ tstart (- send sstart)))))
- (error "vector range for self-copying overlaps"
- vector-reverse-copy!
- `(vector was ,target)
- `(tstart was ,tstart)
- `(sstart was ,sstart)
- `(send was ,send)))
- ((and (<= 0 sstart send source-length)
- (<= (+ tstart (- send sstart)) (vector-length target)))
- (%vector-reverse-copy! target tstart source sstart send))
- (else
- (error "illegal arguments"
- `(while calling ,vector-reverse-copy!)
- `(target was ,target)
- `(target-length was ,(vector-length target))
- `(tstart was ,tstart)
- `(source was ,source)
- `(source-length was ,source-length)
- `(sstart was ,sstart)
- `(send was ,send))))))
- (let ((n (vector-length source)))
- (cond ((null? maybe-sstart+send)
- (doit! 0 n n))
- ((null? (cdr maybe-sstart+send))
- (doit! (car maybe-sstart+send) n n))
- ((null? (cddr maybe-sstart+send))
- (doit! (car maybe-sstart+send) (cadr maybe-sstart+send) n))
- (else
- (error "too many arguments"
- vector-reverse-copy!
- (cddr maybe-sstart+send))))))
-
-;;; (VECTOR-REVERSE! <vector> [<start> <end>]) -> unspecified
-;;; Destructively reverse the contents of the sequence of locations
-;;; in VECTOR between START, whose default is 0, and END, whose
-;;; default is the length of VECTOR.
-(define (vector-reverse! vec . start+end)
- (let-vector-start+end vector-reverse! vec start+end
- (start end)
- (%vector-reverse! vec start end)))
-
-
-
-;;; --------------------
-;;; Conversion
-
-;;; (REVERSE-VECTOR->LIST <vector> [<start> <end>]) -> list
-;;; Produce a list containing the elements in the locations between
-;;; START, whose default is 0, and END, whose default is the length
-;;; of VECTOR, from VECTOR, in reverse order.
-(define (reverse-vector->list vec . maybe-start+end)
- (let-vector-start+end reverse-vector->list vec maybe-start+end
- (start end)
- ;(unfold (lambda (i) (= i end)) ; No SRFI 1.
- ; (lambda (i) (vector-ref vec i))
- ; (lambda (i) (+ i 1))
- ; start)
- (do ((i start (+ i 1))
- (result '() (cons (vector-ref vec i) result)))
- ((= i end) result))))
-
-;;; (LIST->VECTOR <list> [<start> <end>]) -> vector
-;;; [R5RS+] Produce a vector containing the elements in LIST, which
-;;; must be a proper list, between START, whose default is 0, & END,
-;;; whose default is the length of LIST. It is suggested that if the
-;;; length of LIST is known in advance, the START and END arguments
-;;; be passed, so that LIST->VECTOR need not call LENGTH to determine
-;;; the the length.
-;;;
-;;; This implementation diverges on circular lists, unless LENGTH fails
-;;; and causes - to fail as well. Given a LENGTH* that computes the
-;;; length of a list's cycle, this wouldn't diverge, and would work
-;;; great for circular lists.
-(define list->vector
- (case-lambda
- ((lst) (%list->vector lst))
- ((lst start) (list->vector lst start (length lst)))
- ((lst start end)
- (let ((start (check-type nonneg-int? start list->vector))
- (end (check-type nonneg-int? end list->vector)))
- ((lambda (f)
- (vector-unfold f (- end start) (list-tail lst start)))
- (lambda (index l)
- (cond ((null? l)
- (error "list was too short"
- `(list was ,lst)
- `(attempted end was ,end)
- `(while calling ,list->vector)))
- ((pair? l)
- (values (car l) (cdr l)))
- (else
- ;; Make this look as much like what CHECK-TYPE
- ;; would report as possible.
- (error "erroneous value"
- ;; We want SRFI 1's PROPER-LIST?, but it
- ;; would be a waste to link all of SRFI
- ;; 1 to this module for only the single
- ;; function PROPER-LIST?.
- (list list? lst)
- `(while calling
- ,list->vector))))))))))
-
-;;; (REVERSE-LIST->VECTOR <list> [<start> <end>]) -> vector
-;;; Produce a vector containing the elements in LIST, which must be a
-;;; proper list, between START, whose default is 0, and END, whose
-;;; default is the length of LIST, in reverse order. It is suggested
-;;; that if the length of LIST is known in advance, the START and END
-;;; arguments be passed, so that REVERSE-LIST->VECTOR need not call
-;;; LENGTH to determine the the length.
-;;;
-;;; This also diverges on circular lists unless, again, LENGTH returns
-;;; something that makes - bork.
-(define reverse-list->vector
- (case-lambda
- ((lst) (reverse-list->vector lst 0 (length lst)))
- ((lst start) (reverse-list->vector start (length lst)))
- ((lst start end)
- (let ((start (check-type nonneg-int? start reverse-list->vector))
- (end (check-type nonneg-int? end reverse-list->vector)))
- ((lambda (f)
- (vector-unfold-right f (- end start) (list-tail lst start)))
- (lambda (index l)
- (cond ((null? l)
- (error "list too short"
- `(list was ,lst)
- `(attempted end was ,end)
- `(while calling ,reverse-list->vector)))
- ((pair? l)
- (values (car l) (cdr l)))
- (else
- (error "erroneous value"
- (list list? lst)
- `(while calling ,reverse-list->vector))))))))))
-(define-library (srfi 43)
- (export
-
- ;; Constructors
- vector-unfold vector-unfold-right
- vector-reverse-copy
- vector-concatenate
-
- ;; Predicates
- vector-empty?
- vector=
-
- ;; Iteration
- vector-fold vector-fold-right
- vector-map vector-map!
- vector-for-each
- vector-count
-
- ;; Searching
- vector-index vector-index-right
- vector-skip vector-skip-right
- vector-binary-search
- vector-any vector-every
-
- ;; Mutators
- vector-swap!
- vector-reverse!
- vector-reverse-copy!
-
- ;; Conversion
- reverse-vector->list
- list->vector
- reverse-list->vector
-
- )
- (import
- (rename (scheme base) (list->vector %list->vector))
- (scheme case-lambda)
- (scheme cxr)
- (srfi 8)
- (srfi aux))
- (begin
-
- (define-aux-forms check-type let-optionals* #\:optional)
-
- ;; (CHECK-INDEX <vector> <index> <callee>) -> index
- ;; Ensure that INDEX is a valid index into VECTOR; if not, signal an
- ;; error stating that it is not and that this happened in a call to
- ;; CALLEE. Return INDEX when it is valid. (Note that this does NOT
- ;; check that VECTOR is indeed a vector.)
- (define check-index
- (if (debug-mode)
- (lambda (vec index callee)
- (let ((index (check-type integer? index callee)))
- (cond ((< index 0)
- (check-index vec
- (error "vector index too low"
- index
- `(into vector ,vec)
- `(while calling ,callee))
- callee))
- ((>= index (vector-length vec))
- (check-index vec
- (error "vector index too high"
- index
- `(into vector ,vec)
- `(while calling ,callee))
- callee))
- (else index))))
- (lambda (vec index callee)
- index)))
-
- ;; (CHECK-INDICES <vector>
- ;; <start> <start-name>
- ;; <end> <end-name>
- ;; <caller>) -> [start end]
- ;; Ensure that START and END are valid bounds of a range within
- ;; VECTOR; if not, signal an error stating that they are not, with
- ;; the message being informative about what the argument names were
- ;; called -- by using START-NAME & END-NAME --, and that it occurred
- ;; while calling CALLEE. Also ensure that VEC is in fact a vector.
- ;; Returns no useful value.
- (define check-indices
- (if (debug-mode)
- (lambda (vec start start-name end end-name callee)
- (let ((lose (lambda things
- (apply error "vector range out of bounds"
- (append things
- `(vector was ,vec)
- `(,start-name was ,start)
- `(,end-name was ,end)
- `(while calling ,callee)))))
- (start (check-type integer? start callee))
- (end (check-type integer? end callee)))
- (cond ((> start end)
- ;; I'm not sure how well this will work. The intent is that
- ;; the programmer tells the debugger to proceed with both a
- ;; new START & a new END by returning multiple values
- ;; somewhere.
- (receive (new-start new-end)
- (lose `(,end-name < ,start-name))
- (check-indices vec
- new-start start-name
- new-end end-name
- callee)))
- ((< start 0)
- (check-indices vec
- (lose `(,start-name < 0))
- start-name
- end end-name
- callee))
- ((>= start (vector-length vec))
- (check-indices vec
- (lose `(,start-name > len)
- `(len was ,(vector-length vec)))
- start-name
- end end-name
- callee))
- ((> end (vector-length vec))
- (check-indices vec
- start start-name
- (lose `(,end-name > len)
- `(len was ,(vector-length vec)))
- end-name
- callee))
- (else
- (values start end)))))
- (lambda (vec start start-name end end-name callee)
- (values start end))))
-
- )
- (include "43.body.scm"))
-(define-library (srfi 95)
- (export sorted? merge merge! sort sort!)
- (import
- (except (scheme base) equal?)
- (srfi 63))
- (include "95.body.scm"))
-;;;;;; SRFI 43: Vector library -*- Scheme -*-
-;;;
-;;; $Id: vector-lib.scm,v 1.7 2009/03/29 09:46:03 sperber Exp $
-;;;
-;;; Taylor Campbell wrote this code; he places it in the public domain.
-;;; Will Clinger [wdc] made some corrections, also in the public domain.
-
-;;; Copyright (C) Taylor Campbell (2003). All rights reserved.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-;;; --------------------
-;;; Exported procedure index
-;;;
-;;; * Constructors
-;;; make-vector vector
-;;; vector-unfold vector-unfold-right
-;;; vector-copy vector-reverse-copy
-;;; vector-append vector-concatenate
-;;;
-;;; * Predicates
-;;; vector?
-;;; vector-empty?
-;;; vector=
-;;;
-;;; * Selectors
-;;; vector-ref
-;;; vector-length
-;;;
-;;; * Iteration
-;;; vector-fold vector-fold-right
-;;; vector-map vector-map!
-;;; vector-for-each
-;;; vector-count
-;;;
-;;; * Searching
-;;; vector-index vector-skip
-;;; vector-index-right vector-skip-right
-;;; vector-binary-search
-;;; vector-any vector-every
-;;;
-;;; * Mutators
-;;; vector-set!
-;;; vector-swap!
-;;; vector-fill!
-;;; vector-reverse!
-;;; vector-copy! vector-reverse-copy!
-;;; vector-reverse!
-;;;
-;;; * Conversion
-;;; vector->list reverse-vector->list
-;;; list->vector reverse-list->vector
-
-
-
-;;; --------------------
-;;; Commentary on efficiency of the code
-
-;;; This code is somewhat tuned for efficiency. There are several
-;;; internal routines that can be optimized greatly to greatly improve
-;;; the performance of much of the library. These internal procedures
-;;; are already carefully tuned for performance, and lambda-lifted by
-;;; hand. Some other routines are lambda-lifted by hand, but only the
-;;; loops are lambda-lifted, and only if some routine has two possible
-;;; loops -- a fast path and an n-ary case --, whereas _all_ of the
-;;; internal routines' loops are lambda-lifted so as to never cons a
-;;; closure in their body (VECTOR-PARSE-START+END doesn't have a loop),
-;;; even in Scheme systems that perform no loop optimization (which is
-;;; most of them, unfortunately).
-;;;
-;;; Fast paths are provided for common cases in most of the loops in
-;;; this library.
-;;;
-;;; All calls to primitive vector operations are protected by a prior
-;;; type check; they can be safely converted to use unsafe equivalents
-;;; of the operations, if available. Ideally, the compiler should be
-;;; able to determine this, but the state of Scheme compilers today is
-;;; not a happy one.
-;;;
-;;; Efficiency of the actual algorithms is a rather mundane point to
-;;; mention; vector operations are rarely beyond being straightforward.
-
-
-
-;;; --------------------
-;;; Utilities
-
-;;; SRFI 8, too trivial to put in the dependencies list.
-(define-syntax receive
- (syntax-rules ()
- ((receive ?formals ?producer ?body1 ?body2 ...)
- (call-with-values (lambda () ?producer)
- (lambda ?formals ?body1 ?body2 ...)))))
-
-;;; Not the best LET*-OPTIONALS, but not the worst, either. Use Olin's
-;;; if it's available to you.
-(define-syntax let*-optionals
- (syntax-rules ()
- ((let*-optionals (?x ...) ((?var ?default) ...) ?body1 ?body2 ...)
- (let ((args (?x ...)))
- (let*-optionals args ((?var ?default) ...) ?body1 ?body2 ...)))
- ((let*-optionals ?args ((?var ?default) ...) ?body1 ?body2 ...)
- (let*-optionals:aux ?args ?args ((?var ?default) ...)
- ?body1 ?body2 ...))))
-
-(define-syntax let*-optionals:aux
- (syntax-rules ()
- ((aux ?orig-args-var ?args-var () ?body1 ?body2 ...)
- (if (null? ?args-var)
- (let () ?body1 ?body2 ...)
- (error "too many arguments" (length ?orig-args-var)
- ?orig-args-var)))
- ((aux ?orig-args-var ?args-var
- ((?var ?default) ?more ...)
- ?body1 ?body2 ...)
- (if (null? ?args-var)
- (let* ((?var ?default) ?more ...) ?body1 ?body2 ...)
- (let ((?var (car ?args-var))
- (new-args (cdr ?args-var)))
- (let*-optionals:aux ?orig-args-var new-args
- (?more ...)
- ?body1 ?body2 ...))))))
-
-(define (nonneg-int? x)
- (and (integer? x)
- (not (negative? x))))
-
-(define (between? x y z)
- (and (< x y)
- (<= y z)))
-
-(define (unspecified-value) (if #f #f))
-
-;++ This should be implemented more efficiently. It shouldn't cons a
-;++ closure, and the cons cells used in the loops when using this could
-;++ be reused.
-(define (vectors-ref vectors i)
- (map (lambda (v) (vector-ref v i)) vectors))
-
-
-
-;;; --------------------
-;;; Error checking
-
-;;; Error signalling (not checking) is done in a way that tries to be
-;;; as helpful to the person who gets the debugging prompt as possible.
-;;; That said, error _checking_ tries to be as unredundant as possible.
-
-;;; I don't use any sort of general condition mechanism; I use simply
-;;; SRFI 23's ERROR, even in cases where it might be better to use such
-;;; a general condition mechanism. Fix that when porting this to a
-;;; Scheme implementation that has its own condition system.
-
-;;; In argument checks, upon receiving an invalid argument, the checker
-;;; procedure recursively calls itself, but in one of the arguments to
-;;; itself is a call to ERROR; this mechanism is used in the hopes that
-;;; the user may be thrown into a debugger prompt, proceed with another
-;;; value, and let it be checked again.
-
-;;; Type checking is pretty basic, but easily factored out and replaced
-;;; with whatever your implementation's preferred type checking method
-;;; is. I doubt there will be many other methods of index checking,
-;;; though the index checkers might be better implemented natively.
-
-;;; (CHECK-TYPE <type-predicate?> <value> <callee>) -> value
-;;; Ensure that VALUE satisfies TYPE-PREDICATE?; if not, signal an
-;;; error stating that VALUE did not satisfy TYPE-PREDICATE?, showing
-;;; that this happened while calling CALLEE. Return VALUE if no
-;;; error was signalled.
-(define (check-type pred? value callee)
- (if (pred? value)
- value
- ;; Recur: when (or if) the user gets a debugger prompt, he can
- ;; proceed where the call to ERROR was with the correct value.
- (check-type pred?
- (error "erroneous value"
- (list pred? value)
- `(while calling ,callee))
- callee)))
-
-;;; (CHECK-INDEX <vector> <index> <callee>) -> index
-;;; Ensure that INDEX is a valid index into VECTOR; if not, signal an
-;;; error stating that it is not and that this happened in a call to
-;;; CALLEE. Return INDEX when it is valid. (Note that this does NOT
-;;; check that VECTOR is indeed a vector.)
-(define (check-index vec index callee)
- (let ((index (check-type integer? index callee)))
- (cond ((< index 0)
- (check-index vec
- (error "vector index too low"
- index
- `(into vector ,vec)
- `(while calling ,callee))
- callee))
- ((>= index (vector-length vec))
- (check-index vec
- (error "vector index too high"
- index
- `(into vector ,vec)
- `(while calling ,callee))
- callee))
- (else index))))
-
-;;; (CHECK-INDICES <vector>
-;;; <start> <start-name>
-;;; <end> <end-name>
-;;; <caller>) -> [start end]
-;;; Ensure that START and END are valid bounds of a range within
-;;; VECTOR; if not, signal an error stating that they are not, with
-;;; the message being informative about what the argument names were
-;;; called -- by using START-NAME & END-NAME --, and that it occurred
-;;; while calling CALLEE. Also ensure that VEC is in fact a vector.
-;;; Returns no useful value.
-(define (check-indices vec start start-name end end-name callee)
- (let ((lose (lambda things
- (apply error "vector range out of bounds"
- (append things
- `(vector was ,vec)
- `(,start-name was ,start)
- `(,end-name was ,end)
- `(while calling ,callee)))))
- (start (check-type integer? start callee))
- (end (check-type integer? end callee)))
- (cond ((> start end)
- ;; I'm not sure how well this will work. The intent is that
- ;; the programmer tells the debugger to proceed with both a
- ;; new START & a new END by returning multiple values
- ;; somewhere.
- (receive (new-start new-end)
- (lose `(,end-name < ,start-name))
- (check-indices vec
- new-start start-name
- new-end end-name
- callee)))
- ((< start 0)
- (check-indices vec
- (lose `(,start-name < 0))
- start-name
- end end-name
- callee))
- ((>= start (vector-length vec))
- (check-indices vec
- (lose `(,start-name > len)
- `(len was ,(vector-length vec)))
- start-name
- end end-name
- callee))
- ((> end (vector-length vec))
- (check-indices vec
- start start-name
- (lose `(,end-name > len)
- `(len was ,(vector-length vec)))
- end-name
- callee))
- (else
- (values start end)))))
-
-
-
-;;; --------------------
-;;; Internal routines
-
-;;; These should all be integrated, native, or otherwise optimized --
-;;; they're used a _lot_ --. All of the loops and LETs inside loops
-;;; are lambda-lifted by hand, just so as not to cons closures in the
-;;; loops. (If your compiler can do better than that if they're not
-;;; lambda-lifted, then lambda-drop (?) them.)
-
-;;; (VECTOR-PARSE-START+END <vector> <arguments>
-;;; <start-name> <end-name>
-;;; <callee>)
-;;; -> [start end]
-;;; Return two values, composing a valid range within VECTOR, as
-;;; extracted from ARGUMENTS or defaulted from VECTOR -- 0 for START
-;;; and the length of VECTOR for END --; START-NAME and END-NAME are
-;;; purely for error checking.
-(define (vector-parse-start+end vec args start-name end-name callee)
- (let ((len (vector-length vec)))
- (cond ((null? args)
- (values 0 len))
- ((null? (cdr args))
- (check-indices vec
- (car args) start-name
- len end-name
- callee))
- ((null? (cddr args))
- (check-indices vec
- (car args) start-name
- (cadr args) end-name
- callee))
- (else
- (error "too many arguments"
- `(extra args were ,(cddr args))
- `(while calling ,callee))))))
-
-(define-syntax let-vector-start+end
- (syntax-rules ()
- ((let-vector-start+end ?callee ?vec ?args (?start ?end)
- ?body1 ?body2 ...)
- (let ((?vec (check-type vector? ?vec ?callee)))
- (receive (?start ?end)
- (vector-parse-start+end ?vec ?args '?start '?end
- ?callee)
- ?body1 ?body2 ...)))))
-
-;;; (%SMALLEST-LENGTH <vector-list> <default-length> <callee>)
-;;; -> exact, nonnegative integer
-;;; Compute the smallest length of VECTOR-LIST. DEFAULT-LENGTH is
-;;; the length that is returned if VECTOR-LIST is empty. Common use
-;;; of this is in n-ary vector routines:
-;;; (define (f vec . vectors)
-;;; (let ((vec (check-type vector? vec f)))
-;;; ...(%smallest-length vectors (vector-length vec) f)...))
-;;; %SMALLEST-LENGTH takes care of the type checking -- which is what
-;;; the CALLEE argument is for --; thus, the design is tuned for
-;;; avoiding redundant type checks.
-(define %smallest-length
- (letrec ((loop (lambda (vector-list length callee)
- (if (null? vector-list)
- length
- (loop (cdr vector-list)
- (min (vector-length
- (check-type vector?
- (car vector-list)
- callee))
- length)
- callee)))))
- loop))
-
-;;; (%VECTOR-COPY! <target> <tstart> <source> <sstart> <send>)
-;;; Copy elements at locations SSTART to SEND from SOURCE to TARGET,
-;;; starting at TSTART in TARGET.
-;;;
-;;; Optimize this! Probably with some combination of:
-;;; - Force it to be integrated.
-;;; - Let it use unsafe vector element dereferencing routines: bounds
-;;; checking already happens outside of it. (Or use a compiler
-;;; that figures this out, but Olin Shivers' PhD thesis seems to
-;;; have been largely ignored in actual implementations...)
-;;; - Implement it natively as a VM primitive: the VM can undoubtedly
-;;; perform much faster than it can make Scheme perform, even with
-;;; bounds checking.
-;;; - Implement it in assembly: you _want_ the fine control that
-;;; assembly can give you for this.
-;;; I already lambda-lift it by hand, but you should be able to make it
-;;; even better than that.
-(define %vector-copy!
- (letrec ((loop/l->r (lambda (target source send i j)
- (cond ((< i send)
- (vector-set! target j
- (vector-ref source i))
- (loop/l->r target source send
- (+ i 1) (+ j 1))))))
- (loop/r->l (lambda (target source sstart i j)
- (cond ((>= i sstart)
- (vector-set! target j
- (vector-ref source i))
- (loop/r->l target source sstart
- (- i 1) (- j 1)))))))
- (lambda (target tstart source sstart send)
- (if (> sstart tstart) ; Make sure we don't copy over
- ; ourselves.
- (loop/l->r target source send sstart tstart)
- (loop/r->l target source sstart (- send 1)
- (+ -1 tstart send (- sstart)))))))
-
-;;; (%VECTOR-REVERSE-COPY! <target> <tstart> <source> <sstart> <send>)
-;;; Copy elements from SSTART to SEND from SOURCE to TARGET, in the
-;;; reverse order.
-(define %vector-reverse-copy!
- (letrec ((loop (lambda (target source sstart i j)
- (cond ((>= i sstart)
- (vector-set! target j (vector-ref source i))
- (loop target source sstart
- (- i 1)
- (+ j 1)))))))
- (lambda (target tstart source sstart send)
- (loop target source sstart
- (- send 1)
- tstart))))
-
-;;; (%VECTOR-REVERSE! <vector>)
-(define %vector-reverse!
- (letrec ((loop (lambda (vec i j)
- (cond ((<= i j)
- (let ((v (vector-ref vec i)))
- (vector-set! vec i (vector-ref vec j))
- (vector-set! vec j v)
- (loop vec (+ i 1) (- j 1))))))))
- (lambda (vec start end)
- (loop vec start (- end 1)))))
-
-;;; (%VECTOR-FOLD1 <kons> <knil> <vector>) -> knil'
-;;; (KONS <index> <knil> <elt>) -> knil'
-(define %vector-fold1
- (letrec ((loop (lambda (kons knil len vec i)
- (if (= i len)
- knil
- (loop kons
- (kons i knil (vector-ref vec i))
- len vec (+ i 1))))))
- (lambda (kons knil len vec)
- (loop kons knil len vec 0))))
-
-;;; (%VECTOR-FOLD2+ <kons> <knil> <vector> ...) -> knil'
-;;; (KONS <index> <knil> <elt> ...) -> knil'
-(define %vector-fold2+
- (letrec ((loop (lambda (kons knil len vectors i)
- (if (= i len)
- knil
- (loop kons
- (apply kons i knil
- (vectors-ref vectors i))
- len vectors (+ i 1))))))
- (lambda (kons knil len vectors)
- (loop kons knil len vectors 0))))
-
-;;; (%VECTOR-MAP! <f> <target> <length> <vector>) -> target
-;;; (F <index> <elt>) -> elt'
-(define %vector-map1!
- (letrec ((loop (lambda (f target vec i)
- (if (zero? i)
- target
- (let ((j (- i 1)))
- (vector-set! target j
- (f j (vector-ref vec j)))
- (loop f target vec j))))))
- (lambda (f target vec len)
- (loop f target vec len))))
-
-;;; (%VECTOR-MAP2+! <f> <target> <vectors> <len>) -> target
-;;; (F <index> <elt> ...) -> elt'
-(define %vector-map2+!
- (letrec ((loop (lambda (f target vectors i)
- (if (zero? i)
- target
- (let ((j (- i 1)))
- (vector-set! target j
- (apply f j (vectors-ref vectors j)))
- (loop f target vectors j))))))
- (lambda (f target vectors len)
- (loop f target vectors len))))
-
-
-
-;;;;;;;;;;;;;;;;;;;;;;;; ***** vector-lib ***** ;;;;;;;;;;;;;;;;;;;;;;;
-
-;;; --------------------
-;;; Constructors
-
-;;; (MAKE-VECTOR <size> [<fill>]) -> vector
-;;; [R5RS] Create a vector of length LENGTH. If FILL is present,
-;;; initialize each slot in the vector with it; if not, the vector's
-;;; initial contents are unspecified.
-(define make-vector make-vector)
-
-;;; (VECTOR <elt> ...) -> vector
-;;; [R5RS] Create a vector containing ELEMENT ..., in order.
-(define vector vector)
-
-;;; This ought to be able to be implemented much more efficiently -- if
-;;; we have the number of arguments available to us, we can create the
-;;; vector without using LENGTH to determine the number of elements it
-;;; should have.
-;(define (vector . elements) (list->vector elements))
-
-;;; (VECTOR-UNFOLD <f> <length> <initial-seed> ...) -> vector
-;;; (F <index> <seed> ...) -> [elt seed' ...]
-;;; The fundamental vector constructor. Creates a vector whose
-;;; length is LENGTH and iterates across each index K between 0 and
-;;; LENGTH, applying F at each iteration to the current index and the
-;;; current seeds to receive N+1 values: first, the element to put in
-;;; the Kth slot and then N new seeds for the next iteration.
-(define vector-unfold
- (letrec ((tabulate! ; Special zero-seed case.
- (lambda (f vec i len)
- (cond ((< i len)
- (vector-set! vec i (f i))
- (tabulate! f vec (+ i 1) len)))))
- (unfold1! ; Fast path for one seed.
- (lambda (f vec i len seed)
- (if (< i len)
- (receive (elt new-seed)
- (f i seed)
- (vector-set! vec i elt)
- (unfold1! f vec (+ i 1) len new-seed)))))
- (unfold2+! ; Slower variant for N seeds.
- (lambda (f vec i len seeds)
- (if (< i len)
- (receive (elt . new-seeds)
- (apply f i seeds)
- (vector-set! vec i elt)
- (unfold2+! f vec (+ i 1) len new-seeds))))))
- (lambda (f len . initial-seeds)
- (let ((f (check-type procedure? f vector-unfold))
- (len (check-type nonneg-int? len vector-unfold)))
- (let ((vec (make-vector len)))
- (cond ((null? initial-seeds)
- (tabulate! f vec 0 len))
- ((null? (cdr initial-seeds))
- (unfold1! f vec 0 len (car initial-seeds)))
- (else
- (unfold2+! f vec 0 len initial-seeds)))
- vec)))))
-
-;;; (VECTOR-UNFOLD-RIGHT <f> <length> <initial-seed> ...) -> vector
-;;; (F <seed> ...) -> [seed' ...]
-;;; Like VECTOR-UNFOLD, but it generates elements from LENGTH to 0
-;;; (still exclusive with LENGTH and inclusive with 0), not 0 to
-;;; LENGTH as with VECTOR-UNFOLD.
-(define vector-unfold-right
- (letrec ((tabulate!
- (lambda (f vec i)
- (cond ((>= i 0)
- (vector-set! vec i (f i))
- (tabulate! f vec (- i 1))))))
- (unfold1!
- (lambda (f vec i seed)
- (if (>= i 0)
- (receive (elt new-seed)
- (f i seed)
- (vector-set! vec i elt)
- (unfold1! f vec (- i 1) new-seed)))))
- (unfold2+!
- (lambda (f vec i seeds)
- (if (>= i 0)
- (receive (elt . new-seeds)
- (apply f i seeds)
- (vector-set! vec i elt)
- (unfold2+! f vec (- i 1) new-seeds))))))
- (lambda (f len . initial-seeds)
- (let ((f (check-type procedure? f vector-unfold-right))
- (len (check-type nonneg-int? len vector-unfold-right)))
- (let ((vec (make-vector len))
- (i (- len 1)))
- (cond ((null? initial-seeds)
- (tabulate! f vec i))
- ((null? (cdr initial-seeds))
- (unfold1! f vec i (car initial-seeds)))
- (else
- (unfold2+! f vec i initial-seeds)))
- vec)))))
-
-;;; (VECTOR-COPY <vector> [<start> <end> <fill>]) -> vector
-;;; Create a newly allocated vector containing the elements from the
-;;; range [START,END) in VECTOR. START defaults to 0; END defaults
-;;; to the length of VECTOR. END may be greater than the length of
-;;; VECTOR, in which case the vector is enlarged; if FILL is passed,
-;;; the new locations from which there is no respective element in
-;;; VECTOR are filled with FILL.
-(define (vector-copy vec . args)
- (let ((vec (check-type vector? vec vector-copy)))
- ;; We can't use LET-VECTOR-START+END, because we have one more
- ;; argument, and we want finer control, too.
- ;;
- ;; Olin's implementation of LET*-OPTIONALS would prove useful here:
- ;; the built-in argument-checks-as-you-go-along produces almost
- ;; _exactly_ the same code as VECTOR-COPY:PARSE-ARGS.
- (receive (start end fill)
- (vector-copy:parse-args vec args)
- (let ((new-vector (make-vector (- end start) fill)))
- (%vector-copy! new-vector 0
- vec start
- (if (> end (vector-length vec))
- (vector-length vec)
- end))
- new-vector))))
-
-;;; Auxiliary for VECTOR-COPY.
-;;; [wdc] Corrected to allow 0 <= start <= (vector-length vec).
-(define (vector-copy:parse-args vec args)
- (define (parse-args start end n fill)
- (let ((start (check-type nonneg-int? start vector-copy))
- (end (check-type nonneg-int? end vector-copy)))
- (cond ((and (<= 0 start end)
- (<= start n))
- (values start end fill))
- (else
- (error "illegal arguments"
- `(while calling ,vector-copy)
- `(start was ,start)
- `(end was ,end)
- `(vector was ,vec))))))
- (let ((n (vector-length vec)))
- (cond ((null? args)
- (parse-args 0 n n (unspecified-value)))
- ((null? (cdr args))
- (parse-args (car args) n n (unspecified-value)))
- ((null? (cddr args))
- (parse-args (car args) (cadr args) n (unspecified-value)))
- ((null? (cdddr args))
- (parse-args (car args) (cadr args) n (caddr args)))
- (else
- (error "too many arguments"
- vector-copy
- (cdddr args))))))
-
-;;; (VECTOR-REVERSE-COPY <vector> [<start> <end>]) -> vector
-;;; Create a newly allocated vector whose elements are the reversed
-;;; sequence of elements between START and END in VECTOR. START's
-;;; default is 0; END's default is the length of VECTOR.
-(define (vector-reverse-copy vec . maybe-start+end)
- (let-vector-start+end vector-reverse-copy vec maybe-start+end
- (start end)
- (let ((new (make-vector (- end start))))
- (%vector-reverse-copy! new 0 vec start end)
- new)))
-
-;;; (VECTOR-APPEND <vector> ...) -> vector
-;;; Append VECTOR ... into a newly allocated vector and return that
-;;; new vector.
-(define (vector-append . vectors)
- (vector-concatenate:aux vectors vector-append))
-
-;;; (VECTOR-CONCATENATE <vector-list>) -> vector
-;;; Concatenate the vectors in VECTOR-LIST. This is equivalent to
-;;; (apply vector-append VECTOR-LIST)
-;;; but VECTOR-APPEND tends to be implemented in terms of
-;;; VECTOR-CONCATENATE, and some Schemes bork when the list to apply
-;;; a function to is too long.
-;;;
-;;; Actually, they're both implemented in terms of an internal routine.
-(define (vector-concatenate vector-list)
- (vector-concatenate:aux vector-list vector-concatenate))
-
-;;; Auxiliary for VECTOR-APPEND and VECTOR-CONCATENATE
-(define vector-concatenate:aux
- (letrec ((compute-length
- (lambda (vectors len callee)
- (if (null? vectors)
- len
- (let ((vec (check-type vector? (car vectors)
- callee)))
- (compute-length (cdr vectors)
- (+ (vector-length vec) len)
- callee)))))
- (concatenate!
- (lambda (vectors target to)
- (if (null? vectors)
- target
- (let* ((vec1 (car vectors))
- (len (vector-length vec1)))
- (%vector-copy! target to vec1 0 len)
- (concatenate! (cdr vectors) target
- (+ to len)))))))
- (lambda (vectors callee)
- (cond ((null? vectors) ;+++
- (make-vector 0))
- ((null? (cdr vectors)) ;+++
- ;; Blech, we still have to allocate a new one.
- (let* ((vec (check-type vector? (car vectors) callee))
- (len (vector-length vec))
- (new (make-vector len)))
- (%vector-copy! new 0 vec 0 len)
- new))
- (else
- (let ((new-vector
- (make-vector (compute-length vectors 0 callee))))
- (concatenate! vectors new-vector 0)
- new-vector))))))
-
-
-
-;;; --------------------
-;;; Predicates
-
-;;; (VECTOR? <value>) -> boolean
-;;; [R5RS] Return #T if VALUE is a vector and #F if not.
-(define vector? vector?)
-
-;;; (VECTOR-EMPTY? <vector>) -> boolean
-;;; Return #T if VECTOR has zero elements in it, i.e. VECTOR's length
-;;; is 0, and #F if not.
-(define (vector-empty? vec)
- (let ((vec (check-type vector? vec vector-empty?)))
- (zero? (vector-length vec))))
-
-;;; (VECTOR= <elt=?> <vector> ...) -> boolean
-;;; (ELT=? <value> <value>) -> boolean
-;;; Determine vector equality generalized across element comparators.
-;;; Vectors A and B are equal iff their lengths are the same and for
-;;; each respective elements E_a and E_b (element=? E_a E_b) returns
-;;; a true value. ELT=? is always applied to two arguments. Element
-;;; comparison must be consistent wtih EQ?; that is, if (eq? E_a E_b)
-;;; results in a true value, then (ELEMENT=? E_a E_b) must result in a
-;;; true value. This may be exploited to avoid multiple unnecessary
-;;; element comparisons. (This implementation does, but does not deal
-;;; with the situation that ELEMENT=? is EQ? to avoid more unnecessary
-;;; comparisons, but I believe this optimization is probably fairly
-;;; insignificant.)
-;;;
-;;; If the number of vector arguments is zero or one, then #T is
-;;; automatically returned. If there are N vector arguments,
-;;; VECTOR_1 VECTOR_2 ... VECTOR_N, then VECTOR_1 & VECTOR_2 are
-;;; compared; if they are equal, the vectors VECTOR_2 ... VECTOR_N
-;;; are compared. The precise order in which ELT=? is applied is not
-;;; specified.
-(define (vector= elt=? . vectors)
- (let ((elt=? (check-type procedure? elt=? vector=)))
- (cond ((null? vectors)
- #t)
- ((null? (cdr vectors))
- (check-type vector? (car vectors) vector=)
- #t)
- (else
- (let loop ((vecs vectors))
- (let ((vec1 (check-type vector? (car vecs) vector=))
- (vec2+ (cdr vecs)))
- (or (null? vec2+)
- (and (binary-vector= elt=? vec1 (car vec2+))
- (loop vec2+)))))))))
-(define (binary-vector= elt=? vector-a vector-b)
- (or (eq? vector-a vector-b) ;+++
- (let ((length-a (vector-length vector-a))
- (length-b (vector-length vector-b)))
- (letrec ((loop (lambda (i)
- (or (= i length-a)
- (and (< i length-b)
- (test (vector-ref vector-a i)
- (vector-ref vector-b i)
- i)))))
- (test (lambda (elt-a elt-b i)
- (and (or (eq? elt-a elt-b) ;+++
- (elt=? elt-a elt-b))
- (loop (+ i 1))))))
- (and (= length-a length-b)
- (loop 0))))))
-
-
-
-;;; --------------------
-;;; Selectors
-
-;;; (VECTOR-REF <vector> <index>) -> value
-;;; [R5RS] Return the value that the location in VECTOR at INDEX is
-;;; mapped to in the store.
-(define vector-ref vector-ref)
-
-;;; (VECTOR-LENGTH <vector>) -> exact, nonnegative integer
-;;; [R5RS] Return the length of VECTOR.
-(define vector-length vector-length)
-
-
-
-;;; --------------------
-;;; Iteration
-
-;;; (VECTOR-FOLD <kons> <initial-knil> <vector> ...) -> knil
-;;; (KONS <knil> <elt> ...) -> knil' ; N vectors -> N+1 args
-;;; The fundamental vector iterator. KONS is iterated over each
-;;; index in all of the vectors in parallel, stopping at the end of
-;;; the shortest; KONS is applied to an argument list of (list I
-;;; STATE (vector-ref VEC I) ...), where STATE is the current state
-;;; value -- the state value begins with KNIL and becomes whatever
-;;; KONS returned at the respective iteration --, and I is the
-;;; current index in the iteration. The iteration is strictly left-
-;;; to-right.
-;;; (vector-fold KONS KNIL (vector E_1 E_2 ... E_N))
-;;; <=>
-;;; (KONS (... (KONS (KONS KNIL E_1) E_2) ... E_N-1) E_N)
-(define (vector-fold kons knil vec . vectors)
- (let ((kons (check-type procedure? kons vector-fold))
- (vec (check-type vector? vec vector-fold)))
- (if (null? vectors)
- (%vector-fold1 kons knil (vector-length vec) vec)
- (%vector-fold2+ kons knil
- (%smallest-length vectors
- (vector-length vec)
- vector-fold)
- (cons vec vectors)))))
-
-;;; (VECTOR-FOLD-RIGHT <kons> <initial-knil> <vector> ...) -> knil
-;;; (KONS <knil> <elt> ...) -> knil' ; N vectors => N+1 args
-;;; The fundamental vector recursor. Iterates in parallel across
-;;; VECTOR ... right to left, applying KONS to the elements and the
-;;; current state value; the state value becomes what KONS returns
-;;; at each next iteration. KNIL is the initial state value.
-;;; (vector-fold-right KONS KNIL (vector E_1 E_2 ... E_N))
-;;; <=>
-;;; (KONS (... (KONS (KONS KNIL E_N) E_N-1) ... E_2) E_1)
-;;;
-;;; Not implemented in terms of a more primitive operations that might
-;;; called %VECTOR-FOLD-RIGHT due to the fact that it wouldn't be very
-;;; useful elsewhere.
-(define vector-fold-right
- (letrec ((loop1 (lambda (kons knil vec i)
- (if (negative? i)
- knil
- (loop1 kons (kons i knil (vector-ref vec i))
- vec
- (- i 1)))))
- (loop2+ (lambda (kons knil vectors i)
- (if (negative? i)
- knil
- (loop2+ kons
- (apply kons i knil
- (vectors-ref vectors i))
- vectors
- (- i 1))))))
- (lambda (kons knil vec . vectors)
- (let ((kons (check-type procedure? kons vector-fold-right))
- (vec (check-type vector? vec vector-fold-right)))
- (if (null? vectors)
- (loop1 kons knil vec (- (vector-length vec) 1))
- (loop2+ kons knil (cons vec vectors)
- (- (%smallest-length vectors
- (vector-length vec)
- vector-fold-right)
- 1)))))))
-
-;;; (VECTOR-MAP <f> <vector> ...) -> vector
-;;; (F <elt> ...) -> value ; N vectors -> N args
-;;; Constructs a new vector of the shortest length of the vector
-;;; arguments. Each element at index I of the new vector is mapped
-;;; from the old vectors by (F I (vector-ref VECTOR I) ...). The
-;;; dynamic order of application of F is unspecified.
-(define (vector-map f vec . vectors)
- (let ((f (check-type procedure? f vector-map))
- (vec (check-type vector? vec vector-map)))
- (if (null? vectors)
- (let ((len (vector-length vec)))
- (%vector-map1! f (make-vector len) vec len))
- (let ((len (%smallest-length vectors
- (vector-length vec)
- vector-map)))
- (%vector-map2+! f (make-vector len) (cons vec vectors)
- len)))))
-
-;;; (VECTOR-MAP! <f> <vector> ...) -> unspecified
-;;; (F <elt> ...) -> element' ; N vectors -> N args
-;;; Similar to VECTOR-MAP, but rather than mapping the new elements
-;;; into a new vector, the new mapped elements are destructively
-;;; inserted into the first vector. Again, the dynamic order of
-;;; application of F is unspecified, so it is dangerous for F to
-;;; manipulate the first VECTOR.
-(define (vector-map! f vec . vectors)
- (let ((f (check-type procedure? f vector-map!))
- (vec (check-type vector? vec vector-map!)))
- (if (null? vectors)
- (%vector-map1! f vec vec (vector-length vec))
- (%vector-map2+! f vec (cons vec vectors)
- (%smallest-length vectors
- (vector-length vec)
- vector-map!)))
- (unspecified-value)))
-
-;;; (VECTOR-FOR-EACH <f> <vector> ...) -> unspecified
-;;; (F <elt> ...) ; N vectors -> N args
-;;; Simple vector iterator: applies F to each index in the range [0,
-;;; LENGTH), where LENGTH is the length of the smallest vector
-;;; argument passed, and the respective element at that index. In
-;;; contrast with VECTOR-MAP, F is reliably applied to each
-;;; subsequent elements, starting at index 0 from left to right, in
-;;; the vectors.
-(define vector-for-each
- (letrec ((for-each1
- (lambda (f vec i len)
- (cond ((< i len)
- (f i (vector-ref vec i))
- (for-each1 f vec (+ i 1) len)))))
- (for-each2+
- (lambda (f vecs i len)
- (cond ((< i len)
- (apply f i (vectors-ref vecs i))
- (for-each2+ f vecs (+ i 1) len))))))
- (lambda (f vec . vectors)
- (let ((f (check-type procedure? f vector-for-each))
- (vec (check-type vector? vec vector-for-each)))
- (if (null? vectors)
- (for-each1 f vec 0 (vector-length vec))
- (for-each2+ f (cons vec vectors) 0
- (%smallest-length vectors
- (vector-length vec)
- vector-for-each)))))))
-
-;;; (VECTOR-COUNT <predicate?> <vector> ...)
-;;; -> exact, nonnegative integer
-;;; (PREDICATE? <index> <value> ...) ; N vectors -> N+1 args
-;;; PREDICATE? is applied element-wise to the elements of VECTOR ...,
-;;; and a count is tallied of the number of elements for which a
-;;; true value is produced by PREDICATE?. This count is returned.
-(define (vector-count pred? vec . vectors)
- (let ((pred? (check-type procedure? pred? vector-count))
- (vec (check-type vector? vec vector-count)))
- (if (null? vectors)
- (%vector-fold1 (lambda (index count elt)
- (if (pred? index elt)
- (+ count 1)
- count))
- 0
- (vector-length vec)
- vec)
- (%vector-fold2+ (lambda (index count . elts)
- (if (apply pred? index elts)
- (+ count 1)
- count))
- 0
- (%smallest-length vectors
- (vector-length vec)
- vector-count)
- (cons vec vectors)))))
-
-
-
-;;; --------------------
-;;; Searching
-
-;;; (VECTOR-INDEX <predicate?> <vector> ...)
-;;; -> exact, nonnegative integer or #F
-;;; (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args
-;;; Search left-to-right across VECTOR ... in parallel, returning the
-;;; index of the first set of values VALUE ... such that (PREDICATE?
-;;; VALUE ...) returns a true value; if no such set of elements is
-;;; reached, return #F.
-(define (vector-index pred? vec . vectors)
- (vector-index/skip pred? vec vectors vector-index))
-
-;;; (VECTOR-SKIP <predicate?> <vector> ...)
-;;; -> exact, nonnegative integer or #F
-;;; (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args
-;;; (vector-index (lambda elts (not (apply PREDICATE? elts)))
-;;; VECTOR ...)
-;;; Like VECTOR-INDEX, but find the index of the first set of values
-;;; that do _not_ satisfy PREDICATE?.
-(define (vector-skip pred? vec . vectors)
- (vector-index/skip (lambda elts (not (apply pred? elts)))
- vec vectors
- vector-skip))
-
-;;; Auxiliary for VECTOR-INDEX & VECTOR-SKIP
-(define vector-index/skip
- (letrec ((loop1 (lambda (pred? vec len i)
- (cond ((= i len) #f)
- ((pred? (vector-ref vec i)) i)
- (else (loop1 pred? vec len (+ i 1))))))
- (loop2+ (lambda (pred? vectors len i)
- (cond ((= i len) #f)
- ((apply pred? (vectors-ref vectors i)) i)
- (else (loop2+ pred? vectors len
- (+ i 1)))))))
- (lambda (pred? vec vectors callee)
- (let ((pred? (check-type procedure? pred? callee))
- (vec (check-type vector? vec callee)))
- (if (null? vectors)
- (loop1 pred? vec (vector-length vec) 0)
- (loop2+ pred? (cons vec vectors)
- (%smallest-length vectors
- (vector-length vec)
- callee)
- 0))))))
-
-;;; (VECTOR-INDEX-RIGHT <predicate?> <vector> ...)
-;;; -> exact, nonnegative integer or #F
-;;; (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args
-;;; Right-to-left variant of VECTOR-INDEX.
-(define (vector-index-right pred? vec . vectors)
- (vector-index/skip-right pred? vec vectors vector-index-right))
-
-;;; (VECTOR-SKIP-RIGHT <predicate?> <vector> ...)
-;;; -> exact, nonnegative integer or #F
-;;; (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args
-;;; Right-to-left variant of VECTOR-SKIP.
-(define (vector-skip-right pred? vec . vectors)
- (vector-index/skip-right (lambda elts (not (apply pred? elts)))
- vec vectors
- vector-index-right))
-
-(define vector-index/skip-right
- (letrec ((loop1 (lambda (pred? vec i)
- (cond ((negative? i) #f)
- ((pred? (vector-ref vec i)) i)
- (else (loop1 pred? vec (- i 1))))))
- (loop2+ (lambda (pred? vectors i)
- (cond ((negative? i) #f)
- ((apply pred? (vectors-ref vectors i)) i)
- (else (loop2+ pred? vectors (- i 1)))))))
- (lambda (pred? vec vectors callee)
- (let ((pred? (check-type procedure? pred? callee))
- (vec (check-type vector? vec callee)))
- (if (null? vectors)
- (loop1 pred? vec (- (vector-length vec) 1))
- (loop2+ pred? (cons vec vectors)
- (- (%smallest-length vectors
- (vector-length vec)
- callee)
- 1)))))))
-
-;;; (VECTOR-BINARY-SEARCH <vector> <value> <cmp> [<start> <end>])
-;;; -> exact, nonnegative integer or #F
-;;; (CMP <value1> <value2>) -> integer
-;;; positive -> VALUE1 > VALUE2
-;;; zero -> VALUE1 = VALUE2
-;;; negative -> VALUE1 < VALUE2
-;;; Perform a binary search through VECTOR for VALUE, comparing each
-;;; element to VALUE with CMP.
-(define (vector-binary-search vec value cmp . maybe-start+end)
- (let ((cmp (check-type procedure? cmp vector-binary-search)))
- (let-vector-start+end vector-binary-search vec maybe-start+end
- (start end)
- (let loop ((start start) (end end) (j #f))
- (let ((i (quotient (+ start end) 2)))
- (if (or (= start end) (and j (= i j)))
- #f
- (let ((comparison
- (check-type integer?
- (cmp (vector-ref vec i) value)
- `(,cmp for ,vector-binary-search))))
- (cond ((zero? comparison) i)
- ((positive? comparison) (loop start i i))
- (else (loop i end i))))))))))
-
-;;; (VECTOR-ANY <pred?> <vector> ...) -> value
-;;; Apply PRED? to each parallel element in each VECTOR ...; if PRED?
-;;; should ever return a true value, immediately stop and return that
-;;; value; otherwise, when the shortest vector runs out, return #F.
-;;; The iteration and order of application of PRED? across elements
-;;; is of the vectors is strictly left-to-right.
-(define vector-any
- (letrec ((loop1 (lambda (pred? vec i len len-1)
- (and (not (= i len))
- (if (= i len-1)
- (pred? (vector-ref vec i))
- (or (pred? (vector-ref vec i))
- (loop1 pred? vec (+ i 1)
- len len-1))))))
- (loop2+ (lambda (pred? vectors i len len-1)
- (and (not (= i len))
- (if (= i len-1)
- (apply pred? (vectors-ref vectors i))
- (or (apply pred? (vectors-ref vectors i))
- (loop2+ pred? vectors (+ i 1)
- len len-1)))))))
- (lambda (pred? vec . vectors)
- (let ((pred? (check-type procedure? pred? vector-any))
- (vec (check-type vector? vec vector-any)))
- (if (null? vectors)
- (let ((len (vector-length vec)))
- (loop1 pred? vec 0 len (- len 1)))
- (let ((len (%smallest-length vectors
- (vector-length vec)
- vector-any)))
- (loop2+ pred? (cons vec vectors) 0 len (- len 1))))))))
-
-;;; (VECTOR-EVERY <pred?> <vector> ...) -> value
-;;; Apply PRED? to each parallel value in each VECTOR ...; if PRED?
-;;; should ever return #F, immediately stop and return #F; otherwise,
-;;; if PRED? should return a true value for each element, stopping at
-;;; the end of the shortest vector, return the last value that PRED?
-;;; returned. In the case that there is an empty vector, return #T.
-;;; The iteration and order of application of PRED? across elements
-;;; is of the vectors is strictly left-to-right.
-(define vector-every
- (letrec ((loop1 (lambda (pred? vec i len len-1)
- (or (= i len)
- (if (= i len-1)
- (pred? (vector-ref vec i))
- (and (pred? (vector-ref vec i))
- (loop1 pred? vec (+ i 1)
- len len-1))))))
- (loop2+ (lambda (pred? vectors i len len-1)
- (or (= i len)
- (if (= i len-1)
- (apply pred? (vectors-ref vectors i))
- (and (apply pred? (vectors-ref vectors i))
- (loop2+ pred? vectors (+ i 1)
- len len-1)))))))
- (lambda (pred? vec . vectors)
- (let ((pred? (check-type procedure? pred? vector-every))
- (vec (check-type vector? vec vector-every)))
- (if (null? vectors)
- (let ((len (vector-length vec)))
- (loop1 pred? vec 0 len (- len 1)))
- (let ((len (%smallest-length vectors
- (vector-length vec)
- vector-every)))
- (loop2+ pred? (cons vec vectors) 0 len (- len 1))))))))
-
-
-
-;;; --------------------
-;;; Mutators
-
-;;; (VECTOR-SET! <vector> <index> <value>) -> unspecified
-;;; [R5RS] Assign the location at INDEX in VECTOR to VALUE.
-(define vector-set! vector-set!)
-
-;;; (VECTOR-SWAP! <vector> <index1> <index2>) -> unspecified
-;;; Swap the values in the locations at INDEX1 and INDEX2.
-(define (vector-swap! vec i j)
- (let ((vec (check-type vector? vec vector-swap!)))
- (let ((i (check-index vec i vector-swap!))
- (j (check-index vec j vector-swap!)))
- (let ((x (vector-ref vec i)))
- (vector-set! vec i (vector-ref vec j))
- (vector-set! vec j x)))))
-
-;;; (VECTOR-FILL! <vector> <value> [<start> <end>]) -> unspecified
-;;; [R5RS+] Fill the locations in VECTOR between START, whose default
-;;; is 0, and END, whose default is the length of VECTOR, with VALUE.
-;;;
-;;; This one can probably be made really fast natively.
-(define vector-fill!
- (let ((%vector-fill! vector-fill!)) ; Take the native one, under
- ; the assumption that it's
- ; faster, so we can use it if
- ; there are no optional
- ; arguments.
- (lambda (vec value . maybe-start+end)
- (if (null? maybe-start+end)
- (%vector-fill! vec value) ;+++
- (let-vector-start+end vector-fill! vec maybe-start+end
- (start end)
- (do ((i start (+ i 1)))
- ((= i end))
- (vector-set! vec i value)))))))
-
-;;; (VECTOR-COPY! <target> <tstart> <source> [<sstart> <send>])
-;;; -> unspecified
-;;; Copy the values in the locations in [SSTART,SEND) from SOURCE to
-;;; to TARGET, starting at TSTART in TARGET.
-;;; [wdc] Corrected to allow 0 <= sstart <= send <= (vector-length source).
-(define (vector-copy! target tstart source . maybe-sstart+send)
- (define (doit! sstart send source-length)
- (let ((tstart (check-type nonneg-int? tstart vector-copy!))
- (sstart (check-type nonneg-int? sstart vector-copy!))
- (send (check-type nonneg-int? send vector-copy!)))
- (cond ((and (<= 0 sstart send source-length)
- (<= (+ tstart (- send sstart)) (vector-length target)))
- (%vector-copy! target tstart source sstart send))
- (else
- (error "illegal arguments"
- `(while calling ,vector-copy!)
- `(target was ,target)
- `(target-length was ,(vector-length target))
- `(tstart was ,tstart)
- `(source was ,source)
- `(source-length was ,source-length)
- `(sstart was ,sstart)
- `(send was ,send))))))
- (let ((n (vector-length source)))
- (cond ((null? maybe-sstart+send)
- (doit! 0 n n))
- ((null? (cdr maybe-sstart+send))
- (doit! (car maybe-sstart+send) n n))
- ((null? (cddr maybe-sstart+send))
- (doit! (car maybe-sstart+send) (cadr maybe-sstart+send) n))
- (else
- (error "too many arguments"
- vector-copy!
- (cddr maybe-sstart+send))))))
-
-;;; (VECTOR-REVERSE-COPY! <target> <tstart> <source> [<sstart> <send>])
-;;; [wdc] Corrected to allow 0 <= sstart <= send <= (vector-length source).
-(define (vector-reverse-copy! target tstart source . maybe-sstart+send)
- (define (doit! sstart send source-length)
- (let ((tstart (check-type nonneg-int? tstart vector-reverse-copy!))
- (sstart (check-type nonneg-int? sstart vector-reverse-copy!))
- (send (check-type nonneg-int? send vector-reverse-copy!)))
- (cond ((and (eq? target source)
- (or (between? sstart tstart send)
- (between? tstart sstart
- (+ tstart (- send sstart)))))
- (error "vector range for self-copying overlaps"
- vector-reverse-copy!
- `(vector was ,target)
- `(tstart was ,tstart)
- `(sstart was ,sstart)
- `(send was ,send)))
- ((and (<= 0 sstart send source-length)
- (<= (+ tstart (- send sstart)) (vector-length target)))
- (%vector-reverse-copy! target tstart source sstart send))
- (else
- (error "illegal arguments"
- `(while calling ,vector-reverse-copy!)
- `(target was ,target)
- `(target-length was ,(vector-length target))
- `(tstart was ,tstart)
- `(source was ,source)
- `(source-length was ,source-length)
- `(sstart was ,sstart)
- `(send was ,send))))))
- (let ((n (vector-length source)))
- (cond ((null? maybe-sstart+send)
- (doit! 0 n n))
- ((null? (cdr maybe-sstart+send))
- (doit! (car maybe-sstart+send) n n))
- ((null? (cddr maybe-sstart+send))
- (doit! (car maybe-sstart+send) (cadr maybe-sstart+send) n))
- (else
- (error "too many arguments"
- vector-reverse-copy!
- (cddr maybe-sstart+send))))))
-
-;;; (VECTOR-REVERSE! <vector> [<start> <end>]) -> unspecified
-;;; Destructively reverse the contents of the sequence of locations
-;;; in VECTOR between START, whose default is 0, and END, whose
-;;; default is the length of VECTOR.
-(define (vector-reverse! vec . start+end)
- (let-vector-start+end vector-reverse! vec start+end
- (start end)
- (%vector-reverse! vec start end)))
-
-
-
-;;; --------------------
-;;; Conversion
-
-;;; (VECTOR->LIST <vector> [<start> <end>]) -> list
-;;; [R5RS+] Produce a list containing the elements in the locations
-;;; between START, whose default is 0, and END, whose default is the
-;;; length of VECTOR, from VECTOR.
-(define vector->list
- (let ((%vector->list vector->list))
- (lambda (vec . maybe-start+end)
- (if (null? maybe-start+end) ; Oughta use CASE-LAMBDA.
- (%vector->list vec) ;+++
- (let-vector-start+end vector->list vec maybe-start+end
- (start end)
- ;(unfold (lambda (i) ; No SRFI 1.
- ; (< i start))
- ; (lambda (i) (vector-ref vec i))
- ; (lambda (i) (- i 1))
- ; (- end 1))
- (do ((i (- end 1) (- i 1))
- (result '() (cons (vector-ref vec i) result)))
- ((< i start) result)))))))
-
-;;; (REVERSE-VECTOR->LIST <vector> [<start> <end>]) -> list
-;;; Produce a list containing the elements in the locations between
-;;; START, whose default is 0, and END, whose default is the length
-;;; of VECTOR, from VECTOR, in reverse order.
-(define (reverse-vector->list vec . maybe-start+end)
- (let-vector-start+end reverse-vector->list vec maybe-start+end
- (start end)
- ;(unfold (lambda (i) (= i end)) ; No SRFI 1.
- ; (lambda (i) (vector-ref vec i))
- ; (lambda (i) (+ i 1))
- ; start)
- (do ((i start (+ i 1))
- (result '() (cons (vector-ref vec i) result)))
- ((= i end) result))))
-
-;;; (LIST->VECTOR <list> [<start> <end>]) -> vector
-;;; [R5RS+] Produce a vector containing the elements in LIST, which
-;;; must be a proper list, between START, whose default is 0, & END,
-;;; whose default is the length of LIST. It is suggested that if the
-;;; length of LIST is known in advance, the START and END arguments
-;;; be passed, so that LIST->VECTOR need not call LENGTH to determine
-;;; the the length.
-;;;
-;;; This implementation diverges on circular lists, unless LENGTH fails
-;;; and causes - to fail as well. Given a LENGTH* that computes the
-;;; length of a list's cycle, this wouldn't diverge, and would work
-;;; great for circular lists.
-(define list->vector
- (let ((%list->vector list->vector))
- (lambda (lst . maybe-start+end)
- ;; Checking the type of a proper list is expensive, so we do it
- ;; amortizedly, or let %LIST->VECTOR or LIST-TAIL do it.
- (if (null? maybe-start+end) ; Oughta use CASE-LAMBDA.
- (%list->vector lst) ;+++
- ;; We can't use LET-VECTOR-START+END, because we're using the
- ;; bounds of a _list_, not a vector.
- (let*-optionals maybe-start+end
- ((start 0)
- (end (length lst))) ; Ugh -- LENGTH
- (let ((start (check-type nonneg-int? start list->vector))
- (end (check-type nonneg-int? end list->vector)))
- ((lambda (f)
- (vector-unfold f (- end start) (list-tail lst start)))
- (lambda (index l)
- (cond ((null? l)
- (error "list was too short"
- `(list was ,lst)
- `(attempted end was ,end)
- `(while calling ,list->vector)))
- ((pair? l)
- (values (car l) (cdr l)))
- (else
- ;; Make this look as much like what CHECK-TYPE
- ;; would report as possible.
- (error "erroneous value"
- ;; We want SRFI 1's PROPER-LIST?, but it
- ;; would be a waste to link all of SRFI
- ;; 1 to this module for only the single
- ;; function PROPER-LIST?.
- (list list? lst)
- `(while calling
- ,list->vector))))))))))))
-
-;;; (REVERSE-LIST->VECTOR <list> [<start> <end>]) -> vector
-;;; Produce a vector containing the elements in LIST, which must be a
-;;; proper list, between START, whose default is 0, and END, whose
-;;; default is the length of LIST, in reverse order. It is suggested
-;;; that if the length of LIST is known in advance, the START and END
-;;; arguments be passed, so that REVERSE-LIST->VECTOR need not call
-;;; LENGTH to determine the the length.
-;;;
-;;; This also diverges on circular lists unless, again, LENGTH returns
-;;; something that makes - bork.
-(define (reverse-list->vector lst . maybe-start+end)
- (let*-optionals maybe-start+end
- ((start 0)
- (end (length lst))) ; Ugh -- LENGTH
- (let ((start (check-type nonneg-int? start reverse-list->vector))
- (end (check-type nonneg-int? end reverse-list->vector)))
- ((lambda (f)
- (vector-unfold-right f (- end start) (list-tail lst start)))
- (lambda (index l)
- (cond ((null? l)
- (error "list too short"
- `(list was ,lst)
- `(attempted end was ,end)
- `(while calling ,reverse-list->vector)))
- ((pair? l)
- (values (car l) (cdr l)))
- (else
- (error "erroneous value"
- (list list? lst)
- `(while calling ,reverse-list->vector)))))))))
-;;; SPDX-FileCopyrightText: 2014 Taylan Kammer <taylan.kammer@gmail.com>
-;;;
-;;; SPDX-License-Identifier: MIT
-
-(define-library (srfi 48)
- (export format)
- (import (rename (scheme base)
- (exact inexact->exact)
- (inexact exact->inexact))
- (scheme char)
- (scheme complex)
- (rename (scheme write)
- (write-shared write-with-shared-structure)))
- (include "48.upstream.scm"))
-;;; "sort.scm" Defines: sorted?, merge, merge!, sort, sort!
-;;; Author : Richard A. O'Keefe (based on Prolog code by D.H.D.Warren)
-
-;;; Copyright (C) Aubrey Jaffer 2006. All Rights Reserved.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-;;; Updated: 11 June 1991
-;;; Modified for scheme library: Aubrey Jaffer 19 Sept. 1991
-;;; Updated: 19 June 1995
-;;; (sort, sort!, sorted?): Generalized to strings by jaffer: 2003-09-09
-;;; (sort, sort!, sorted?): Generalized to arrays by jaffer: 2003-10-04
-;;; jaffer: 2006-10-08:
-;;; (sort, sort!, sorted?, merge, merge!): Added optional KEY argument.
-;;; jaffer: 2006-11-05:
-;;; (sorted?, merge, merge!, sort, sort!): Call KEY arg at most once
-;;; per element.
-
-(require 'array)
-
-;;; (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 (sorted? seq less? . opt-key)
- (define key (if (null? opt-key) identity (car opt-key)))
- (cond ((null? seq) #t)
- ((array? seq)
- (let ((dimax (+ -1 (car (array-dimensions seq)))))
- (or (<= dimax 1)
- (let loop ((idx (+ -1 dimax))
- (last (key (array-ref seq dimax))))
- (or (negative? idx)
- (let ((nxt (key (array-ref seq idx))))
- (and (less? nxt last)
- (loop (+ -1 idx) nxt))))))))
- ((null? (cdr seq)) #t)
- (else
- (let loop ((last (key (car seq)))
- (next (cdr seq)))
- (or (null? next)
- (let ((nxt (key (car next))))
- (and (not (less? nxt last))
- (loop nxt (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 arrays. See below.
-;@
-(define (merge a b less? . opt-key)
- (define key (if (null? opt-key) identity (car opt-key)))
- (cond ((null? a) b)
- ((null? b) a)
- (else
- (let loop ((x (car a)) (kx (key (car a))) (a (cdr a))
- (y (car b)) (ky (key (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? ky kx)
- (if (null? b)
- (cons y (cons x a))
- (cons y (loop x kx a (car b) (key (car b)) (cdr b))))
- ;; x <= y
- (if (null? a)
- (cons x (cons y b))
- (cons x (loop (car a) (key (car a)) (cdr a) y ky b))))))))
-
-(define (sort:merge! a b less? key)
- (define (loop r a kcara b kcarb)
- (cond ((less? kcarb kcara)
- (set-cdr! r b)
- (if (null? (cdr b))
- (set-cdr! b a)
- (loop b a kcara (cdr b) (key (cadr b)))))
- (else ; (car a) <= (car b)
- (set-cdr! r a)
- (if (null? (cdr a))
- (set-cdr! a b)
- (loop a (cdr a) (key (cadr a)) b kcarb)))))
- (cond ((null? a) b)
- ((null? b) a)
- (else
- (let ((kcara (key (car a)))
- (kcarb (key (car b))))
- (cond
- ((less? kcarb kcara)
- (if (null? (cdr b))
- (set-cdr! b a)
- (loop b a kcara (cdr b) (key (cadr b))))
- b)
- (else ; (car a) <= (car b)
- (if (null? (cdr a))
- (set-cdr! a b)
- (loop a (cdr a) (key (cadr a)) b kcarb))
- a))))))
-
-;;; 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 arrays.
-;@
-(define (merge! a b less? . opt-key)
- (sort:merge! a b less? (if (null? opt-key) identity (car opt-key))))
-
-(define (sort:sort-list! seq less? key)
- (define keyer (if key car identity))
- (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? keyer)))
- ((= n 2) (let ((x (car seq))
- (y (cadr seq))
- (p seq))
- (set! seq (cddr seq))
- (cond ((less? (keyer y) (keyer x))
- (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 '())))
- (define (key-wrap! lst)
- (cond ((null? lst))
- (else (set-car! lst (cons (key (car lst)) (car lst)))
- (key-wrap! (cdr lst)))))
- (define (key-unwrap! lst)
- (cond ((null? lst))
- (else (set-car! lst (cdar lst))
- (key-unwrap! (cdr lst)))))
- (cond (key
- (key-wrap! seq)
- (set! seq (step (length seq)))
- (key-unwrap! seq)
- seq)
- (else
- (step (length seq)))))
-
-(define (rank-1-array->list array)
- (define dimensions (array-dimensions array))
- (do ((idx (+ -1 (car dimensions)) (+ -1 idx))
- (lst '() (cons (array-ref array idx) lst)))
- ((< idx 0) lst)))
-
-;;; (sort! sequence less?)
-;;; sorts the list, array, or string 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.
-;;; A. Jaffer modified to always return the original list.
-;@
-(define (sort! seq less? . opt-key)
- (define key (if (null? opt-key) #f (car opt-key)))
- (cond ((array? seq)
- (let ((dims (array-dimensions seq)))
- (do ((sorted (sort:sort-list! (rank-1-array->list seq) less? key)
- (cdr sorted))
- (i 0 (+ i 1)))
- ((null? sorted) seq)
- (array-set! seq (car sorted) i))))
- (else ; otherwise, assume it is a list
- (let ((ret (sort:sort-list! seq less? key)))
- (if (not (eq? ret seq))
- (do ((crt ret (cdr crt)))
- ((eq? (cdr crt) seq)
- (set-cdr! crt ret)
- (let ((scar (car seq)) (scdr (cdr seq)))
- (set-car! seq (car ret)) (set-cdr! seq (cdr ret))
- (set-car! ret scar) (set-cdr! ret scdr)))))
- seq))))
-
-;;; (sort sequence less?)
-;;; sorts a array, string, 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 seq less? . opt-key)
- (define key (if (null? opt-key) #f (car opt-key)))
- (cond ((array? seq)
- (let ((dims (array-dimensions seq)))
- (define newra (apply make-array seq dims))
- (do ((sorted (sort:sort-list! (rank-1-array->list seq) less? key)
- (cdr sorted))
- (i 0 (+ i 1)))
- ((null? sorted) newra)
- (array-set! newra (car sorted) i))))
- (else (sort:sort-list! (append seq '()) less? key))))
-;;; SPDX-FileCopyrightText: 2003 Kenneth A Dickey <ken.dickey@allvantage.com>
-;;; SPDX-FileCopyrightText: 2017 Hamayama <hamay1010@gmail.com>
-;;;
-;;; SPDX-License-Identifier: MIT
-
-;; IMPLEMENTATION DEPENDENT options
-
-(define ascii-tab (integer->char 9)) ;; NB: assumes ASCII encoding
-(define dont-print (if (eq? #t #f) 1))
-;;(define DONT-PRINT (string->symbol ""))
-;;(define DONT-PRINT (void))
-;;(define DONT-PRINT #!void)
-(define pretty-print write) ; ugly but permitted
-;; (require 'srfi-38) ;; write-with-shared-structure
-
-;; Following three procedures are used by format ~F .
-;; 'inexact-number->string' determines whether output is fixed-point
-;; notation or exponential notation. In the current definition,
-;; the notation depends on the implementation of 'number->string'.
-;; 'exact-number->string' is expected to output only numeric characters
-;; (not including such as '#', 'e', '.', '/') if the input is an positive
-;; integer or zero.
-;; 'real-number->string' is used when the digits of ~F is not specified.
-(define (inexact-number->string x) (number->string (exact->inexact x)))
-(define (exact-number->string x) (number->string (inexact->exact x)))
-(define (real-number->string x) (number->string x))
-
-;; FORMAT
-(define (format . args)
- (cond
- ((null? args)
- (error "FORMAT: required format-string argument is missing")
- )
- ((string? (car args))
- (apply format (cons #f args)))
- ((< (length args) 2)
- (error (format #f "FORMAT: too few arguments ~s" (cons 'format args)))
- )
- (else
- (let ( (output-port (car args))
- (format-string (cadr args))
- (args (cddr args))
- )
- (letrec ( (port
- (cond ((output-port? output-port) output-port)
- ((eq? output-port #t) (current-output-port))
- ((eq? output-port #f) (open-output-string))
- (else (error
- (format #f "FORMAT: bad output-port argument: ~s"
- output-port)))
- ) )
- (return-value
- (if (eq? output-port #f) ;; if format into a string
- (lambda () (get-output-string port)) ;; then return the string
- (lambda () dont-print)) ;; else do something harmless
- )
- )
-
- (define (string-index str c)
- (let ( (len (string-length str)) )
- (let loop ( (i 0) )
- (cond ((= i len) #f)
- ((eqv? c (string-ref str i)) i)
- (else (loop (+ i 1)))))))
-
- (define (string-grow str len char)
- (let ( (off (- len (string-length str))) )
- (if (positive? off)
- (string-append (make-string off char) str)
- str)))
-
- (define (compose-with-digits digits pre-str frac-str exp-str)
- (let ( (frac-len (string-length frac-str)) )
- (cond
- ((< frac-len digits) ;; grow frac part, pad with zeros
- (string-append pre-str "."
- frac-str (make-string (- digits frac-len) #\0)
- exp-str)
- )
- ((= frac-len digits) ;; frac-part is exactly the right size
- (string-append pre-str "."
- frac-str
- exp-str)
- )
- (else ;; must round to shrink it
- (let* ( (minus-flag (and (> (string-length pre-str) 0)
- (char=? (string-ref pre-str 0) #\-)))
- (pre-str* (if minus-flag
- (substring pre-str 1 (string-length pre-str))
- pre-str))
- (first-part (substring frac-str 0 digits))
- (last-part (substring frac-str digits frac-len))
- (temp-str
- (string-grow
- (exact-number->string
- (round (string->number
- (string-append pre-str* first-part "." last-part))))
- digits
- #\0))
- (temp-len (string-length temp-str))
- (new-pre (substring temp-str 0 (- temp-len digits)))
- (new-frac (substring temp-str (- temp-len digits) temp-len))
- )
- (string-append
- (if minus-flag "-" "")
- (if (string=? new-pre "")
- ;; check if the system displays integer part of numbers
- ;; whose absolute value is 0 < x < 1.
- (if (and (string=? pre-str* "")
- (> digits 0)
- (not (= (string->number new-frac) 0)))
- "" "0")
- new-pre)
- "."
- new-frac
- exp-str)))
- ) ) )
-
- (define (format-fixed number-or-string width digits) ; returns a string
- (cond
- ((string? number-or-string)
- (string-grow number-or-string width #\space)
- )
- ((number? number-or-string)
- (let ( (real (real-part number-or-string))
- (imag (imag-part number-or-string))
- )
- (cond
- ((not (zero? imag))
- (string-grow
- (string-append (format-fixed real 0 digits)
- (if (negative? imag) "" "+")
- (format-fixed imag 0 digits)
- "i")
- width
- #\space)
- )
- (digits
- (let* ( (num-str (inexact-number->string real))
- (dot-index (string-index num-str #\.))
- (exp-index (string-index num-str #\e))
- (length (string-length num-str))
- (pre-string
- (if dot-index
- (substring num-str 0 dot-index)
- (if exp-index
- (substring num-str 0 exp-index)
- num-str))
- )
- (exp-string
- (if exp-index
- (substring num-str exp-index length)
- "")
- )
- (frac-string
- (if dot-index
- (if exp-index
- (substring num-str (+ dot-index 1) exp-index)
- (substring num-str (+ dot-index 1) length))
- "")
- )
- )
- ;; check +inf.0, -inf.0, +nan.0, -nan.0
- (if (string-index num-str #\n)
- (string-grow num-str width #\space)
- (string-grow
- (compose-with-digits digits
- pre-string
- frac-string
- exp-string)
- width
- #\space))
- ))
- (else ;; no digits
- (string-grow (real-number->string real) width #\space)))
- ))
- (else
- (error
- (format "FORMAT: ~F requires a number or a string, got ~s" number-or-string)))
- ))
-
- (define documentation-string
-"(format [<port>] <format-string> [<arg>...]) -- <port> is #t, #f or an output-port
-OPTION [MNEMONIC] DESCRIPTION -- Implementation Assumes ASCII Text Encoding
-~H [Help] output this text
-~A [Any] (display arg) for humans
-~S [Slashified] (write arg) for parsers
-~W [WriteCircular] like ~s but outputs circular and recursive data structures
-~~ [tilde] output a tilde
-~T [Tab] output a tab character
-~% [Newline] output a newline character
-~& [Freshline] output a newline character if the previous output was not a newline
-~D [Decimal] the arg is a number which is output in decimal radix
-~X [heXadecimal] the arg is a number which is output in hexdecimal radix
-~O [Octal] the arg is a number which is output in octal radix
-~B [Binary] the arg is a number which is output in binary radix
-~w,dF [Fixed] the arg is a string or number which has width w and d digits after the decimal
-~C [Character] charater arg is output by write-char
-~_ [Space] a single space character is output
-~Y [Yuppify] the list arg is pretty-printed to the output
-~? [Indirection] recursive format: next 2 args are format-string and list of arguments
-~K [Indirection] same as ~?
-"
- )
-
- (define (require-an-arg args)
- (if (null? args)
- (error "FORMAT: too few arguments" ))
- )
-
- (define (format-help format-strg arglist)
-
- (letrec (
- (length-of-format-string (string-length format-strg))
-
- (anychar-dispatch
- (lambda (pos arglist last-was-newline)
- (if (>= pos length-of-format-string)
- arglist ; return unused args
- (let ( (char (string-ref format-strg pos)) )
- (cond
- ((eqv? char #\~)
- (tilde-dispatch (+ pos 1) arglist last-was-newline))
- (else
- (write-char char port)
- (anychar-dispatch (+ pos 1) arglist #f)
- ))
- ))
- )) ; end anychar-dispatch
-
- (has-newline?
- (lambda (whatever last-was-newline)
- (or (eqv? whatever #\newline)
- (and (string? whatever)
- (let ( (len (string-length whatever)) )
- (if (zero? len)
- last-was-newline
- (eqv? #\newline (string-ref whatever (- len 1)))))))
- )) ; end has-newline?
-
- (tilde-dispatch
- (lambda (pos arglist last-was-newline)
- (cond
- ((>= pos length-of-format-string)
- (write-char #\~ port) ; tilde at end of string is just output
- arglist ; return unused args
- )
- (else
- (case (char-upcase (string-ref format-strg pos))
- ((#\A) ; Any -- for humans
- (require-an-arg arglist)
- (let ( (whatever (car arglist)) )
- (display whatever port)
- (anychar-dispatch (+ pos 1)
- (cdr arglist)
- (has-newline? whatever last-was-newline))
- ))
- ((#\S) ; Slashified -- for parsers
- (require-an-arg arglist)
- (let ( (whatever (car arglist)) )
- (write whatever port)
- (anychar-dispatch (+ pos 1)
- (cdr arglist)
- (has-newline? whatever last-was-newline))
- ))
- ((#\W)
- (require-an-arg arglist)
- (let ( (whatever (car arglist)) )
- (write-with-shared-structure whatever port) ;; srfi-38
- (anychar-dispatch (+ pos 1)
- (cdr arglist)
- (has-newline? whatever last-was-newline))
- ))
- ((#\D) ; Decimal
- (require-an-arg arglist)
- (display (number->string (car arglist) 10) port)
- (anychar-dispatch (+ pos 1) (cdr arglist) #f)
- )
- ((#\X) ; HeXadecimal
- (require-an-arg arglist)
- (display (number->string (car arglist) 16) port)
- (anychar-dispatch (+ pos 1) (cdr arglist) #f)
- )
- ((#\O) ; Octal
- (require-an-arg arglist)
- (display (number->string (car arglist) 8) port)
- (anychar-dispatch (+ pos 1) (cdr arglist) #f)
- )
- ((#\B) ; Binary
- (require-an-arg arglist)
- (display (number->string (car arglist) 2) port)
- (anychar-dispatch (+ pos 1) (cdr arglist) #f)
- )
- ((#\C) ; Character
- (require-an-arg arglist)
- (write-char (car arglist) port)
- (anychar-dispatch (+ pos 1) (cdr arglist) (eqv? (car arglist) #\newline))
- )
- ((#\~) ; Tilde
- (write-char #\~ port)
- (anychar-dispatch (+ pos 1) arglist #f)
- )
- ((#\%) ; Newline
- (newline port)
- (anychar-dispatch (+ pos 1) arglist #t)
- )
- ((#\&) ; Freshline
- (if (not last-was-newline) ;; (unless last-was-newline ..
- (newline port))
- (anychar-dispatch (+ pos 1) arglist #t)
- )
- ((#\_) ; Space
- (write-char #\space port)
- (anychar-dispatch (+ pos 1) arglist #f)
- )
- ((#\T) ; Tab -- IMPLEMENTATION DEPENDENT ENCODING
- (write-char ascii-tab port)
- (anychar-dispatch (+ pos 1) arglist #f)
- )
- ((#\Y) ; Pretty-print
- (pretty-print (car arglist) port) ;; IMPLEMENTATION DEPENDENT
- (anychar-dispatch (+ pos 1) (cdr arglist) #f)
- )
- ((#\F)
- (require-an-arg arglist)
- (display (format-fixed (car arglist) 0 #f) port)
- (anychar-dispatch (+ pos 1) (cdr arglist) #f)
- )
- ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) ;; gather "~w[,d]F" w and d digits
- (let loop ( (index (+ pos 1))
- (w-digits (list (string-ref format-strg pos)))
- (d-digits '())
- (in-width? #t)
- )
- (if (>= index length-of-format-string)
- (error
- (format "FORMAT: improper numeric format directive in ~s" format-strg))
- (let ( (next-char (string-ref format-strg index)) )
- (cond
- ((char-numeric? next-char)
- (if in-width?
- (loop (+ index 1)
- (cons next-char w-digits)
- d-digits
- in-width?)
- (loop (+ index 1)
- w-digits
- (cons next-char d-digits)
- in-width?))
- )
- ((char=? (char-upcase next-char) #\F)
- (let ( (width (string->number (list->string (reverse w-digits))))
- (digits (if (zero? (length d-digits))
- #f
- (string->number (list->string (reverse d-digits)))))
- )
- (display (format-fixed (car arglist) width digits) port)
- (anychar-dispatch (+ index 1) (cdr arglist) #f))
- )
- ((char=? next-char #\,)
- (if in-width?
- (loop (+ index 1)
- w-digits
- d-digits
- #f)
- (error
- (format "FORMAT: too many commas in directive ~s" format-strg)))
- )
- (else
- (error (format "FORMAT: ~~w.dF directive ill-formed in ~s" format-strg))))))
- ))
- ((#\? #\K) ; indirection -- take next arg as format string
- (cond ; and following arg as list of format args
- ((< (length arglist) 2)
- (error
- (format "FORMAT: less arguments than specified for ~~?: ~s" arglist))
- )
- ((not (string? (car arglist)))
- (error
- (format "FORMAT: ~~? requires a string: ~s" (car arglist)))
- )
- (else
- (format-help (car arglist) (cadr arglist))
- (anychar-dispatch (+ pos 1) (cddr arglist) #f)
- )))
- ((#\H) ; Help
- (display documentation-string port)
- (anychar-dispatch (+ pos 1) arglist #t)
- )
- (else
- (error (format "FORMAT: unknown tilde escape: ~s"
- (string-ref format-strg pos))))
- )))
- )) ; end tilde-dispatch
- ) ; end letrec
-
- ; format-help main
- (anychar-dispatch 0 arglist #f)
- )) ; end format-help
-
- ; format main
- (let ( (unused-args (format-help format-string args)) )
- (if (not (null? unused-args))
- (error
- (format "FORMAT: unused arguments ~s" unused-args)))
- (return-value))
-
- )) ; end letrec, if
-))) ; end format
-;; Copyright (C) Taylan Ulrich Bayırlı/Kammer (2015). All Rights Reserved.
-
-;; Permission is hereby granted, free of charge, to any person obtaining
-;; a copy of this software and associated documentation files (the
-;; "Software"), to deal in the Software without restriction, including
-;; without limitation the rights to use, copy, modify, merge, publish,
-;; distribute, sublicense, and/or sell copies of the Software, and to
-;; permit persons to whom the Software is furnished to do so, subject to
-;; the following conditions:
-
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-;; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-(define-library (srfi 5)
- (export (rename let+ let))
- (import (scheme base))
- (begin
-
- (define-syntax let+
- (syntax-rules ()
- ;; Unnamed, no rest args.
- ((_ ((var val) ...) body ...)
- (let ((var val) ...) body ...))
- ;; Unnamed, with rest args.
- ((_ ((var val) spec ...) body ...)
- (rest ((var val) spec ...) () () body ...))
- ;; Signature style, no rest args.
- ((_ (name (var val) ...) body ...)
- (let name ((var val) ...) body ...))
- ;; Signature style, with rest args.
- ((_ (name (var val) spec ...) body ...)
- (rest/named name ((var val) spec ...) () () body ...))
- ;; Named let, no rest args.
- ((_ name ((var val) ...) body ...)
- (let name ((var val) ...) body ...))
- ;; Named let, with rest args.
- ((_ name ((var val) spec ...) body ...)
- (rest/named name ((var val) spec ...) () () body ...))))
-
- (define-syntax rest
- (syntax-rules ()
- ((_ ((var val) spec ...) (var* ...) (val* ...) body ...)
- (rest name (spec ...) (var var* ...) (val val* ...) body ...))
- ((_ (rest-var rest-val ...) (var ...) (val ...) body ...)
- (let ((var val)
- ...
- (rest-var (list rest-val ...)))
- body ...))))
-
- (define-syntax rest/named
- (syntax-rules ()
- ((_ name ((var val) spec ...) (var* ...) (val* ...) body ...)
- (rest/named name (spec ...) (var var* ...) (val val* ...) body ...))
- ((_ name (rest-var rest-val ...) (var ...) (val ...) body ...)
- (letrec ((name (lambda (var ... . rest-var) body ...)))
- (name val ... rest-val ...)))))
-
- ))
-(define-library (srfi 51)
- (export
- rest-values
- arg-and
- arg-ands
- err-and
- err-ands
- arg-or
- arg-ors
- err-or
- err-ors
- )
- (import
- (scheme base)
- (srfi 1))
- (include "51.upstream.scm"))
-(define-library (srfi aux)
- (import
- (scheme base)
- (scheme case-lambda)
- (srfi 31))
- (export
- debug-mode
- define/opt
- lambda/opt
- define-check-arg
- )
- (begin
-
- (define debug-mode (make-parameter #f))
-
- ;; Emacs indentation help:
- ;; (put 'define/opt 'scheme-indent-function 1)
- ;; (put 'lambda/opt 'scheme-indent-function 1)
-
- (define-syntax define/opt
- (syntax-rules ()
- ((_ (name . args) . body)
- (define name (lambda/opt args . body)))))
-
- (define-syntax lambda/opt
- (syntax-rules ()
- ((lambda* args . body)
- (rec name (opt/split-args name () () args body)))))
-
- (define-syntax opt/split-args
- (syntax-rules ()
- ((_ name non-opts (opts ...) ((opt) . rest) body)
- (opt/split-args name non-opts (opts ... (opt #f)) rest body))
- ((_ name non-opts (opts ...) ((opt def) . rest) body)
- (opt/split-args name non-opts (opts ... (opt def)) rest body))
- ((_ name (non-opts ...) opts (non-opt . rest) body)
- (opt/split-args name (non-opts ... non-opt) opts rest body))
- ;; Rest could be () or a rest-arg here; just propagate it.
- ((_ name non-opts opts rest body)
- (opt/make-clauses name () rest non-opts opts body))))
-
- (define-syntax opt/make-clauses
- (syntax-rules ()
- ;; Handle special-case with no optargs.
- ((_ name () rest (taken ...) () body)
- (lambda (taken ... . rest)
- . body))
- ;; Add clause where no optargs are provided.
- ((_ name () rest (taken ...) ((opt def) ...) body)
- (opt/make-clauses
- name
- (((taken ...)
- (name taken ... def ...)))
- rest
- (taken ...)
- ((opt def) ...)
- body))
- ;; Add clauses where 1 to n-1 optargs are provided
- ((_ name (clause ...) rest (taken ...) ((opt def) (opt* def*) ... x) body)
- (opt/make-clauses
- name
- (clause
- ...
- ((taken ... opt)
- (name taken ... opt def* ...)))
- rest
- (taken ... opt)
- ((opt* def*) ... x)
- body))
- ;; Add clause where all optargs were given, and possibly more.
- ((_ name (clause ...) rest (taken ...) ((opt def)) body)
- (case-lambda
- clause
- ...
- ((taken ... opt . rest)
- . body)))))
-
- (define-syntax define-check-arg
- (syntax-rules ()
- ((_ check-arg)
- (define check-arg
- (if (debug-mode)
- (lambda (pred val proc)
- (if (pred val)
- val
- (error "Type assertion failed:"
- `(value ,val)
- `(expected-type ,pred)
- `(callee ,proc))))
- (lambda (pred val proc)
- val))))))
-
- ))
-;;; Copyright (C) Joo ChurlSoo (2004). All Rights Reserved.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-(define (rest-values rest . default)
- (let* ((caller (if (or (null? default)
- (boolean? (car default))
- (integer? (car default))
- (memq (car default) (list + -)))
- '()
- (if (string? rest) rest (list rest))))
- (rest-list (if (null? caller) rest (car default)))
- (rest-length (if (list? rest-list)
- (length rest-list)
- (if (string? caller)
- (error caller rest-list 'rest-list
- '(list? rest-list))
- (apply error "bad rest list" rest-list 'rest-list
- '(list? rest-list) caller))))
- (default (if (null? caller) default (cdr default)))
- (default-list (if (null? default) default (cdr default)))
- (default-length (length default-list))
- (number
- (and (not (null? default))
- (let ((option (car default)))
- (or (and (integer? option)
- (or (and (> rest-length (abs option))
- (if (string? caller)
- (error caller rest-list 'rest-list
- `(<= (length rest-list)
- ,(abs option)))
- (apply error "too many arguments"
- rest-list 'rest-list
- `(<= (length rest-list)
- ,(abs option))
- caller)))
- (and (> default-length (abs option))
- (if (string? caller)
- (error caller default-list
- 'default-list
- `(<= (length default-list)
- ,(abs option)))
- (apply error "too many defaults"
- default-list 'default-list
- `(<= (length default-list)
- ,(abs option))
- caller)))
- option))
- (eq? option #t)
- (and (not option) 'false)
- (and (eq? option +) +)
- (and (eq? option -) -)
- (if (string? caller)
- (error caller option 'option
- '(or (boolean? option)
- (integer? option)
- (memq option (list + -))))
- (apply error "bad optional argument" option 'option
- '(or (boolean? option)
- (integer? option)
- (memq option (list + -)))
- caller)))))))
- (cond
- ((or (eq? #t number) (eq? 'false number))
- (and (not (every pair? default-list))
- (if (string? caller)
- (error caller default-list 'default-list
- '(every pair? default-list))
- (apply error "bad default list" default-list 'default-list
- '(every pair? default-list) caller)))
- (let loop ((rest-list rest-list)
- (default-list default-list)
- (result '()))
- (if (null? default-list)
- (if (null? rest-list)
- (apply values (reverse result))
- (if (eq? #t number)
- (if (string? caller)
- (error caller rest-list 'rest-list '(null? rest-list))
- (apply error "bad argument" rest-list 'rest-list
- '(null? rest-list) caller))
- (apply values (append-reverse result rest-list))))
- (if (null? rest-list)
- (apply values (append-reverse result (map car default-list)))
- (let ((default (car default-list)))
- (let lp ((rest rest-list)
- (head '()))
- (if (null? rest)
- (loop (reverse head)
- (cdr default-list)
- (cons (car default) result))
- (if (list? default)
- (if (member (car rest) default)
- (loop (append-reverse head (cdr rest))
- (cdr default-list)
- (cons (car rest) result))
- (lp (cdr rest) (cons (car rest) head)))
- (if ((cdr default) (car rest))
- (loop (append-reverse head (cdr rest))
- (cdr default-list)
- (cons (car rest) result))
- (lp (cdr rest) (cons (car rest) head)))))))))))
- ((or (and (integer? number) (> number 0))
- (eq? number +))
- (and (not (every pair? default-list))
- (if (string? caller)
- (error caller default-list 'default-list
- '(every pair? default-list))
- (apply error "bad default list" default-list 'default-list
- '(every pair? default-list) caller)))
- (let loop ((rest rest-list)
- (default default-list))
- (if (or (null? rest) (null? default))
- (apply values
- (if (> default-length rest-length)
- (append rest-list
- (map car (list-tail default-list rest-length)))
- rest-list))
- (let ((arg (car rest))
- (par (car default)))
- (if (list? par)
- (if (member arg par)
- (loop (cdr rest) (cdr default))
- (if (string? caller)
- (error caller arg 'arg `(member arg ,par))
- (apply error "unmatched argument"
- arg 'arg `(member arg ,par) caller)))
- (if ((cdr par) arg)
- (loop (cdr rest) (cdr default))
- (if (string? caller)
- (error caller arg 'arg `(,(cdr par) arg))
- (apply error "incorrect argument"
- arg 'arg `(,(cdr par) arg) caller))))))))
- (else
- (apply values (if (> default-length rest-length)
- (append rest-list (list-tail default-list rest-length))
- rest-list))))))
-
-(define-syntax arg-and
- (syntax-rules()
- ((arg-and arg (a1 a2 ...) ...)
- (and (or (symbol? 'arg)
- (error "bad syntax" 'arg '(symbol? 'arg)
- '(arg-and arg (a1 a2 ...) ...)))
- (or (a1 a2 ...)
- (error "incorrect argument" arg 'arg '(a1 a2 ...)))
- ...))
- ((arg-and caller arg (a1 a2 ...) ...)
- (and (or (symbol? 'arg)
- (error "bad syntax" 'arg '(symbol? 'arg)
- '(arg-and caller arg (a1 a2 ...) ...)))
- (or (a1 a2 ...)
- (if (string? caller)
- (error caller arg 'arg '(a1 a2 ...))
- (error "incorrect argument" arg 'arg '(a1 a2 ...) caller)))
- ...))))
-
-;; accessory macro for arg-ands
-(define-syntax caller-arg-and
- (syntax-rules()
- ((caller-arg-and caller arg (a1 a2 ...) ...)
- (and (or (symbol? 'arg)
- (error "bad syntax" 'arg '(symbol? 'arg)
- '(caller-arg-and caller arg (a1 a2 ...) ...)))
- (or (a1 a2 ...)
- (if (string? caller)
- (error caller arg 'arg '(a1 a2 ...))
- (error "incorrect argument" arg 'arg '(a1 a2 ...) caller)))
- ...))
- ((caller-arg-and null caller arg (a1 a2 ...) ...)
- (and (or (symbol? 'arg)
- (error "bad syntax" 'arg '(symbol? 'arg)
- '(caller-arg-and caller arg (a1 a2 ...) ...)))
- (or (a1 a2 ...)
- (if (string? caller)
- (error caller arg 'arg '(a1 a2 ...))
- (error "incorrect argument" arg 'arg '(a1 a2 ...) caller)))
- ...))))
-
-(define-syntax arg-ands
- (syntax-rules (common)
- ((arg-ands (a1 a2 ...) ...)
- (and (arg-and a1 a2 ...) ...))
- ((arg-ands common caller (a1 a2 ...) ...)
- (and (caller-arg-and caller a1 a2 ...) ...))))
-
-(define-syntax arg-or
- (syntax-rules()
- ((arg-or arg (a1 a2 ...) ...)
- (or (and (not (symbol? 'arg))
- (error "bad syntax" 'arg '(symbol? 'arg)
- '(arg-or arg (a1 a2 ...) ...)))
- (and (a1 a2 ...)
- (error "incorrect argument" arg 'arg '(a1 a2 ...)))
- ...))
- ((arg-or caller arg (a1 a2 ...) ...)
- (or (and (not (symbol? 'arg))
- (error "bad syntax" 'arg '(symbol? 'arg)
- '(arg-or caller arg (a1 a2 ...) ...)))
- (and (a1 a2 ...)
- (if (string? caller)
- (error caller arg 'arg '(a1 a2 ...))
- (error "incorrect argument" arg 'arg '(a1 a2 ...) caller)))
- ...))))
-
-;; accessory macro for arg-ors
-(define-syntax caller-arg-or
- (syntax-rules()
- ((caller-arg-or caller arg (a1 a2 ...) ...)
- (or (and (not (symbol? 'arg))
- (error "bad syntax" 'arg '(symbol? 'arg)
- '(caller-arg-or caller arg (a1 a2 ...) ...)))
- (and (a1 a2 ...)
- (if (string? caller)
- (error caller arg 'arg '(a1 a2 ...))
- (error "incorrect argument" arg 'arg '(a1 a2 ...) caller)))
- ...))
- ((caller-arg-or null caller arg (a1 a2 ...) ...)
- (or (and (not (symbol? 'arg))
- (error "bad syntax" 'arg '(symbol? 'arg)
- '(caller-arg-or caller arg (a1 a2 ...) ...)))
- (and (a1 a2 ...)
- (if (string? caller)
- (error caller arg 'arg '(a1 a2 ...))
- (error "incorrect argument" arg 'arg '(a1 a2 ...) caller)))
- ...))))
-
-(define-syntax arg-ors
- (syntax-rules (common)
- ((arg-ors (a1 a2 ...) ...)
- (or (arg-or a1 a2 ...) ...))
- ((arg-ors common caller (a1 a2 ...) ...)
- (or (caller-arg-or caller a1 a2 ...) ...))))
-
-(define-syntax err-and
- (syntax-rules ()
- ((err-and err expression ...)
- (and (or expression
- (if (string? err)
- (error err 'expression)
- (error "false expression" 'expression err)))
- ...))))
-
-(define-syntax err-ands
- (syntax-rules ()
- ((err-ands (err expression ...) ...)
- (and (err-and err expression ...)
- ...))))
-
-(define-syntax err-or
- (syntax-rules ()
- ((err-or err expression ...)
- (or (and expression
- (if (string? err)
- (error err 'expression)
- (error "true expression" 'expression err)))
- ...))))
-
-(define-syntax err-ors
- (syntax-rules ()
- ((err-ors (err expression ...) ...)
- (or (err-or err expression ...)
- ...))))
-;;; Copyright (C) Joo ChurlSoo (2004). All Rights Reserved.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-(define-syntax alet-cat* ; borrowed from SRFI-86
- (syntax-rules ()
- ((alet-cat* z (a . e) bd ...)
- (let ((y z))
- (%alet-cat* y (a . e) bd ...)))))
-
-(define-syntax %alet-cat* ; borrowed from SRFI-86
- (syntax-rules ()
- ((%alet-cat* z ((n d t ...)) bd ...)
- (let ((n (if (null? z)
- d
- (if (null? (cdr z))
- (wow-cat-end z n t ...)
- (error "cat: too many arguments" (cdr z))))))
- bd ...))
- ((%alet-cat* z ((n d t ...) . e) bd ...)
- (let ((n (if (null? z)
- d
- (wow-cat! z n d t ...))))
- (%alet-cat* z e bd ...)))
- ((%alet-cat* z e bd ...)
- (let ((e z)) bd ...))))
-
-(define-syntax wow-cat! ; borrowed from SRFI-86
- (syntax-rules ()
- ((wow-cat! z n d)
- (let ((n (car z)))
- (set! z (cdr z))
- n))
- ((wow-cat! z n d t)
- (let ((n (car z)))
- (if t
- (begin (set! z (cdr z)) n)
- (let lp ((head (list n)) (tail (cdr z)))
- (if (null? tail)
- d
- (let ((n (car tail)))
- (if t
- (begin (set! z (append (reverse head) (cdr tail))) n)
- (lp (cons n head) (cdr tail)))))))))
- ((wow-cat! z n d t ts)
- (let ((n (car z)))
- (if t
- (begin (set! z (cdr z)) ts)
- (let lp ((head (list n)) (tail (cdr z)))
- (if (null? tail)
- d
- (let ((n (car tail)))
- (if t
- (begin (set! z (append (reverse head) (cdr tail))) ts)
- (lp (cons n head) (cdr tail)))))))))
- ((wow-cat! z n d t ts fs)
- (let ((n (car z)))
- (if t
- (begin (set! z (cdr z)) ts)
- (begin (set! z (cdr z)) fs))))))
-
-(define-syntax wow-cat-end ; borrowed from SRFI-86
- (syntax-rules ()
- ((wow-cat-end z n)
- (car z))
- ((wow-cat-end z n t)
- (let ((n (car z)))
- (if t n (error "cat: too many argument" z))))
- ((wow-cat-end z n t ts)
- (let ((n (car z)))
- (if t ts (error "cat: too many argument" z))))
- ((wow-cat-end z n t ts fs)
- (let ((n (car z)))
- (if t ts fs)))))
-
-(define (str-index str char)
- (let ((len (string-length str)))
- (let lp ((n 0))
- (and (< n len)
- (if (char=? char (string-ref str n))
- n
- (lp (+ n 1)))))))
-
-(define (every? pred ls)
- (let lp ((ls ls))
- (or (null? ls)
- (and (pred (car ls))
- (lp (cdr ls))))))
-
-(define (part pred ls)
- (let lp ((ls ls) (true '()) (false '()))
- (cond
- ((null? ls) (cons (reverse true) (reverse false)))
- ((pred (car ls)) (lp (cdr ls) (cons (car ls) true) false))
- (else (lp (cdr ls) true (cons (car ls) false))))))
-
-(define (e-mold num pre)
- (let* ((str (number->string (inexact num)))
- (e-index (str-index str #\e)))
- (if e-index
- (string-append (mold (substring str 0 e-index) pre)
- (substring str e-index (string-length str)))
- (mold str pre))))
-
-(define (mold str pre)
- (let ((ind (str-index str #\.)))
- (if ind
- (let ((d-len (- (string-length str) (+ ind 1))))
- (cond
- ((= d-len pre) str)
- ((< d-len pre) (string-append str (make-string (- pre d-len) #\0)))
- ;;((char<? #\4 (string-ref str (+ 1 ind pre)))
- ;;(let ((com (expt 10 pre)))
- ;; (number->string (/ (round (* (string->number str) com)) com))))
- ((or (char<? #\5 (string-ref str (+ 1 ind pre)))
- (and (char=? #\5 (string-ref str (+ 1 ind pre)))
- (or (< (+ 1 pre) d-len)
- (memv (string-ref str (+ ind (if (= 0 pre) -1 pre)))
- '(#\1 #\3 #\5 #\7 #\9)))))
- (apply
- string
- (let* ((minus (char=? #\- (string-ref str 0)))
- (str (substring str (if minus 1 0) (+ 1 ind pre)))
- (char-list
- (reverse
- (let lp ((index (- (string-length str) 1))
- (raise #t))
- (if (= -1 index)
- (if raise '(#\1) '())
- (let ((chr (string-ref str index)))
- (if (char=? #\. chr)
- (cons chr (lp (- index 1) raise))
- (if raise
- (if (char=? #\9 chr)
- (cons #\0 (lp (- index 1) raise))
- (cons (integer->char
- (+ 1 (char->integer chr)))
- (lp (- index 1) #f)))
- (cons chr (lp (- index 1) raise))))))))))
- (if minus (cons #\- char-list) char-list))))
- (else
- (substring str 0 (+ 1 ind pre)))))
- (string-append str "." (make-string pre #\0)))))
-
-(define (separate str sep num opt)
- (let* ((len (string-length str))
- (pos (if opt
- (let ((pos (remainder (if (eq? opt 'minus) (- len 1) len)
- num)))
- (if (= 0 pos) num pos))
- num)))
- (apply string-append
- (let loop ((ini 0)
- (pos (if (eq? opt 'minus) (+ pos 1) pos)))
- (if (< pos len)
- (cons (substring str ini pos)
- (cons sep (loop pos (+ pos num))))
- (list (substring str ini len)))))))
-
-(define (cat object . rest)
- (let* ((str-rest (part string? rest))
- (str-list (car str-rest))
- (rest-list (cdr str-rest)))
- (if (null? rest-list)
- (apply string-append
- (cond
- ((number? object) (number->string object))
- ((string? object) object)
- ((char? object) (string object))
- ((boolean? object) (if object "#t" "#f"))
- ((symbol? object) (symbol->string object))
- (else
- (get-output-string
- (let ((str-port (open-output-string)))
- (write object str-port)
- str-port))))
- str-list)
- (alet-cat* rest-list
- ((width 0 (and (integer? width) (exact? width)))
- (port #f (or (boolean? port) (output-port? port))
- (if (eq? port #t) (current-output-port) port))
- (char #\space (char? char))
- (converter #f (and (pair? converter)
- (procedure? (car converter))
- (procedure? (cdr converter))))
- (precision #f (and (integer? precision)
- (inexact? precision)))
- (sign #f (eq? 'sign sign))
- (radix 'decimal
- (memq radix '(decimal octal binary hexadecimal)))
- (exactness #f (memq exactness '(exact inexact)))
- (separator #f (and (list? separator)
- (< 0 (length separator) 3)
- (char? (car separator))
- (or (null? (cdr separator))
- (let ((n (cadr separator)))
- (and (integer? n) (exact? n)
- (< 0 n))))))
- (writer #f (procedure? writer))
- (pipe #f (and (list? pipe)
- (not (null? pipe))
- (every? procedure? pipe)))
- (take #f (and (list? take)
- (< 0 (length take) 3)
- (every? (lambda (x)
- (and (integer? x) (exact? x)))
- take))))
- (let* ((str
- (cond
- ((and converter
- ((car converter) object))
- (let* ((str ((cdr converter) object))
- (pad (- (abs width) (string-length str))))
- (cond
- ((<= pad 0) str)
- ((< 0 width) (string-append (make-string pad char) str))
- (else (string-append str (make-string pad char))))))
- ((number? object)
- (and (not (eq? radix 'decimal)) precision
- (error "cat: non-decimal cannot have a decimal point"))
- (and precision (< precision 0) (eq? exactness 'exact)
- (error "cat: exact number cannot have a decimal point without exact sign"))
- (let* ((exact-sign (and precision
- (<= 0 precision)
- (or (eq? exactness 'exact)
- (and (exact? object)
- (not (eq? exactness
- 'inexact))))
- "#e"))
- (inexact-sign (and (not (eq? radix 'decimal))
- (or (and (inexact? object)
- (not (eq? exactness
- 'exact)))
- (eq? exactness 'inexact))
- "#i"))
- (radix-sign (cdr (assq radix
- '((decimal . #f)
- (octal . "#o")
- (binary . "#b")
- (hexadecimal . "#x")))))
- (plus-sign (and sign (< 0 (real-part object)) "+"))
- (exactness-sign (or exact-sign inexact-sign))
- (str
- (if precision
- (let ((precision (exact
- (abs precision)))
- (imag (imag-part object)))
- (if (= 0 imag)
- (e-mold object precision)
- (string-append
- (e-mold (real-part object) precision)
- (if (< 0 imag) "+" "")
- (e-mold imag precision)
- "i")))
- (number->string
- (cond
- (inexact-sign (exact object))
- (exactness
- (if (eq? exactness 'exact)
- (exact object)
- (inexact object)))
- (else object))
- (cdr (assq radix '((decimal . 10)
- (octal . 8)
- (binary . 2)
- (hexadecimal . 16)))))))
- (str
- (if (and separator
- (not (or (and (eq? radix 'decimal)
- (str-index str #\e))
- (str-index str #\i)
- (str-index str #\/))))
- (let ((sep (string (car separator)))
- (num (if (null? (cdr separator))
- 3 (cadr separator)))
- (dot-index (str-index str #\.)))
- (if dot-index
- (string-append
- (separate (substring str 0 dot-index)
- sep num (if (< object 0)
- 'minus #t))
- "."
- (separate (substring
- str (+ 1 dot-index)
- (string-length str))
- sep num #f))
- (separate str sep num (if (< object 0)
- 'minus #t))))
- str))
- (pad (- (abs width)
- (+ (string-length str)
- (if exactness-sign 2 0)
- (if radix-sign 2 0)
- (if plus-sign 1 0))))
- (pad (if (< 0 pad) pad 0)))
- (if (< 0 width)
- (if (char-numeric? char)
- (if (< (real-part object) 0)
- (string-append (or exactness-sign "")
- (or radix-sign "")
- "-"
- (make-string pad char)
- (substring str 1
- (string-length
- str)))
- (string-append (or exactness-sign "")
- (or radix-sign "")
- (or plus-sign "")
- (make-string pad char)
- str))
- (string-append (make-string pad char)
- (or exactness-sign "")
- (or radix-sign "")
- (or plus-sign "")
- str))
- (string-append (or exactness-sign "")
- (or radix-sign "")
- (or plus-sign "")
- str
- (make-string pad char)))))
- (else
- (let* ((str (cond
- (writer (get-output-string
- (let ((str-port
- (open-output-string)))
- (writer object str-port)
- str-port)))
- ((string? object) object)
- ((char? object) (string object))
- ((boolean? object) (if object "#t" "#f"))
- ((symbol? object) (symbol->string object))
- (else (get-output-string
- (let ((str-port (open-output-string)))
- (write object str-port)
- str-port)))))
- (str (if pipe
- (let loop ((str ((car pipe) str))
- (fns (cdr pipe)))
- (if (null? fns)
- str
- (loop ((car fns) str)
- (cdr fns))))
- str))
- (str
- (if take
- (let ((left (car take))
- (right (if (null? (cdr take))
- 0 (cadr take)))
- (len (string-length str)))
- (define (substr str beg end)
- (let ((end (cond
- ((< end 0) 0)
- ((< len end) len)
- (else end)))
- (beg (cond
- ((< beg 0) 0)
- ((< len beg) len)
- (else beg))))
- (if (and (= beg 0) (= end len))
- str
- (substring str beg end))))
- (string-append
- (if (< left 0)
- (substr str (abs left) len)
- (substr str 0 left))
- (if (< right 0)
- (substr str 0 (+ len right))
- (substr str (- len right) len))))
- str))
- (pad (- (abs width) (string-length str))))
- (cond
- ((<= pad 0) str)
- ((< 0 width) (string-append (make-string pad char) str))
- (else (string-append str (make-string pad char))))))))
- (str (apply string-append str str-list)))
- (and port (display str port))
- str)))))
-
-;;; eof
-(define-library (srfi 54)
- (export cat)
- (import
- (scheme base)
- (scheme char)
- (scheme complex)
- (scheme write)
- (srfi 1))
- (include "54.body.scm"))
-;;; Copyright (C) Joo ChurlSoo (2004). All Rights Reserved.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-(define (cat object . rest)
- (let* ((str-rest (part string? rest))
- (str-list (car str-rest))
- (rest-list (cdr str-rest)))
- (if (null? rest-list)
- (apply string-append
- (cond
- ((number? object) (number->string object))
- ((string? object) object)
- ((char? object) (string object))
- ((boolean? object) (if object "#t" "#f"))
- ((symbol? object) (symbol->string object))
- (else
- (get-output-string
- (let ((str-port (open-output-string)))
- (write object str-port)
- str-port))))
- str-list)
- (alet-cat* rest-list
- ((width 0 (and (integer? width) (exact? width)))
- (port #f (or (boolean? port) (output-port? port))
- (if (eq? port #t) (current-output-port) port))
- (char #\space (char? char))
- (converter #f (and (pair? converter)
- (procedure? (car converter))
- (procedure? (cdr converter))))
- (precision #f (and (integer? precision)
- (inexact? precision)))
- (sign #f (eq? 'sign sign))
- (radix 'decimal
- (memq radix '(decimal octal binary hexadecimal)))
- (exactness #f (memq exactness '(exact inexact)))
- (separator #f (and (list? separator)
- (< 0 (length separator) 3)
- (char? (car separator))
- (or (null? (cdr separator))
- (let ((n (cadr separator)))
- (and (integer? n) (exact? n)
- (< 0 n))))))
- (writer #f (procedure? writer))
- (pipe #f (and (list? pipe)
- (not (null? pipe))
- (every? procedure? pipe)))
- (take #f (and (list? take)
- (< 0 (length take) 3)
- (every? (lambda (x)
- (and (integer? x) (exact? x)))
- take))))
- (let* ((str
- (cond
- ((and converter
- ((car converter) object))
- (let* ((str ((cdr converter) object))
- (pad (- (abs width) (string-length str))))
- (cond
- ((<= pad 0) str)
- ((< 0 width) (string-append (make-string pad char) str))
- (else (string-append str (make-string pad char))))))
- ((number? object)
- (and (not (eq? radix 'decimal)) precision
- (error "cat: non-decimal cannot have a decimal point"))
- (and precision (< precision 0) (eq? exactness 'exact)
- (error "cat: exact number cannot have a decimal point without exact sign"))
- (let* ((exact-sign (and precision
- (<= 0 precision)
- (or (eq? exactness 'exact)
- (and (exact? object)
- (not (eq? exactness
- 'inexact))))
- "#e"))
- (inexact-sign (and (not (eq? radix 'decimal))
- (or (and (inexact? object)
- (not (eq? exactness
- 'exact)))
- (eq? exactness 'inexact))
- "#i"))
- (radix-sign (cdr (assq radix
- '((decimal . #f)
- (octal . "#o")
- (binary . "#b")
- (hexadecimal . "#x")))))
- (plus-sign (and sign (< 0 (real-part object)) "+"))
- (exactness-sign (or exact-sign inexact-sign))
- (str
- (if precision
- (let ((precision (inexact->exact
- (abs precision)))
- (imag (imag-part object)))
- (if (= 0 imag)
- (e-mold object precision)
- (string-append
- (e-mold (real-part object) precision)
- (if (< 0 imag) "+" "")
- (e-mold imag precision)
- "i")))
- (number->string
- (cond
- (inexact-sign (inexact->exact object))
- (exactness
- (if (eq? exactness 'exact)
- (inexact->exact object)
- (exact->inexact object)))
- (else object))
- (cdr (assq radix '((decimal . 10)
- (octal . 8)
- (binary . 2)
- (hexadecimal . 16)))))))
- (str
- (if (and separator
- (not (or (and (eq? radix 'decimal)
- (str-index str #\e))
- (str-index str #\i)
- (str-index str #\/))))
- (let ((sep (string (car separator)))
- (num (if (null? (cdr separator))
- 3 (cadr separator)))
- (dot-index (str-index str #\.)))
- (if dot-index
- (string-append
- (separate (substring str 0 dot-index)
- sep num (if (< object 0)
- 'minus #t))
- "."
- (separate (substring
- str (+ 1 dot-index)
- (string-length str))
- sep num #f))
- (separate str sep num (if (< object 0)
- 'minus #t))))
- str))
- (pad (- (abs width)
- (+ (string-length str)
- (if exactness-sign 2 0)
- (if radix-sign 2 0)
- (if plus-sign 1 0))))
- (pad (if (< 0 pad) pad 0)))
- (if (< 0 width)
- (if (char-numeric? char)
- (if (< (real-part object) 0)
- (string-append (or exactness-sign "")
- (or radix-sign "")
- "-"
- (make-string pad char)
- (substring str 1
- (string-length
- str)))
- (string-append (or exactness-sign "")
- (or radix-sign "")
- (or plus-sign "")
- (make-string pad char)
- str))
- (string-append (make-string pad char)
- (or exactness-sign "")
- (or radix-sign "")
- (or plus-sign "")
- str))
- (string-append (or exactness-sign "")
- (or radix-sign "")
- (or plus-sign "")
- str
- (make-string pad char)))))
- (else
- (let* ((str (cond
- (writer (get-output-string
- (let ((str-port
- (open-output-string)))
- (writer object str-port)
- str-port)))
- ((string? object) object)
- ((char? object) (string object))
- ((boolean? object) (if object "#t" "#f"))
- ((symbol? object) (symbol->string object))
- (else (get-output-string
- (let ((str-port (open-output-string)))
- (write object str-port)
- str-port)))))
- (str (if pipe
- (let loop ((str ((car pipe) str))
- (fns (cdr pipe)))
- (if (null? fns)
- str
- (loop ((car fns) str)
- (cdr fns))))
- str))
- (str
- (if take
- (let ((left (car take))
- (right (if (null? (cdr take))
- 0 (cadr take)))
- (len (string-length str)))
- (define (substr str beg end)
- (let ((end (cond
- ((< end 0) 0)
- ((< len end) len)
- (else end)))
- (beg (cond
- ((< beg 0) 0)
- ((< len beg) len)
- (else beg))))
- (if (and (= beg 0) (= end len))
- str
- (substring str beg end))))
- (string-append
- (if (< left 0)
- (substr str (abs left) len)
- (substr str 0 left))
- (if (< right 0)
- (substr str 0 (+ len right))
- (substr str (- len right) len))))
- str))
- (pad (- (abs width) (string-length str))))
- (cond
- ((<= pad 0) str)
- ((< 0 width) (string-append (make-string pad char) str))
- (else (string-append str (make-string pad char))))))))
- (str (apply string-append str str-list)))
- (and port (display str port))
- str)))))
-
-(define-syntax alet-cat* ; borrowed from SRFI-86
- (syntax-rules ()
- ((alet-cat* z (a . e) bd ...)
- (let ((y z))
- (%alet-cat* y (a . e) bd ...)))))
-
-(define-syntax %alet-cat* ; borrowed from SRFI-86
- (syntax-rules ()
- ((%alet-cat* z ((n d t ...)) bd ...)
- (let ((n (if (null? z)
- d
- (if (null? (cdr z))
- (wow-cat-end z n t ...)
- (error "cat: too many arguments" (cdr z))))))
- bd ...))
- ((%alet-cat* z ((n d t ...) . e) bd ...)
- (let ((n (if (null? z)
- d
- (wow-cat! z n d t ...))))
- (%alet-cat* z e bd ...)))
- ((%alet-cat* z e bd ...)
- (let ((e z)) bd ...))))
-
-(define-syntax wow-cat! ; borrowed from SRFI-86
- (syntax-rules ()
- ((wow-cat! z n d)
- (let ((n (car z)))
- (set! z (cdr z))
- n))
- ((wow-cat! z n d t)
- (let ((n (car z)))
- (if t
- (begin (set! z (cdr z)) n)
- (let lp ((head (list n)) (tail (cdr z)))
- (if (null? tail)
- d
- (let ((n (car tail)))
- (if t
- (begin (set! z (append (reverse head) (cdr tail))) n)
- (lp (cons n head) (cdr tail)))))))))
- ((wow-cat! z n d t ts)
- (let ((n (car z)))
- (if t
- (begin (set! z (cdr z)) ts)
- (let lp ((head (list n)) (tail (cdr z)))
- (if (null? tail)
- d
- (let ((n (car tail)))
- (if t
- (begin (set! z (append (reverse head) (cdr tail))) ts)
- (lp (cons n head) (cdr tail)))))))))
- ((wow-cat! z n d t ts fs)
- (let ((n (car z)))
- (if t
- (begin (set! z (cdr z)) ts)
- (begin (set! z (cdr z)) fs))))))
-
-(define-syntax wow-cat-end ; borrowed from SRFI-86
- (syntax-rules ()
- ((wow-cat-end z n)
- (car z))
- ((wow-cat-end z n t)
- (let ((n (car z)))
- (if t n (error "cat: too many argument" z))))
- ((wow-cat-end z n t ts)
- (let ((n (car z)))
- (if t ts (error "cat: too many argument" z))))
- ((wow-cat-end z n t ts fs)
- (let ((n (car z)))
- (if t ts fs)))))
-
-(define (str-index str char)
- (let ((len (string-length str)))
- (let lp ((n 0))
- (and (< n len)
- (if (char=? char (string-ref str n))
- n
- (lp (+ n 1)))))))
-
-(define (every? pred ls)
- (let lp ((ls ls))
- (or (null? ls)
- (and (pred (car ls))
- (lp (cdr ls))))))
-
-(define (part pred ls)
- (let lp ((ls ls) (true '()) (false '()))
- (cond
- ((null? ls) (cons (reverse true) (reverse false)))
- ((pred (car ls)) (lp (cdr ls) (cons (car ls) true) false))
- (else (lp (cdr ls) true (cons (car ls) false))))))
-
-(define (e-mold num pre)
- (let* ((str (number->string (exact->inexact num)))
- (e-index (str-index str #\e)))
- (if e-index
- (string-append (mold (substring str 0 e-index) pre)
- (substring str e-index (string-length str)))
- (mold str pre))))
-
-(define (mold str pre)
- (let ((ind (str-index str #\.)))
- (if ind
- (let ((d-len (- (string-length str) (+ ind 1))))
- (cond
- ((= d-len pre) str)
- ((< d-len pre) (string-append str (make-string (- pre d-len) #\0)))
- ;;((char<? #\4 (string-ref str (+ 1 ind pre)))
- ;;(let ((com (expt 10 pre)))
- ;; (number->string (/ (round (* (string->number str) com)) com))))
- ((or (char<? #\5 (string-ref str (+ 1 ind pre)))
- (and (char=? #\5 (string-ref str (+ 1 ind pre)))
- (or (< (+ 1 pre) d-len)
- (memv (string-ref str (+ ind (if (= 0 pre) -1 pre)))
- '(#\1 #\3 #\5 #\7 #\9)))))
- (apply
- string
- (let* ((minus (char=? #\- (string-ref str 0)))
- (str (substring str (if minus 1 0) (+ 1 ind pre)))
- (char-list
- (reverse
- (let lp ((index (- (string-length str) 1))
- (raise #t))
- (if (= -1 index)
- (if raise '(#\1) '())
- (let ((chr (string-ref str index)))
- (if (char=? #\. chr)
- (cons chr (lp (- index 1) raise))
- (if raise
- (if (char=? #\9 chr)
- (cons #\0 (lp (- index 1) raise))
- (cons (integer->char
- (+ 1 (char->integer chr)))
- (lp (- index 1) #f)))
- (cons chr (lp (- index 1) raise))))))))))
- (if minus (cons #\- char-list) char-list))))
- (else
- (substring str 0 (+ 1 ind pre)))))
- (string-append str "." (make-string pre #\0)))))
-
-(define (separate str sep num opt)
- (let* ((len (string-length str))
- (pos (if opt
- (let ((pos (remainder (if (eq? opt 'minus) (- len 1) len)
- num)))
- (if (= 0 pos) num pos))
- num)))
- (apply string-append
- (let loop ((ini 0)
- (pos (if (eq? opt 'minus) (+ pos 1) pos)))
- (if (< pos len)
- (cons (substring str ini pos)
- (cons sep (loop pos (+ pos num))))
- (list (substring str ini len)))))))
-
-;;; eof
-(define-library (srfi 57)
- (export
- define-record-type
- define-record-scheme
- record-update
- record-update!
- record-compose
- )
- (import
- (rename (scheme base) (define-record-type srfi-9:define-record-type))
- (scheme case-lambda))
- (include "57.upstream.scm"))
-;; Copyright (c) 2005, 2006, 2007, 2012, 2013 Per Bothner
-;; Added "full" support for Chicken, Gauche, Guile and SISC.
-;; Alex Shinn, Copyright (c) 2005.
-;; Modified for Scheme Spheres by Álvaro Castro-Castilla, Copyright (c) 2012.
-;; Support for Guile 2 by Mark H Weaver <mhw@netris.org>, Copyright (c) 2014.
-;;
-;; Permission is hereby granted, free of charge, to any person
-;; obtaining a copy of this software and associated documentation
-;; files (the "Software"), to deal in the Software without
-;; restriction, including without limitation the rights to use, copy,
-;; modify, merge, publish, distribute, sublicense, and/or sell copies
-;; of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-;;
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-;;
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-(cond-expand
- (chicken
- (require-extension syntax-case))
- (guile-2
- (use-modules (srfi srfi-9)
- ;; In 2.0.9, srfi-34 and srfi-35 are not well integrated
- ;; with either Guile's native exceptions or R6RS exceptions.
- ;;(srfi srfi-34) (srfi srfi-35)
- (srfi srfi-39)))
- (guile
- (use-modules (ice-9 syncase) (srfi srfi-9)
- ;;(srfi srfi-34) (srfi srfi-35) - not in Guile 1.6.7
- (srfi srfi-39)))
- (sisc
- (require-extension (srfi 9 34 35 39)))
- (kawa
- (module-compile-options warn-undefined-variable\: #t
- warn-invoke-unknown-method\: #t)
- (provide 'srfi-64)
- (provide 'testing)
- (require 'srfi-34)
- (require 'srfi-35))
- (else ()
- ))
-
-(cond-expand
- (kawa
- (define-syntax %test-export
- (syntax-rules ()
- ((%test-export test-begin . other-names)
- (module-export %test-begin . other-names)))))
- (else
- (define-syntax %test-export
- (syntax-rules ()
- ((%test-export . names) (if #f #f))))))
-
-;; List of exported names
-(%test-export
- test-begin ;; must be listed first, since in Kawa (at least) it is "magic".
- test-end test-assert test-eqv test-eq test-equal
- test-approximate test-assert test-error test-apply test-with-runner
- test-match-nth test-match-all test-match-any test-match-name
- test-skip test-expect-fail test-read-eval-string
- test-runner-group-path test-group test-group-with-cleanup
- test-result-ref test-result-set! test-result-clear test-result-remove
- test-result-kind test-passed?
- test-log-to-file
- ; Misc test-runner functions
- test-runner? test-runner-reset test-runner-null
- test-runner-simple test-runner-current test-runner-factory test-runner-get
- test-runner-create test-runner-test-name
- ;; test-runner field setter and getter functions - see %test-record-define:
- test-runner-pass-count test-runner-pass-count!
- test-runner-fail-count test-runner-fail-count!
- test-runner-xpass-count test-runner-xpass-count!
- test-runner-xfail-count test-runner-xfail-count!
- test-runner-skip-count test-runner-skip-count!
- test-runner-group-stack test-runner-group-stack!
- test-runner-on-test-begin test-runner-on-test-begin!
- test-runner-on-test-end test-runner-on-test-end!
- test-runner-on-group-begin test-runner-on-group-begin!
- test-runner-on-group-end test-runner-on-group-end!
- test-runner-on-final test-runner-on-final!
- test-runner-on-bad-count test-runner-on-bad-count!
- test-runner-on-bad-end-name test-runner-on-bad-end-name!
- test-result-alist test-result-alist!
- test-runner-aux-value test-runner-aux-value!
- ;; default/simple call-back functions, used in default test-runner,
- ;; but can be called to construct more complex ones.
- test-on-group-begin-simple test-on-group-end-simple
- test-on-bad-count-simple test-on-bad-end-name-simple
- test-on-final-simple test-on-test-end-simple
- test-on-final-simple)
-
-(cond-expand
- (srfi-9
- (define-syntax %test-record-define
- (syntax-rules ()
- ((%test-record-define alloc runner? (name index setter getter) ...)
- (define-record-type test-runner
- (alloc)
- runner?
- (name setter getter) ...)))))
- (else
- (define %test-runner-cookie (list "test-runner"))
- (define-syntax %test-record-define
- (syntax-rules ()
- ((%test-record-define alloc runner? (name index getter setter) ...)
- (begin
- (define (runner? obj)
- (and (vector? obj)
- (> (vector-length obj) 1)
- (eq (vector-ref obj 0) %test-runner-cookie)))
- (define (alloc)
- (let ((runner (make-vector 23)))
- (vector-set! runner 0 %test-runner-cookie)
- runner))
- (begin
- (define (getter runner)
- (vector-ref runner index)) ...)
- (begin
- (define (setter runner value)
- (vector-set! runner index value)) ...)))))))
-
-(%test-record-define
- %test-runner-alloc test-runner?
- ;; Cumulate count of all tests that have passed and were expected to.
- (pass-count 1 test-runner-pass-count test-runner-pass-count!)
- (fail-count 2 test-runner-fail-count test-runner-fail-count!)
- (xpass-count 3 test-runner-xpass-count test-runner-xpass-count!)
- (xfail-count 4 test-runner-xfail-count test-runner-xfail-count!)
- (skip-count 5 test-runner-skip-count test-runner-skip-count!)
- (skip-list 6 %test-runner-skip-list %test-runner-skip-list!)
- (fail-list 7 %test-runner-fail-list %test-runner-fail-list!)
- ;; Normally #t, except when in a test-apply.
- (run-list 8 %test-runner-run-list %test-runner-run-list!)
- (skip-save 9 %test-runner-skip-save %test-runner-skip-save!)
- (fail-save 10 %test-runner-fail-save %test-runner-fail-save!)
- (group-stack 11 test-runner-group-stack test-runner-group-stack!)
- (on-test-begin 12 test-runner-on-test-begin test-runner-on-test-begin!)
- (on-test-end 13 test-runner-on-test-end test-runner-on-test-end!)
- ;; Call-back when entering a group. Takes (runner suite-name count).
- (on-group-begin 14 test-runner-on-group-begin test-runner-on-group-begin!)
- ;; Call-back when leaving a group.
- (on-group-end 15 test-runner-on-group-end test-runner-on-group-end!)
- ;; Call-back when leaving the outermost group.
- (on-final 16 test-runner-on-final test-runner-on-final!)
- ;; Call-back when expected number of tests was wrong.
- (on-bad-count 17 test-runner-on-bad-count test-runner-on-bad-count!)
- ;; Call-back when name in test=end doesn't match test-begin.
- (on-bad-end-name 18 test-runner-on-bad-end-name test-runner-on-bad-end-name!)
- ;; Cumulate count of all tests that have been done.
- (total-count 19 %test-runner-total-count %test-runner-total-count!)
- ;; Stack (list) of (count-at-start . expected-count):
- (count-list 20 %test-runner-count-list %test-runner-count-list!)
- (result-alist 21 test-result-alist test-result-alist!)
- ;; Field can be used by test-runner for any purpose.
- ;; test-runner-simple uses it for a log file.
- (aux-value 22 test-runner-aux-value test-runner-aux-value!)
-)
-
-(define (test-runner-reset runner)
- (test-result-alist! runner '())
- (test-runner-pass-count! runner 0)
- (test-runner-fail-count! runner 0)
- (test-runner-xpass-count! runner 0)
- (test-runner-xfail-count! runner 0)
- (test-runner-skip-count! runner 0)
- (%test-runner-total-count! runner 0)
- (%test-runner-count-list! runner '())
- (%test-runner-run-list! runner #t)
- (%test-runner-skip-list! runner '())
- (%test-runner-fail-list! runner '())
- (%test-runner-skip-save! runner '())
- (%test-runner-fail-save! runner '())
- (test-runner-group-stack! runner '()))
-
-(define (test-runner-group-path runner)
- (reverse (test-runner-group-stack runner)))
-
-(define (%test-null-callback runner) #f)
-
-(define (test-runner-null)
- (let ((runner (%test-runner-alloc)))
- (test-runner-reset runner)
- (test-runner-on-group-begin! runner (lambda (runner name count) #f))
- (test-runner-on-group-end! runner %test-null-callback)
- (test-runner-on-final! runner %test-null-callback)
- (test-runner-on-test-begin! runner %test-null-callback)
- (test-runner-on-test-end! runner %test-null-callback)
- (test-runner-on-bad-count! runner (lambda (runner count expected) #f))
- (test-runner-on-bad-end-name! runner (lambda (runner begin end) #f))
- runner))
-
-;; Not part of the specification. FIXME
-;; Controls whether a log file is generated.
-(define test-log-to-file #t)
-
-(define (test-runner-simple)
- (let ((runner (%test-runner-alloc)))
- (test-runner-reset runner)
- (test-runner-on-group-begin! runner test-on-group-begin-simple)
- (test-runner-on-group-end! runner test-on-group-end-simple)
- (test-runner-on-final! runner test-on-final-simple)
- (test-runner-on-test-begin! runner test-on-test-begin-simple)
- (test-runner-on-test-end! runner test-on-test-end-simple)
- (test-runner-on-bad-count! runner test-on-bad-count-simple)
- (test-runner-on-bad-end-name! runner test-on-bad-end-name-simple)
- runner))
-
-(cond-expand
- (srfi-39
- (define test-runner-current (make-parameter #f))
- (define test-runner-factory (make-parameter test-runner-simple)))
- (else
- (define %test-runner-current #f)
- (define-syntax test-runner-current
- (syntax-rules ()
- ((test-runner-current)
- %test-runner-current)
- ((test-runner-current runner)
- (set! %test-runner-current runner))))
- (define %test-runner-factory test-runner-simple)
- (define-syntax test-runner-factory
- (syntax-rules ()
- ((test-runner-factory)
- %test-runner-factory)
- ((test-runner-factory runner)
- (set! %test-runner-factory runner))))))
-
-;; A safer wrapper to test-runner-current.
-(define (test-runner-get)
- (let ((r (test-runner-current)))
- (if (not r)
- (cond-expand
- (srfi-23 (error "test-runner not initialized - test-begin missing?"))
- (else #t)))
- r))
-
-(define (%test-specifier-matches spec runner)
- (spec runner))
-
-(define (test-runner-create)
- ((test-runner-factory)))
-
-(define (%test-any-specifier-matches list runner)
- (let ((result #f))
- (let loop ((l list))
- (cond ((null? l) result)
- (else
- (if (%test-specifier-matches (car l) runner)
- (set! result #t))
- (loop (cdr l)))))))
-
-;; Returns #f, #t, or 'xfail.
-(define (%test-should-execute runner)
- (let ((run (%test-runner-run-list runner)))
- (cond ((or
- (not (or (eqv? run #t)
- (%test-any-specifier-matches run runner)))
- (%test-any-specifier-matches
- (%test-runner-skip-list runner)
- runner))
- (test-result-set! runner 'result-kind 'skip)
- #f)
- ((%test-any-specifier-matches
- (%test-runner-fail-list runner)
- runner)
- (test-result-set! runner 'result-kind 'xfail)
- 'xfail)
- (else #t))))
-
-(define (%test-begin suite-name count)
- (if (not (test-runner-current))
- (test-runner-current (test-runner-create)))
- (let ((runner (test-runner-current)))
- ((test-runner-on-group-begin runner) runner suite-name count)
- (%test-runner-skip-save! runner
- (cons (%test-runner-skip-list runner)
- (%test-runner-skip-save runner)))
- (%test-runner-fail-save! runner
- (cons (%test-runner-fail-list runner)
- (%test-runner-fail-save runner)))
- (%test-runner-count-list! runner
- (cons (cons (%test-runner-total-count runner)
- count)
- (%test-runner-count-list runner)))
- (test-runner-group-stack! runner (cons suite-name
- (test-runner-group-stack runner)))))
-(cond-expand
- (kawa
- ;; Kawa has test-begin built in, implemented as:
- ;; (begin
- ;; (cond-expand (srfi-64 #!void) (else (require 'srfi-64)))
- ;; (%test-begin suite-name [count]))
- ;; This puts test-begin but only test-begin in the default environment.,
- ;; which makes normal test suites loadable without non-portable commands.
- )
- (else
- (define-syntax test-begin
- (syntax-rules ()
- ((test-begin suite-name)
- (%test-begin suite-name #f))
- ((test-begin suite-name count)
- (%test-begin suite-name count))))))
-
-(define (test-on-group-begin-simple runner suite-name count)
- (if (null? (test-runner-group-stack runner))
- (begin
- (display "%%%% Starting test ")
- (display suite-name)
- (if test-log-to-file
- (let* ((log-file-name
- (if (string? test-log-to-file) test-log-to-file
- (string-append suite-name ".log")))
- (log-file
- (cond-expand (mzscheme
- (open-output-file log-file-name 'truncate/replace))
- (else (open-output-file log-file-name)))))
- (display "%%%% Starting test " log-file)
- (display suite-name log-file)
- (newline log-file)
- (test-runner-aux-value! runner log-file)
- (display " (Writing full log to \"")
- (display log-file-name)
- (display "\")")))
- (newline)))
- (let ((log (test-runner-aux-value runner)))
- (if (output-port? log)
- (begin
- (display "Group begin: " log)
- (display suite-name log)
- (newline log))))
- #f)
-
-(define (test-on-group-end-simple runner)
- (let ((log (test-runner-aux-value runner)))
- (if (output-port? log)
- (begin
- (display "Group end: " log)
- (display (car (test-runner-group-stack runner)) log)
- (newline log))))
- #f)
-
-(define (%test-on-bad-count-write runner count expected-count port)
- (display "*** Total number of tests was " port)
- (display count port)
- (display " but should be " port)
- (display expected-count port)
- (display ". ***" port)
- (newline port)
- (display "*** Discrepancy indicates testsuite error or exceptions. ***" port)
- (newline port))
-
-(define (test-on-bad-count-simple runner count expected-count)
- (%test-on-bad-count-write runner count expected-count (current-output-port))
- (let ((log (test-runner-aux-value runner)))
- (if (output-port? log)
- (%test-on-bad-count-write runner count expected-count log))))
-
-(define (test-on-bad-end-name-simple runner begin-name end-name)
- (let ((msg (string-append (%test-format-line runner) "test-end " begin-name
- " does not match test-begin " end-name)))
- (cond-expand
- (srfi-23 (error msg))
- (else (display msg) (newline)))))
-
-
-(define (%test-final-report1 value label port)
- (if (> value 0)
- (begin
- (display label port)
- (display value port)
- (newline port))))
-
-(define (%test-final-report-simple runner port)
- (%test-final-report1 (test-runner-pass-count runner)
- "# of expected passes " port)
- (%test-final-report1 (test-runner-xfail-count runner)
- "# of expected failures " port)
- (%test-final-report1 (test-runner-xpass-count runner)
- "# of unexpected successes " port)
- (%test-final-report1 (test-runner-fail-count runner)
- "# of unexpected failures " port)
- (%test-final-report1 (test-runner-skip-count runner)
- "# of skipped tests " port))
-
-(define (test-on-final-simple runner)
- (%test-final-report-simple runner (current-output-port))
- (let ((log (test-runner-aux-value runner)))
- (if (output-port? log)
- (%test-final-report-simple runner log))))
-
-(define (%test-format-line runner)
- (let* ((line-info (test-result-alist runner))
- (source-file (assq 'source-file line-info))
- (source-line (assq 'source-line line-info))
- (file (if source-file (cdr source-file) "")))
- (if source-line
- (string-append file ":"
- (number->string (cdr source-line)) ": ")
- "")))
-
-(define (%test-end suite-name line-info)
- (let* ((r (test-runner-get))
- (groups (test-runner-group-stack r))
- (line (%test-format-line r)))
- (test-result-alist! r line-info)
- (if (null? groups)
- (let ((msg (string-append line "test-end not in a group")))
- (cond-expand
- (srfi-23 (error msg))
- (else (display msg) (newline)))))
- (if (and suite-name (not (equal? suite-name (car groups))))
- ((test-runner-on-bad-end-name r) r suite-name (car groups)))
- (let* ((count-list (%test-runner-count-list r))
- (expected-count (cdar count-list))
- (saved-count (caar count-list))
- (group-count (- (%test-runner-total-count r) saved-count)))
- (if (and expected-count
- (not (= expected-count group-count)))
- ((test-runner-on-bad-count r) r group-count expected-count))
- ((test-runner-on-group-end r) r)
- (test-runner-group-stack! r (cdr (test-runner-group-stack r)))
- (%test-runner-skip-list! r (car (%test-runner-skip-save r)))
- (%test-runner-skip-save! r (cdr (%test-runner-skip-save r)))
- (%test-runner-fail-list! r (car (%test-runner-fail-save r)))
- (%test-runner-fail-save! r (cdr (%test-runner-fail-save r)))
- (%test-runner-count-list! r (cdr count-list))
- (if (null? (test-runner-group-stack r))
- ((test-runner-on-final r) r)))))
-
-(define-syntax test-group
- (syntax-rules ()
- ((test-group suite-name . body)
- (let ((r (test-runner-current)))
- ;; Ideally should also set line-number, if available.
- (test-result-alist! r (list (cons 'test-name suite-name)))
- (if (%test-should-execute r)
- (dynamic-wind
- (lambda () (test-begin suite-name))
- (lambda () . body)
- (lambda () (test-end suite-name))))))))
-
-(define-syntax test-group-with-cleanup
- (syntax-rules ()
- ((test-group-with-cleanup suite-name form cleanup-form)
- (test-group suite-name
- (dynamic-wind
- (lambda () #f)
- (lambda () form)
- (lambda () cleanup-form))))
- ((test-group-with-cleanup suite-name cleanup-form)
- (test-group-with-cleanup suite-name #f cleanup-form))
- ((test-group-with-cleanup suite-name form1 form2 form3 . rest)
- (test-group-with-cleanup suite-name (begin form1 form2) form3 . rest))))
-
-(define (test-on-test-begin-simple runner)
- (let ((log (test-runner-aux-value runner)))
- (if (output-port? log)
- (let* ((results (test-result-alist runner))
- (source-file (assq 'source-file results))
- (source-line (assq 'source-line results))
- (source-form (assq 'source-form results))
- (test-name (assq 'test-name results)))
- (display "Test begin:" log)
- (newline log)
- (if test-name (%test-write-result1 test-name log))
- (if source-file (%test-write-result1 source-file log))
- (if source-line (%test-write-result1 source-line log))
- (if source-form (%test-write-result1 source-form log))))))
-
-(define-syntax test-result-ref
- (syntax-rules ()
- ((test-result-ref runner pname)
- (test-result-ref runner pname #f))
- ((test-result-ref runner pname default)
- (let ((p (assq pname (test-result-alist runner))))
- (if p (cdr p) default)))))
-
-(define (test-on-test-end-simple runner)
- (let ((log (test-runner-aux-value runner))
- (kind (test-result-ref runner 'result-kind)))
- (if (memq kind '(fail xpass))
- (let* ((results (test-result-alist runner))
- (source-file (assq 'source-file results))
- (source-line (assq 'source-line results))
- (test-name (assq 'test-name results)))
- (if (or source-file source-line)
- (begin
- (if source-file (display (cdr source-file)))
- (display ":")
- (if source-line (display (cdr source-line)))
- (display ": ")))
- (display (if (eq? kind 'xpass) "XPASS" "FAIL"))
- (if test-name
- (begin
- (display " ")
- (display (cdr test-name))))
- (newline)))
- (if (output-port? log)
- (begin
- (display "Test end:" log)
- (newline log)
- (let loop ((list (test-result-alist runner)))
- (if (pair? list)
- (let ((pair (car list)))
- ;; Write out properties not written out by on-test-begin.
- (if (not (memq (car pair)
- '(test-name source-file source-line source-form)))
- (%test-write-result1 pair log))
- (loop (cdr list)))))))))
-
-(define (%test-write-result1 pair port)
- (display " " port)
- (display (car pair) port)
- (display ": " port)
- (write (cdr pair) port)
- (newline port))
-
-(define (test-result-set! runner pname value)
- (let* ((alist (test-result-alist runner))
- (p (assq pname alist)))
- (if p
- (set-cdr! p value)
- (test-result-alist! runner (cons (cons pname value) alist)))))
-
-(define (test-result-clear runner)
- (test-result-alist! runner '()))
-
-(define (test-result-remove runner pname)
- (let* ((alist (test-result-alist runner))
- (p (assq pname alist)))
- (if p
- (test-result-alist! runner
- (let loop ((r alist))
- (if (eq? r p) (cdr r)
- (cons (car r) (loop (cdr r)))))))))
-
-(define (test-result-kind . rest)
- (let ((runner (if (pair? rest) (car rest) (test-runner-current))))
- (test-result-ref runner 'result-kind)))
-
-(define (test-passed? . rest)
- (let ((runner (if (pair? rest) (car rest) (test-runner-get))))
- (memq (test-result-ref runner 'result-kind) '(pass xpass))))
-
-(define (%test-report-result)
- (let* ((r (test-runner-get))
- (result-kind (test-result-kind r)))
- (case result-kind
- ((pass)
- (test-runner-pass-count! r (+ 1 (test-runner-pass-count r))))
- ((fail)
- (test-runner-fail-count! r (+ 1 (test-runner-fail-count r))))
- ((xpass)
- (test-runner-xpass-count! r (+ 1 (test-runner-xpass-count r))))
- ((xfail)
- (test-runner-xfail-count! r (+ 1 (test-runner-xfail-count r))))
- (else
- (test-runner-skip-count! r (+ 1 (test-runner-skip-count r)))))
- (%test-runner-total-count! r (+ 1 (%test-runner-total-count r)))
- ((test-runner-on-test-end r) r)))
-
-(cond-expand
- (guile
- (define-syntax %test-evaluate-with-catch
- (syntax-rules ()
- ((%test-evaluate-with-catch test-expression)
- (catch #t
- (lambda () test-expression)
- (lambda (key . args)
- (test-result-set! (test-runner-current) 'actual-error
- (cons key args))
- #f))))))
- (kawa
- (define-syntax %test-evaluate-with-catch
- (syntax-rules ()
- ((%test-evaluate-with-catch test-expression)
- (try-catch test-expression
- (ex <java.lang.Throwable>
- (test-result-set! (test-runner-current) 'actual-error ex)
- #f))))))
- (srfi-34
- (define-syntax %test-evaluate-with-catch
- (syntax-rules ()
- ((%test-evaluate-with-catch test-expression)
- (guard (err (else #f)) test-expression)))))
- (chicken
- (define-syntax %test-evaluate-with-catch
- (syntax-rules ()
- ((%test-evaluate-with-catch test-expression)
- (condition-case test-expression (ex () #f))))))
- (else
- (define-syntax %test-evaluate-with-catch
- (syntax-rules ()
- ((%test-evaluate-with-catch test-expression)
- test-expression)))))
-
-(cond-expand
- ((or kawa mzscheme)
- (cond-expand
- (mzscheme
- (define-for-syntax (%test-syntax-file form)
- (let ((source (syntax-source form)))
- (cond ((string? source) file)
- ((path? source) (path->string source))
- (else #f)))))
- (kawa
- (define (%test-syntax-file form)
- (syntax-source form))))
- (define (%test-source-line2 form)
- (let* ((line (syntax-line form))
- (file (%test-syntax-file form))
- (line-pair (if line (list (cons 'source-line line)) '())))
- (cons (cons 'source-form (syntax-object->datum form))
- (if file (cons (cons 'source-file file) line-pair) line-pair)))))
- (guile-2
- (define (%test-source-line2 form)
- (let* ((src-props (syntax-source form))
- (file (and src-props (assq-ref src-props 'filename)))
- (line (and src-props (assq-ref src-props 'line)))
- (file-alist (if file
- `((source-file . ,file))
- '()))
- (line-alist (if line
- `((source-line . ,(+ line 1)))
- '())))
- (datum->syntax (syntax here)
- `((source-form . ,(syntax->datum form))
- ,@file-alist
- ,@line-alist)))))
- (else
- (define (%test-source-line2 form)
- '())))
-
-(define (%test-on-test-begin r)
- (%test-should-execute r)
- ((test-runner-on-test-begin r) r)
- (not (eq? 'skip (test-result-ref r 'result-kind))))
-
-(define (%test-on-test-end r result)
- (test-result-set! r 'result-kind
- (if (eq? (test-result-ref r 'result-kind) 'xfail)
- (if result 'xpass 'xfail)
- (if result 'pass 'fail))))
-
-(define (test-runner-test-name runner)
- (test-result-ref runner 'test-name ""))
-
-(define-syntax %test-comp2body
- (syntax-rules ()
- ((%test-comp2body r comp expected expr)
- (let ()
- (if (%test-on-test-begin r)
- (let ((exp expected))
- (test-result-set! r 'expected-value exp)
- (let ((res (%test-evaluate-with-catch expr)))
- (test-result-set! r 'actual-value res)
- (%test-on-test-end r (comp exp res)))))
- (%test-report-result)))))
-
-(define (%test-approximate= error)
- (lambda (value expected)
- (let ((rval (real-part value))
- (ival (imag-part value))
- (rexp (real-part expected))
- (iexp (imag-part expected)))
- (and (>= rval (- rexp error))
- (>= ival (- iexp error))
- (<= rval (+ rexp error))
- (<= ival (+ iexp error))))))
-
-(define-syntax %test-comp1body
- (syntax-rules ()
- ((%test-comp1body r expr)
- (let ()
- (if (%test-on-test-begin r)
- (let ()
- (let ((res (%test-evaluate-with-catch expr)))
- (test-result-set! r 'actual-value res)
- (%test-on-test-end r res))))
- (%test-report-result)))))
-
-(cond-expand
- ((or kawa mzscheme guile-2)
- ;; Should be made to work for any Scheme with syntax-case
- ;; However, I haven't gotten the quoting working. FIXME.
- (define-syntax test-end
- (lambda (x)
- (syntax-case (list x (list (syntax quote) (%test-source-line2 x))) ()
- (((mac suite-name) line)
- (syntax
- (%test-end suite-name line)))
- (((mac) line)
- (syntax
- (%test-end #f line))))))
- (define-syntax test-assert
- (lambda (x)
- (syntax-case (list x (list (syntax quote) (%test-source-line2 x))) ()
- (((mac tname expr) line)
- (syntax
- (let* ((r (test-runner-get))
- (name tname))
- (test-result-alist! r (cons (cons 'test-name tname) line))
- (%test-comp1body r expr))))
- (((mac expr) line)
- (syntax
- (let* ((r (test-runner-get)))
- (test-result-alist! r line)
- (%test-comp1body r expr)))))))
- (define (%test-comp2 comp x)
- (syntax-case (list x (list (syntax quote) (%test-source-line2 x)) comp) ()
- (((mac tname expected expr) line comp)
- (syntax
- (let* ((r (test-runner-get))
- (name tname))
- (test-result-alist! r (cons (cons 'test-name tname) line))
- (%test-comp2body r comp expected expr))))
- (((mac expected expr) line comp)
- (syntax
- (let* ((r (test-runner-get)))
- (test-result-alist! r line)
- (%test-comp2body r comp expected expr))))))
- (define-syntax test-eqv
- (lambda (x) (%test-comp2 (syntax eqv?) x)))
- (define-syntax test-eq
- (lambda (x) (%test-comp2 (syntax eq?) x)))
- (define-syntax test-equal
- (lambda (x) (%test-comp2 (syntax equal?) x)))
- (define-syntax test-approximate ;; FIXME - needed for non-Kawa
- (lambda (x)
- (syntax-case (list x (list (syntax quote) (%test-source-line2 x))) ()
- (((mac tname expected expr error) line)
- (syntax
- (let* ((r (test-runner-get))
- (name tname))
- (test-result-alist! r (cons (cons 'test-name tname) line))
- (%test-comp2body r (%test-approximate= error) expected expr))))
- (((mac expected expr error) line)
- (syntax
- (let* ((r (test-runner-get)))
- (test-result-alist! r line)
- (%test-comp2body r (%test-approximate= error) expected expr))))))))
- (else
- (define-syntax test-end
- (syntax-rules ()
- ((test-end)
- (%test-end #f '()))
- ((test-end suite-name)
- (%test-end suite-name '()))))
- (define-syntax test-assert
- (syntax-rules ()
- ((test-assert tname test-expression)
- (let* ((r (test-runner-get))
- (name tname))
- (test-result-alist! r '((test-name . tname)))
- (%test-comp1body r test-expression)))
- ((test-assert test-expression)
- (let* ((r (test-runner-get)))
- (test-result-alist! r '())
- (%test-comp1body r test-expression)))))
- (define-syntax %test-comp2
- (syntax-rules ()
- ((%test-comp2 comp tname expected expr)
- (let* ((r (test-runner-get))
- (name tname))
- (test-result-alist! r (list (cons 'test-name tname)))
- (%test-comp2body r comp expected expr)))
- ((%test-comp2 comp expected expr)
- (let* ((r (test-runner-get)))
- (test-result-alist! r '())
- (%test-comp2body r comp expected expr)))))
- (define-syntax test-equal
- (syntax-rules ()
- ((test-equal . rest)
- (%test-comp2 equal? . rest))))
- (define-syntax test-eqv
- (syntax-rules ()
- ((test-eqv . rest)
- (%test-comp2 eqv? . rest))))
- (define-syntax test-eq
- (syntax-rules ()
- ((test-eq . rest)
- (%test-comp2 eq? . rest))))
- (define-syntax test-approximate
- (syntax-rules ()
- ((test-approximate tname expected expr error)
- (%test-comp2 (%test-approximate= error) tname expected expr))
- ((test-approximate expected expr error)
- (%test-comp2 (%test-approximate= error) expected expr))))))
-
-(cond-expand
- (guile
- (define-syntax %test-error
- (syntax-rules ()
- ((%test-error r etype expr)
- (cond ((%test-on-test-begin r)
- (let ((et etype))
- (test-result-set! r 'expected-error et)
- (%test-on-test-end r
- (catch #t
- (lambda ()
- (test-result-set! r 'actual-value expr)
- #f)
- (lambda (key . args)
- ;; TODO: decide how to specify expected
- ;; error types for Guile.
- (test-result-set! r 'actual-error
- (cons key args))
- #t)))
- (%test-report-result))))))))
- (mzscheme
- (define-syntax %test-error
- (syntax-rules ()
- ((%test-error r etype expr)
- (%test-comp1body r (with-handlers (((lambda (h) #t) (lambda (h) #t)))
- (let ()
- (test-result-set! r 'actual-value expr)
- #f)))))))
- (chicken
- (define-syntax %test-error
- (syntax-rules ()
- ((%test-error r etype expr)
- (%test-comp1body r (condition-case expr (ex () #t)))))))
- (kawa
- (define-syntax %test-error
- (syntax-rules ()
- ((%test-error r #t expr)
- (cond ((%test-on-test-begin r)
- (test-result-set! r 'expected-error #t)
- (%test-on-test-end r
- (try-catch
- (let ()
- (test-result-set! r 'actual-value expr)
- #f)
- (ex <java.lang.Throwable>
- (test-result-set! r 'actual-error ex)
- #t)))
- (%test-report-result))))
- ((%test-error r etype expr)
- (if (%test-on-test-begin r)
- (let ((et etype))
- (test-result-set! r 'expected-error et)
- (%test-on-test-end r
- (try-catch
- (let ()
- (test-result-set! r 'actual-value expr)
- #f)
- (ex <java.lang.Throwable>
- (test-result-set! r 'actual-error ex)
- (cond ((and (instance? et <gnu.bytecode.ClassType>)
- (gnu.bytecode.ClassType:isSubclass et <java.lang.Throwable>))
- (instance? ex et))
- (else #t)))))
- (%test-report-result)))))))
- ((and srfi-34 srfi-35)
- (define-syntax %test-error
- (syntax-rules ()
- ((%test-error r etype expr)
- (%test-comp1body r (guard (ex ((condition-type? etype)
- (and (condition? ex) (condition-has-type? ex etype)))
- ((procedure? etype)
- (etype ex))
- ((equal? etype #t)
- #t)
- (else #t))
- expr #f))))))
- (srfi-34
- (define-syntax %test-error
- (syntax-rules ()
- ((%test-error r etype expr)
- (%test-comp1body r (guard (ex (else #t)) expr #f))))))
- (else
- (define-syntax %test-error
- (syntax-rules ()
- ((%test-error r etype expr)
- (begin
- ((test-runner-on-test-begin r) r)
- (test-result-set! r 'result-kind 'skip)
- (%test-report-result)))))))
-
-(cond-expand
- ((or kawa mzscheme guile-2)
-
- (define-syntax test-error
- (lambda (x)
- (syntax-case (list x (list (syntax quote) (%test-source-line2 x))) ()
- (((mac tname etype expr) line)
- (syntax
- (let* ((r (test-runner-get))
- (name tname))
- (test-result-alist! r (cons (cons 'test-name tname) line))
- (%test-error r etype expr))))
- (((mac etype expr) line)
- (syntax
- (let* ((r (test-runner-get)))
- (test-result-alist! r line)
- (%test-error r etype expr))))
- (((mac expr) line)
- (syntax
- (let* ((r (test-runner-get)))
- (test-result-alist! r line)
- (%test-error r #t expr))))))))
- (else
- (define-syntax test-error
- (syntax-rules ()
- ((test-error name etype expr)
- (let ((r (test-runner-get)))
- (test-result-alist! r `((test-name . ,name)))
- (%test-error r etype expr)))
- ((test-error etype expr)
- (let ((r (test-runner-get)))
- (test-result-alist! r '())
- (%test-error r etype expr)))
- ((test-error expr)
- (let ((r (test-runner-get)))
- (test-result-alist! r '())
- (%test-error r #t expr)))))))
-
-(define (test-apply first . rest)
- (if (test-runner? first)
- (test-with-runner first (apply test-apply rest))
- (let ((r (test-runner-current)))
- (if r
- (let ((run-list (%test-runner-run-list r)))
- (cond ((null? rest)
- (%test-runner-run-list! r (reverse run-list))
- (first)) ;; actually apply procedure thunk
- (else
- (%test-runner-run-list!
- r
- (if (eq? run-list #t) (list first) (cons first run-list)))
- (apply test-apply rest)
- (%test-runner-run-list! r run-list))))
- (let ((r (test-runner-create)))
- (test-with-runner r (apply test-apply first rest))
- ((test-runner-on-final r) r))))))
-
-(define-syntax test-with-runner
- (syntax-rules ()
- ((test-with-runner runner form ...)
- (let ((saved-runner (test-runner-current)))
- (dynamic-wind
- (lambda () (test-runner-current runner))
- (lambda () form ...)
- (lambda () (test-runner-current saved-runner)))))))
-
-;;; Predicates
-
-(define (%test-match-nth n count)
- (let ((i 0))
- (lambda (runner)
- (set! i (+ i 1))
- (and (>= i n) (< i (+ n count))))))
-
-(define-syntax test-match-nth
- (syntax-rules ()
- ((test-match-nth n)
- (test-match-nth n 1))
- ((test-match-nth n count)
- (%test-match-nth n count))))
-
-(define (%test-match-all . pred-list)
- (lambda (runner)
- (let ((result #t))
- (let loop ((l pred-list))
- (if (null? l)
- result
- (begin
- (if (not ((car l) runner))
- (set! result #f))
- (loop (cdr l))))))))
-
-(define-syntax test-match-all
- (syntax-rules ()
- ((test-match-all pred ...)
- (%test-match-all (%test-as-specifier pred) ...))))
-
-(define (%test-match-any . pred-list)
- (lambda (runner)
- (let ((result #f))
- (let loop ((l pred-list))
- (if (null? l)
- result
- (begin
- (if ((car l) runner)
- (set! result #t))
- (loop (cdr l))))))))
-
-(define-syntax test-match-any
- (syntax-rules ()
- ((test-match-any pred ...)
- (%test-match-any (%test-as-specifier pred) ...))))
-
-;; Coerce to a predicate function:
-(define (%test-as-specifier specifier)
- (cond ((procedure? specifier) specifier)
- ((integer? specifier) (test-match-nth 1 specifier))
- ((string? specifier) (test-match-name specifier))
- (else
- (error "not a valid test specifier"))))
-
-(define-syntax test-skip
- (syntax-rules ()
- ((test-skip pred ...)
- (let ((runner (test-runner-get)))
- (%test-runner-skip-list! runner
- (cons (test-match-all (%test-as-specifier pred) ...)
- (%test-runner-skip-list runner)))))))
-
-(define-syntax test-expect-fail
- (syntax-rules ()
- ((test-expect-fail pred ...)
- (let ((runner (test-runner-get)))
- (%test-runner-fail-list! runner
- (cons (test-match-all (%test-as-specifier pred) ...)
- (%test-runner-fail-list runner)))))))
-
-(define (test-match-name name)
- (lambda (runner)
- (equal? name (test-runner-test-name runner))))
-
-(define (test-read-eval-string string)
- (let* ((port (open-input-string string))
- (form (read port)))
- (if (eof-object? (read-char port))
- (cond-expand
- (guile (eval form (current-module)))
- (else (eval form)))
- (cond-expand
- (srfi-23 (error "(not at eof)"))
- (else "error")))))
-
-(define-library (srfi 67)
- (export
- </<=?
- </<?
- <=/<=?
- <=/<?
- <=?
- <?
- =?
- >/>=?
- >/>?
- >=/>=?
- >=/>?
- >=?
- >?
- boolean-compare
- chain<=?
- chain<?
- chain=?
- chain>=?
- chain>?
- char-compare
- char-compare-ci
- compare-by<
- compare-by<=
- compare-by=/<
- compare-by=/>
- compare-by>
- compare-by>=
- complex-compare
- cond-compare
- debug-compare
- default-compare
- if-not=?
- if3
- if<=?
- if<?
- if=?
- if>=?
- if>?
- integer-compare
- kth-largest
- list-compare
- list-compare-as-vector
- max-compare
- min-compare
- not=?
- number-compare
- pair-compare
- pair-compare-car
- pair-compare-cdr
- pairwise-not=?
- rational-compare
- real-compare
- refine-compare
- select-compare
- symbol-compare
- vector-compare
- vector-compare-as-list
- bytevector-compare
- bytevector-compare-as-list
- )
- (import
- (scheme base)
- (scheme case-lambda)
- (scheme char)
- (scheme complex)
- (srfi 27))
- (include "67.upstream.scm")
- (begin
-
- (define (bytevector-compare bv1 bv2)
- (let ((len1 (bytevector-length bv1))
- (len2 (bytevector-length bv2)))
- (cond
- ((< len1 len2) -1)
- ((> len1 len2) +1)
- (else
- (let lp ((i 0))
- (if (= i len1)
- 0
- (let ((b1 (bytevector-u8-ref bv1 i))
- (b2 (bytevector-u8-ref bv2 i)))
- (cond
- ((< b1 b2) -1)
- ((> b1 b2) +1)
- (else
- (lp (+ 1 i)))))))))))
-
- (define (bytevector-compare-as-list bv1 bv2)
- (let ((len1 (bytevector-length bv1))
- (len2 (bytevector-length bv2)))
- (let lp ((i 0))
- (cond
- ((or (= i len1) (= i len2))
- (cond ((< len1 len2) -1)
- ((> len1 len2) +1)
- (else 0)))
- (else
- (let ((b1 (bytevector-u8-ref bv1 i))
- (b2 (bytevector-u8-ref bv2 i)))
- (cond
- ((< b1 b2) -1)
- ((> b1 b2) +1)
- (else
- (lp (+ 1 i))))))))))
-
- ))
-; Copyright (c) 2005 Sebastian Egner and Jens Axel S{\o}gaard.
-;
-; Permission is hereby granted, free of charge, to any person obtaining
-; a copy of this software and associated documentation files (the
-; ``Software''), to deal in the Software without restriction, including
-; without limitation the rights to use, copy, modify, merge, publish,
-; distribute, sublicense, and/or sell copies of the Software, and to
-; permit persons to whom the Software is furnished to do so, subject to
-; the following conditions:
-;
-; The above copyright notice and this permission notice shall be
-; included in all copies or substantial portions of the Software.
-;
-; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
-; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-;
-; -----------------------------------------------------------------------
-;
-; Compare procedures SRFI (reference implementation)
-; Sebastian.Egner@philips.com, Jensaxel@soegaard.net
-; history of this file:
-; SE, 14-Oct-2004: first version
-; SE, 18-Oct-2004: 1st redesign: axioms for 'compare function'
-; SE, 29-Oct-2004: 2nd redesign: higher order reverse/map/refine/unite
-; SE, 2-Nov-2004: 3rd redesign: macros cond/refine-compare replace h.o.f's
-; SE, 10-Nov-2004: (im,re) replaced by (re,im) in complex-compare
-; SE, 11-Nov-2004: case-compare by case (not by cond); select-compare added
-; SE, 12-Jan-2005: pair-compare-cdr
-; SE, 15-Feb-2005: stricter typing for compare-<type>; pairwise-not=?
-; SE, 16-Feb-2005: case-compare -> if-compare -> if3; <? </<? chain<? etc.
-; JS, 24-Feb-2005: selection-compare added
-; SE, 25-Feb-2005: selection-compare -> kth-largest modified; if<? etc.
-; JS, 28-Feb-2005: kth-largest modified - is "stable" now
-; SE, 28-Feb-2005: simplified pairwise-not=?/kth-largest; min/max debugged
-; SE, 07-Apr-2005: compare-based type checks made explicit
-; SE, 18-Apr-2005: added (rel? compare) and eq?-test
-; SE, 16-May-2005: naming convention changed; compare-by< etc. optional x y
-
-; =============================================================================
-
-; Reference Implementation
-; ========================
-;
-; in R5RS (including hygienic macros)
-; + SRFI-16 (case-lambda)
-; + SRFI-23 (error)
-; + SRFI-27 (random-integer)
-
-; Implementation remarks:
-; * In general, the emphasis of this implementation is on correctness
-; and portability, not on efficiency.
-; * Variable arity procedures are expressed in terms of case-lambda
-; in the hope that this will produce efficient code for the case
-; where the arity is statically known at the call site.
-; * In procedures that are required to type-check their arguments,
-; we use (compare x x) for executing extra checks. This relies on
-; the assumption that eq? is used to catch this case quickly.
-; * Care has been taken to reference comparison procedures of R5RS
-; only at the time the operations here are being defined. This
-; makes it possible to redefine these operations, if need be.
-; * For the sake of efficiency, some inlining has been done by hand.
-; This is mainly expressed by macros producing defines.
-; * Identifiers of the form compare:<something> are private.
-;
-; Hints for low-level implementation:
-; * The basis of this SRFI are the atomic compare procedures,
-; i.e. boolean-compare, char-compare, etc. and the conditionals
-; if3, if=?, if<? etc., and default-compare. These should make
-; optimal use of the available type information.
-; * For the sake of speed, the reference implementation does not
-; use a LET to save the comparison value c for the ERROR call.
-; This can be fixed in a low-level implementation at no cost.
-; * Type-checks based on (compare x x) are made explicit by the
-; expression (compare:check result compare x ...).
-; * Eq? should can used to speed up built-in compare procedures,
-; but it can only be used after type-checking at least one of
-; the arguments.
-
-(define (compare:checked result compare . args)
- (for-each (lambda (x) (compare x x)) args)
- result)
-
-
-; 3-sided conditional
-
-(define-syntax if3
- (syntax-rules ()
- ((if3 c less equal greater)
- (case c
- ((-1) less)
- (( 0) equal)
- (( 1) greater)
- (else (error "comparison value not in {-1,0,1}"))))))
-
-
-; 2-sided conditionals for comparisons
-
-(define-syntax compare:if-rel?
- (syntax-rules ()
- ((compare:if-rel? c-cases a-cases c consequence)
- (compare:if-rel? c-cases a-cases c consequence (if #f #f)))
- ((compare:if-rel? c-cases a-cases c consequence alternate)
- (case c
- (c-cases consequence)
- (a-cases alternate)
- (else (error "comparison value not in {-1,0,1}"))))))
-
-(define-syntax if=?
- (syntax-rules ()
- ((if=? arg ...)
- (compare:if-rel? (0) (-1 1) arg ...))))
-
-(define-syntax if<?
- (syntax-rules ()
- ((if<? arg ...)
- (compare:if-rel? (-1) (0 1) arg ...))))
-
-(define-syntax if>?
- (syntax-rules ()
- ((if>? arg ...)
- (compare:if-rel? (1) (-1 0) arg ...))))
-
-(define-syntax if<=?
- (syntax-rules ()
- ((if<=? arg ...)
- (compare:if-rel? (-1 0) (1) arg ...))))
-
-(define-syntax if>=?
- (syntax-rules ()
- ((if>=? arg ...)
- (compare:if-rel? (0 1) (-1) arg ...))))
-
-(define-syntax if-not=?
- (syntax-rules ()
- ((if-not=? arg ...)
- (compare:if-rel? (-1 1) (0) arg ...))))
-
-
-; predicates from compare procedures
-
-(define-syntax compare:define-rel?
- (syntax-rules ()
- ((compare:define-rel? rel? if-rel?)
- (define rel?
- (case-lambda
- (() (lambda (x y) (if-rel? (default-compare x y) #t #f)))
- ((compare) (lambda (x y) (if-rel? (compare x y) #t #f)))
- ((x y) (if-rel? (default-compare x y) #t #f))
- ((compare x y)
- (if (procedure? compare)
- (if-rel? (compare x y) #t #f)
- (error "not a procedure (Did you mean rel/rel??): " compare))))))))
-
-(compare:define-rel? =? if=?)
-(compare:define-rel? <? if<?)
-(compare:define-rel? >? if>?)
-(compare:define-rel? <=? if<=?)
-(compare:define-rel? >=? if>=?)
-(compare:define-rel? not=? if-not=?)
-
-
-; chains of length 3
-
-(define-syntax compare:define-rel1/rel2?
- (syntax-rules ()
- ((compare:define-rel1/rel2? rel1/rel2? if-rel1? if-rel2?)
- (define rel1/rel2?
- (case-lambda
- (()
- (lambda (x y z)
- (if-rel1? (default-compare x y)
- (if-rel2? (default-compare y z) #t #f)
- (compare:checked #f default-compare z))))
- ((compare)
- (lambda (x y z)
- (if-rel1? (compare x y)
- (if-rel2? (compare y z) #t #f)
- (compare:checked #f compare z))))
- ((x y z)
- (if-rel1? (default-compare x y)
- (if-rel2? (default-compare y z) #t #f)
- (compare:checked #f default-compare z)))
- ((compare x y z)
- (if-rel1? (compare x y)
- (if-rel2? (compare y z) #t #f)
- (compare:checked #f compare z))))))))
-
-(compare:define-rel1/rel2? </<? if<? if<?)
-(compare:define-rel1/rel2? </<=? if<? if<=?)
-(compare:define-rel1/rel2? <=/<? if<=? if<?)
-(compare:define-rel1/rel2? <=/<=? if<=? if<=?)
-(compare:define-rel1/rel2? >/>? if>? if>?)
-(compare:define-rel1/rel2? >/>=? if>? if>=?)
-(compare:define-rel1/rel2? >=/>? if>=? if>?)
-(compare:define-rel1/rel2? >=/>=? if>=? if>=?)
-
-
-; chains of arbitrary length
-
-(define-syntax compare:define-chain-rel?
- (syntax-rules ()
- ((compare:define-chain-rel? chain-rel? if-rel?)
- (define chain-rel?
- (case-lambda
- ((compare)
- #t)
- ((compare x1)
- (compare:checked #t compare x1))
- ((compare x1 x2)
- (if-rel? (compare x1 x2) #t #f))
- ((compare x1 x2 x3)
- (if-rel? (compare x1 x2)
- (if-rel? (compare x2 x3) #t #f)
- (compare:checked #f compare x3)))
- ((compare x1 x2 . x3+)
- (if-rel? (compare x1 x2)
- (let chain? ((head x2) (tail x3+))
- (if (null? tail)
- #t
- (if-rel? (compare head (car tail))
- (chain? (car tail) (cdr tail))
- (apply compare:checked #f
- compare (cdr tail)))))
- (apply compare:checked #f compare x3+))))))))
-
-(compare:define-chain-rel? chain=? if=?)
-(compare:define-chain-rel? chain<? if<?)
-(compare:define-chain-rel? chain>? if>?)
-(compare:define-chain-rel? chain<=? if<=?)
-(compare:define-chain-rel? chain>=? if>=?)
-
-
-; pairwise inequality
-
-(define pairwise-not=?
- (let ((= =) (<= <=))
- (case-lambda
- ((compare)
- #t)
- ((compare x1)
- (compare:checked #t compare x1))
- ((compare x1 x2)
- (if-not=? (compare x1 x2) #t #f))
- ((compare x1 x2 x3)
- (if-not=? (compare x1 x2)
- (if-not=? (compare x2 x3)
- (if-not=? (compare x1 x3) #t #f)
- #f)
- (compare:checked #f compare x3)))
- ((compare . x1+)
- (let unequal? ((x x1+) (n (length x1+)) (unchecked? #t))
- (if (< n 2)
- (if (and unchecked? (= n 1))
- (compare:checked #t compare (car x))
- #t)
- (let* ((i-pivot (random-integer n))
- (x-pivot (list-ref x i-pivot)))
- (let split ((i 0) (x x) (x< '()) (x> '()))
- (if (null? x)
- (and (unequal? x< (length x<) #f)
- (unequal? x> (length x>) #f))
- (if (= i i-pivot)
- (split (+ i 1) (cdr x) x< x>)
- (if3 (compare (car x) x-pivot)
- (split (+ i 1) (cdr x) (cons (car x) x<) x>)
- (if unchecked?
- (apply compare:checked #f compare (cdr x))
- #f)
- (split (+ i 1) (cdr x) x< (cons (car x) x>)))))))))))))
-
-
-; min/max
-
-(define min-compare
- (case-lambda
- ((compare x1)
- (compare:checked x1 compare x1))
- ((compare x1 x2)
- (if<=? (compare x1 x2) x1 x2))
- ((compare x1 x2 x3)
- (if<=? (compare x1 x2)
- (if<=? (compare x1 x3) x1 x3)
- (if<=? (compare x2 x3) x2 x3)))
- ((compare x1 x2 x3 x4)
- (if<=? (compare x1 x2)
- (if<=? (compare x1 x3)
- (if<=? (compare x1 x4) x1 x4)
- (if<=? (compare x3 x4) x3 x4))
- (if<=? (compare x2 x3)
- (if<=? (compare x2 x4) x2 x4)
- (if<=? (compare x3 x4) x3 x4))))
- ((compare x1 x2 . x3+)
- (let min ((xmin (if<=? (compare x1 x2) x1 x2)) (xs x3+))
- (if (null? xs)
- xmin
- (min (if<=? (compare xmin (car xs)) xmin (car xs))
- (cdr xs)))))))
-
-(define max-compare
- (case-lambda
- ((compare x1)
- (compare:checked x1 compare x1))
- ((compare x1 x2)
- (if>=? (compare x1 x2) x1 x2))
- ((compare x1 x2 x3)
- (if>=? (compare x1 x2)
- (if>=? (compare x1 x3) x1 x3)
- (if>=? (compare x2 x3) x2 x3)))
- ((compare x1 x2 x3 x4)
- (if>=? (compare x1 x2)
- (if>=? (compare x1 x3)
- (if>=? (compare x1 x4) x1 x4)
- (if>=? (compare x3 x4) x3 x4))
- (if>=? (compare x2 x3)
- (if>=? (compare x2 x4) x2 x4)
- (if>=? (compare x3 x4) x3 x4))))
- ((compare x1 x2 . x3+)
- (let max ((xmax (if>=? (compare x1 x2) x1 x2)) (xs x3+))
- (if (null? xs)
- xmax
- (max (if>=? (compare xmax (car xs)) xmax (car xs))
- (cdr xs)))))))
-
-
-; kth-largest
-
-(define kth-largest
- (let ((= =) (< <))
- (case-lambda
- ((compare k x0)
- (case (modulo k 1)
- ((0) (compare:checked x0 compare x0))
- (else (error "bad index" k))))
- ((compare k x0 x1)
- (case (modulo k 2)
- ((0) (if<=? (compare x0 x1) x0 x1))
- ((1) (if<=? (compare x0 x1) x1 x0))
- (else (error "bad index" k))))
- ((compare k x0 x1 x2)
- (case (modulo k 3)
- ((0) (if<=? (compare x0 x1)
- (if<=? (compare x0 x2) x0 x2)
- (if<=? (compare x1 x2) x1 x2)))
- ((1) (if3 (compare x0 x1)
- (if<=? (compare x1 x2)
- x1
- (if<=? (compare x0 x2) x2 x0))
- (if<=? (compare x0 x2) x1 x0)
- (if<=? (compare x0 x2)
- x0
- (if<=? (compare x1 x2) x2 x1))))
- ((2) (if<=? (compare x0 x1)
- (if<=? (compare x1 x2) x2 x1)
- (if<=? (compare x0 x2) x2 x0)))
- (else (error "bad index" k))))
- ((compare k x0 . x1+) ; |x1+| >= 1
- (if (not (and (integer? k) (exact? k)))
- (error "bad index" k))
- (let ((n (+ 1 (length x1+))))
- (let kth ((k (modulo k n))
- (n n) ; = |x|
- (rev #t) ; are x<, x=, x> reversed?
- (x (cons x0 x1+)))
- (let ((pivot (list-ref x (random-integer n))))
- (let split ((x x) (x< '()) (n< 0) (x= '()) (n= 0) (x> '()) (n> 0))
- (if (null? x)
- (cond
- ((< k n<)
- (kth k n< (not rev) x<))
- ((< k (+ n< n=))
- (if rev
- (list-ref x= (- (- n= 1) (- k n<)))
- (list-ref x= (- k n<))))
- (else
- (kth (- k (+ n< n=)) n> (not rev) x>)))
- (if3 (compare (car x) pivot)
- (split (cdr x) (cons (car x) x<) (+ n< 1) x= n= x> n>)
- (split (cdr x) x< n< (cons (car x) x=) (+ n= 1) x> n>)
- (split (cdr x) x< n< x= n= (cons (car x) x>) (+ n> 1))))))))))))
-
-
-; compare functions from predicates
-
-(define compare-by<
- (case-lambda
- ((lt) (lambda (x y) (if (lt x y) -1 (if (lt y x) 1 0))))
- ((lt x y) (if (lt x y) -1 (if (lt y x) 1 0)))))
-
-(define compare-by>
- (case-lambda
- ((gt) (lambda (x y) (if (gt x y) 1 (if (gt y x) -1 0))))
- ((gt x y) (if (gt x y) 1 (if (gt y x) -1 0)))))
-
-(define compare-by<=
- (case-lambda
- ((le) (lambda (x y) (if (le x y) (if (le y x) 0 -1) 1)))
- ((le x y) (if (le x y) (if (le y x) 0 -1) 1))))
-
-(define compare-by>=
- (case-lambda
- ((ge) (lambda (x y) (if (ge x y) (if (ge y x) 0 1) -1)))
- ((ge x y) (if (ge x y) (if (ge y x) 0 1) -1))))
-
-(define compare-by=/<
- (case-lambda
- ((eq lt) (lambda (x y) (if (eq x y) 0 (if (lt x y) -1 1))))
- ((eq lt x y) (if (eq x y) 0 (if (lt x y) -1 1)))))
-
-(define compare-by=/>
- (case-lambda
- ((eq gt) (lambda (x y) (if (eq x y) 0 (if (gt x y) 1 -1))))
- ((eq gt x y) (if (eq x y) 0 (if (gt x y) 1 -1)))))
-
-; refine and extend construction
-
-(define-syntax refine-compare
- (syntax-rules ()
- ((refine-compare)
- 0)
- ((refine-compare c1)
- c1)
- ((refine-compare c1 c2 cs ...)
- (if3 c1 -1 (refine-compare c2 cs ...) 1))))
-
-(define-syntax select-compare
- (syntax-rules (else)
- ((select-compare x y clause ...)
- (let ((x-val x) (y-val y))
- (select-compare (x-val y-val clause ...))))
- ; used internally: (select-compare (x y clause ...))
- ((select-compare (x y))
- 0)
- ((select-compare (x y (else c ...)))
- (refine-compare c ...))
- ((select-compare (x y (t? c ...) clause ...))
- (let ((t?-val t?))
- (let ((tx (t?-val x)) (ty (t?-val y)))
- (if tx
- (if ty (refine-compare c ...) -1)
- (if ty 1 (select-compare (x y clause ...)))))))))
-
-(define-syntax cond-compare
- (syntax-rules (else)
- ((cond-compare)
- 0)
- ((cond-compare (else cs ...))
- (refine-compare cs ...))
- ((cond-compare ((tx ty) cs ...) clause ...)
- (let ((tx-val tx) (ty-val ty))
- (if tx-val
- (if ty-val (refine-compare cs ...) -1)
- (if ty-val 1 (cond-compare clause ...)))))))
-
-
-; R5RS atomic types
-
-(define-syntax compare:type-check
- (syntax-rules ()
- ((compare:type-check type? type-name x)
- (if (not (type? x))
- (error (string-append "not " type-name ":") x)))
- ((compare:type-check type? type-name x y)
- (begin (compare:type-check type? type-name x)
- (compare:type-check type? type-name y)))))
-
-(define-syntax compare:define-by=/<
- (syntax-rules ()
- ((compare:define-by=/< compare = < type? type-name)
- (define compare
- (let ((= =) (< <))
- (lambda (x y)
- (if (type? x)
- (if (eq? x y)
- 0
- (if (type? y)
- (if (= x y) 0 (if (< x y) -1 1))
- (error (string-append "not " type-name ":") y)))
- (error (string-append "not " type-name ":") x))))))))
-
-(define (boolean-compare x y)
- (compare:type-check boolean? "boolean" x y)
- (if x (if y 0 1) (if y -1 0)))
-
-(compare:define-by=/< char-compare char=? char<? char? "char")
-
-(compare:define-by=/< char-compare-ci char-ci=? char-ci<? char? "char")
-
-(compare:define-by=/< string-compare string=? string<? string? "string")
-
-(compare:define-by=/< string-compare-ci string-ci=? string-ci<? string? "string")
-
-(define (symbol-compare x y)
- (compare:type-check symbol? "symbol" x y)
- (string-compare (symbol->string x) (symbol->string y)))
-
-(compare:define-by=/< integer-compare = < integer? "integer")
-
-(compare:define-by=/< rational-compare = < rational? "rational")
-
-(compare:define-by=/< real-compare = < real? "real")
-
-(define (complex-compare x y)
- (compare:type-check complex? "complex" x y)
- (if (and (real? x) (real? y))
- (real-compare x y)
- (refine-compare (real-compare (real-part x) (real-part y))
- (real-compare (imag-part x) (imag-part y)))))
-
-(define (number-compare x y)
- (compare:type-check number? "number" x y)
- (complex-compare x y))
-
-
-; R5RS compound data structures: dotted pair, list, vector
-
-(define (pair-compare-car compare)
- (lambda (x y)
- (compare (car x) (car y))))
-
-(define (pair-compare-cdr compare)
- (lambda (x y)
- (compare (cdr x) (cdr y))))
-
-(define pair-compare
- (case-lambda
-
- ; dotted pair
- ((pair-compare-car pair-compare-cdr x y)
- (refine-compare (pair-compare-car (car x) (car y))
- (pair-compare-cdr (cdr x) (cdr y))))
-
- ; possibly improper lists
- ((compare x y)
- (cond-compare
- (((null? x) (null? y)) 0)
- (((pair? x) (pair? y)) (compare (car x) (car y))
- (pair-compare compare (cdr x) (cdr y)))
- (else (compare x y))))
-
- ; for convenience
- ((x y)
- (pair-compare default-compare x y))))
-
-(define list-compare
- (case-lambda
- ((compare x y empty? head tail)
- (cond-compare
- (((empty? x) (empty? y)) 0)
- (else (compare (head x) (head y))
- (list-compare compare (tail x) (tail y) empty? head tail))))
-
- ; for convenience
- (( x y empty? head tail)
- (list-compare default-compare x y empty? head tail))
- ((compare x y )
- (list-compare compare x y null? car cdr))
- (( x y )
- (list-compare default-compare x y null? car cdr))))
-
-(define list-compare-as-vector
- (case-lambda
- ((compare x y empty? head tail)
- (refine-compare
- (let compare-length ((x x) (y y))
- (cond-compare
- (((empty? x) (empty? y)) 0)
- (else (compare-length (tail x) (tail y)))))
- (list-compare compare x y empty? head tail)))
-
- ; for convenience
- (( x y empty? head tail)
- (list-compare-as-vector default-compare x y empty? head tail))
- ((compare x y )
- (list-compare-as-vector compare x y null? car cdr))
- (( x y )
- (list-compare-as-vector default-compare x y null? car cdr))))
-
-(define vector-compare
- (let ((= =))
- (case-lambda
- ((compare x y size ref)
- (let ((n (size x)) (m (size y)))
- (refine-compare
- (integer-compare n m)
- (let compare-rest ((i 0)) ; compare x[i..n-1] y[i..n-1]
- (if (= i n)
- 0
- (refine-compare (compare (ref x i) (ref y i))
- (compare-rest (+ i 1))))))))
-
- ; for convenience
- (( x y size ref)
- (vector-compare default-compare x y size ref))
- ((compare x y )
- (vector-compare compare x y vector-length vector-ref))
- (( x y )
- (vector-compare default-compare x y vector-length vector-ref)))))
-
-(define vector-compare-as-list
- (let ((= =))
- (case-lambda
- ((compare x y size ref)
- (let ((nx (size x)) (ny (size y)))
- (let ((n (min nx ny)))
- (let compare-rest ((i 0)) ; compare x[i..n-1] y[i..n-1]
- (if (= i n)
- (integer-compare nx ny)
- (refine-compare (compare (ref x i) (ref y i))
- (compare-rest (+ i 1))))))))
-
- ; for convenience
- (( x y size ref)
- (vector-compare-as-list default-compare x y size ref))
- ((compare x y )
- (vector-compare-as-list compare x y vector-length vector-ref))
- (( x y )
- (vector-compare-as-list default-compare x y vector-length vector-ref)))))
-
-
-; default compare
-
-(define (default-compare x y)
- (select-compare
- x y
- (null? 0)
- (pair? (default-compare (car x) (car y))
- (default-compare (cdr x) (cdr y)))
- (boolean? (boolean-compare x y))
- (char? (char-compare x y))
- (string? (string-compare x y))
- (symbol? (symbol-compare x y))
- (number? (number-compare x y))
- (vector? (vector-compare default-compare x y))
- (else (error "unrecognized type in default-compare" x y))))
-
-; Note that we pass default-compare to compare-{pair,vector} explictly.
-; This makes sure recursion proceeds with this default-compare, which
-; need not be the one in the lexical scope of compare-{pair,vector}.
-
-
-; debug compare
-
-(define (debug-compare c)
-
- (define (checked-value c x y)
- (let ((c-xy (c x y)))
- (if (or (eqv? c-xy -1) (eqv? c-xy 0) (eqv? c-xy 1))
- c-xy
- (error "compare value not in {-1,0,1}" c-xy (list c x y)))))
-
- (define (random-boolean)
- (zero? (random-integer 2)))
-
- (define q ; (u v w) such that u <= v, v <= w, and not u <= w
- '#(
- ;x < y x = y x > y [x < z]
- 0 0 0 ; y < z
- 0 (z y x) (z y x) ; y = z
- 0 (z y x) (z y x) ; y > z
-
- ;x < y x = y x > y [x = z]
- (y z x) (z x y) 0 ; y < z
- (y z x) 0 (x z y) ; y = z
- 0 (y x z) (x z y) ; y > z
-
- ;x < y x = y x > y [x > z]
- (x y z) (x y z) 0 ; y < z
- (x y z) (x y z) 0 ; y = z
- 0 0 0 ; y > z
- ))
-
- (let ((z? #f) (z #f)) ; stored element from previous call
- (lambda (x y)
- (let ((c-xx (checked-value c x x))
- (c-yy (checked-value c y y))
- (c-xy (checked-value c x y))
- (c-yx (checked-value c y x)))
- (if (not (zero? c-xx))
- (error "compare error: not reflexive" c x))
- (if (not (zero? c-yy))
- (error "compare error: not reflexive" c y))
- (if (not (zero? (+ c-xy c-yx)))
- (error "compare error: not anti-symmetric" c x y))
- (if z?
- (let ((c-xz (checked-value c x z))
- (c-zx (checked-value c z x))
- (c-yz (checked-value c y z))
- (c-zy (checked-value c z y)))
- (if (not (zero? (+ c-xz c-zx)))
- (error "compare error: not anti-symmetric" c x z))
- (if (not (zero? (+ c-yz c-zy)))
- (error "compare error: not anti-symmetric" c y z))
- (let ((ijk (vector-ref q (+ c-xy (* 3 c-yz) (* 9 c-xz) 13))))
- (if (list? ijk)
- (apply error
- "compare error: not transitive"
- c
- (map (lambda (i) (case i ((x) x) ((y) y) ((z) z)))
- ijk)))))
- (set! z? #t))
- (set! z (if (random-boolean) x y)) ; randomized testing
- c-xy))))
-;;; Copyright © Panu Kalliokoski (2005). All Rights Reserved.
-
-;;; Made an R7RS library by Taylan Ulrich Bayırlı/Kammer, Copyright © 2014.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-(define default-bound (make-parameter (- (expt 2 29) 3)))
-
-(define (%string-hash s ch-conv bound)
- (let ((hash 31)
- (len (string-length s)))
- (do ((index 0 (+ index 1)))
- ((>= index len) (modulo hash bound))
- (set! hash (modulo (+ (* 37 hash)
- (char->integer (ch-conv (string-ref s index))))
- (default-bound))))))
-
-(define string-hash
- (case-lambda
- ((s) (string-hash s (default-bound)))
- ((s bound)
- (%string-hash s (lambda (x) x) bound))))
-
-(define string-ci-hash
- (case-lambda
- ((s) (string-ci-hash s (default-bound)))
- ((s bound)
- (%string-hash s char-downcase bound))))
-
-(define symbol-hash
- (case-lambda
- ((s) (symbol-hash s (default-bound)))
- ((s bound)
- (%string-hash (symbol->string s) (lambda (x) x) bound))))
-
-(define hash
- (case-lambda
- ((obj) (hash obj (default-bound)))
- ((obj bound)
- (cond ((integer? obj) (modulo obj bound))
- ((string? obj) (string-hash obj bound))
- ((symbol? obj) (symbol-hash obj bound))
- ((real? obj) (modulo (+ (numerator obj) (denominator obj)) bound))
- ((number? obj)
- (modulo (+ (hash (real-part obj)) (* 3 (hash (imag-part obj))))
- bound))
- ((char? obj) (modulo (char->integer obj) bound))
- ((vector? obj) (vector-hash obj bound))
- ((pair? obj) (modulo (+ (hash (car obj)) (* 3 (hash (cdr obj))))
- bound))
- ((null? obj) 0)
- ((not obj) 0)
- ((procedure? obj) (error "hash: procedures cannot be hashed" obj))
- (else 1)))))
-
-(define hash-by-identity hash)
-
-(define (vector-hash v bound)
- (let ((hashvalue 571)
- (len (vector-length v)))
- (do ((index 0 (+ index 1)))
- ((>= index len) (modulo hashvalue bound))
- (set! hashvalue (modulo (+ (* 257 hashvalue) (hash (vector-ref v index)))
- (default-bound))))))
-
-(define %make-hash-node cons)
-(define %hash-node-set-value! set-cdr!)
-(define %hash-node-key car)
-(define %hash-node-value cdr)
-
-(define-record-type <srfi-hash-table>
- (%make-hash-table size hash compare associate entries)
- hash-table?
- (size hash-table-size hash-table-set-size!)
- (hash hash-table-hash-function)
- (compare hash-table-equivalence-function)
- (associate hash-table-association-function)
- (entries hash-table-entries hash-table-set-entries!))
-
-(define default-table-size (make-parameter 64))
-
-(define (appropriate-hash-function-for comparison)
- (or (and (eq? comparison eq?) hash-by-identity)
- (and (eq? comparison string=?) string-hash)
- (and (eq? comparison string-ci=?) string-ci-hash)
- hash))
-
-(define make-hash-table
- (case-lambda
- (()
- (make-hash-table equal?
- (appropriate-hash-function-for equal?)
- (default-table-size)))
- ((comparison)
- (make-hash-table comparison
- (appropriate-hash-function-for comparison)
- (default-table-size)))
- ((comparison hash)
- (make-hash-table comparison
- hash
- (default-table-size)))
- ((comparison hash size)
- (let ((association (or (and (eq? comparison eq?) assq)
- (and (eq? comparison eqv?) assv)
- (and (eq? comparison equal?) assoc)
- (rec (associate val alist)
- (cond
- ((null? alist) #f)
- ((comparison val (caar alist)) (car alist))
- (else (associate val (cdr alist))))))))
- (%make-hash-table
- 0 hash comparison association (make-vector size '()))))))
-
-(define (make-hash-table-maker comp hash)
- (lambda args (apply make-hash-table (cons comp (cons hash args)))))
-(define make-symbol-hash-table
- (make-hash-table-maker eq? symbol-hash))
-(define make-string-hash-table
- (make-hash-table-maker string=? string-hash))
-(define make-string-ci-hash-table
- (make-hash-table-maker string-ci=? string-ci-hash))
-(define make-integer-hash-table
- (make-hash-table-maker = modulo))
-
-(define (%hash-table-hash hash-table key)
- ((hash-table-hash-function hash-table)
- key (vector-length (hash-table-entries hash-table))))
-
-(define (%hash-table-find entries associate hash key)
- (associate key (vector-ref entries hash)))
-
-(define (%hash-table-add! entries hash key value)
- (vector-set! entries hash
- (cons (%make-hash-node key value)
- (vector-ref entries hash))))
-
-(define (%hash-table-delete! entries compare hash key)
- (let ((entrylist (vector-ref entries hash)))
- (cond ((null? entrylist) #f)
- ((compare key (caar entrylist))
- (vector-set! entries hash (cdr entrylist)) #t)
- (else
- (let loop ((current (cdr entrylist)) (previous entrylist))
- (cond ((null? current) #f)
- ((compare key (caar current))
- (set-cdr! previous (cdr current)) #t)
- (else (loop (cdr current) current))))))))
-
-(define (%hash-table-walk proc entries)
- (do ((index (- (vector-length entries) 1) (- index 1)))
- ((< index 0)) (for-each proc (vector-ref entries index))))
-
-(define (%hash-table-maybe-resize! hash-table)
- (let* ((old-entries (hash-table-entries hash-table))
- (hash-length (vector-length old-entries)))
- (if (> (hash-table-size hash-table) hash-length)
- (let* ((new-length (* 2 hash-length))
- (new-entries (make-vector new-length '()))
- (hash (hash-table-hash-function hash-table)))
- (%hash-table-walk
- (lambda (node)
- (%hash-table-add! new-entries
- (hash (%hash-node-key node) new-length)
- (%hash-node-key node) (%hash-node-value node)))
- old-entries)
- (hash-table-set-entries! hash-table new-entries)))))
-
-(define (not-found-error key)
- (lambda ()
- (error "No value associated with key:" key)))
-
-(define hash-table-ref
- (case-lambda
- ((hash-table key) (hash-table-ref hash-table key (not-found-error key)))
- ((hash-table key default-thunk)
- (cond ((%hash-table-find (hash-table-entries hash-table)
- (hash-table-association-function hash-table)
- (%hash-table-hash hash-table key) key)
- => %hash-node-value)
- (else (default-thunk))))))
-
-(define (hash-table-ref/default hash-table key default)
- (hash-table-ref hash-table key (lambda () default)))
-
-(define (hash-table-set! hash-table key value)
- (let ((hash (%hash-table-hash hash-table key))
- (entries (hash-table-entries hash-table)))
- (cond ((%hash-table-find entries
- (hash-table-association-function hash-table)
- hash key)
- => (lambda (node) (%hash-node-set-value! node value)))
- (else (%hash-table-add! entries hash key value)
- (hash-table-set-size! hash-table
- (+ 1 (hash-table-size hash-table)))
- (%hash-table-maybe-resize! hash-table)))))
-
-(define hash-table-update!
- (case-lambda
- ((hash-table key function)
- (hash-table-update! hash-table key function (not-found-error key)))
- ((hash-table key function default-thunk)
- (let ((hash (%hash-table-hash hash-table key))
- (entries (hash-table-entries hash-table)))
- (cond ((%hash-table-find entries
- (hash-table-association-function hash-table)
- hash key)
- => (lambda (node)
- (%hash-node-set-value!
- node (function (%hash-node-value node)))))
- (else (%hash-table-add! entries hash key
- (function (default-thunk)))
- (hash-table-set-size! hash-table
- (+ 1 (hash-table-size hash-table)))
- (%hash-table-maybe-resize! hash-table)))))))
-
-(define (hash-table-update!/default hash-table key function default)
- (hash-table-update! hash-table key function (lambda () default)))
-
-(define (hash-table-delete! hash-table key)
- (if (%hash-table-delete! (hash-table-entries hash-table)
- (hash-table-equivalence-function hash-table)
- (%hash-table-hash hash-table key) key)
- (hash-table-set-size! hash-table (- (hash-table-size hash-table) 1))))
-
-(define (hash-table-exists? hash-table key)
- (and (%hash-table-find (hash-table-entries hash-table)
- (hash-table-association-function hash-table)
- (%hash-table-hash hash-table key) key) #t))
-
-(define (hash-table-walk hash-table proc)
- (%hash-table-walk
- (lambda (node) (proc (%hash-node-key node) (%hash-node-value node)))
- (hash-table-entries hash-table)))
-
-(define (hash-table-fold hash-table f acc)
- (hash-table-walk hash-table
- (lambda (key value) (set! acc (f key value acc))))
- acc)
-
-(define (appropriate-size-for-alist alist)
- (max (default-table-size) (* 2 (length alist))))
-
-(define alist->hash-table
- (case-lambda
- ((alist)
- (alist->hash-table alist
- equal?
- (appropriate-hash-function-for equal?)
- (appropriate-size-for-alist alist)))
- ((alist comparison)
- (alist->hash-table alist
- comparison
- (appropriate-hash-function-for comparison)
- (appropriate-size-for-alist alist)))
- ((alist comparison hash)
- (alist->hash-table alist
- comparison
- hash
- (appropriate-size-for-alist alist)))
- ((alist comparison hash size)
- (let ((hash-table (make-hash-table comparison hash size)))
- (for-each
- (lambda (elem)
- (hash-table-update!/default
- hash-table (car elem) (lambda (x) x) (cdr elem)))
- alist)
- hash-table))))
-
-(define (hash-table->alist hash-table)
- (hash-table-fold hash-table
- (lambda (key val acc) (cons (cons key val) acc)) '()))
-
-(define (hash-table-copy hash-table)
- (let ((new (make-hash-table (hash-table-equivalence-function hash-table)
- (hash-table-hash-function hash-table)
- (max (default-table-size)
- (* 2 (hash-table-size hash-table))))))
- (hash-table-walk hash-table
- (lambda (key value) (hash-table-set! new key value)))
- new))
-
-(define (hash-table-merge! hash-table1 hash-table2)
- (hash-table-walk
- hash-table2
- (lambda (key value) (hash-table-set! hash-table1 key value)))
- hash-table1)
-
-(define (hash-table-keys hash-table)
- (hash-table-fold hash-table (lambda (key val acc) (cons key acc)) '()))
-
-(define (hash-table-values hash-table)
- (hash-table-fold hash-table (lambda (key val acc) (cons val acc)) '()))
-(define-library (srfi 69)
- (export
- ;; Type constructors and predicate
- make-hash-table hash-table? alist->hash-table
- ;; Reflective queries
- hash-table-equivalence-function hash-table-hash-function
- ;; Dealing with single elements
- hash-table-ref hash-table-ref/default hash-table-set! hash-table-delete!
- hash-table-exists? hash-table-update! hash-table-update!/default
- ;; Dealing with the whole contents
- hash-table-size hash-table-keys hash-table-values hash-table-walk
- hash-table-fold hash-table->alist hash-table-copy hash-table-merge!
- ;; Hashing
- hash string-hash string-ci-hash hash-by-identity
- )
- (import
- (scheme base)
- (scheme case-lambda)
- (scheme char)
- (scheme complex)
- (scheme cxr)
- (srfi 1)
- (srfi 31))
- (include "69.body.scm"))
-;;; Copyright © Panu Kalliokoski (2005). All Rights Reserved.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the "Software"), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-(define *default-bound* (- (expt 2 29) 3))
-
-(define (%string-hash s ch-conv bound)
- (let ((hash 31)
- (len (string-length s)))
- (do ((index 0 (+ index 1)))
- ((>= index len) (modulo hash bound))
- (set! hash (modulo (+ (* 37 hash)
- (char->integer (ch-conv (string-ref s index))))
- *default-bound*)))))
-
-(define (string-hash s . maybe-bound)
- (let ((bound (if (null? maybe-bound) *default-bound* (car maybe-bound))))
- (%string-hash s (lambda (x) x) bound)))
-
-(define (string-ci-hash s . maybe-bound)
- (let ((bound (if (null? maybe-bound) *default-bound* (car maybe-bound))))
- (%string-hash s char-downcase bound)))
-
-(define (symbol-hash s . maybe-bound)
- (let ((bound (if (null? maybe-bound) *default-bound* (car maybe-bound))))
- (%string-hash (symbol->string s) (lambda (x) x) bound)))
-
-(define (hash obj . maybe-bound)
- (let ((bound (if (null? maybe-bound) *default-bound* (car maybe-bound))))
- (cond ((integer? obj) (modulo obj bound))
- ((string? obj) (string-hash obj bound))
- ((symbol? obj) (symbol-hash obj bound))
- ((real? obj) (modulo (+ (numerator obj) (denominator obj)) bound))
- ((number? obj)
- (modulo (+ (hash (real-part obj)) (* 3 (hash (imag-part obj))))
- bound))
- ((char? obj) (modulo (char->integer obj) bound))
- ((vector? obj) (vector-hash obj bound))
- ((pair? obj) (modulo (+ (hash (car obj)) (* 3 (hash (cdr obj))))
- bound))
- ((null? obj) 0)
- ((not obj) 0)
- ((procedure? obj) (error "hash: procedures cannot be hashed" obj))
- (else 1))))
-
-(define hash-by-identity hash)
-
-(define (vector-hash v bound)
- (let ((hashvalue 571)
- (len (vector-length v)))
- (do ((index 0 (+ index 1)))
- ((>= index len) (modulo hashvalue bound))
- (set! hashvalue (modulo (+ (* 257 hashvalue) (hash (vector-ref v index)))
- *default-bound*)))))
-
-(define %make-hash-node cons)
-(define %hash-node-set-value! set-cdr!)
-(define %hash-node-key car)
-(define %hash-node-value cdr)
-
-(define-record-type <srfi-hash-table>
- (%make-hash-table size hash compare associate entries)
- hash-table?
- (size hash-table-size hash-table-set-size!)
- (hash hash-table-hash-function)
- (compare hash-table-equivalence-function)
- (associate hash-table-association-function)
- (entries hash-table-entries hash-table-set-entries!))
-
-(define *default-table-size* 64)
-
-(define (appropriate-hash-function-for comparison)
- (or (and (eq? comparison eq?) hash-by-identity)
- (and (eq? comparison string=?) string-hash)
- (and (eq? comparison string-ci=?) string-ci-hash)
- hash))
-
-(define (make-hash-table . args)
- (let* ((comparison (if (null? args) equal? (car args)))
- (hash
- (if (or (null? args) (null? (cdr args)))
- (appropriate-hash-function-for comparison) (cadr args)))
- (size
- (if (or (null? args) (null? (cdr args)) (null? (cddr args)))
- *default-table-size* (caddr args)))
- (association
- (or (and (eq? comparison eq?) assq)
- (and (eq? comparison eqv?) assv)
- (and (eq? comparison equal?) assoc)
- (letrec
- ((associate
- (lambda (val alist)
- (cond ((null? alist) #f)
- ((comparison val (caar alist)) (car alist))
- (else (associate val (cdr alist)))))))
- associate))))
- (%make-hash-table 0 hash comparison association (make-vector size '()))))
-
-(define (make-hash-table-maker comp hash)
- (lambda args (apply make-hash-table (cons comp (cons hash args)))))
-(define make-symbol-hash-table
- (make-hash-table-maker eq? symbol-hash))
-(define make-string-hash-table
- (make-hash-table-maker string=? string-hash))
-(define make-string-ci-hash-table
- (make-hash-table-maker string-ci=? string-ci-hash))
-(define make-integer-hash-table
- (make-hash-table-maker = modulo))
-
-(define (%hash-table-hash hash-table key)
- ((hash-table-hash-function hash-table)
- key (vector-length (hash-table-entries hash-table))))
-
-(define (%hash-table-find entries associate hash key)
- (associate key (vector-ref entries hash)))
-
-(define (%hash-table-add! entries hash key value)
- (vector-set! entries hash
- (cons (%make-hash-node key value)
- (vector-ref entries hash))))
-
-(define (%hash-table-delete! entries compare hash key)
- (let ((entrylist (vector-ref entries hash)))
- (cond ((null? entrylist) #f)
- ((compare key (caar entrylist))
- (vector-set! entries hash (cdr entrylist)) #t)
- (else
- (let loop ((current (cdr entrylist)) (previous entrylist))
- (cond ((null? current) #f)
- ((compare key (caar current))
- (set-cdr! previous (cdr current)) #t)
- (else (loop (cdr current) current))))))))
-
-(define (%hash-table-walk proc entries)
- (do ((index (- (vector-length entries) 1) (- index 1)))
- ((< index 0)) (for-each proc (vector-ref entries index))))
-
-(define (%hash-table-maybe-resize! hash-table)
- (let* ((old-entries (hash-table-entries hash-table))
- (hash-length (vector-length old-entries)))
- (if (> (hash-table-size hash-table) hash-length)
- (let* ((new-length (* 2 hash-length))
- (new-entries (make-vector new-length '()))
- (hash (hash-table-hash-function hash-table)))
- (%hash-table-walk
- (lambda (node)
- (%hash-table-add! new-entries
- (hash (%hash-node-key node) new-length)
- (%hash-node-key node) (%hash-node-value node)))
- old-entries)
- (hash-table-set-entries! hash-table new-entries)))))
-
-(define (hash-table-ref hash-table key . maybe-default)
- (cond ((%hash-table-find (hash-table-entries hash-table)
- (hash-table-association-function hash-table)
- (%hash-table-hash hash-table key) key)
- => %hash-node-value)
- ((null? maybe-default)
- (error "hash-table-ref: no value associated with" key))
- (else ((car maybe-default)))))
-
-(define (hash-table-ref/default hash-table key default)
- (hash-table-ref hash-table key (lambda () default)))
-
-(define (hash-table-set! hash-table key value)
- (let ((hash (%hash-table-hash hash-table key))
- (entries (hash-table-entries hash-table)))
- (cond ((%hash-table-find entries
- (hash-table-association-function hash-table)
- hash key)
- => (lambda (node) (%hash-node-set-value! node value)))
- (else (%hash-table-add! entries hash key value)
- (hash-table-set-size! hash-table
- (+ 1 (hash-table-size hash-table)))
- (%hash-table-maybe-resize! hash-table)))))
-
-(define (hash-table-update! hash-table key function . maybe-default)
- (let ((hash (%hash-table-hash hash-table key))
- (entries (hash-table-entries hash-table)))
- (cond ((%hash-table-find entries
- (hash-table-association-function hash-table)
- hash key)
- => (lambda (node)
- (%hash-node-set-value!
- node (function (%hash-node-value node)))))
- ((null? maybe-default)
- (error "hash-table-update!: no value exists for key" key))
- (else (%hash-table-add! entries hash key
- (function ((car maybe-default))))
- (hash-table-set-size! hash-table
- (+ 1 (hash-table-size hash-table)))
- (%hash-table-maybe-resize! hash-table)))))
-
-(define (hash-table-update!/default hash-table key function default)
- (hash-table-update! hash-table key function (lambda () default)))
-
-(define (hash-table-delete! hash-table key)
- (if (%hash-table-delete! (hash-table-entries hash-table)
- (hash-table-equivalence-function hash-table)
- (%hash-table-hash hash-table key) key)
- (hash-table-set-size! hash-table (- (hash-table-size hash-table) 1))))
-
-(define (hash-table-exists? hash-table key)
- (and (%hash-table-find (hash-table-entries hash-table)
- (hash-table-association-function hash-table)
- (%hash-table-hash hash-table key) key) #t))
-
-(define (hash-table-walk hash-table proc)
- (%hash-table-walk
- (lambda (node) (proc (%hash-node-key node) (%hash-node-value node)))
- (hash-table-entries hash-table)))
-
-(define (hash-table-fold hash-table f acc)
- (hash-table-walk hash-table
- (lambda (key value) (set! acc (f key value acc))))
- acc)
-
-(define (alist->hash-table alist . args)
- (let* ((comparison (if (null? args) equal? (car args)))
- (hash
- (if (or (null? args) (null? (cdr args)))
- (appropriate-hash-function-for comparison) (cadr args)))
- (size
- (if (or (null? args) (null? (cdr args)) (null? (cddr args)))
- (max *default-table-size* (* 2 (length alist))) (caddr args)))
- (hash-table (make-hash-table comparison hash size)))
- (for-each
- (lambda (elem)
- (hash-table-update!/default
- hash-table (car elem) (lambda (x) x) (cdr elem)))
- alist)
- hash-table))
-
-(define (hash-table->alist hash-table)
- (hash-table-fold hash-table
- (lambda (key val acc) (cons (cons key val) acc)) '()))
-
-(define (hash-table-copy hash-table)
- (let ((new (make-hash-table (hash-table-equivalence-function hash-table)
- (hash-table-hash-function hash-table)
- (max *default-table-size*
- (* 2 (hash-table-size hash-table))))))
- (hash-table-walk hash-table
- (lambda (key value) (hash-table-set! new key value)))
- new))
-
-(define (hash-table-merge! hash-table1 hash-table2)
- (hash-table-walk
- hash-table2
- (lambda (key value) (hash-table-set! hash-table1 key value)))
- hash-table1)
-
-(define (hash-table-keys hash-table)
- (hash-table-fold hash-table (lambda (key val acc) (cons key acc)) '()))
-
-(define (hash-table-values hash-table)
- (hash-table-fold hash-table (lambda (key val acc) (cons val acc)) '()))
-(define-library (srfi 71)
- (export
- (rename srfi-letrec* letrec*)
- (rename srfi-letrec letrec)
- (rename srfi-let* let*)
- (rename srfi-let let)
- uncons
- uncons-2
- uncons-3
- uncons-4
- uncons-cons
- unlist
- unvector
- )
- (import
- (rename (scheme base)
- (let r5rs-let)
- (letrec r5rs-letrec))
- (scheme cxr))
- (include "71.upstream.scm"))
-;;; Copyright (c) 2005 Sebastian Egner.
-
-;;; Permission is hereby granted, free of charge, to any person obtaining a copy
-;;; of this software and associated documentation files (the ``Software''), to
-;;; deal in the Software without restriction, including without limitation the
-;;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-;;; sell copies of the Software, and to permit persons to whom the Software is
-;;; furnished to do so, subject to the following conditions:
-
-;;; The above copyright notice and this permission notice shall be included in
-;;; all copies or substantial portions of the Software.
-
-;;; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-;;; IN THE SOFTWARE.
-
-; Reference implementation of SRFI-71 (generic part)
-; Sebastian.Egner@philips.com, 20-May-2005, PLT 208
-;
-; In order to avoid conflicts with the existing let etc.
-; the macros defined here are called srfi-let etc.,
-; and they are defined in terms of r5rs-let etc.
-; It is up to the actual implementation to save let/*/rec
-; in r5rs-let/*/rec first and redefine let/*/rec
-; by srfi-let/*/rec then.
-;
-; There is also a srfi-letrec* being defined (in view of R6RS.)
-;
-; Macros used internally are named i:<something>.
-;
-; Abbreviations for macro arguments:
-; bs - <binding spec>
-; b - component of a binding spec (values, <variable>, or <expression>)
-; v - <variable>
-; vr - <variable> for rest list
-; x - <expression>
-; t - newly introduced temporary variable
-; vx - (<variable> <expression>)
-; rec - flag if letrec is produced (and not let)
-; cwv - call-with-value skeleton of the form (x formals)
-; (call-with-values (lambda () x) (lambda formals /payload/))
-; where /payload/ is of the form (let (vx ...) body1 body ...).
-;
-; Remark (*):
-; We bind the variables of a letrec to i:undefined since there is
-; no portable (R5RS) way of binding a variable to a values that
-; raises an error when read uninitialized.
-
-(define i:undefined 'undefined)
-
-(define-syntax srfi-letrec* ; -> srfi-letrec
- (syntax-rules ()
- ((srfi-letrec* () body1 body ...)
- (srfi-letrec () body1 body ...))
- ((srfi-letrec* (bs) body1 body ...)
- (srfi-letrec (bs) body1 body ...))
- ((srfi-letrec* (bs1 bs2 bs ...) body1 body ...)
- (srfi-letrec (bs1) (srfi-letrec* (bs2 bs ...) body1 body ...)))))
-
-(define-syntax srfi-letrec ; -> i:let
- (syntax-rules ()
- ((srfi-letrec ((b1 b2 b ...) ...) body1 body ...)
- (i:let "bs" #t () () (body1 body ...) ((b1 b2 b ...) ...)))))
-
-(define-syntax srfi-let* ; -> srfi-let
- (syntax-rules ()
- ((srfi-let* () body1 body ...)
- (srfi-let () body1 body ...))
- ((srfi-let* (bs) body1 body ...)
- (srfi-let (bs) body1 body ...))
- ((srfi-let* (bs1 bs2 bs ...) body1 body ...)
- (srfi-let (bs1) (srfi-let* (bs2 bs ...) body1 body ...)))))
-
-(define-syntax srfi-let ; -> i:let or i:named-let
- (syntax-rules ()
- ((srfi-let ((b1 b2 b ...) ...) body1 body ...)
- (i:let "bs" #f () () (body1 body ...) ((b1 b2 b ...) ...)))
- ((srfi-let tag ((b1 b2 b ...) ...) body1 body ...)
- (i:named-let tag () (body1 body ...) ((b1 b2 b ...) ...)))))
-
-(define-syntax i:let
- (syntax-rules (values)
-
-; (i:let "bs" rec (cwv ...) (vx ...) body (bs ...))
-; processes the binding specs bs ... by adding call-with-values
-; skeletons to cwv ... and bindings to vx ..., and afterwards
-; wrapping the skeletons around the payload (let (vx ...) . body).
-
- ; no more bs to process -> wrap call-with-values skeletons
- ((i:let "bs" rec (cwv ...) vxs body ())
- (i:let "wrap" rec vxs body cwv ...))
-
- ; recognize form1 without variable -> dummy binding for side-effects
- ((i:let "bs" rec cwvs (vx ...) body (((values) x) bs ...))
- (i:let "bs" rec cwvs (vx ... (dummy (begin x #f))) body (bs ...)))
-
- ; recognize form1 with single variable -> just extend vx ...
- ((i:let "bs" rec cwvs (vx ...) body (((values v) x) bs ...))
- (i:let "bs" rec cwvs (vx ... (v x)) body (bs ...)))
-
- ; recognize form1 without rest arg -> generate cwv
- ((i:let "bs" rec cwvs vxs body (((values v ...) x) bs ...))
- (i:let "form1" rec cwvs vxs body (bs ...) (x ()) (values v ...)))
-
- ; recognize form1 with rest arg -> generate cwv
- ((i:let "bs" rec cwvs vxs body (((values . vs) x) bs ...))
- (i:let "form1+" rec cwvs vxs body (bs ...) (x ()) (values . vs)))
-
- ; recognize form2 with single variable -> just extend vx ...
- ((i:let "bs" rec cwvs (vx ...) body ((v x) bs ...))
- (i:let "bs" rec cwvs (vx ... (v x)) body (bs ...)))
-
- ; recognize form2 with >=2 variables -> transform to form1
- ((i:let "bs" rec cwvs vxs body ((b1 b2 b3 b ...) bs ...))
- (i:let "form2" rec cwvs vxs body (bs ...) (b1 b2) (b3 b ...)))
-
-; (i:let "form1" rec cwvs vxs body bss (x (t ...)) (values v1 v2 v ...))
-; processes the variables in v1 v2 v ... adding them to (t ...)
-; and producing a cwv when finished. There is not rest argument.
-
- ((i:let "form1" rec (cwv ...) vxs body bss (x ts) (values))
- (i:let "bs" rec (cwv ... (x ts)) vxs body bss))
- ((i:let "form1" rec cwvs (vx ...) body bss (x (t ...)) (values v1 v ...))
- (i:let "form1" rec cwvs (vx ... (v1 t1)) body bss (x (t ... t1)) (values v ...)))
-
-; (i:let "form1+" rec cwvs vxs body bss (x (t ...)) (values v ... . vr))
-; processes the variables in v ... . vr adding them to (t ...)
-; and producing a cwv when finished. The rest arg is vr.
-
- ((i:let "form1+" rec cwvs (vx ...) body bss (x (t ...)) (values v1 v2 . vs))
- (i:let "form1+" rec cwvs (vx ... (v1 t1)) body bss (x (t ... t1)) (values v2 . vs)))
- ((i:let "form1+" rec (cwv ...) (vx ...) body bss (x (t ...)) (values v1 . vr))
- (i:let "bs" rec (cwv ... (x (t ... t1 . tr))) (vx ... (v1 t1) (vr tr)) body bss))
- ((i:let "form1+" rec (cwv ...) (vx ...) body bss (x ()) (values . vr))
- (i:let "bs" rec (cwv ... (x tr)) (vx ... (vr tr)) body bss))
-
-; (i:let "form2" rec cwvs vxs body bss (v ...) (b ... x))
-; processes the binding items (b ... x) from form2 as in
-; (v ... b ... x) into ((values v ... b ...) x), i.e. form1.
-; Then call "bs" recursively.
-
- ((i:let "form2" rec cwvs vxs body (bs ...) (v ...) (x))
- (i:let "bs" rec cwvs vxs body (((values v ...) x) bs ...)))
- ((i:let "form2" rec cwvs vxs body bss (v ...) (b1 b2 b ...))
- (i:let "form2" rec cwvs vxs body bss (v ... b1) (b2 b ...)))
-
-; (i:let "wrap" rec ((v x) ...) (body ...) cwv ...)
-; wraps cwv ... around the payload generating the actual code.
-; For letrec this is of course different than for let.
-
- ((i:let "wrap" #f vxs body)
- (r5rs-let vxs . body))
- ((i:let "wrap" #f vxs body (x formals) cwv ...)
- (call-with-values
- (lambda () x)
- (lambda formals (i:let "wrap" #f vxs body cwv ...))))
-
- ((i:let "wrap" #t vxs body)
- (r5rs-letrec vxs . body))
- ((i:let "wrap" #t ((v t) ...) body cwv ...)
- (r5rs-let ((v i:undefined) ...) ; (*)
- (i:let "wraprec" ((v t) ...) body cwv ...)))
-
-; (i:let "wraprec" ((v t) ...) body cwv ...)
-; generate the inner code for a letrec. The variables v ...
-; are the user-visible variables (bound outside), and t ...
-; are the temporary variables bound by the cwv consumers.
-
- ((i:let "wraprec" ((v t) ...) (body ...))
- (begin (set! v t) ... (r5rs-let () body ...)))
- ((i:let "wraprec" vxs body (x formals) cwv ...)
- (call-with-values
- (lambda () x)
- (lambda formals (i:let "wraprec" vxs body cwv ...))))
-
- ))
-
-(define-syntax i:named-let
- (syntax-rules (values)
-
-; (i:named-let tag (vx ...) body (bs ...))
-; processes the binding specs bs ... by extracting the variable
-; and expression, adding them to vx and turning the result into
-; an ordinary named let.
-
- ((i:named-let tag vxs body ())
- (r5rs-let tag vxs . body))
- ((i:named-let tag (vx ...) body (((values v) x) bs ...))
- (i:named-let tag (vx ... (v x)) body (bs ...)))
- ((i:named-let tag (vx ...) body ((v x) bs ...))
- (i:named-let tag (vx ... (v x)) body (bs ...)))))
-
-; --- standard procedures ---
-
-(define (uncons pair)
- (values (car pair) (cdr pair)))
-
-(define (uncons-2 list)
- (values (car list) (cadr list) (cddr list)))
-
-(define (uncons-3 list)
- (values (car list) (cadr list) (caddr list) (cdddr list)))
-
-(define (uncons-4 list)
- (values (car list) (cadr list) (caddr list) (cadddr list) (cddddr list)))
-
-(define (uncons-cons alist)
- (values (caar alist) (cdar alist) (cdr alist)))
-
-(define (unlist list)
- (apply values list))
-
-(define (unvector vector)
- (apply values (vector->list vector)))
-
-; --- standard macros ---
-
-(define-syntax values->list
- (syntax-rules ()
- ((values->list x)
- (call-with-values (lambda () x) list))))
-
-(define-syntax values->vector
- (syntax-rules ()
- ((values->vector x)
- (call-with-values (lambda () x) vector))))
-(define-library (srfi 78)
- (export
- check
- check-ec
- check-report
- check-set-mode!
- check-reset!
- check-passed?
- )
- (import
- (scheme base)
- (scheme cxr)
- (scheme write)
- (srfi 42))
- (include "78.upstream.scm"))
-; <PLAINTEXT>
-; Copyright (c) 2005-2006 Sebastian Egner.
-;
-; Permission is hereby granted, free of charge, to any person obtaining
-; a copy of this software and associated documentation files (the
-; ``Software''), to deal in the Software without restriction, including
-; without limitation the rights to use, copy, modify, merge, publish,
-; distribute, sublicense, and/or sell copies of the Software, and to
-; permit persons to whom the Software is furnished to do so, subject to
-; the following conditions:
-;
-; The above copyright notice and this permission notice shall be
-; included in all copies or substantial portions of the Software.
-;
-; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
-; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-;
-; -----------------------------------------------------------------------
-;
-; Lightweight testing (reference implementation)
-; ==============================================
-;
-; Sebastian.Egner@philips.com
-; in R5RS + SRFI 23 (error) + SRFI 42 (comprehensions)
-;
-; history of this file:
-; SE, 25-Oct-2004: first version based on code used in SRFIs 42 and 67
-; SE, 19-Jan-2006: (arg ...) made optional in check-ec
-;
-; Naming convention "check:<identifier>" is used only internally.
-
-; -- portability --
-
-; PLT: (require (lib "23.ss" "srfi") (lib "42.ss" "srfi"))
-; Scheme48: ,open srfi-23 srfi-42
-
-; -- utilities --
-
-(define check:write write)
-
-; You can also use a pretty printer if you have one.
-; However, the output might not improve for most cases
-; because the pretty printers usually output a trailing
-; newline.
-
-; PLT: (require (lib "pretty.ss")) (define check:write pretty-print)
-; Scheme48: ,open pp (define check:write p)
-
-; -- mode --
-
-(define check:mode #f)
-
-(define (check-set-mode! mode)
- (set! check:mode
- (case mode
- ((off) 0)
- ((summary) 1)
- ((report-failed) 10)
- ((report) 100)
- (else (error "unrecognized mode" mode)))))
-
-(check-set-mode! 'report)
-
-; -- state --
-
-(define check:correct #f)
-(define check:failed #f)
-
-(define (check-reset!)
- (set! check:correct 0)
- (set! check:failed '()))
-
-(define (check:add-correct!)
- (set! check:correct (+ check:correct 1)))
-
-(define (check:add-failed! expression actual-result expected-result)
- (set! check:failed
- (cons (list expression actual-result expected-result)
- check:failed)))
-
-(check-reset!)
-
-; -- reporting --
-
-(define (check:report-expression expression)
- (newline)
- (check:write expression)
- (display " => "))
-
-(define (check:report-actual-result actual-result)
- (check:write actual-result)
- (display " ; "))
-
-(define (check:report-correct cases)
- (display "correct")
- (if (not (= cases 1))
- (begin (display " (")
- (display cases)
- (display " cases checked)")))
- (newline))
-
-(define (check:report-failed expected-result)
- (display "*** failed ***")
- (newline)
- (display " ; expected result: ")
- (check:write expected-result)
- (newline))
-
-(define (check-report)
- (if (>= check:mode 1)
- (begin
- (newline)
- (display "; *** checks *** : ")
- (display check:correct)
- (display " correct, ")
- (display (length check:failed))
- (display " failed.")
- (if (or (null? check:failed) (<= check:mode 1))
- (newline)
- (let* ((w (car (reverse check:failed)))
- (expression (car w))
- (actual-result (cadr w))
- (expected-result (caddr w)))
- (display " First failed example:")
- (newline)
- (check:report-expression expression)
- (check:report-actual-result actual-result)
- (check:report-failed expected-result))))))
-
-(define (check-passed? expected-total-count)
- (and (= (length check:failed) 0)
- (= check:correct expected-total-count)))
-
-; -- simple checks --
-
-(define (check:proc expression thunk equal expected-result)
- (case check:mode
- ((0) #f)
- ((1)
- (let ((actual-result (thunk)))
- (if (equal actual-result expected-result)
- (check:add-correct!)
- (check:add-failed! expression actual-result expected-result))))
- ((10)
- (let ((actual-result (thunk)))
- (if (equal actual-result expected-result)
- (check:add-correct!)
- (begin
- (check:report-expression expression)
- (check:report-actual-result actual-result)
- (check:report-failed expected-result)
- (check:add-failed! expression actual-result expected-result)))))
- ((100)
- (check:report-expression expression)
- (let ((actual-result (thunk)))
- (check:report-actual-result actual-result)
- (if (equal actual-result expected-result)
- (begin (check:report-correct 1)
- (check:add-correct!))
- (begin (check:report-failed expected-result)
- (check:add-failed! expression
- actual-result
- expected-result)))))
- (else (error "unrecognized check:mode" check:mode)))
- (if #f #f))
-
-(define-syntax check
- (syntax-rules (=>)
- ((check expr => expected)
- (check expr (=> equal?) expected))
- ((check expr (=> equal) expected)
- (if (>= check:mode 1)
- (check:proc 'expr (lambda () expr) equal expected)))))
-
-; -- parametric checks --
-
-(define (check:proc-ec w)
- (let ((correct? (car w))
- (expression (cadr w))
- (actual-result (caddr w))
- (expected-result (cadddr w))
- (cases (car (cddddr w))))
- (if correct?
- (begin (if (>= check:mode 100)
- (begin (check:report-expression expression)
- (check:report-actual-result actual-result)
- (check:report-correct cases)))
- (check:add-correct!))
- (begin (if (>= check:mode 10)
- (begin (check:report-expression expression)
- (check:report-actual-result actual-result)
- (check:report-failed expected-result)))
- (check:add-failed! expression
- actual-result
- expected-result)))))
-
-(define-syntax check-ec:make
- (syntax-rules (=>)
- ((check-ec:make qualifiers expr (=> equal) expected (arg ...))
- (if (>= check:mode 1)
- (check:proc-ec
- (let ((cases 0))
- (let ((w (first-ec
- #f
- qualifiers
- (\:let equal-pred equal)
- (\:let expected-result expected)
- (\:let actual-result
- (let ((arg arg) ...) ; (*)
- expr))
- (begin (set! cases (+ cases 1)))
- (if (not (equal-pred actual-result expected-result)))
- (list (list 'let (list (list 'arg arg) ...) 'expr)
- actual-result
- expected-result
- cases))))
- (if w
- (cons #f w)
- (list #t
- '(check-ec qualifiers
- expr (=> equal)
- expected (arg ...))
- (if #f #f)
- (if #f #f)
- cases)))))))))
-
-; (*) is a compile-time check that (arg ...) is a list
-; of pairwise disjoint bound variables at this point.
-
-(define-syntax check-ec
- (syntax-rules (nested =>)
- ((check-ec expr => expected)
- (check-ec:make (nested) expr (=> equal?) expected ()))
- ((check-ec expr (=> equal) expected)
- (check-ec:make (nested) expr (=> equal) expected ()))
- ((check-ec expr => expected (arg ...))
- (check-ec:make (nested) expr (=> equal?) expected (arg ...)))
- ((check-ec expr (=> equal) expected (arg ...))
- (check-ec:make (nested) expr (=> equal) expected (arg ...)))
-
- ((check-ec qualifiers expr => expected)
- (check-ec:make qualifiers expr (=> equal?) expected ()))
- ((check-ec qualifiers expr (=> equal) expected)
- (check-ec:make qualifiers expr (=> equal) expected ()))
- ((check-ec qualifiers expr => expected (arg ...))
- (check-ec:make qualifiers expr (=> equal?) expected (arg ...)))
- ((check-ec qualifiers expr (=> equal) expected (arg ...))
- (check-ec:make qualifiers expr (=> equal) expected (arg ...)))
-
- ((check-ec (nested q1 ...) q etc ...)
- (check-ec (nested q1 ... q) etc ...))
- ((check-ec q1 q2 etc ...)
- (check-ec (nested q1 q2) etc ...))))
-(import (scheme base)
- (scheme eval)
- (scheme file)
- (srfi 1)
- (srfi 48)
- (srfi 64))
-
-(test-runner-current (test-runner-simple "tests.log"))
-
-(test-begin "SRFI")
-
-(for-each
- (lambda (n)
- (let ((srfi-n (string->symbol (format #f "srfi-~s" n)))
- (file-name (format #f "srfi-tests/srfi-~s.sld" n))
- (test-name (format #f "SRFI-~s" n)))
- (when (file-exists? file-name)
- (test-assert test-name
- (guard (err (else #f))
- (eval '(run-tests) (environment `(srfi-tests ,srfi-n))))))))
- (iota 200))
-
-(test-end "SRFI")
-
-(test-exit)
-;;; SRFI 13 string library reference implementation -*- Scheme -*-
-;;; Olin Shivers 7/2000
-;;;
-;;; Copyright (c) 1988-1994 Massachusetts Institute of Technology.
-;;; Copyright (c) 1998, 1999, 2000 Olin Shivers. All rights reserved.
-;;; The details of the copyrights appear at the end of the file. Short
-;;; summary: BSD-style open source.
-
-;;; Exports:
-;;; string-map string-map!
-;;; string-fold string-unfold
-;;; string-fold-right string-unfold-right
-;;; string-tabulate string-for-each string-for-each-index
-;;; string-every string-any
-;;; string-hash string-hash-ci
-;;; string-compare string-compare-ci
-;;; string= string< string> string<= string>= string<>
-;;; string-ci= string-ci< string-ci> string-ci<= string-ci>= string-ci<>
-;;; string-downcase string-upcase string-titlecase
-;;; string-downcase! string-upcase! string-titlecase!
-;;; string-take string-take-right
-;;; string-drop string-drop-right
-;;; string-pad string-pad-right
-;;; string-trim string-trim-right string-trim-both
-;;; string-filter string-delete
-;;; string-index string-index-right
-;;; string-skip string-skip-right
-;;; string-count
-;;; string-prefix-length string-prefix-length-ci
-;;; string-suffix-length string-suffix-length-ci
-;;; string-prefix? string-prefix-ci?
-;;; string-suffix? string-suffix-ci?
-;;; string-contains string-contains-ci
-;;; string-copy! substring/shared
-;;; string-reverse string-reverse! reverse-list->string
-;;; string-concatenate string-concatenate/shared string-concatenate-reverse
-;;; string-append/shared
-;;; xsubstring string-xcopy!
-;;; string-null?
-;;; string-join
-;;; string-tokenize
-;;; string-replace
-;;;
-;;; R5RS extended:
-;;; string->list string-copy string-fill!
-;;;
-;;; R5RS re-exports:
-;;; string? make-string string-length string-ref string-set!
-;;;
-;;; R5RS re-exports (also defined here but commented-out):
-;;; string string-append list->string
-;;;
-;;; Low-level routines:
-;;; make-kmp-restart-vector string-kmp-partial-search kmp-step
-;;; string-parse-start+end
-;;; string-parse-final-start+end
-;;; let-string-start+end
-;;; check-substring-spec
-;;; substring-spec-ok?
-
-;;; Imports
-;;; This is a fairly large library. While it was written for portability, you
-;;; must be aware of its dependencies in order to run it in a given scheme
-;;; implementation. Here is a complete list of the dependencies it has and the
-;;; assumptions it makes beyond stock R5RS Scheme:
-;;;
-;;; This code has the following non-R5RS dependencies:
-;;; - (RECEIVE (var ...) mv-exp body ...) multiple-value binding macro;
-;;;
-;;; - Various imports from the char-set library for the routines that can
-;;; take char-set arguments;
-;;;
-;;; - An n-ary ERROR procedure;
-;;;
-;;; - BITWISE-AND for the hash functions;
-;;;
-;;; - A simple CHECK-ARG procedure for checking parameter values; it is
-;;; (lambda (pred val proc)
-;;; (if (pred val) val (error "Bad arg" val pred proc)))
-;;;
-;;; - #\:OPTIONAL and LET-OPTIONALS* macros for parsing, defaulting &
-;;; type-checking optional parameters from a rest argument;
-;;;
-;;; - CHAR-CASED? and CHAR-TITLECASE for the STRING-TITLECASE &
-;;; STRING-TITLECASE! procedures. The former returns true iff a character is
-;;; one that has case distinctions; in ASCII it returns true on a-z and A-Z.
-;;; CHAR-TITLECASE is analagous to CHAR-UPCASE and CHAR-DOWNCASE. In ASCII &
-;;; Latin-1, it is the same as CHAR-UPCASE.
-;;;
-;;; The code depends upon a small set of core string primitives from R5RS:
-;;; MAKE-STRING STRING-REF STRING-SET! STRING? STRING-LENGTH SUBSTRING
-;;; (Actually, SUBSTRING is not a primitive, but we assume that an
-;;; implementation's native version is probably faster than one we could
-;;; define, so we import it from R5RS.)
-;;;
-;;; The code depends upon a small set of R5RS character primitives:
-;;; char? char=? char-ci=? char<? char-ci<?
-;;; char-upcase char-downcase
-;;; char->integer (for the hash functions)
-;;;
-;;; We assume the following:
-;;; - CHAR-DOWNCASE o CHAR-UPCASE = CHAR-DOWNCASE
-;;; - CHAR-CI=? is equivalent to
-;;; (lambda (c1 c2) (char=? (char-downcase (char-upcase c1))
-;;; (char-downcase (char-upcase c2))))
-;;; - CHAR-UPCASE, CHAR-DOWNCASE and CHAR-TITLECASE are locale-insensitive
-;;; and consistent with Unicode's 1-1 char-mapping spec.
-;;; These things are typically true, but if not, you would need to modify
-;;; the case-mapping and case-insensitive routines.
-
-;;; Enough introductory blather. On to the source code. (But see the end of
-;;; the file for further notes on porting & performance tuning.)
-
-
-;;; Support for START/END substring specs
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; This macro parses optional start/end arguments from arg lists, defaulting
-;;; them to 0/(string-length s), and checks them for correctness.
-
-(define-syntax let-string-start+end
- (syntax-rules ()
- ((let-string-start+end (start end) proc s-exp args-exp body ...)
- (receive (start end) (string-parse-final-start+end proc s-exp args-exp)
- body ...))
- ((let-string-start+end (start end rest) proc s-exp args-exp body ...)
- (receive (rest start end) (string-parse-start+end proc s-exp args-exp)
- body ...))))
-
-;;; This one parses out a *pair* of final start/end indices.
-;;; Not exported; for internal use.
-(define-syntax let-string-start+end2
- (syntax-rules ()
- ((l-s-s+e2 (start1 end1 start2 end2) proc s1 s2 args body ...)
- (let ((procv proc)) ; Make sure PROC is only evaluated once.
- (let-string-start+end (start1 end1 rest) procv s1 args
- (let-string-start+end (start2 end2) procv s2 rest
- body ...))))))
-
-
-;;; Returns three values: rest start end
-
-(define (string-parse-start+end proc s args)
- (if (not (string? s)) (error "Non-string value" proc s))
- (let ((slen (string-length s)))
- (if (pair? args)
-
- (let ((start (car args))
- (args (cdr args)))
- (if (and (integer? start) (exact? start) (>= start 0))
- (receive (end args)
- (if (pair? args)
- (let ((end (car args))
- (args (cdr args)))
- (if (and (integer? end) (exact? end) (<= end slen))
- (values end args)
- (error "Illegal substring END spec" proc end s)))
- (values slen args))
- (if (<= start end) (values args start end)
- (error "Illegal substring START/END spec"
- proc start end s)))
- (error "Illegal substring START spec" proc start s)))
-
- (values '() 0 slen))))
-
-(define (string-parse-final-start+end proc s args)
- (receive (rest start end) (string-parse-start+end proc s args)
- (if (pair? rest) (error "Extra arguments to procedure" proc rest)
- (values start end))))
-
-(define (substring-spec-ok? s start end)
- (and (string? s)
- (integer? start)
- (exact? start)
- (integer? end)
- (exact? end)
- (<= 0 start)
- (<= start end)
- (<= end (string-length s))))
-
-(define (check-substring-spec proc s start end)
- (if (not (substring-spec-ok? s start end))
- (error "Illegal substring spec." proc s start end)))
-
-
-;;; Defined by R5RS, so commented out here.
-;(define (string . chars)
-; (let* ((len (length chars))
-; (ans (make-string len)))
-; (do ((i 0 (+ i 1))
-; (chars chars (cdr chars)))
-; ((>= i len))
-; (string-set! ans i (car chars)))
-; ans))
-;
-;(define (string . chars) (string-unfold null? car cdr chars))
-
-
-
-;;; substring/shared S START [END]
-;;; string-copy S [START END]
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;;; All this goop is just arg parsing & checking surrounding a call to the
-;;; actual primitive, %SUBSTRING/SHARED.
-
-(define (substring/shared s start . maybe-end)
- (check-arg string? s substring/shared)
- (let ((slen (string-length s)))
- (check-arg (lambda (start) (and (integer? start) (exact? start) (<= 0 start)))
- start substring/shared)
- (%substring/shared s start
- (#\:optional maybe-end slen
- (lambda (end) (and (integer? end)
- (exact? end)
- (<= start end)
- (<= end slen)))))))
-
-;;; Split out so that other routines in this library can avoid arg-parsing
-;;; overhead for END parameter.
-(define (%substring/shared s start end)
- (if (and (zero? start) (= end (string-length s))) s
- (substring s start end)))
-
-(define (string-copy s . maybe-start+end)
- (let-string-start+end (start end) string-copy s maybe-start+end
- (substring s start end)))
-
-;This library uses the R5RS SUBSTRING, but doesn't export it.
-;Here is a definition, just for completeness.
-;(define (substring s start end)
-; (check-substring-spec substring s start end)
-; (let* ((slen (- end start))
-; (ans (make-string slen)))
-; (do ((i 0 (+ i 1))
-; (j start (+ j 1)))
-; ((>= i slen) ans)
-; (string-set! ans i (string-ref s j)))))
-
-;;; Basic iterators and other higher-order abstractions
-;;; (string-map proc s [start end])
-;;; (string-map! proc s [start end])
-;;; (string-fold kons knil s [start end])
-;;; (string-fold-right kons knil s [start end])
-;;; (string-unfold p f g seed [base make-final])
-;;; (string-unfold-right p f g seed [base make-final])
-;;; (string-for-each proc s [start end])
-;;; (string-for-each-index proc s [start end])
-;;; (string-every char-set/char/pred s [start end])
-;;; (string-any char-set/char/pred s [start end])
-;;; (string-tabulate proc len)
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; You want compiler support for high-level transforms on fold and unfold ops.
-;;; You'd at least like a lot of inlining for clients of these procedures.
-;;; Don't hold your breath.
-
-(define (string-map proc s . maybe-start+end)
- (check-arg procedure? proc string-map)
- (let-string-start+end (start end) string-map s maybe-start+end
- (%string-map proc s start end)))
-
-(define (%string-map proc s start end) ; Internal utility
- (let* ((len (- end start))
- (ans (make-string len)))
- (do ((i (- end 1) (- i 1))
- (j (- len 1) (- j 1)))
- ((< j 0))
- (string-set! ans j (proc (string-ref s i))))
- ans))
-
-(define (string-map! proc s . maybe-start+end)
- (check-arg procedure? proc string-map!)
- (let-string-start+end (start end) string-map! s maybe-start+end
- (%string-map! proc s start end)))
-
-(define (%string-map! proc s start end)
- (do ((i (- end 1) (- i 1)))
- ((< i start))
- (string-set! s i (proc (string-ref s i)))))
-
-(define (string-fold kons knil s . maybe-start+end)
- (check-arg procedure? kons string-fold)
- (let-string-start+end (start end) string-fold s maybe-start+end
- (let lp ((v knil) (i start))
- (if (< i end) (lp (kons (string-ref s i) v) (+ i 1))
- v))))
-
-(define (string-fold-right kons knil s . maybe-start+end)
- (check-arg procedure? kons string-fold-right)
- (let-string-start+end (start end) string-fold-right s maybe-start+end
- (let lp ((v knil) (i (- end 1)))
- (if (>= i start) (lp (kons (string-ref s i) v) (- i 1))
- v))))
-
-;;; (string-unfold p f g seed [base make-final])
-;;; This is the fundamental constructor for strings.
-;;; - G is used to generate a series of "seed" values from the initial seed:
-;;; SEED, (G SEED), (G^2 SEED), (G^3 SEED), ...
-;;; - P tells us when to stop -- when it returns true when applied to one
-;;; of these seed values.
-;;; - F maps each seed value to the corresponding character
-;;; in the result string. These chars are assembled into the
-;;; string in a left-to-right order.
-;;; - BASE is the optional initial/leftmost portion of the constructed string;
-;;; it defaults to the empty string "".
-;;; - MAKE-FINAL is applied to the terminal seed value (on which P returns
-;;; true) to produce the final/rightmost portion of the constructed string.
-;;; It defaults to (LAMBDA (X) "").
-;;;
-;;; In other words, the following (simple, inefficient) definition holds:
-;;; (define (string-unfold p f g seed base make-final)
-;;; (string-append base
-;;; (let recur ((seed seed))
-;;; (if (p seed) (make-final seed)
-;;; (string-append (string (f seed))
-;;; (recur (g seed)))))))
-;;;
-;;; STRING-UNFOLD is a fairly powerful constructor -- you can use it to
-;;; reverse a string, copy a string, convert a list to a string, read
-;;; a port into a string, and so forth. Examples:
-;;; (port->string port) =
-;;; (string-unfold (compose eof-object? peek-char)
-;;; read-char values port)
-;;;
-;;; (list->string lis) = (string-unfold null? car cdr lis)
-;;;
-;;; (tabulate-string f size) = (string-unfold (lambda (i) (= i size)) f add1 0)
-
-;;; A problem with the following simple formulation is that it pushes one
-;;; stack frame for every char in the result string -- an issue if you are
-;;; using it to read a 100kchar string. So we don't use it -- but I include
-;;; it to give a clear, straightforward description of what the function
-;;; does.
-
-;(define (string-unfold p f g seed base make-final)
-; (let ((ans (let recur ((seed seed) (i (string-length base)))
-; (if (p seed)
-; (let* ((final (make-final seed))
-; (ans (make-string (+ i (string-length final)))))
-; (string-copy! ans i final)
-; ans)
-;
-; (let* ((c (f seed))
-; (s (recur (g seed) (+ i 1))))
-; (string-set! s i c)
-; s)))))
-; (string-copy! ans 0 base)
-; ans))
-
-;;; The strategy is to allocate a series of chunks into which we stash the
-;;; chars as we generate them. Chunk size goes up in powers of two starting
-;;; with 40 and levelling out at 4k, i.e.
-;;; 40 40 80 160 320 640 1280 2560 4096 4096 4096 4096 4096...
-;;; This should work pretty well for short strings, 1-line (80 char) strings,
-;;; and longer ones. When done, we allocate an answer string and copy the
-;;; chars over from the chunk buffers.
-
-(define (string-unfold p f g seed . base+make-final)
- (check-arg procedure? p string-unfold)
- (check-arg procedure? f string-unfold)
- (check-arg procedure? g string-unfold)
- (let-optionals* base+make-final
- ((base "" (string? base))
- (make-final (lambda (x) "") (procedure? make-final)))
- (let lp ((chunks '()) ; Previously filled chunks
- (nchars 0) ; Number of chars in CHUNKS
- (chunk (make-string 40)) ; Current chunk into which we write
- (chunk-len 40)
- (i 0) ; Number of chars written into CHUNK
- (seed seed))
- (let lp2 ((i i) (seed seed))
- (if (not (p seed))
- (let ((c (f seed))
- (seed (g seed)))
- (if (< i chunk-len)
- (begin (string-set! chunk i c)
- (lp2 (+ i 1) seed))
-
- (let* ((nchars2 (+ chunk-len nchars))
- (chunk-len2 (min 4096 nchars2))
- (new-chunk (make-string chunk-len2)))
- (string-set! new-chunk 0 c)
- (lp (cons chunk chunks) (+ nchars chunk-len)
- new-chunk chunk-len2 1 seed))))
-
- ;; We're done. Make the answer string & install the bits.
- (let* ((final (make-final seed))
- (flen (string-length final))
- (base-len (string-length base))
- (j (+ base-len nchars i))
- (ans (make-string (+ j flen))))
- (%string-copy! ans j final 0 flen) ; Install FINAL.
- (let ((j (- j i)))
- (%string-copy! ans j chunk 0 i) ; Install CHUNK[0,I).
- (let lp ((j j) (chunks chunks)) ; Install CHUNKS.
- (if (pair? chunks)
- (let* ((chunk (car chunks))
- (chunks (cdr chunks))
- (chunk-len (string-length chunk))
- (j (- j chunk-len)))
- (%string-copy! ans j chunk 0 chunk-len)
- (lp j chunks)))))
- (%string-copy! ans 0 base 0 base-len) ; Install BASE.
- ans))))))
-
-(define (string-unfold-right p f g seed . base+make-final)
- (let-optionals* base+make-final
- ((base "" (string? base))
- (make-final (lambda (x) "") (procedure? make-final)))
- (let lp ((chunks '()) ; Previously filled chunks
- (nchars 0) ; Number of chars in CHUNKS
- (chunk (make-string 40)) ; Current chunk into which we write
- (chunk-len 40)
- (i 40) ; Number of chars available in CHUNK
- (seed seed))
- (let lp2 ((i i) (seed seed)) ; Fill up CHUNK from right
- (if (not (p seed)) ; to left.
- (let ((c (f seed))
- (seed (g seed)))
- (if (> i 0)
- (let ((i (- i 1)))
- (string-set! chunk i c)
- (lp2 i seed))
-
- (let* ((nchars2 (+ chunk-len nchars))
- (chunk-len2 (min 4096 nchars2))
- (new-chunk (make-string chunk-len2))
- (i (- chunk-len2 1)))
- (string-set! new-chunk i c)
- (lp (cons chunk chunks) (+ nchars chunk-len)
- new-chunk chunk-len2 i seed))))
-
- ;; We're done. Make the answer string & install the bits.
- (let* ((final (make-final seed))
- (flen (string-length final))
- (base-len (string-length base))
- (chunk-used (- chunk-len i))
- (j (+ base-len nchars chunk-used))
- (ans (make-string (+ j flen))))
- (%string-copy! ans 0 final 0 flen) ; Install FINAL.
- (%string-copy! ans flen chunk i chunk-len); Install CHUNK[I,).
- (let lp ((j (+ flen chunk-used)) ; Install CHUNKS.
- (chunks chunks))
- (if (pair? chunks)
- (let* ((chunk (car chunks))
- (chunks (cdr chunks))
- (chunk-len (string-length chunk)))
- (%string-copy! ans j chunk 0 chunk-len)
- (lp (+ j chunk-len) chunks))
- (%string-copy! ans j base 0 base-len))); Install BASE.
- ans))))))
-
-
-(define (string-for-each proc s . maybe-start+end)
- (check-arg procedure? proc string-for-each)
- (let-string-start+end (start end) string-for-each s maybe-start+end
- (let lp ((i start))
- (if (< i end)
- (begin (proc (string-ref s i))
- (lp (+ i 1)))))))
-
-(define (string-for-each-index proc s . maybe-start+end)
- (check-arg procedure? proc string-for-each-index)
- (let-string-start+end (start end) string-for-each-index s maybe-start+end
- (let lp ((i start))
- (if (< i end) (begin (proc i) (lp (+ i 1)))))))
-
-(define (string-every criterion s . maybe-start+end)
- (let-string-start+end (start end) string-every s maybe-start+end
- (cond ((char? criterion)
- (let lp ((i start))
- (or (>= i end)
- (and (char=? criterion (string-ref s i))
- (lp (+ i 1))))))
-
- ((char-set? criterion)
- (let lp ((i start))
- (or (>= i end)
- (and (char-set-contains? criterion (string-ref s i))
- (lp (+ i 1))))))
-
- ((procedure? criterion) ; Slightly funky loop so that
- (or (= start end) ; final (PRED S[END-1]) call
- (let lp ((i start)) ; is a tail call.
- (let ((c (string-ref s i))
- (i1 (+ i 1)))
- (if (= i1 end) (criterion c) ; Tail call.
- (and (criterion c) (lp i1)))))))
-
- (else (error "Second param is neither char-set, char, or predicate procedure."
- string-every criterion)))))
-
-
-(define (string-any criterion s . maybe-start+end)
- (let-string-start+end (start end) string-any s maybe-start+end
- (cond ((char? criterion)
- (let lp ((i start))
- (and (< i end)
- (or (char=? criterion (string-ref s i))
- (lp (+ i 1))))))
-
- ((char-set? criterion)
- (let lp ((i start))
- (and (< i end)
- (or (char-set-contains? criterion (string-ref s i))
- (lp (+ i 1))))))
-
- ((procedure? criterion) ; Slightly funky loop so that
- (and (< start end) ; final (PRED S[END-1]) call
- (let lp ((i start)) ; is a tail call.
- (let ((c (string-ref s i))
- (i1 (+ i 1)))
- (if (= i1 end) (criterion c) ; Tail call
- (or (criterion c) (lp i1)))))))
-
- (else (error "Second param is neither char-set, char, or predicate procedure."
- string-any criterion)))))
-
-
-(define (string-tabulate proc len)
- (check-arg procedure? proc string-tabulate)
- (check-arg (lambda (val) (and (integer? val) (exact? val) (<= 0 val)))
- len string-tabulate)
- (let ((s (make-string len)))
- (do ((i (- len 1) (- i 1)))
- ((< i 0))
- (string-set! s i (proc i)))
- s))
-
-
-
-;;; string-prefix-length[-ci] s1 s2 [start1 end1 start2 end2]
-;;; string-suffix-length[-ci] s1 s2 [start1 end1 start2 end2]
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; Find the length of the common prefix/suffix.
-;;; It is not required that the two substrings passed be of equal length.
-;;; This was microcode in MIT Scheme -- a very tightly bummed primitive.
-;;; %STRING-PREFIX-LENGTH is the core routine of all string-comparisons,
-;;; so should be as tense as possible.
-
-(define (%string-prefix-length s1 start1 end1 s2 start2 end2)
- (let* ((delta (min (- end1 start1) (- end2 start2)))
- (end1 (+ start1 delta)))
-
- (if (and (eq? s1 s2) (= start1 start2)) ; EQ fast path
- delta
-
- (let lp ((i start1) (j start2)) ; Regular path
- (if (or (>= i end1)
- (not (char=? (string-ref s1 i)
- (string-ref s2 j))))
- (- i start1)
- (lp (+ i 1) (+ j 1)))))))
-
-(define (%string-suffix-length s1 start1 end1 s2 start2 end2)
- (let* ((delta (min (- end1 start1) (- end2 start2)))
- (start1 (- end1 delta)))
-
- (if (and (eq? s1 s2) (= end1 end2)) ; EQ fast path
- delta
-
- (let lp ((i (- end1 1)) (j (- end2 1))) ; Regular path
- (if (or (< i start1)
- (not (char=? (string-ref s1 i)
- (string-ref s2 j))))
- (- (- end1 i) 1)
- (lp (- i 1) (- j 1)))))))
-
-(define (%string-prefix-length-ci s1 start1 end1 s2 start2 end2)
- (let* ((delta (min (- end1 start1) (- end2 start2)))
- (end1 (+ start1 delta)))
-
- (if (and (eq? s1 s2) (= start1 start2)) ; EQ fast path
- delta
-
- (let lp ((i start1) (j start2)) ; Regular path
- (if (or (>= i end1)
- (not (char-ci=? (string-ref s1 i)
- (string-ref s2 j))))
- (- i start1)
- (lp (+ i 1) (+ j 1)))))))
-
-(define (%string-suffix-length-ci s1 start1 end1 s2 start2 end2)
- (let* ((delta (min (- end1 start1) (- end2 start2)))
- (start1 (- end1 delta)))
-
- (if (and (eq? s1 s2) (= end1 end2)) ; EQ fast path
- delta
-
- (let lp ((i (- end1 1)) (j (- end2 1))) ; Regular path
- (if (or (< i start1)
- (not (char-ci=? (string-ref s1 i)
- (string-ref s2 j))))
- (- (- end1 i) 1)
- (lp (- i 1) (- j 1)))))))
-
-
-(define (string-prefix-length s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string-prefix-length s1 s2 maybe-starts+ends
- (%string-prefix-length s1 start1 end1 s2 start2 end2)))
-
-(define (string-suffix-length s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string-suffix-length s1 s2 maybe-starts+ends
- (%string-suffix-length s1 start1 end1 s2 start2 end2)))
-
-(define (string-prefix-length-ci s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string-prefix-length-ci s1 s2 maybe-starts+ends
- (%string-prefix-length-ci s1 start1 end1 s2 start2 end2)))
-
-(define (string-suffix-length-ci s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string-suffix-length-ci s1 s2 maybe-starts+ends
- (%string-suffix-length-ci s1 start1 end1 s2 start2 end2)))
-
-
-;;; string-prefix? s1 s2 [start1 end1 start2 end2]
-;;; string-suffix? s1 s2 [start1 end1 start2 end2]
-;;; string-prefix-ci? s1 s2 [start1 end1 start2 end2]
-;;; string-suffix-ci? s1 s2 [start1 end1 start2 end2]
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; These are all simple derivatives of the previous counting funs.
-
-(define (string-prefix? s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string-prefix? s1 s2 maybe-starts+ends
- (%string-prefix? s1 start1 end1 s2 start2 end2)))
-
-(define (string-suffix? s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string-suffix? s1 s2 maybe-starts+ends
- (%string-suffix? s1 start1 end1 s2 start2 end2)))
-
-(define (string-prefix-ci? s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string-prefix-ci? s1 s2 maybe-starts+ends
- (%string-prefix-ci? s1 start1 end1 s2 start2 end2)))
-
-(define (string-suffix-ci? s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string-suffix-ci? s1 s2 maybe-starts+ends
- (%string-suffix-ci? s1 start1 end1 s2 start2 end2)))
-
-
-;;; Here are the internal routines that do the real work.
-
-(define (%string-prefix? s1 start1 end1 s2 start2 end2)
- (let ((len1 (- end1 start1)))
- (and (<= len1 (- end2 start2)) ; Quick check
- (= (%string-prefix-length s1 start1 end1
- s2 start2 end2)
- len1))))
-
-(define (%string-suffix? s1 start1 end1 s2 start2 end2)
- (let ((len1 (- end1 start1)))
- (and (<= len1 (- end2 start2)) ; Quick check
- (= len1 (%string-suffix-length s1 start1 end1
- s2 start2 end2)))))
-
-(define (%string-prefix-ci? s1 start1 end1 s2 start2 end2)
- (let ((len1 (- end1 start1)))
- (and (<= len1 (- end2 start2)) ; Quick check
- (= len1 (%string-prefix-length-ci s1 start1 end1
- s2 start2 end2)))))
-
-(define (%string-suffix-ci? s1 start1 end1 s2 start2 end2)
- (let ((len1 (- end1 start1)))
- (and (<= len1 (- end2 start2)) ; Quick check
- (= len1 (%string-suffix-length-ci s1 start1 end1
- s2 start2 end2)))))
-
-
-;;; string-compare s1 s2 proc< proc= proc> [start1 end1 start2 end2]
-;;; string-compare-ci s1 s2 proc< proc= proc> [start1 end1 start2 end2]
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; Primitive string-comparison functions.
-;;; Continuation order is different from MIT Scheme.
-;;; Continuations are applied to s1's mismatch index;
-;;; in the case of equality, this is END1.
-
-(define (%string-compare s1 start1 end1 s2 start2 end2
- proc< proc= proc>)
- (let ((size1 (- end1 start1))
- (size2 (- end2 start2)))
- (let ((match (%string-prefix-length s1 start1 end1 s2 start2 end2)))
- (if (= match size1)
- ((if (= match size2) proc= proc<) end1)
- ((if (= match size2)
- proc>
- (if (char<? (string-ref s1 (+ start1 match))
- (string-ref s2 (+ start2 match)))
- proc< proc>))
- (+ match start1))))))
-
-(define (%string-compare-ci s1 start1 end1 s2 start2 end2
- proc< proc= proc>)
- (let ((size1 (- end1 start1))
- (size2 (- end2 start2)))
- (let ((match (%string-prefix-length-ci s1 start1 end1 s2 start2 end2)))
- (if (= match size1)
- ((if (= match size2) proc= proc<) end1)
- ((if (= match size2) proc>
- (if (char-ci<? (string-ref s1 (+ start1 match))
- (string-ref s2 (+ start2 match)))
- proc< proc>))
- (+ start1 match))))))
-
-(define (string-compare s1 s2 proc< proc= proc> . maybe-starts+ends)
- (check-arg procedure? proc< string-compare)
- (check-arg procedure? proc= string-compare)
- (check-arg procedure? proc> string-compare)
- (let-string-start+end2 (start1 end1 start2 end2)
- string-compare s1 s2 maybe-starts+ends
- (%string-compare s1 start1 end1 s2 start2 end2 proc< proc= proc>)))
-
-(define (string-compare-ci s1 s2 proc< proc= proc> . maybe-starts+ends)
- (check-arg procedure? proc< string-compare-ci)
- (check-arg procedure? proc= string-compare-ci)
- (check-arg procedure? proc> string-compare-ci)
- (let-string-start+end2 (start1 end1 start2 end2)
- string-compare-ci s1 s2 maybe-starts+ends
- (%string-compare-ci s1 start1 end1 s2 start2 end2 proc< proc= proc>)))
-
-
-
-;;; string= string<> string-ci= string-ci<>
-;;; string< string> string-ci< string-ci>
-;;; string<= string>= string-ci<= string-ci>=
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; Simple definitions in terms of the previous comparison funs.
-;;; I sure hope the %STRING-COMPARE calls get integrated.
-
-(define (string= s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string= s1 s2 maybe-starts+ends
- (and (= (- end1 start1) (- end2 start2)) ; Quick filter
- (or (and (eq? s1 s2) (= start1 start2)) ; Fast path
- (%string-compare s1 start1 end1 s2 start2 end2 ; Real test
- (lambda (i) #f)
- values
- (lambda (i) #f))))))
-
-(define (string<> s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string<> s1 s2 maybe-starts+ends
- (or (not (= (- end1 start1) (- end2 start2))) ; Fast path
- (and (not (and (eq? s1 s2) (= start1 start2))) ; Quick filter
- (%string-compare s1 start1 end1 s2 start2 end2 ; Real test
- values
- (lambda (i) #f)
- values)))))
-
-(define (string< s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string< s1 s2 maybe-starts+ends
- (if (and (eq? s1 s2) (= start1 start2)) ; Fast path
- (< end1 end2)
-
- (%string-compare s1 start1 end1 s2 start2 end2 ; Real test
- values
- (lambda (i) #f)
- (lambda (i) #f)))))
-
-(define (string> s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string> s1 s2 maybe-starts+ends
- (if (and (eq? s1 s2) (= start1 start2)) ; Fast path
- (> end1 end2)
-
- (%string-compare s1 start1 end1 s2 start2 end2 ; Real test
- (lambda (i) #f)
- (lambda (i) #f)
- values))))
-
-(define (string<= s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string<= s1 s2 maybe-starts+ends
- (if (and (eq? s1 s2) (= start1 start2)) ; Fast path
- (<= end1 end2)
-
- (%string-compare s1 start1 end1 s2 start2 end2 ; Real test
- values
- values
- (lambda (i) #f)))))
-
-(define (string>= s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string>= s1 s2 maybe-starts+ends
- (if (and (eq? s1 s2) (= start1 start2)) ; Fast path
- (>= end1 end2)
-
- (%string-compare s1 start1 end1 s2 start2 end2 ; Real test
- (lambda (i) #f)
- values
- values))))
-
-(define (string-ci= s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string-ci= s1 s2 maybe-starts+ends
- (and (= (- end1 start1) (- end2 start2)) ; Quick filter
- (or (and (eq? s1 s2) (= start1 start2)) ; Fast path
- (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test
- (lambda (i) #f)
- values
- (lambda (i) #f))))))
-
-(define (string-ci<> s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string-ci<> s1 s2 maybe-starts+ends
- (or (not (= (- end1 start1) (- end2 start2))) ; Fast path
- (and (not (and (eq? s1 s2) (= start1 start2))) ; Quick filter
- (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test
- values
- (lambda (i) #f)
- values)))))
-
-(define (string-ci< s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string-ci< s1 s2 maybe-starts+ends
- (if (and (eq? s1 s2) (= start1 start2)) ; Fast path
- (< end1 end2)
-
- (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test
- values
- (lambda (i) #f)
- (lambda (i) #f)))))
-
-(define (string-ci> s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string-ci> s1 s2 maybe-starts+ends
- (if (and (eq? s1 s2) (= start1 start2)) ; Fast path
- (> end1 end2)
-
- (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test
- (lambda (i) #f)
- (lambda (i) #f)
- values))))
-
-(define (string-ci<= s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string-ci<= s1 s2 maybe-starts+ends
- (if (and (eq? s1 s2) (= start1 start2)) ; Fast path
- (<= end1 end2)
-
- (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test
- values
- values
- (lambda (i) #f)))))
-
-(define (string-ci>= s1 s2 . maybe-starts+ends)
- (let-string-start+end2 (start1 end1 start2 end2)
- string-ci>= s1 s2 maybe-starts+ends
- (if (and (eq? s1 s2) (= start1 start2)) ; Fast path
- (>= end1 end2)
-
- (%string-compare-ci s1 start1 end1 s2 start2 end2 ; Real test
- (lambda (i) #f)
- values
- values))))
-
-
-;;; Hash
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; Compute (c + 37 c + 37^2 c + ...) modulo BOUND, with sleaze thrown in
-;;; to keep the intermediate values small. (We do the calculation with just
-;;; enough bits to represent BOUND, masking off high bits at each step in
-;;; calculation. If this screws up any important properties of the hash
-;;; function I'd like to hear about it. -Olin)
-;;;
-;;; If you keep BOUND small enough, the intermediate calculations will
-;;; always be fixnums. How small is dependent on the underlying Scheme system;
-;;; we use a default BOUND of 2^22 = 4194304, which should hack it in
-;;; Schemes that give you at least 29 signed bits for fixnums. The core
-;;; calculation that you don't want to overflow is, worst case,
-;;; (+ 65535 (* 37 (- bound 1)))
-;;; where 65535 is the max character code. Choose the default BOUND to be the
-;;; biggest power of two that won't cause this expression to fixnum overflow,
-;;; and everything will be copacetic.
-
-(define (%string-hash s char->int bound start end)
- (let ((iref (lambda (s i) (char->int (string-ref s i))))
- ;; Compute a 111...1 mask that will cover BOUND-1:
- (mask (let lp ((i #x10000)) ; Let's skip first 16 iterations, eh?
- (if (>= i bound) (- i 1) (lp (+ i i))))))
- (let lp ((i start) (ans 0))
- (if (>= i end) (modulo ans bound)
- (lp (+ i 1) (bitwise-and mask (+ (* 37 ans) (iref s i))))))))
-
-(define (string-hash s . maybe-bound+start+end)
- (let-optionals* maybe-bound+start+end ((bound 4194304 (and (integer? bound)
- (exact? bound)
- (<= 0 bound)))
- rest)
- (let ((bound (if (zero? bound) 4194304 bound))) ; 0 means default.
- (let-string-start+end (start end) string-hash s rest
- (%string-hash s char->integer bound start end)))))
-
-(define (string-hash-ci s . maybe-bound+start+end)
- (let-optionals* maybe-bound+start+end ((bound 4194304 (and (integer? bound)
- (exact? bound)
- (<= 0 bound)))
- rest)
- (let ((bound (if (zero? bound) 4194304 bound))) ; 0 means default.
- (let-string-start+end (start end) string-hash-ci s rest
- (%string-hash s (lambda (c) (char->integer (char-downcase c)))
- bound start end)))))
-
-;;; Case hacking
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; string-upcase s [start end]
-;;; string-upcase! s [start end]
-;;; string-downcase s [start end]
-;;; string-downcase! s [start end]
-;;;
-;;; string-titlecase s [start end]
-;;; string-titlecase! s [start end]
-;;; Capitalize every contiguous alpha sequence: capitalise
-;;; first char, lowercase rest.
-
-(define (string-upcase s . maybe-start+end)
- (let-string-start+end (start end) string-upcase s maybe-start+end
- (%string-map char-upcase s start end)))
-
-(define (string-upcase! s . maybe-start+end)
- (let-string-start+end (start end) string-upcase! s maybe-start+end
- (%string-map! char-upcase s start end)))
-
-(define (string-downcase s . maybe-start+end)
- (let-string-start+end (start end) string-downcase s maybe-start+end
- (%string-map char-downcase s start end)))
-
-(define (string-downcase! s . maybe-start+end)
- (let-string-start+end (start end) string-downcase! s maybe-start+end
- (%string-map! char-downcase s start end)))
-
-(define (%string-titlecase! s start end)
- (let lp ((i start))
- (cond ((string-index s char-cased? i end) =>
- (lambda (i)
- (string-set! s i (char-titlecase (string-ref s i)))
- (let ((i1 (+ i 1)))
- (cond ((string-skip s char-cased? i1 end) =>
- (lambda (j)
- (string-downcase! s i1 j)
- (lp (+ j 1))))
- (else (string-downcase! s i1 end)))))))))
-
-(define (string-titlecase! s . maybe-start+end)
- (let-string-start+end (start end) string-titlecase! s maybe-start+end
- (%string-titlecase! s start end)))
-
-(define (string-titlecase s . maybe-start+end)
- (let-string-start+end (start end) string-titlecase! s maybe-start+end
- (let ((ans (substring s start end)))
- (%string-titlecase! ans 0 (- end start))
- ans)))
-
-
-;;; Cutting & pasting strings
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; string-take string nchars
-;;; string-drop string nchars
-;;;
-;;; string-take-right string nchars
-;;; string-drop-right string nchars
-;;;
-;;; string-pad string k [char start end]
-;;; string-pad-right string k [char start end]
-;;;
-;;; string-trim string [char/char-set/pred start end]
-;;; string-trim-right string [char/char-set/pred start end]
-;;; string-trim-both string [char/char-set/pred start end]
-;;;
-;;; These trimmers invert the char-set meaning from MIT Scheme -- you
-;;; say what you want to trim.
-
-(define (string-take s n)
- (check-arg string? s string-take)
- (check-arg (lambda (val) (and (integer? n) (exact? n)
- (<= 0 n (string-length s))))
- n string-take)
- (%substring/shared s 0 n))
-
-(define (string-take-right s n)
- (check-arg string? s string-take-right)
- (let ((len (string-length s)))
- (check-arg (lambda (val) (and (integer? n) (exact? n) (<= 0 n len)))
- n string-take-right)
- (%substring/shared s (- len n) len)))
-
-(define (string-drop s n)
- (check-arg string? s string-drop)
- (let ((len (string-length s)))
- (check-arg (lambda (val) (and (integer? n) (exact? n) (<= 0 n len)))
- n string-drop)
- (%substring/shared s n len)))
-
-(define (string-drop-right s n)
- (check-arg string? s string-drop-right)
- (let ((len (string-length s)))
- (check-arg (lambda (val) (and (integer? n) (exact? n) (<= 0 n len)))
- n string-drop-right)
- (%substring/shared s 0 (- len n))))
-
-
-(define (string-trim s . criterion+start+end)
- (let-optionals* criterion+start+end ((criterion char-set:whitespace) rest)
- (let-string-start+end (start end) string-trim s rest
- (cond ((string-skip s criterion start end) =>
- (lambda (i) (%substring/shared s i end)))
- (else "")))))
-
-(define (string-trim-right s . criterion+start+end)
- (let-optionals* criterion+start+end ((criterion char-set:whitespace) rest)
- (let-string-start+end (start end) string-trim-right s rest
- (cond ((string-skip-right s criterion start end) =>
- (lambda (i) (%substring/shared s start (+ 1 i))))
- (else "")))))
-
-(define (string-trim-both s . criterion+start+end)
- (let-optionals* criterion+start+end ((criterion char-set:whitespace) rest)
- (let-string-start+end (start end) string-trim-both s rest
- (cond ((string-skip s criterion start end) =>
- (lambda (i)
- (%substring/shared s i (+ 1 (string-skip-right s criterion i end)))))
- (else "")))))
-
-
-(define (string-pad-right s n . char+start+end)
- (let-optionals* char+start+end ((char #\space (char? char)) rest)
- (let-string-start+end (start end) string-pad-right s rest
- (check-arg (lambda (n) (and (integer? n) (exact? n) (<= 0 n)))
- n string-pad-right)
- (let ((len (- end start)))
- (if (<= n len)
- (%substring/shared s start (+ start n))
- (let ((ans (make-string n char)))
- (%string-copy! ans 0 s start end)
- ans))))))
-
-(define (string-pad s n . char+start+end)
- (let-optionals* char+start+end ((char #\space (char? char)) rest)
- (let-string-start+end (start end) string-pad s rest
- (check-arg (lambda (n) (and (integer? n) (exact? n) (<= 0 n)))
- n string-pad)
- (let ((len (- end start)))
- (if (<= n len)
- (%substring/shared s (- end n) end)
- (let ((ans (make-string n char)))
- (%string-copy! ans (- n len) s start end)
- ans))))))
-
-
-
-;;; Filtering strings
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; string-delete char/char-set/pred string [start end]
-;;; string-filter char/char-set/pred string [start end]
-;;;
-;;; If the criterion is a char or char-set, we scan the string twice with
-;;; string-fold -- once to determine the length of the result string,
-;;; and once to do the filtered copy.
-;;; If the criterion is a predicate, we don't do this double-scan strategy,
-;;; because the predicate might have side-effects or be very expensive to
-;;; compute. So we preallocate a temp buffer pessimistically, and only do
-;;; one scan over S. This is likely to be faster and more space-efficient
-;;; than consing a list.
-
-(define (string-delete criterion s . maybe-start+end)
- (let-string-start+end (start end) string-delete s maybe-start+end
- (if (procedure? criterion)
- (let* ((slen (- end start))
- (temp (make-string slen))
- (ans-len (string-fold (lambda (c i)
- (if (criterion c) i
- (begin (string-set! temp i c)
- (+ i 1))))
- 0 s start end)))
- (if (= ans-len slen) temp (substring temp 0 ans-len)))
-
- (let* ((cset (cond ((char-set? criterion) criterion)
- ((char? criterion) (char-set criterion))
- (else (error "string-delete criterion not predicate, char or char-set" criterion))))
- (len (string-fold (lambda (c i) (if (char-set-contains? cset c)
- i
- (+ i 1)))
- 0 s start end))
- (ans (make-string len)))
- (string-fold (lambda (c i) (if (char-set-contains? cset c)
- i
- (begin (string-set! ans i c)
- (+ i 1))))
- 0 s start end)
- ans))))
-
-(define (string-filter criterion s . maybe-start+end)
- (let-string-start+end (start end) string-filter s maybe-start+end
- (if (procedure? criterion)
- (let* ((slen (- end start))
- (temp (make-string slen))
- (ans-len (string-fold (lambda (c i)
- (if (criterion c)
- (begin (string-set! temp i c)
- (+ i 1))
- i))
- 0 s start end)))
- (if (= ans-len slen) temp (substring temp 0 ans-len)))
-
- (let* ((cset (cond ((char-set? criterion) criterion)
- ((char? criterion) (char-set criterion))
- (else (error "string-delete criterion not predicate, char or char-set" criterion))))
-
- (len (string-fold (lambda (c i) (if (char-set-contains? cset c)
- (+ i 1)
- i))
- 0 s start end))
- (ans (make-string len)))
- (string-fold (lambda (c i) (if (char-set-contains? cset c)
- (begin (string-set! ans i c)
- (+ i 1))
- i))
- 0 s start end)
- ans))))
-
-
-;;; String search
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; string-index string char/char-set/pred [start end]
-;;; string-index-right string char/char-set/pred [start end]
-;;; string-skip string char/char-set/pred [start end]
-;;; string-skip-right string char/char-set/pred [start end]
-;;; string-count string char/char-set/pred [start end]
-;;; There's a lot of replicated code here for efficiency.
-;;; For example, the char/char-set/pred discrimination has
-;;; been lifted above the inner loop of each proc.
-
-(define (string-index str criterion . maybe-start+end)
- (let-string-start+end (start end) string-index str maybe-start+end
- (cond ((char? criterion)
- (let lp ((i start))
- (and (< i end)
- (if (char=? criterion (string-ref str i)) i
- (lp (+ i 1))))))
- ((char-set? criterion)
- (let lp ((i start))
- (and (< i end)
- (if (char-set-contains? criterion (string-ref str i)) i
- (lp (+ i 1))))))
- ((procedure? criterion)
- (let lp ((i start))
- (and (< i end)
- (if (criterion (string-ref str i)) i
- (lp (+ i 1))))))
- (else (error "Second param is neither char-set, char, or predicate procedure."
- string-index criterion)))))
-
-(define (string-index-right str criterion . maybe-start+end)
- (let-string-start+end (start end) string-index-right str maybe-start+end
- (cond ((char? criterion)
- (let lp ((i (- end 1)))
- (and (>= i start)
- (if (char=? criterion (string-ref str i)) i
- (lp (- i 1))))))
- ((char-set? criterion)
- (let lp ((i (- end 1)))
- (and (>= i start)
- (if (char-set-contains? criterion (string-ref str i)) i
- (lp (- i 1))))))
- ((procedure? criterion)
- (let lp ((i (- end 1)))
- (and (>= i start)
- (if (criterion (string-ref str i)) i
- (lp (- i 1))))))
- (else (error "Second param is neither char-set, char, or predicate procedure."
- string-index-right criterion)))))
-
-(define (string-skip str criterion . maybe-start+end)
- (let-string-start+end (start end) string-skip str maybe-start+end
- (cond ((char? criterion)
- (let lp ((i start))
- (and (< i end)
- (if (char=? criterion (string-ref str i))
- (lp (+ i 1))
- i))))
- ((char-set? criterion)
- (let lp ((i start))
- (and (< i end)
- (if (char-set-contains? criterion (string-ref str i))
- (lp (+ i 1))
- i))))
- ((procedure? criterion)
- (let lp ((i start))
- (and (< i end)
- (if (criterion (string-ref str i)) (lp (+ i 1))
- i))))
- (else (error "Second param is neither char-set, char, or predicate procedure."
- string-skip criterion)))))
-
-(define (string-skip-right str criterion . maybe-start+end)
- (let-string-start+end (start end) string-skip-right str maybe-start+end
- (cond ((char? criterion)
- (let lp ((i (- end 1)))
- (and (>= i start)
- (if (char=? criterion (string-ref str i))
- (lp (- i 1))
- i))))
- ((char-set? criterion)
- (let lp ((i (- end 1)))
- (and (>= i start)
- (if (char-set-contains? criterion (string-ref str i))
- (lp (- i 1))
- i))))
- ((procedure? criterion)
- (let lp ((i (- end 1)))
- (and (>= i start)
- (if (criterion (string-ref str i)) (lp (- i 1))
- i))))
- (else (error "CRITERION param is neither char-set or char."
- string-skip-right criterion)))))
-
-
-(define (string-count s criterion . maybe-start+end)
- (let-string-start+end (start end) string-count s maybe-start+end
- (cond ((char? criterion)
- (do ((i start (+ i 1))
- (count 0 (if (char=? criterion (string-ref s i))
- (+ count 1)
- count)))
- ((>= i end) count)))
-
- ((char-set? criterion)
- (do ((i start (+ i 1))
- (count 0 (if (char-set-contains? criterion (string-ref s i))
- (+ count 1)
- count)))
- ((>= i end) count)))
-
- ((procedure? criterion)
- (do ((i start (+ i 1))
- (count 0 (if (criterion (string-ref s i)) (+ count 1) count)))
- ((>= i end) count)))
-
- (else (error "CRITERION param is neither char-set or char."
- string-count criterion)))))
-
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; string-fill! string char [start end]
-;;;
-;;; string-copy! to tstart from [fstart fend]
-;;; Guaranteed to work, even if s1 eq s2.
-
-(define (string-fill! s char . maybe-start+end)
- (check-arg char? char string-fill!)
- (let-string-start+end (start end) string-fill! s maybe-start+end
- (do ((i (- end 1) (- i 1)))
- ((< i start))
- (string-set! s i char))))
-
-(define (string-copy! to tstart from . maybe-fstart+fend)
- (let-string-start+end (fstart fend) string-copy! from maybe-fstart+fend
- (check-arg integer? tstart string-copy!)
- (check-substring-spec string-copy! to tstart (+ tstart (- fend fstart)))
- (%string-copy! to tstart from fstart fend)))
-
-;;; Library-internal routine
-(define (%string-copy! to tstart from fstart fend)
- (if (> fstart tstart)
- (do ((i fstart (+ i 1))
- (j tstart (+ j 1)))
- ((>= i fend))
- (string-set! to j (string-ref from i)))
-
- (do ((i (- fend 1) (- i 1))
- (j (+ -1 tstart (- fend fstart)) (- j 1)))
- ((< i fstart))
- (string-set! to j (string-ref from i)))))
-
-
-
-;;; Returns starting-position in STRING or #f if not true.
-;;; This implementation is slow & simple. It is useful as a "spec" or for
-;;; comparison testing with fancier implementations.
-;;; See below for fast KMP version.
-
-;(define (string-contains string substring . maybe-starts+ends)
-; (let-string-start+end2 (start1 end1 start2 end2)
-; string-contains string substring maybe-starts+ends
-; (let* ((len (- end2 start2))
-; (i-bound (- end1 len)))
-; (let lp ((i start1))
-; (and (< i i-bound)
-; (if (string= string substring i (+ i len) start2 end2)
-; i
-; (lp (+ i 1))))))))
-
-
-;;; Searching for an occurrence of a substring
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define (string-contains text pattern . maybe-starts+ends)
- (let-string-start+end2 (t-start t-end p-start p-end)
- string-contains text pattern maybe-starts+ends
- (%kmp-search pattern text char=? p-start p-end t-start t-end)))
-
-(define (string-contains-ci text pattern . maybe-starts+ends)
- (let-string-start+end2 (t-start t-end p-start p-end)
- string-contains-ci text pattern maybe-starts+ends
- (%kmp-search pattern text char-ci=? p-start p-end t-start t-end)))
-
-
-;;; Knuth-Morris-Pratt string searching
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; See
-;;; "Fast pattern matching in strings"
-;;; SIAM J. Computing 6(2):323-350 1977
-;;; D. E. Knuth, J. H. Morris and V. R. Pratt
-;;; also described in
-;;; "Pattern matching in strings"
-;;; Alfred V. Aho
-;;; Formal Language Theory - Perspectives and Open Problems
-;;; Ronald V. Brook (editor)
-;;; This algorithm is O(m + n) where m and n are the
-;;; lengths of the pattern and string respectively
-
-;;; KMP search source[start,end) for PATTERN. Return starting index of
-;;; leftmost match or #f.
-
-(define (%kmp-search pattern text c= p-start p-end t-start t-end)
- (let ((plen (- p-end p-start))
- (rv (make-kmp-restart-vector pattern c= p-start p-end)))
-
- ;; The search loop. TJ & PJ are redundant state.
- (let lp ((ti t-start) (pi 0)
- (tj (- t-end t-start)) ; (- tlen ti) -- how many chars left.
- (pj plen)) ; (- plen pi) -- how many chars left.
-
- (if (= pi plen)
- (- ti plen) ; Win.
- (and (<= pj tj) ; Lose.
- (if (c= (string-ref text ti) ; Search.
- (string-ref pattern (+ p-start pi)))
- (lp (+ 1 ti) (+ 1 pi) (- tj 1) (- pj 1)) ; Advance.
-
- (let ((pi (vector-ref rv pi))) ; Retreat.
- (if (= pi -1)
- (lp (+ ti 1) 0 (- tj 1) plen) ; Punt.
- (lp ti pi tj (- plen pi))))))))))
-
-;;; (make-kmp-restart-vector pattern [c= start end]) -> integer-vector
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; Compute the KMP restart vector RV for string PATTERN. If
-;;; we have matched chars 0..i-1 of PATTERN against a search string S, and
-;;; PATTERN[i] doesn't match S[k], then reset i := RV[i], and try again to
-;;; match S[k]. If RV[i] = -1, then punt S[k] completely, and move on to
-;;; S[k+1] and PATTERN[0] -- no possible match of PAT[0..i] contains S[k].
-;;;
-;;; In other words, if you have matched the first i chars of PATTERN, but
-;;; the i+1'th char doesn't match, RV[i] tells you what the next-longest
-;;; prefix of PATTERN is that you have matched.
-;;;
-;;; - C= (default CHAR=?) is used to compare characters for equality.
-;;; Pass in CHAR-CI=? for case-folded string search.
-;;;
-;;; - START & END restrict the pattern to the indicated substring; the
-;;; returned vector will be of length END - START. The numbers stored
-;;; in the vector will be values in the range [0,END-START) -- that is,
-;;; they are valid indices into the restart vector; you have to add START
-;;; to them to use them as indices into PATTERN.
-;;;
-;;; I've split this out as a separate function in case other constant-string
-;;; searchers might want to use it.
-;;;
-;;; E.g.:
-;;; a b d a b x
-;;; #(-1 0 0 -1 1 2)
-
-(define (make-kmp-restart-vector pattern . maybe-c=+start+end)
- (let-optionals* maybe-c=+start+end
- ((c= char=? (procedure? c=))
- ((start end) (lambda (args)
- (string-parse-start+end make-kmp-restart-vector
- pattern args))))
- (let* ((rvlen (- end start))
- (rv (make-vector rvlen -1)))
- (if (> rvlen 0)
- (let ((rvlen-1 (- rvlen 1))
- (c0 (string-ref pattern start)))
-
- ;; Here's the main loop. We have set rv[0] ... rv[i].
- ;; K = I + START -- it is the corresponding index into PATTERN.
- (let lp1 ((i 0) (j -1) (k start))
- (if (< i rvlen-1)
- ;; lp2 invariant:
- ;; pat[(k-j) .. k-1] matches pat[start .. start+j-1]
- ;; or j = -1.
- (let lp2 ((j j))
- (cond ((= j -1)
- (let ((i1 (+ 1 i)))
- (if (not (c= (string-ref pattern (+ k 1)) c0))
- (vector-set! rv i1 0))
- (lp1 i1 0 (+ k 1))))
- ;; pat[(k-j) .. k] matches pat[start..start+j].
- ((c= (string-ref pattern k) (string-ref pattern (+ j start)))
- (let* ((i1 (+ 1 i))
- (j1 (+ 1 j)))
- (vector-set! rv i1 j1)
- (lp1 i1 j1 (+ k 1))))
-
- (else (lp2 (vector-ref rv j)))))))))
- rv)))
-
-
-;;; We've matched I chars from PAT. C is the next char from the search string.
-;;; Return the new I after handling C.
-;;;
-;;; The pattern is (VECTOR-LENGTH RV) chars long, beginning at index PAT-START
-;;; in PAT (PAT-START is usually 0). The I chars of the pattern we've matched
-;;; are
-;;; PAT[PAT-START .. PAT-START + I].
-;;;
-;;; It's *not* an oversight that there is no friendly error checking or
-;;; defaulting of arguments. This is a low-level, inner-loop procedure
-;;; that we want integrated/inlined into the point of call.
-
-(define (kmp-step pat rv c i c= p-start)
- (let lp ((i i))
- (if (c= c (string-ref pat (+ i p-start))) ; Match =>
- (+ i 1) ; Done.
- (let ((i (vector-ref rv i))) ; Back up in PAT.
- (if (= i -1) 0 ; Can't back up further.
- (lp i)))))) ; Keep trying for match.
-
-;;; Zip through S[start,end), looking for a match of PAT. Assume we've
-;;; already matched the first I chars of PAT when we commence at S[start].
-;;; - <0: If we find a match *ending* at index J, return -J.
-;;; - >=0: If we get to the end of the S[start,end) span without finding
-;;; a complete match, return the number of chars from PAT we'd matched
-;;; when we ran off the end.
-;;;
-;;; This is useful for searching *across* buffers -- that is, when your
-;;; input comes in chunks of text. We hand-integrate the KMP-STEP loop
-;;; for speed.
-
-(define (string-kmp-partial-search pat rv s i . c=+p-start+s-start+s-end)
- (check-arg vector? rv string-kmp-partial-search)
- (let-optionals* c=+p-start+s-start+s-end
- ((c= char=? (procedure? c=))
- (p-start 0 (and (integer? p-start) (exact? p-start) (<= 0 p-start)))
- ((s-start s-end) (lambda (args)
- (string-parse-start+end string-kmp-partial-search
- s args))))
- (let ((patlen (vector-length rv)))
- (check-arg (lambda (i) (and (integer? i) (exact? i) (<= 0 i) (< i patlen)))
- i string-kmp-partial-search)
-
- ;; Enough prelude. Here's the actual code.
- (let lp ((si s-start) ; An index into S.
- (vi i)) ; An index into RV.
- (cond ((= vi patlen) (- si)) ; Win.
- ((= si s-end) vi) ; Ran off the end.
- (else ; Match s[si] & loop.
- (let ((c (string-ref s si)))
- (lp (+ si 1)
- (let lp2 ((vi vi)) ; This is just KMP-STEP.
- (if (c= c (string-ref pat (+ vi p-start)))
- (+ vi 1)
- (let ((vi (vector-ref rv vi)))
- (if (= vi -1) 0
- (lp2 vi)))))))))))))
-
-
-;;; Misc
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; (string-null? s)
-;;; (string-reverse s [start end])
-;;; (string-reverse! s [start end])
-;;; (reverse-list->string clist)
-;;; (string->list s [start end])
-
-(define (string-null? s) (zero? (string-length s)))
-
-(define (string-reverse s . maybe-start+end)
- (let-string-start+end (start end) string-reverse s maybe-start+end
- (let* ((len (- end start))
- (ans (make-string len)))
- (do ((i start (+ i 1))
- (j (- len 1) (- j 1)))
- ((< j 0))
- (string-set! ans j (string-ref s i)))
- ans)))
-
-(define (string-reverse! s . maybe-start+end)
- (let-string-start+end (start end) string-reverse! s maybe-start+end
- (do ((i (- end 1) (- i 1))
- (j start (+ j 1)))
- ((<= i j))
- (let ((ci (string-ref s i)))
- (string-set! s i (string-ref s j))
- (string-set! s j ci)))))
-
-
-(define (reverse-list->string clist)
- (let* ((len (length clist))
- (s (make-string len)))
- (do ((i (- len 1) (- i 1)) (clist clist (cdr clist)))
- ((not (pair? clist)))
- (string-set! s i (car clist)))
- s))
-
-
-;(define (string->list s . maybe-start+end)
-; (apply string-fold-right cons '() s maybe-start+end))
-
-(define (string->list s . maybe-start+end)
- (let-string-start+end (start end) string->list s maybe-start+end
- (do ((i (- end 1) (- i 1))
- (ans '() (cons (string-ref s i) ans)))
- ((< i start) ans))))
-
-;;; Defined by R5RS, so commented out here.
-;(define (list->string lis) (string-unfold null? car cdr lis))
-
-
-;;; string-concatenate string-list -> string
-;;; string-concatenate/shared string-list -> string
-;;; string-append/shared s ... -> string
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; STRING-APPEND/SHARED has license to return a string that shares storage
-;;; with any of its arguments. In particular, if there is only one non-empty
-;;; string amongst its parameters, it is permitted to return that string as
-;;; its result. STRING-APPEND, by contrast, always allocates new storage.
-;;;
-;;; STRING-CONCATENATE & STRING-CONCATENATE/SHARED are passed a list of
-;;; strings, which they concatenate into a result string. STRING-CONCATENATE
-;;; always allocates a fresh string; STRING-CONCATENATE/SHARED may (or may
-;;; not) return a result that shares storage with any of its arguments. In
-;;; particular, if it is applied to a singleton list, it is permitted to
-;;; return the car of that list as its value.
-
-(define (string-append/shared . strings) (string-concatenate/shared strings))
-
-(define (string-concatenate/shared strings)
- (let lp ((strings strings) (nchars 0) (first #f))
- (cond ((pair? strings) ; Scan the args, add up total
- (let* ((string (car strings)) ; length, remember 1st
- (tail (cdr strings)) ; non-empty string.
- (slen (string-length string)))
- (if (zero? slen)
- (lp tail nchars first)
- (lp tail (+ nchars slen) (or first strings)))))
-
- ((zero? nchars) "")
-
- ;; Just one non-empty string! Return it.
- ((= nchars (string-length (car first))) (car first))
-
- (else (let ((ans (make-string nchars)))
- (let lp ((strings first) (i 0))
- (if (pair? strings)
- (let* ((s (car strings))
- (slen (string-length s)))
- (%string-copy! ans i s 0 slen)
- (lp (cdr strings) (+ i slen)))))
- ans)))))
-
-
-; Alas, Scheme 48's APPLY blows up if you have many, many arguments.
-;(define (string-concatenate strings) (apply string-append strings))
-
-;;; Here it is written out. I avoid using REDUCE to add up string lengths
-;;; to avoid non-R5RS dependencies.
-(define (string-concatenate strings)
- (let* ((total (do ((strings strings (cdr strings))
- (i 0 (+ i (string-length (car strings)))))
- ((not (pair? strings)) i)))
- (ans (make-string total)))
- (let lp ((i 0) (strings strings))
- (if (pair? strings)
- (let* ((s (car strings))
- (slen (string-length s)))
- (%string-copy! ans i s 0 slen)
- (lp (+ i slen) (cdr strings)))))
- ans))
-
-
-;;; Defined by R5RS, so commented out here.
-;(define (string-append . strings) (string-concatenate strings))
-
-;;; string-concatenate-reverse string-list [final-string end] -> string
-;;; string-concatenate-reverse/shared string-list [final-string end] -> string
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; Return
-;;; (string-concatenate
-;;; (reverse
-;;; (cons (substring final-string 0 end) string-list)))
-
-(define (string-concatenate-reverse string-list . maybe-final+end)
- (let-optionals* maybe-final+end ((final "" (string? final))
- (end (string-length final)
- (and (integer? end)
- (exact? end)
- (<= 0 end (string-length final)))))
- (let ((len (let lp ((sum 0) (lis string-list))
- (if (pair? lis)
- (lp (+ sum (string-length (car lis))) (cdr lis))
- sum))))
-
- (%finish-string-concatenate-reverse len string-list final end))))
-
-(define (string-concatenate-reverse/shared string-list . maybe-final+end)
- (let-optionals* maybe-final+end ((final "" (string? final))
- (end (string-length final)
- (and (integer? end)
- (exact? end)
- (<= 0 end (string-length final)))))
- ;; Add up the lengths of all the strings in STRING-LIST; also get a
- ;; pointer NZLIST into STRING-LIST showing where the first non-zero-length
- ;; string starts.
- (let lp ((len 0) (nzlist #f) (lis string-list))
- (if (pair? lis)
- (let ((slen (string-length (car lis))))
- (lp (+ len slen)
- (if (or nzlist (zero? slen)) nzlist lis)
- (cdr lis)))
-
- (cond ((zero? len) (substring/shared final 0 end))
-
- ;; LEN > 0, so NZLIST is non-empty.
-
- ((and (zero? end) (= len (string-length (car nzlist))))
- (car nzlist))
-
- (else (%finish-string-concatenate-reverse len nzlist final end)))))))
-
-(define (%finish-string-concatenate-reverse len string-list final end)
- (let ((ans (make-string (+ end len))))
- (%string-copy! ans len final 0 end)
- (let lp ((i len) (lis string-list))
- (if (pair? lis)
- (let* ((s (car lis))
- (lis (cdr lis))
- (slen (string-length s))
- (i (- i slen)))
- (%string-copy! ans i s 0 slen)
- (lp i lis))))
- ans))
-
-
-
-
-;;; string-replace s1 s2 start1 end1 [start2 end2] -> string
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; Replace S1[START1,END1) with S2[START2,END2).
-
-(define (string-replace s1 s2 start1 end1 . maybe-start+end)
- (check-substring-spec string-replace s1 start1 end1)
- (let-string-start+end (start2 end2) string-replace s2 maybe-start+end
- (let* ((slen1 (string-length s1))
- (sublen2 (- end2 start2))
- (alen (+ (- slen1 (- end1 start1)) sublen2))
- (ans (make-string alen)))
- (%string-copy! ans 0 s1 0 start1)
- (%string-copy! ans start1 s2 start2 end2)
- (%string-copy! ans (+ start1 sublen2) s1 end1 slen1)
- ans)))
-
-
-;;; string-tokenize s [token-set start end] -> list
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; Break S up into a list of token strings, where a token is a maximal
-;;; non-empty contiguous sequence of chars belonging to TOKEN-SET.
-;;; (string-tokenize "hello, world") => ("hello," "world")
-
-(define (string-tokenize s . token-chars+start+end)
- (let-optionals* token-chars+start+end
- ((token-chars char-set:graphic (char-set? token-chars)) rest)
- (let-string-start+end (start end) string-tokenize s rest
- (let lp ((i end) (ans '()))
- (cond ((and (< start i) (string-index-right s token-chars start i)) =>
- (lambda (tend-1)
- (let ((tend (+ 1 tend-1)))
- (cond ((string-skip-right s token-chars start tend-1) =>
- (lambda (tstart-1)
- (lp tstart-1
- (cons (substring s (+ 1 tstart-1) tend)
- ans))))
- (else (cons (substring s start tend) ans))))))
- (else ans))))))
-
-
-;;; xsubstring s from [to start end] -> string
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; S is a string; START and END are optional arguments that demarcate
-;;; a substring of S, defaulting to 0 and the length of S (e.g., the whole
-;;; string). Replicate this substring up and down index space, in both the
-;; positive and negative directions. For example, if S = "abcdefg", START=3,
-;;; and END=6, then we have the conceptual bidirectionally-infinite string
-;;; ... d e f d e f d e f d e f d e f d e f d e f ...
-;;; ... -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 ...
-;;; XSUBSTRING returns the substring of this string beginning at index FROM,
-;;; and ending at TO (which defaults to FROM+(END-START)).
-;;;
-;;; You can use XSUBSTRING in many ways:
-;;; - To rotate a string left: (xsubstring "abcdef" 2) => "cdefab"
-;;; - To rotate a string right: (xsubstring "abcdef" -2) => "efabcd"
-;;; - To replicate a string: (xsubstring "abc" 0 7) => "abcabca"
-;;;
-;;; Note that
-;;; - The FROM/TO indices give a half-open range -- the characters from
-;;; index FROM up to, but not including index TO.
-;;; - The FROM/TO indices are not in terms of the index space for string S.
-;;; They are in terms of the replicated index space of the substring
-;;; defined by S, START, and END.
-;;;
-;;; It is an error if START=END -- although this is allowed by special
-;;; dispensation when FROM=TO.
-
-(define (xsubstring s from . maybe-to+start+end)
- (check-arg (lambda (val) (and (integer? val) (exact? val)))
- from xsubstring)
- (receive (to start end)
- (if (pair? maybe-to+start+end)
- (let-string-start+end (start end) xsubstring s (cdr maybe-to+start+end)
- (let ((to (car maybe-to+start+end)))
- (check-arg (lambda (val) (and (integer? val)
- (exact? val)
- (<= from val)))
- to xsubstring)
- (values to start end)))
- (let ((slen (string-length (check-arg string? s xsubstring))))
- (values (+ from slen) 0 slen)))
- (let ((slen (- end start))
- (anslen (- to from)))
- (cond ((zero? anslen) "")
- ((zero? slen) (error "Cannot replicate empty (sub)string"
- xsubstring s from to start end))
-
- ((= 1 slen) ; Fast path for 1-char replication.
- (make-string anslen (string-ref s start)))
-
- ;; Selected text falls entirely within one span.
- ((= (floor (/ from slen)) (floor (/ to slen)))
- (substring s (+ start (modulo from slen))
- (+ start (modulo to slen))))
-
- ;; Selected text requires multiple spans.
- (else (let ((ans (make-string anslen)))
- (%multispan-repcopy! ans 0 s from to start end)
- ans))))))
-
-
-;;; string-xcopy! target tstart s sfrom [sto start end] -> unspecific
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; Exactly the same as xsubstring, but the extracted text is written
-;;; into the string TARGET starting at index TSTART.
-;;; This operation is not defined if (EQ? TARGET S) -- you cannot copy
-;;; a string on top of itself.
-
-(define (string-xcopy! target tstart s sfrom . maybe-sto+start+end)
- (check-arg (lambda (val) (and (integer? val) (exact? val)))
- sfrom string-xcopy!)
- (receive (sto start end)
- (if (pair? maybe-sto+start+end)
- (let-string-start+end (start end) string-xcopy! s (cdr maybe-sto+start+end)
- (let ((sto (car maybe-sto+start+end)))
- (check-arg (lambda (val) (and (integer? val) (exact? val)))
- sto string-xcopy!)
- (values sto start end)))
- (let ((slen (string-length s)))
- (values (+ sfrom slen) 0 slen)))
-
- (let* ((tocopy (- sto sfrom))
- (tend (+ tstart tocopy))
- (slen (- end start)))
- (check-substring-spec string-xcopy! target tstart tend)
- (cond ((zero? tocopy))
- ((zero? slen) (error "Cannot replicate empty (sub)string"
- string-xcopy!
- target tstart s sfrom sto start end))
-
- ((= 1 slen) ; Fast path for 1-char replication.
- (string-fill! target (string-ref s start) tstart tend))
-
- ;; Selected text falls entirely within one span.
- ((= (floor (/ sfrom slen)) (floor (/ sto slen)))
- (%string-copy! target tstart s
- (+ start (modulo sfrom slen))
- (+ start (modulo sto slen))))
-
- ;; Multi-span copy.
- (else (%multispan-repcopy! target tstart s sfrom sto start end))))))
-
-;;; This is the core copying loop for XSUBSTRING and STRING-XCOPY!
-;;; Internal -- not exported, no careful arg checking.
-(define (%multispan-repcopy! target tstart s sfrom sto start end)
- (let* ((slen (- end start))
- (i0 (+ start (modulo sfrom slen)))
- (total-chars (- sto sfrom)))
-
- ;; Copy the partial span @ the beginning
- (%string-copy! target tstart s i0 end)
-
- (let* ((ncopied (- end i0)) ; We've copied this many.
- (nleft (- total-chars ncopied)) ; # chars left to copy.
- (nspans (quotient nleft slen))) ; # whole spans to copy
-
- ;; Copy the whole spans in the middle.
- (do ((i (+ tstart ncopied) (+ i slen)) ; Current target index.
- (nspans nspans (- nspans 1))) ; # spans to copy
- ((zero? nspans)
- ;; Copy the partial-span @ the end & we're done.
- (%string-copy! target i s start (+ start (- total-chars (- i tstart)))))
-
- (%string-copy! target i s start end))))); Copy a whole span.
-
-
-
-;;; (string-join string-list [delimiter grammar]) => string
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; Paste strings together using the delimiter string.
-;;;
-;;; (join-strings '("foo" "bar" "baz") ":") => "foo:bar:baz"
-;;;
-;;; DELIMITER defaults to a single space " "
-;;; GRAMMAR is one of the symbols {prefix, infix, strict-infix, suffix}
-;;; and defaults to 'infix.
-;;;
-;;; I could rewrite this more efficiently -- precompute the length of the
-;;; answer string, then allocate & fill it in iteratively. Using
-;;; STRING-CONCATENATE is less efficient.
-
-(define (string-join strings . delim+grammar)
- (let-optionals* delim+grammar ((delim " " (string? delim))
- (grammar 'infix))
- (let ((buildit (lambda (lis final)
- (let recur ((lis lis))
- (if (pair? lis)
- (cons delim (cons (car lis) (recur (cdr lis))))
- final)))))
-
- (cond ((pair? strings)
- (string-concatenate
- (case grammar
-
- ((infix strict-infix)
- (cons (car strings) (buildit (cdr strings) '())))
-
- ((prefix) (buildit strings '()))
-
- ((suffix)
- (cons (car strings) (buildit (cdr strings) (list delim))))
-
- (else (error "Illegal join grammar"
- grammar string-join)))))
-
- ((not (null? strings))
- (error "STRINGS parameter not list." strings string-join))
-
- ;; STRINGS is ()
-
- ((eq? grammar 'strict-infix)
- (error "Empty list cannot be joined with STRICT-INFIX grammar."
- string-join))
-
- (else ""))))) ; Special-cased for infix grammar.
-
-
-;;; Porting & performance-tuning notes
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; See the section at the beginning of this file on external dependencies.
-;;;
-;;; The biggest issue with respect to porting is the LET-OPTIONALS* macro.
-;;; There are many, many optional arguments in this library; the complexity
-;;; of parsing, defaulting & type-testing these parameters is handled with the
-;;; aid of this macro. There are about 15 uses of LET-OPTIONALS*. You can
-;;; rewrite the uses, port the hairy macro definition (which is implemented
-;;; using a Clinger-Rees low-level explicit-renaming macro system), or port
-;;; the simple, high-level definition, which is less efficient.
-;;;
-;;; There is a fair amount of argument checking. This is, strictly speaking,
-;;; unnecessary -- the actual body of the procedures will blow up if, say, a
-;;; START/END index is improper. However, the error message will not be as
-;;; good as if the error were caught at the "higher level." Also, a very, very
-;;; smart Scheme compiler may be able to exploit having the type checks done
-;;; early, so that the actual body of the procedures can assume proper values.
-;;; This isn't likely; this kind of compiler technology isn't common any
-;;; longer.
-;;;
-;;; The overhead of optional-argument parsing is irritating. The optional
-;;; arguments must be consed into a rest list on entry, and then parsed out.
-;;; Function call should be a matter of a few register moves and a jump; it
-;;; should not involve heap allocation! Your Scheme system may have a superior
-;;; non-R5RS optional-argument system that can eliminate this overhead. If so,
-;;; then this is a prime candidate for optimising these procedures,
-;;; *especially* the many optional START/END index parameters.
-;;;
-;;; Note that optional arguments are also a barrier to procedure integration.
-;;; If your Scheme system permits you to specify alternate entry points
-;;; for a call when the number of optional arguments is known in a manner
-;;; that enables inlining/integration, this can provide performance
-;;; improvements.
-;;;
-;;; There is enough *explicit* error checking that *all* string-index
-;;; operations should *never* produce a bounds error. Period. Feel like
-;;; living dangerously? *Big* performance win to be had by replacing
-;;; STRING-REF's and STRING-SET!'s with unsafe equivalents in the loops.
-;;; Similarly, fixnum-specific operators can speed up the arithmetic done on
-;;; the index values in the inner loops. The only arguments that are not
-;;; completely error checked are
-;;; - string lists (complete checking requires time proportional to the
-;;; length of the list)
-;;; - procedure arguments, such as char->char maps & predicates.
-;;; There is no way to check the range & domain of procedures in Scheme.
-;;; Procedures that take these parameters cannot fully check their
-;;; arguments. But all other types to all other procedures are fully
-;;; checked.
-;;;
-;;; This does open up the alternate possibility of simply *removing* these
-;;; checks, and letting the safe primitives raise the errors. On a dumb
-;;; Scheme system, this would provide speed (by eliminating the redundant
-;;; error checks) at the cost of error-message clarity.
-;;;
-;;; See the comments preceding the hash function code for notes on tuning
-;;; the default bound so that the code never overflows your implementation's
-;;; fixnum size into bignum calculation.
-;;;
-;;; In an interpreted Scheme, some of these procedures, or the internal
-;;; routines with % prefixes, are excellent candidates for being rewritten
-;;; in C. Consider STRING-HASH, %STRING-COMPARE, the
-;;; %STRING-{SUF,PRE}FIX-LENGTH routines, STRING-COPY!, STRING-INDEX &
-;;; STRING-SKIP (char-set & char cases), SUBSTRING and SUBSTRING/SHARED,
-;;; %KMP-SEARCH, and %MULTISPAN-REPCOPY!.
-;;;
-;;; It would also be nice to have the ability to mark some of these
-;;; routines as candidates for inlining/integration.
-;;;
-;;; All the %-prefixed routines in this source code are written
-;;; to be called internally to this library. They do *not* perform
-;;; friendly error checks on the inputs; they assume everything is
-;;; proper. They also do not take optional arguments. These two properties
-;;; save calling overhead and enable procedure integration -- but they
-;;; are not appropriate for exported routines.
-
-
-;;; Copyright details
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; The prefix/suffix and comparison routines in this code had (extremely
-;;; distant) origins in MIT Scheme's string lib, and was substantially
-;;; reworked by Olin Shivers (shivers@ai.mit.edu) 9/98. As such, it is
-;;; covered by MIT Scheme's open source copyright. See below for details.
-;;;
-;;; The KMP string-search code was influenced by implementations written
-;;; by Stephen Bevan, Brian Dehneyer and Will Fitzgerald. However, this
-;;; version was written from scratch by myself.
-;;;
-;;; The remainder of this code was written from scratch by myself for scsh.
-;;; The scsh copyright is a BSD-style open source copyright. See below for
-;;; details.
-;;; -Olin Shivers
-
-;;; MIT Scheme copyright terms
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; This material was developed by the Scheme project at the Massachusetts
-;;; Institute of Technology, Department of Electrical Engineering and
-;;; Computer Science. Permission to copy and modify this software, to
-;;; redistribute either the original software or a modified version, and
-;;; to use this software 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.
-
-;;; Scsh copyright terms
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; All rights reserved.
-;;;
-;;; Redistribution and use in source and binary forms, with or without
-;;; modification, are permitted provided that the following conditions
-;;; are met:
-;;; 1. Redistributions of source code must retain the above copyright
-;;; notice, this list of conditions and the following disclaimer.
-;;; 2. Redistributions in binary form must reproduce the above copyright
-;;; notice, this list of conditions and the following disclaimer in the
-;;; documentation and/or other materials provided with the distribution.
-;;; 3. The name of the authors may not be used to endorse or promote products
-;;; derived from this software without specific prior written permission.
-;;;
-;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
-;;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-;;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-;;; IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-;;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-;;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-;;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-;;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-;;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-(define-library (srfi 14)
- (export
- ;; Predicates & comparison
- char-set? char-set= char-set<= char-set-hash
-
- ;; Iterating over character sets
- char-set-cursor char-set-ref char-set-cursor-next end-of-char-set?
- char-set-fold char-set-unfold char-set-unfold!
- char-set-for-each char-set-map
-
- ;; Creating character sets
- char-set-copy char-set
-
- list->char-set string->char-set
- list->char-set! string->char-set!
-
- char-set-filter ucs-range->char-set
- char-set-filter! ucs-range->char-set!
-
- ->char-set
-
- ;; Querying character sets
- char-set->list char-set->string
- char-set-size char-set-count char-set-contains?
- char-set-every char-set-any
-
- ;; Character-set algebra
- char-set-adjoin char-set-delete
- char-set-adjoin! char-set-delete!
-
- char-set-complement char-set-union char-set-intersection
- char-set-complement! char-set-union! char-set-intersection!
-
- char-set-difference char-set-xor char-set-diff+intersection
- char-set-difference! char-set-xor! char-set-diff+intersection!
-
- ;; Standard character sets
- char-set:lower-case char-set:upper-case char-set:title-case
- char-set:letter char-set:digit char-set:letter+digit
- char-set:graphic char-set:printing char-set:whitespace
- char-set:iso-control char-set:punctuation char-set:symbol
- char-set:hex-digit char-set:blank char-set:ascii
- char-set:empty char-set:full
- )
- (import
- (scheme base)
- (srfi 60)
- (srfi aux))
- (include "14.upstream.scm"))
-;;; SRFI-14 character-sets library -*- Scheme -*-
-;;;
-;;; - Ported from MIT Scheme runtime by Brian D. Carlstrom.
-;;; - Massively rehacked & extended by Olin Shivers 6/98.
-;;; - Massively redesigned and rehacked 5/2000 during SRFI process.
-;;; At this point, the code bears the following relationship to the
-;;; MIT Scheme code: "This is my grandfather's axe. My father replaced
-;;; the head, and I have replaced the handle." Nonetheless, we preserve
-;;; the MIT Scheme copyright:
-;;; Copyright (c) 1988-1995 Massachusetts Institute of Technology
-;;; The MIT Scheme license is a "free software" license. See the end of
-;;; this file for the tedious details.
-
-;;; Exports:
-;;; char-set? char-set= char-set<=
-;;; char-set-hash
-;;; char-set-cursor char-set-ref char-set-cursor-next end-of-char-set?
-;;; char-set-fold char-set-unfold char-set-unfold!
-;;; char-set-for-each char-set-map
-;;; char-set-copy char-set
-;;;
-;;; list->char-set string->char-set
-;;; list->char-set! string->char-set!
-;;;
-;;; filterchar-set ucs-range->char-set ->char-set
-;;; filterchar-set! ucs-range->char-set!
-;;;
-;;; char-set->list char-set->string
-;;;
-;;; char-set-size char-set-count char-set-contains?
-;;; char-set-every char-set-any
-;;;
-;;; char-set-adjoin char-set-delete
-;;; char-set-adjoin! char-set-delete!
-;;;
-
-;;; char-set-complement char-set-union char-set-intersection
-;;; char-set-complement! char-set-union! char-set-intersection!
-;;;
-;;; char-set-difference char-set-xor char-set-diff+intersection
-;;; char-set-difference! char-set-xor! char-set-diff+intersection!
-;;;
-;;; char-set:lower-case char-set:upper-case char-set:title-case
-;;; char-set:letter char-set:digit char-set:letter+digit
-;;; char-set:graphic char-set:printing char-set:whitespace
-;;; char-set:iso-control char-set:punctuation char-set:symbol
-;;; char-set:hex-digit char-set:blank char-set:ascii
-;;; char-set:empty char-set:full
-
-;;; Imports
-;;; This code has the following non-R5RS dependencies:
-;;; - ERROR
-;;; - %LATIN1->CHAR %CHAR->LATIN1
-;;; - LET-OPTIONALS* and #\:OPTIONAL macros for parsing, checking & defaulting
-;;; optional arguments from rest lists.
-;;; - BITWISE-AND for CHAR-SET-HASH
-;;; - The SRFI-19 DEFINE-RECORD-TYPE record macro
-;;; - A simple CHECK-ARG procedure:
-;;; (lambda (pred val caller) (if (not (pred val)) (error val caller)))
-
-;;; This is simple code, not great code. Char sets are represented as 256-char
-;;; strings. If char I is ASCII/Latin-1 0, then it isn't in the set; if char I
-;;; is ASCII/Latin-1 1, then it is in the set.
-;;; - Should be rewritten to use bit strings or byte vecs.
-;;; - Is Latin-1 specific. Would certainly have to be rewritten for Unicode.
-
-;;; See the end of the file for porting and performance-tuning notes.
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define-record-type \:char-set
- (make-char-set s)
- char-set?
- (s char-set:s))
-
-
-(define (%string-copy s) (substring s 0 (string-length s)))
-
-;;; Parse, type-check & default a final optional BASE-CS parameter from
-;;; a rest argument. Return a *fresh copy* of the underlying string.
-;;; The default is the empty set. The PROC argument is to help us
-;;; generate informative error exceptions.
-
-(define (%default-base maybe-base proc)
- (if (pair? maybe-base)
- (let ((bcs (car maybe-base))
- (tail (cdr maybe-base)))
- (if (null? tail)
- (if (char-set? bcs) (%string-copy (char-set:s bcs))
- (error "BASE-CS parameter not a char-set" proc bcs))
- (error "Expected final base char set -- too many parameters"
- proc maybe-base)))
- (make-string 256 (%latin1->char 0))))
-
-;;; If CS is really a char-set, do CHAR-SET:S, otw report an error msg on
-;;; behalf of our caller, PROC. This procedure exists basically to provide
-;;; explicit error-checking & reporting.
-
-(define (%char-set:s/check cs proc)
- (let lp ((cs cs))
- (if (char-set? cs) (char-set:s cs)
- (lp (error "Not a char-set" cs proc)))))
-
-
-
-;;; These internal functions hide a lot of the dependency on the
-;;; underlying string representation of char sets. They should be
-;;; inlined if possible.
-
-(define (si=0? s i) (zero? (%char->latin1 (string-ref s i))))
-(define (si=1? s i) (not (si=0? s i)))
-(define c0 (%latin1->char 0))
-(define c1 (%latin1->char 1))
-(define (si s i) (%char->latin1 (string-ref s i)))
-(define (%set0! s i) (string-set! s i c0))
-(define (%set1! s i) (string-set! s i c1))
-
-;;; These do various "s[i] := s[i] op val" operations -- see
-;;; %CHAR-SET-ALGEBRA. They are used to implement the various
-;;; set-algebra procedures.
-(define (setv! s i v) (string-set! s i (%latin1->char v))) ; SET to a Value.
-(define (%not! s i v) (setv! s i (- 1 v)))
-(define (%and! s i v) (if (zero? v) (%set0! s i)))
-(define (%or! s i v) (if (not (zero? v)) (%set1! s i)))
-(define (%minus! s i v) (if (not (zero? v)) (%set0! s i)))
-(define (%xor! s i v) (if (not (zero? v)) (setv! s i (- 1 (si s i)))))
-
-
-(define (char-set-copy cs)
- (make-char-set (%string-copy (%char-set:s/check cs char-set-copy))))
-
-(define (char-set= . rest)
- (or (null? rest)
- (let* ((cs1 (car rest))
- (rest (cdr rest))
- (s1 (%char-set:s/check cs1 char-set=)))
- (let lp ((rest rest))
- (or (not (pair? rest))
- (and (string=? s1 (%char-set:s/check (car rest) char-set=))
- (lp (cdr rest))))))))
-
-(define (char-set<= . rest)
- (or (null? rest)
- (let ((cs1 (car rest))
- (rest (cdr rest)))
- (let lp ((s1 (%char-set:s/check cs1 char-set<=)) (rest rest))
- (or (not (pair? rest))
- (let ((s2 (%char-set:s/check (car rest) char-set<=))
- (rest (cdr rest)))
- (if (eq? s1 s2) (lp s2 rest) ; Fast path
- (let lp2 ((i 255)) ; Real test
- (if (< i 0) (lp s2 rest)
- (and (<= (si s1 i) (si s2 i))
- (lp2 (- i 1))))))))))))
-
-;;; Hash
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; Compute (c + 37 c + 37^2 c + ...) modulo BOUND, with sleaze thrown in
-;;; to keep the intermediate values small. (We do the calculation with just
-;;; enough bits to represent BOUND, masking off high bits at each step in
-;;; calculation. If this screws up any important properties of the hash
-;;; function I'd like to hear about it. -Olin)
-;;;
-;;; If you keep BOUND small enough, the intermediate calculations will
-;;; always be fixnums. How small is dependent on the underlying Scheme system;
-;;; we use a default BOUND of 2^22 = 4194304, which should hack it in
-;;; Schemes that give you at least 29 signed bits for fixnums. The core
-;;; calculation that you don't want to overflow is, worst case,
-;;; (+ 65535 (* 37 (- bound 1)))
-;;; where 65535 is the max character code. Choose the default BOUND to be the
-;;; biggest power of two that won't cause this expression to fixnum overflow,
-;;; and everything will be copacetic.
-
-(define (char-set-hash cs . maybe-bound)
- (let* ((bound (#\:optional maybe-bound 4194304 (lambda (n) (and (integer? n)
- (exact? n)
- (<= 0 n)))))
- (bound (if (zero? bound) 4194304 bound)) ; 0 means default.
- (s (%char-set:s/check cs char-set-hash))
- ;; Compute a 111...1 mask that will cover BOUND-1:
- (mask (let lp ((i #x10000)) ; Let's skip first 16 iterations, eh?
- (if (>= i bound) (- i 1) (lp (+ i i))))))
-
- (let lp ((i 255) (ans 0))
- (if (< i 0) (modulo ans bound)
- (lp (- i 1)
- (if (si=0? s i) ans
- (bitwise-and mask (+ (* 37 ans) i))))))))
-
-
-(define (char-set-contains? cs char)
- (si=1? (%char-set:s/check cs char-set-contains?)
- (%char->latin1 (check-arg char? char char-set-contains?))))
-
-(define (char-set-size cs)
- (let ((s (%char-set:s/check cs char-set-size)))
- (let lp ((i 255) (size 0))
- (if (< i 0) size
- (lp (- i 1) (+ size (si s i)))))))
-
-(define (char-set-count pred cset)
- (check-arg procedure? pred char-set-count)
- (let ((s (%char-set:s/check cset char-set-count)))
- (let lp ((i 255) (count 0))
- (if (< i 0) count
- (lp (- i 1)
- (if (and (si=1? s i) (pred (%latin1->char i)))
- (+ count 1)
- count))))))
-
-
-;;; -- Adjoin & delete
-
-(define (%set-char-set set proc cs chars)
- (let ((s (%string-copy (%char-set:s/check cs proc))))
- (for-each (lambda (c) (set s (%char->latin1 c)))
- chars)
- (make-char-set s)))
-
-(define (%set-char-set! set proc cs chars)
- (let ((s (%char-set:s/check cs proc)))
- (for-each (lambda (c) (set s (%char->latin1 c)))
- chars))
- cs)
-
-(define (char-set-adjoin cs . chars)
- (%set-char-set %set1! char-set-adjoin cs chars))
-(define (char-set-adjoin! cs . chars)
- (%set-char-set! %set1! char-set-adjoin! cs chars))
-(define (char-set-delete cs . chars)
- (%set-char-set %set0! char-set-delete cs chars))
-(define (char-set-delete! cs . chars)
- (%set-char-set! %set0! char-set-delete! cs chars))
-
-
-;;; Cursors
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; Simple implementation. A cursors is an integer index into the
-;;; mark vector, and -1 for the end-of-char-set cursor.
-;;;
-;;; If we represented char sets as a bit set, we could do the following
-;;; trick to pick the lowest bit out of the set:
-;;; (count-bits (xor (- cset 1) cset))
-;;; (But first mask out the bits already scanned by the cursor first.)
-
-(define (char-set-cursor cset)
- (%char-set-cursor-next cset 256 char-set-cursor))
-
-(define (end-of-char-set? cursor) (< cursor 0))
-
-(define (char-set-ref cset cursor) (%latin1->char cursor))
-
-(define (char-set-cursor-next cset cursor)
- (check-arg (lambda (i) (and (integer? i) (exact? i) (<= 0 i 255))) cursor
- char-set-cursor-next)
- (%char-set-cursor-next cset cursor char-set-cursor-next))
-
-(define (%char-set-cursor-next cset cursor proc) ; Internal
- (let ((s (%char-set:s/check cset proc)))
- (let lp ((cur cursor))
- (let ((cur (- cur 1)))
- (if (or (< cur 0) (si=1? s cur)) cur
- (lp cur))))))
-
-
-;;; -- for-each map fold unfold every any
-
-(define (char-set-for-each proc cs)
- (check-arg procedure? proc char-set-for-each)
- (let ((s (%char-set:s/check cs char-set-for-each)))
- (let lp ((i 255))
- (cond ((>= i 0)
- (if (si=1? s i) (proc (%latin1->char i)))
- (lp (- i 1)))))))
-
-(define (char-set-map proc cs)
- (check-arg procedure? proc char-set-map)
- (let ((s (%char-set:s/check cs char-set-map))
- (ans (make-string 256 c0)))
- (let lp ((i 255))
- (cond ((>= i 0)
- (if (si=1? s i)
- (%set1! ans (%char->latin1 (proc (%latin1->char i)))))
- (lp (- i 1)))))
- (make-char-set ans)))
-
-(define (char-set-fold kons knil cs)
- (check-arg procedure? kons char-set-fold)
- (let ((s (%char-set:s/check cs char-set-fold)))
- (let lp ((i 255) (ans knil))
- (if (< i 0) ans
- (lp (- i 1)
- (if (si=0? s i) ans
- (kons (%latin1->char i) ans)))))))
-
-(define (char-set-every pred cs)
- (check-arg procedure? pred char-set-every)
- (let ((s (%char-set:s/check cs char-set-every)))
- (let lp ((i 255))
- (or (< i 0)
- (and (or (si=0? s i) (pred (%latin1->char i)))
- (lp (- i 1)))))))
-
-(define (char-set-any pred cs)
- (check-arg procedure? pred char-set-any)
- (let ((s (%char-set:s/check cs char-set-any)))
- (let lp ((i 255))
- (and (>= i 0)
- (or (and (si=1? s i) (pred (%latin1->char i)))
- (lp (- i 1)))))))
-
-
-(define (%char-set-unfold! proc p f g s seed)
- (check-arg procedure? p proc)
- (check-arg procedure? f proc)
- (check-arg procedure? g proc)
- (let lp ((seed seed))
- (cond ((not (p seed)) ; P says we are done.
- (%set1! s (%char->latin1 (f seed))) ; Add (F SEED) to set.
- (lp (g seed)))))) ; Loop on (G SEED).
-
-(define (char-set-unfold p f g seed . maybe-base)
- (let ((bs (%default-base maybe-base char-set-unfold)))
- (%char-set-unfold! char-set-unfold p f g bs seed)
- (make-char-set bs)))
-
-(define (char-set-unfold! p f g seed base-cset)
- (%char-set-unfold! char-set-unfold! p f g
- (%char-set:s/check base-cset char-set-unfold!)
- seed)
- base-cset)
-
-
-
-;;; list <--> char-set
-
-(define (%list->char-set! chars s)
- (for-each (lambda (char) (%set1! s (%char->latin1 char)))
- chars))
-
-(define (char-set . chars)
- (let ((s (make-string 256 c0)))
- (%list->char-set! chars s)
- (make-char-set s)))
-
-(define (list->char-set chars . maybe-base)
- (let ((bs (%default-base maybe-base list->char-set)))
- (%list->char-set! chars bs)
- (make-char-set bs)))
-
-(define (list->char-set! chars base-cs)
- (%list->char-set! chars (%char-set:s/check base-cs list->char-set!))
- base-cs)
-
-
-(define (char-set->list cs)
- (let ((s (%char-set:s/check cs char-set->list)))
- (let lp ((i 255) (ans '()))
- (if (< i 0) ans
- (lp (- i 1)
- (if (si=0? s i) ans
- (cons (%latin1->char i) ans)))))))
-
-
-
-;;; string <--> char-set
-
-(define (%string->char-set! str bs proc)
- (check-arg string? str proc)
- (do ((i (- (string-length str) 1) (- i 1)))
- ((< i 0))
- (%set1! bs (%char->latin1 (string-ref str i)))))
-
-(define (string->char-set str . maybe-base)
- (let ((bs (%default-base maybe-base string->char-set)))
- (%string->char-set! str bs string->char-set)
- (make-char-set bs)))
-
-(define (string->char-set! str base-cs)
- (%string->char-set! str (%char-set:s/check base-cs string->char-set!)
- string->char-set!)
- base-cs)
-
-
-(define (char-set->string cs)
- (let* ((s (%char-set:s/check cs char-set->string))
- (ans (make-string (char-set-size cs))))
- (let lp ((i 255) (j 0))
- (if (< i 0) ans
- (let ((j (if (si=0? s i) j
- (begin (string-set! ans j (%latin1->char i))
- (+ j 1)))))
- (lp (- i 1) j))))))
-
-
-;;; -- UCS-range -> char-set
-
-(define (%ucs-range->char-set! lower upper error? bs proc)
- (check-arg (lambda (x) (and (integer? x) (exact? x) (<= 0 x))) lower proc)
- (check-arg (lambda (x) (and (integer? x) (exact? x) (<= lower x))) upper proc)
-
- (if (and (< lower upper) (< 256 upper) error?)
- (error "Requested UCS range contains unavailable characters -- this implementation only supports Latin-1"
- proc lower upper))
-
- (let lp ((i (- (min upper 256) 1)))
- (cond ((<= lower i) (%set1! bs i) (lp (- i 1))))))
-
-(define (ucs-range->char-set lower upper . rest)
- (let-optionals* rest ((error? #f) rest)
- (let ((bs (%default-base rest ucs-range->char-set)))
- (%ucs-range->char-set! lower upper error? bs ucs-range->char-set)
- (make-char-set bs))))
-
-(define (ucs-range->char-set! lower upper error? base-cs)
- (%ucs-range->char-set! lower upper error?
- (%char-set:s/check base-cs ucs-range->char-set!)
- ucs-range->char-set)
- base-cs)
-
-
-;;; -- predicate -> char-set
-
-(define (%char-set-filter! pred ds bs proc)
- (check-arg procedure? pred proc)
- (let lp ((i 255))
- (cond ((>= i 0)
- (if (and (si=1? ds i) (pred (%latin1->char i)))
- (%set1! bs i))
- (lp (- i 1))))))
-
-(define (char-set-filter predicate domain . maybe-base)
- (let ((bs (%default-base maybe-base char-set-filter)))
- (%char-set-filter! predicate
- (%char-set:s/check domain char-set-filter!)
- bs
- char-set-filter)
- (make-char-set bs)))
-
-(define (char-set-filter! predicate domain base-cs)
- (%char-set-filter! predicate
- (%char-set:s/check domain char-set-filter!)
- (%char-set:s/check base-cs char-set-filter!)
- char-set-filter!)
- base-cs)
-
-
-;;; {string, char, char-set, char predicate} -> char-set
-
-(define (->char-set x)
- (cond ((char-set? x) x)
- ((string? x) (string->char-set x))
- ((char? x) (char-set x))
- (else (error "->char-set: Not a charset, string or char." x))))
-
-
-
-;;; Set algebra
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; The exported ! procs are "linear update" -- allowed, but not required, to
-;;; side-effect their first argument when computing their result. In other
-;;; words, you must use them as if they were completely functional, just like
-;;; their non-! counterparts, and you must additionally ensure that their
-;;; first arguments are "dead" at the point of call. In return, we promise a
-;;; more efficient result, plus allowing you to always assume char-sets are
-;;; unchangeable values.
-
-;;; Apply P to each index and its char code in S: (P I VAL).
-;;; Used by the set-algebra ops.
-
-(define (%string-iter p s)
- (let lp ((i (- (string-length s) 1)))
- (cond ((>= i 0)
- (p i (%char->latin1 (string-ref s i)))
- (lp (- i 1))))))
-
-;;; String S represents some initial char-set. (OP s i val) does some
-;;; kind of s[i] := s[i] op val update. Do
-;;; S := S OP CSETi
-;;; for all the char-sets in the list CSETS. The n-ary set-algebra ops
-;;; all use this internal proc.
-
-(define (%char-set-algebra s csets op proc)
- (for-each (lambda (cset)
- (let ((s2 (%char-set:s/check cset proc)))
- (let lp ((i 255))
- (cond ((>= i 0)
- (op s i (si s2 i))
- (lp (- i 1)))))))
- csets))
-
-
-;;; -- Complement
-
-(define (char-set-complement cs)
- (let ((s (%char-set:s/check cs char-set-complement))
- (ans (make-string 256)))
- (%string-iter (lambda (i v) (%not! ans i v)) s)
- (make-char-set ans)))
-
-(define (char-set-complement! cset)
- (let ((s (%char-set:s/check cset char-set-complement!)))
- (%string-iter (lambda (i v) (%not! s i v)) s))
- cset)
-
-
-;;; -- Union
-
-(define (char-set-union! cset1 . csets)
- (%char-set-algebra (%char-set:s/check cset1 char-set-union!)
- csets %or! char-set-union!)
- cset1)
-
-(define (char-set-union . csets)
- (if (pair? csets)
- (let ((s (%string-copy (%char-set:s/check (car csets) char-set-union))))
- (%char-set-algebra s (cdr csets) %or! char-set-union)
- (make-char-set s))
- (char-set-copy char-set:empty)))
-
-
-;;; -- Intersection
-
-(define (char-set-intersection! cset1 . csets)
- (%char-set-algebra (%char-set:s/check cset1 char-set-intersection!)
- csets %and! char-set-intersection!)
- cset1)
-
-(define (char-set-intersection . csets)
- (if (pair? csets)
- (let ((s (%string-copy (%char-set:s/check (car csets) char-set-intersection))))
- (%char-set-algebra s (cdr csets) %and! char-set-intersection)
- (make-char-set s))
- (char-set-copy char-set:full)))
-
-
-;;; -- Difference
-
-(define (char-set-difference! cset1 . csets)
- (%char-set-algebra (%char-set:s/check cset1 char-set-difference!)
- csets %minus! char-set-difference!)
- cset1)
-
-(define (char-set-difference cs1 . csets)
- (if (pair? csets)
- (let ((s (%string-copy (%char-set:s/check cs1 char-set-difference))))
- (%char-set-algebra s csets %minus! char-set-difference)
- (make-char-set s))
- (char-set-copy cs1)))
-
-
-;;; -- Xor
-
-(define (char-set-xor! cset1 . csets)
- (%char-set-algebra (%char-set:s/check cset1 char-set-xor!)
- csets %xor! char-set-xor!)
- cset1)
-
-(define (char-set-xor . csets)
- (if (pair? csets)
- (let ((s (%string-copy (%char-set:s/check (car csets) char-set-xor))))
- (%char-set-algebra s (cdr csets) %xor! char-set-xor)
- (make-char-set s))
- (char-set-copy char-set:empty)))
-
-
-;;; -- Difference & intersection
-
-(define (%char-set-diff+intersection! diff int csets proc)
- (for-each (lambda (cs)
- (%string-iter (lambda (i v)
- (if (not (zero? v))
- (cond ((si=1? diff i)
- (%set0! diff i)
- (%set1! int i)))))
- (%char-set:s/check cs proc)))
- csets))
-
-(define (char-set-diff+intersection! cs1 cs2 . csets)
- (let ((s1 (%char-set:s/check cs1 char-set-diff+intersection!))
- (s2 (%char-set:s/check cs2 char-set-diff+intersection!)))
- (%string-iter (lambda (i v) (if (zero? v)
- (%set0! s2 i)
- (if (si=1? s2 i) (%set0! s1 i))))
- s1)
- (%char-set-diff+intersection! s1 s2 csets char-set-diff+intersection!))
- (values cs1 cs2))
-
-(define (char-set-diff+intersection cs1 . csets)
- (let ((diff (string-copy (%char-set:s/check cs1 char-set-diff+intersection)))
- (int (make-string 256 c0)))
- (%char-set-diff+intersection! diff int csets char-set-diff+intersection)
- (values (make-char-set diff) (make-char-set int))))
-
-
-;;;; System character sets
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; These definitions are for Latin-1.
-;;;
-;;; If your Scheme implementation allows you to mark the underlying strings
-;;; as immutable, you should do so -- it would be very, very bad if a client's
-;;; buggy code corrupted these constants.
-
-(define char-set:empty (char-set))
-(define char-set:full (char-set-complement char-set:empty))
-
-(define char-set:lower-case
- (let* ((a-z (ucs-range->char-set #x61 #x7B))
- (latin1 (ucs-range->char-set! #xdf #xf7 #t a-z))
- (latin2 (ucs-range->char-set! #xf8 #x100 #t latin1)))
- (char-set-adjoin! latin2 (%latin1->char #xb5))))
-
-(define char-set:upper-case
- (let ((A-Z (ucs-range->char-set #x41 #x5B)))
- ;; Add in the Latin-1 upper-case chars.
- (ucs-range->char-set! #xd8 #xdf #t
- (ucs-range->char-set! #xc0 #xd7 #t A-Z))))
-
-(define char-set:title-case char-set:empty)
-
-(define char-set:letter
- (let ((u/l (char-set-union char-set:upper-case char-set:lower-case)))
- (char-set-adjoin! u/l
- (%latin1->char #xaa) ; FEMININE ORDINAL INDICATOR
- (%latin1->char #xba)))) ; MASCULINE ORDINAL INDICATOR
-
-(define char-set:digit (string->char-set "0123456789"))
-(define char-set:hex-digit (string->char-set "0123456789abcdefABCDEF"))
-
-(define char-set:letter+digit
- (char-set-union char-set:letter char-set:digit))
-
-(define char-set:punctuation
- (let ((ascii (string->char-set "!\"#%&'()*,-./:;?@[\\]_{}"))
- (latin-1-chars (map %latin1->char '(#xA1 ; INVERTED EXCLAMATION MARK
- #xAB ; LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
- #xAD ; SOFT HYPHEN
- #xB7 ; MIDDLE DOT
- #xBB ; RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
- #xBF)))) ; INVERTED QUESTION MARK
- (list->char-set! latin-1-chars ascii)))
-
-(define char-set:symbol
- (let ((ascii (string->char-set "$+<=>^`|~"))
- (latin-1-chars (map %latin1->char '(#x00A2 ; CENT SIGN
- #x00A3 ; POUND SIGN
- #x00A4 ; CURRENCY SIGN
- #x00A5 ; YEN SIGN
- #x00A6 ; BROKEN BAR
- #x00A7 ; SECTION SIGN
- #x00A8 ; DIAERESIS
- #x00A9 ; COPYRIGHT SIGN
- #x00AC ; NOT SIGN
- #x00AE ; REGISTERED SIGN
- #x00AF ; MACRON
- #x00B0 ; DEGREE SIGN
- #x00B1 ; PLUS-MINUS SIGN
- #x00B4 ; ACUTE ACCENT
- #x00B6 ; PILCROW SIGN
- #x00B8 ; CEDILLA
- #x00D7 ; MULTIPLICATION SIGN
- #x00F7)))) ; DIVISION SIGN
- (list->char-set! latin-1-chars ascii)))
-
-
-(define char-set:graphic
- (char-set-union char-set:letter+digit char-set:punctuation char-set:symbol))
-
-(define char-set:whitespace
- (list->char-set (map %latin1->char '(#x09 ; HORIZONTAL TABULATION
- #x0A ; LINE FEED
- #x0B ; VERTICAL TABULATION
- #x0C ; FORM FEED
- #x0D ; CARRIAGE RETURN
- #x20 ; SPACE
- #xA0))))
-
-(define char-set:printing (char-set-union char-set:whitespace char-set:graphic)) ; NO-BREAK SPACE
-
-(define char-set:blank
- (list->char-set (map %latin1->char '(#x09 ; HORIZONTAL TABULATION
- #x20 ; SPACE
- #xA0)))) ; NO-BREAK SPACE
-
-
-(define char-set:iso-control
- (ucs-range->char-set! #x7F #xA0 #t (ucs-range->char-set 0 32)))
-
-(define char-set:ascii (ucs-range->char-set 0 128))
-
-
-;;; Porting & performance-tuning notes
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; See the section at the beginning of this file on external dependencies.
-;;;
-;;; First and foremost, rewrite this code to use bit vectors of some sort.
-;;; This will give big speedup and memory savings.
-;;;
-;;; - LET-OPTIONALS* macro.
-;;; This is only used once. You can rewrite the use, port the hairy macro
-;;; definition (which is implemented using a Clinger-Rees low-level
-;;; explicit-renaming macro system), or port the simple, high-level
-;;; definition, which is less efficient.
-;;;
-;;; - #\:OPTIONAL macro
-;;; Very simply defined using an R5RS high-level macro.
-;;;
-;;; Implementations that can arrange for the base char sets to be immutable
-;;; should do so. (E.g., Scheme 48 allows one to mark a string as immutable,
-;;; which can be used to protect the underlying strings.) It would be very,
-;;; very bad if a client's buggy code corrupted these constants.
-;;;
-;;; There is a fair amount of argument checking. This is, strictly speaking,
-;;; unnecessary -- the actual body of the procedures will blow up if an
-;;; illegal value is passed in. However, the error message will not be as good
-;;; as if the error were caught at the "higher level." Also, a very, very
-;;; smart Scheme compiler may be able to exploit having the type checks done
-;;; early, so that the actual body of the procedures can assume proper values.
-;;; This isn't likely; this kind of compiler technology isn't common any
-;;; longer.
-;;;
-;;; The overhead of optional-argument parsing is irritating. The optional
-;;; arguments must be consed into a rest list on entry, and then parsed out.
-;;; Function call should be a matter of a few register moves and a jump; it
-;;; should not involve heap allocation! Your Scheme system may have a superior
-;;; non-R5RS optional-argument system that can eliminate this overhead. If so,
-;;; then this is a prime candidate for optimising these procedures,
-;;; *especially* the many optional BASE-CS parameters.
-;;;
-;;; Note that optional arguments are also a barrier to procedure integration.
-;;; If your Scheme system permits you to specify alternate entry points
-;;; for a call when the number of optional arguments is known in a manner
-;;; that enables inlining/integration, this can provide performance
-;;; improvements.
-;;;
-;;; There is enough *explicit* error checking that *all* internal operations
-;;; should *never* produce a type or index-range error. Period. Feel like
-;;; living dangerously? *Big* performance win to be had by replacing string
-;;; and record-field accessors and setters with unsafe equivalents in the
-;;; code. Similarly, fixnum-specific operators can speed up the arithmetic
-;;; done on the index values in the inner loops. The only arguments that are
-;;; not completely error checked are
-;;; - string lists (complete checking requires time proportional to the
-;;; length of the list)
-;;; - procedure arguments, such as char->char maps & predicates.
-;;; There is no way to check the range & domain of procedures in Scheme.
-;;; Procedures that take these parameters cannot fully check their
-;;; arguments. But all other types to all other procedures are fully
-;;; checked.
-;;;
-;;; This does open up the alternate possibility of simply *removing* these
-;;; checks, and letting the safe primitives raise the errors. On a dumb
-;;; Scheme system, this would provide speed (by eliminating the redundant
-;;; error checks) at the cost of error-message clarity.
-;;;
-;;; In an interpreted Scheme, some of these procedures, or the internal
-;;; routines with % prefixes, are excellent candidates for being rewritten
-;;; in C.
-;;;
-;;; It would also be nice to have the ability to mark some of these
-;;; routines as candidates for inlining/integration.
-;;;
-;;; See the comments preceding the hash function code for notes on tuning
-;;; the default bound so that the code never overflows your implementation's
-;;; fixnum size into bignum calculation.
-;;;
-;;; All the %-prefixed routines in this source code are written
-;;; to be called internally to this library. They do *not* perform
-;;; friendly error checks on the inputs; they assume everything is
-;;; proper. They also do not take optional arguments. These two properties
-;;; save calling overhead and enable procedure integration -- but they
-;;; are not appropriate for exported routines.
-
-;;; Copyright notice
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; Copyright (c) 1988-1995 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 and modify this software, to
-;;; redistribute either the original software or a modified version, and
-;;; to use this software 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.
-(define-library (srfi test) ; -*- scheme -*-
- (import (except (scheme base) cond)
- (scheme write)
- (srfi 61))
- (begin
- (display (cond
- ((values 0 1) (lambda (x y) #t)
- => list)))
- (newline)))
-(use-modules (srfi srfi-11))
-
-(define (assert bool explanation)
- (unless bool
- (error explanation)))
-
-(define (id= x y)
- (and (identifier? x)
- (identifier? y)
- (free-identifier=? x y)))
-
-(define-syntax uq
- (syntax-rules ()
- ((uq . x) (syntax-error "Unquote used outside quasiquote."))))
-
-(define-syntax uq-s
- (syntax-rules ()
- ((uq-s . x) (syntax-error "Unquote-splicing used outside quasiquote."))))
-
-(define-syntax qq
- (lambda (stx)
- (define (handle-node node level splicable?)
- (if (zero? level)
- (values 'one node)
- (let ((node (syntax->datum node)))
- (if (pair? node)
- (handle-pair node level splicable?)
- (handle-atom node level)))))
- (define (handle-pair pair level splicable?)
- (let ((car (datum->syntax stx (car pair)))
- (cdr (datum->syntax stx (cdr pair))))
- (cond
- ((id= car #'qq)
- (handle-qq pair level))
- ((id= car #'uq)
- (handle-uq pair level))
- ((and splicable? (id= car #'uq-s))
- (handle-uq-s pair level))
- (else
- (let-values (((type car) (handle-node car level #t))
- ((_ cdr) (handle-node cdr level #f)))
- (case type
- ((one)
- (values 'one #`(cons #,car #,cdr)))
- ((many)
- (values 'one #`(append #,car #,cdr)))))))))
- (define (handle-qq qq-form level)
- (assert (and (list? qq-form) (= 2 (length qq-form)))
- "Quasiquote expects exactly one operand.")
- (let ((operand (datum->syntax stx (cadr qq-form))))
- (let-values (((_ val) (handle-node operand (+ level 1) #f)))
- (values 'one #`(list 'qq #,val)))))
- (define (handle-uq uq-form level)
- (assert (and (list? uq-form) (= 2 (length uq-form)))
- "Unquote expects exactly one operand.")
- (let ((operand (datum->syntax stx (cadr uq-form))))
- (let-values (((type val) (handle-node operand (- level 1) #t)))
- (if (= level 1)
- (values type val)
- (case type
- ((one)
- (values 'one #`(list 'uq #,val)))
- ((many)
- (values 'one #`(apply list 'uq #,val))))))))
- (define (handle-uq-s uq-s-form level)
- (assert (and (list? uq-s-form) (= 2 (length uq-s-form)))
- "Unquote-splicing expects exactly one operand.")
- (let ((operand (datum->syntax stx (cadr uq-s-form))))
- (let-values (((type val) (handle-node operand (- level 1) #t)))
- (if (= 1 level)
- (values 'many val)
- (values 'one #`(list 'uq-s #,val))))))
- (define (handle-atom atom level)
- (let ((atom (datum->syntax stx atom)))
- (values 'one #`(quote #,atom))))
- (syntax-case stx ()
- ((qq operand)
- (let-values (((_ val) (handle-node #'operand 1 #f)))
- val))
- ((qq . x)
- (error "Quasiquote expects exactly one operand.")))))
-;;;; benchmark-suite/lib.scm --- generic support for benchmarking
-;;;; Copyright (C) 2002, 2006, 2011, 2012 Free Software Foundation, Inc.
-;;;;
-;;;; This program is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3, or (at your option) any later version.
-;;;;
-;;;; This program is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;;;; GNU Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this software; see the file COPYING.LESSER.
-;;;; If not, write to the Free Software Foundation, Inc., 51 Franklin
-;;;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (benchmark-suite lib)
- #\use-module (srfi srfi-9)
- #\export (;; Controlling the execution.
- iteration-factor
- scale-iterations
-
- ;; Running benchmarks.
- run-benchmark
- benchmark
-
- ;; Naming groups of benchmarks in a regular fashion.
- with-benchmark-prefix with-benchmark-prefix*
- current-benchmark-prefix format-benchmark-name
-
- ;; <benchmark-result> accessors
- benchmark-result:name
- benchmark-result:iterations
- benchmark-result:real-time
- benchmark-result:run-time
- benchmark-result:gc-time
- benchmark-result:core-time
-
- ;; Reporting results in various ways.
- report current-reporter
- register-reporter unregister-reporter reporter-registered?
- make-log-reporter
- full-reporter
- user-reporter))
-
-
-;;;; If you're using Emacs's Scheme mode:
-;;;; (put 'with-benchmark-prefix 'scheme-indent-function 1)
-;;;; (put 'benchmark 'scheme-indent-function 1)
-
-
-;;;; CORE FUNCTIONS
-;;;;
-;;;; The function (run-benchmark name iterations thunk) is the heart of the
-;;;; benchmarking environment. The first parameter NAME is a unique name for
-;;;; the benchmark to be executed (for an explanation of this parameter see
-;;;; below under ;;;; NAMES. The second parameter ITERATIONS is a positive
-;;;; integer value that indicates how often the thunk shall be executed (for
-;;;; an explanation of how iteration counts should be used, see below under
-;;;; ;;;; ITERATION COUNTS). For example:
-;;;;
-;;;; (run-benchmark "small integer addition" 100000 (lambda () (+ 1 1)))
-;;;;
-;;;; This will run the function (lambda () (+ 1 1)) a 100000 times (the
-;;;; iteration count can, however be scaled. See below for details). Some
-;;;; different time data for running the thunk for the given number of
-;;;; iterations is measured and reported.
-;;;;
-;;;; Convenience macro
-;;;;
-;;;; * (benchmark name iterations body) is a short form for
-;;;; (run-benchmark name iterations (lambda () body))
-
-
-;;;; NAMES
-;;;;
-;;;; Every benchmark in the benchmark suite has a unique name to be able to
-;;;; compare the results of individual benchmarks across several runs of the
-;;;; benchmark suite.
-;;;;
-;;;; A benchmark name is a list of printable objects. For example:
-;;;; ("ports.scm" "file" "read and write back list of strings")
-;;;; ("ports.scm" "pipe" "read")
-;;;;
-;;;; Benchmark names may contain arbitrary objects, but they always have
-;;;; the following properties:
-;;;; - Benchmark names can be compared with EQUAL?.
-;;;; - Benchmark names can be reliably stored and retrieved with the standard
-;;;; WRITE and READ procedures; doing so preserves their identity.
-;;;;
-;;;; For example:
-;;;;
-;;;; (benchmark "simple addition" 100000 (+ 2 2))
-;;;;
-;;;; In that case, the benchmark name is the list ("simple addition").
-;;;;
-;;;; The WITH-BENCHMARK-PREFIX syntax and WITH-BENCHMARK-PREFIX* procedure
-;;;; establish a prefix for the names of all benchmarks whose results are
-;;;; reported within their dynamic scope. For example:
-;;;;
-;;;; (begin
-;;;; (with-benchmark-prefix "basic arithmetic"
-;;;; (benchmark "addition" 100000 (+ 2 2))
-;;;; (benchmark "subtraction" 100000 (- 4 2)))
-;;;; (benchmark "multiplication" 100000 (* 2 2))))
-;;;;
-;;;; In that example, the three benchmark names are:
-;;;; ("basic arithmetic" "addition"),
-;;;; ("basic arithmetic" "subtraction"), and
-;;;; ("multiplication").
-;;;;
-;;;; WITH-BENCHMARK-PREFIX can be nested. Each WITH-BENCHMARK-PREFIX
-;;;; appends a new element to the current prefix:
-;;;;
-;;;; (with-benchmark-prefix "arithmetic"
-;;;; (with-benchmark-prefix "addition"
-;;;; (benchmark "integer" 100000 (+ 2 2))
-;;;; (benchmark "complex" 100000 (+ 2+3i 4+5i)))
-;;;; (with-benchmark-prefix "subtraction"
-;;;; (benchmark "integer" 100000 (- 2 2))
-;;;; (benchmark "complex" 100000 (- 2+3i 1+2i))))
-;;;;
-;;;; The four benchmark names here are:
-;;;; ("arithmetic" "addition" "integer")
-;;;; ("arithmetic" "addition" "complex")
-;;;; ("arithmetic" "subtraction" "integer")
-;;;; ("arithmetic" "subtraction" "complex")
-;;;;
-;;;; To print a name for a human reader, we DISPLAY its elements,
-;;;; separated by ": ". So, the last set of benchmark names would be
-;;;; reported as:
-;;;;
-;;;; arithmetic: addition: integer
-;;;; arithmetic: addition: complex
-;;;; arithmetic: subtraction: integer
-;;;; arithmetic: subtraction: complex
-;;;;
-;;;; The Guile benchmarks use with-benchmark-prefix to include the name of
-;;;; the source file containing the benchmark in the benchmark name, to
-;;;; provide each file with its own namespace.
-
-
-;;;; ITERATION COUNTS
-;;;;
-;;;; Every benchmark has to be given an iteration count that indicates how
-;;;; often it should be executed. The reason is, that in most cases a single
-;;;; execution of the benchmark code would not deliver usable timing results:
-;;;; The resolution of the system time is not arbitrarily fine. Thus, some
-;;;; benchmarks would be executed too quickly to be measured at all. A rule
-;;;; of thumb is, that the longer a benchmark runs, the more exact is the
-;;;; information about the execution time.
-;;;;
-;;;; However, execution time depends on several influences: First, the
-;;;; machine you are running the benchmark on. Second, the compiler you use.
-;;;; Third, which compiler options you use. Fourth, which version of guile
-;;;; you are using. Fifth, which guile options you are using (for example if
-;;;; you are using the debugging evaluator or not). There are even more
-;;;; influences.
-;;;;
-;;;; For this reason, the same number of iterations for a single benchmark may
-;;;; lead to completely different execution times in different
-;;;; constellations. For someone working on a slow machine, the default
-;;;; execution counts may lead to an inacceptable execution time of the
-;;;; benchmark suite. For someone on a very fast machine, however, it may be
-;;;; desireable to increase the number of iterations in order to increase the
-;;;; accuracy of the time data.
-;;;;
-;;;; For this reason, the benchmark suite allows to scale the number of
-;;;; executions by a global factor, stored in the exported variable
-;;;; iteration-factor. The default for iteration-factor is 1. A number of 2
-;;;; means, that all benchmarks are executed twice as often, which will also
-;;;; roughly double the execution time for the benchmark suite. Similarly, if
-;;;; iteration-factor holds a value of 0.5, only about half the execution time
-;;;; will be required.
-;;;;
-;;;; It is probably a good idea to choose the iteration count for each
-;;;; benchmark such that all benchmarks will take about the same time, for
-;;;; example one second. To achieve this, the benchmark suite holds an empty
-;;;; benchmark in the file 0-reference.bm named "reference benchmark for
-;;;; iteration counts". It's iteration count is calibrated to make the
-;;;; benchmark run about one second on Dirk's laptop :-) If you are adding
-;;;; benchmarks to the suite, it would be nice if you could calibrate the
-;;;; number of iterations such that each of your added benchmarks takes about
-;;;; as long to run as the reference benchmark. But: Don't be too accurate
-;;;; to figure out the correct iteration count.
-
-
-;;;; REPORTERS
-;;;;
-;;;; A reporter is a function which we apply to each benchmark outcome.
-;;;; Reporters can log results, print interesting results to the standard
-;;;; output, collect statistics, etc.
-;;;;
-;;;; A reporter function takes the following arguments: NAME ITERATIONS
-;;;; BEFORE AFTER GC-TIME. The argument NAME holds the name of the benchmark,
-;;;; ITERATIONS holds the actual number of iterations that were performed.
-;;;; BEFORE holds the result of the function (times) at the very beginning of
-;;;; the excution of the benchmark, AFTER holds the result of the function
-;;;; (times) after the execution of the benchmark. GC-TIME, finally, holds
-;;;; the difference of calls to (gc-run-time) before and after the execution
-;;;; of the benchmark.
-;;;;
-;;;; This library provides some standard reporters for logging results
-;;;; to a file, reporting interesting results to the user, (FIXME: and
-;;;; collecting totals).
-;;;;
-;;;; You can use the REGISTER-REPORTER function and friends to add whatever
-;;;; reporting functions you like. See under ;;;; TIMING DATA to see how the
-;;;; library helps you to extract relevant timing information from the values
-;;;; ITERATIONS, BEFORE, AFTER and GC-TIME. If you don't register any
-;;;; reporters, the library uses USER-REPORTER, which writes the most
-;;;; interesting results to the standard output.
-
-
-;;;; TIME CALCULATION
-;;;;
-;;;; The library uses the guile functions `get-internal-run-time',
-;;;; `get-internal-real-time', and `gc-run-time' to determine the
-;;;; execution time for a single benchmark. Based on these functions,
-;;;; Guile makes a <benchmark-result>, a record containing the elapsed
-;;;; run time, real time, gc time, and possibly other metrics. These
-;;;; times include the time needed to executed the benchmark code
-;;;; itself, but also the surrounding code that implements the loop to
-;;;; run the benchmark code for the given number of times. This is
-;;;; undesirable, since one would prefer to only get the timing data for
-;;;; the benchmarking code.
-;;;;
-;;;; To cope with this, the benchmarking framework uses a trick: During
-;;;; initialization of the library, the time for executing an empty
-;;;; benchmark is measured and stored. This is an estimate for the time
-;;;; needed by the benchmarking framework itself. For later benchmarks,
-;;;; this time can then be subtracted from the measured execution times.
-;;;; Note that for very short benchmarks, this may result in a negative
-;;;; number.
-;;;;
-;;;; The benchmarking framework provides the following accessors for
-;;;; <benchmark-result> values. Note that all time values are in
-;;;; internal time units; divide by internal-time-units-per-second to
-;;;; get seconds.
-;;;;
-;;;; benchmark-result:name : Return the name of the benchmark.
-;;;;
-;;;; benchmark-result:iterations : Return the number of iterations that
-;;;; this benchmark ran for.
-;;;;
-;;;; benchmark-result:real-time : Return the clock time elapsed while
-;;;; this benchmark executed.
-;;;;
-;;;; benchmark-result:run-time : Return the CPU time elapsed while this
-;;;; benchmark executed, both in user and kernel space.
-;;;;
-;;;; benchmark-result:gc-time : Return the approximate amount of time
-;;;; spent in garbage collection while this benchmark executed, both
-;;;; in user and kernel space.
-;;;;
-;;;; benchmark-result:core-time : Like benchmark-result:run-time, but
-;;;; also estimates the time spent by the framework for the number
-;;;; of iterations, and subtracts off that time from the result.
-;;;;
-
-;;;; This module is used when benchmarking different Guiles, and so it
-;;;; should run on all the Guiles of interest. Currently this set
-;;;; includes Guile 1.8, so be careful with introducing features that
-;;;; only Guile 2.0 supports.
-
-
-;;;; MISCELLANEOUS
-;;;;
-
-(define-record-type <benchmark-result>
- (make-benchmark-result name iterations real-time run-time gc-time)
- benchmark-result?
- (name benchmark-result:name)
- (iterations benchmark-result:iterations)
- (real-time benchmark-result:real-time)
- (run-time benchmark-result:run-time)
- (gc-time benchmark-result:gc-time))
-
-;;; Perform a division and convert the result to inexact.
-(define (->seconds time)
- (/ time 1.0 internal-time-units-per-second))
-
-;;; Scale the number of iterations according to the given scaling factor.
-(define iteration-factor 1)
-(define (scale-iterations iterations)
- (let* ((i (inexact->exact (round (* iterations iteration-factor)))))
- (if (< i 1) 1 i)))
-
-;;; Parameters.
-(cond-expand
- (srfi-39 #t)
- (else (use-modules (srfi srfi-39))))
-
-;;;; CORE FUNCTIONS
-;;;;
-
-;;; The central routine for executing benchmarks.
-;;; The idea is taken from Greg, the GNUstep regression test environment.
-(define benchmark-running? (make-parameter #f))
-(define (run-benchmark name iterations thunk)
- (if (benchmark-running?)
- (error "Nested calls to run-benchmark are not permitted."))
- (if (not (and (integer? iterations) (exact? iterations)))
- (error "Expected exact integral number of iterations"))
- (parameterize ((benchmark-running? #t))
- ;; Warm up the benchmark first. This will resolve any toplevel-ref
- ;; forms.
- (thunk)
- (gc)
- (let* ((before-gc-time (gc-run-time))
- (before-real-time (get-internal-real-time))
- (before-run-time (get-internal-run-time)))
- (do ((i iterations (1- i)))
- ((zero? i))
- (thunk))
- (let ((after-run-time (get-internal-run-time))
- (after-real-time (get-internal-real-time))
- (after-gc-time (gc-run-time)))
- (report (make-benchmark-result (full-name name) iterations
- (- after-real-time before-real-time)
- (- after-run-time before-run-time)
- (- after-gc-time before-gc-time)))))))
-
-;;; A short form for benchmarks.
-(cond-expand
- (guile-2
- (define-syntax-rule (benchmark name iterations body body* ...)
- (run-benchmark name iterations (lambda () body body* ...))))
- (else
- (defmacro benchmark (name iterations body . rest)
- `(run-benchmark ,name ,iterations (lambda () ,body ,@rest)))))
-
-
-;;;; BENCHMARK NAMES
-;;;;
-
-;;;; Turn a benchmark name into a nice human-readable string.
-(define (format-benchmark-name name)
- (string-join name ": "))
-
-;;;; For a given benchmark-name, deliver the full name including all prefixes.
-(define (full-name name)
- (append (current-benchmark-prefix) (list name)))
-
-;;; A parameter containing the current benchmark prefix, as a list.
-(define current-benchmark-prefix
- (make-parameter '()))
-
-;;; Postpend PREFIX to the current name prefix while evaluting THUNK.
-;;; The name prefix is only changed within the dynamic scope of the
-;;; call to with-benchmark-prefix*. Return the value returned by THUNK.
-(define (with-benchmark-prefix* prefix thunk)
- (parameterize ((current-benchmark-prefix (full-name prefix)))
- (thunk)))
-
-;;; (with-benchmark-prefix PREFIX BODY ...)
-;;; Postpend PREFIX to the current name prefix while evaluating BODY ...
-;;; The name prefix is only changed within the dynamic scope of the
-;;; with-benchmark-prefix expression. Return the value returned by the last
-;;; BODY expression.
-(cond-expand
- (guile-2
- (define-syntax-rule (with-benchmark-prefix prefix body body* ...)
- (with-benchmark-prefix* prefix (lambda () body body* ...))))
- (else
- (defmacro with-benchmark-prefix (prefix . body)
- `(with-benchmark-prefix* ,prefix (lambda () ,@body)))))
-
-
-;;;; Benchmark results
-;;;;
-
-(define *calibration-result*
- "<will be set during initialization>")
-
-(define (benchmark-overhead iterations accessor)
- (* (/ iterations 1.0 (benchmark-result:iterations *calibration-result*))
- (accessor *calibration-result*)))
-
-(define (benchmark-result:core-time result)
- (- (benchmark-result:run-time result)
- (benchmark-overhead (benchmark-result:iterations result)
- benchmark-result:run-time)))
-
-
-;;;; REPORTERS
-;;;;
-
-;;; The global set of reporters.
-(define report-hook (make-hook 1))
-
-(define (default-reporter result)
- (if (hook-empty? report-hook)
- (user-reporter result)
- (run-hook report-hook result)))
-
-(define current-reporter
- (make-parameter default-reporter))
-
-(define (register-reporter reporter)
- (add-hook! report-hook reporter))
-
-(define (unregister-reporter reporter)
- (remove-hook! report-hook reporter))
-
-;;; Return true iff REPORTER is in the current set of reporter functions.
-(define (reporter-registered? reporter)
- (if (memq reporter (hook->list report-hook)) #t #f))
-
-;;; Send RESULT to all currently registered reporter functions.
-(define (report result)
- ((current-reporter) result))
-
-
-;;;; Some useful standard reporters:
-;;;; Log reporters write all benchmark results to a given log file.
-;;;; Full reporters write all benchmark results to the standard output.
-;;;; User reporters write some interesting results to the standard output.
-
-;;; Display a single benchmark result to the given port
-(define (print-result port result)
- (let ((name (format-benchmark-name (benchmark-result:name result)))
- (iterations (benchmark-result:iterations result))
- (real-time (benchmark-result:real-time result))
- (run-time (benchmark-result:run-time result))
- (gc-time (benchmark-result:gc-time result))
- (core-time (benchmark-result:core-time result)))
- (write (list name iterations
- 'total (->seconds real-time)
- 'user (->seconds run-time)
- 'system 0
- 'frame (->seconds (- run-time core-time))
- 'benchmark (->seconds core-time)
- 'user/interp (->seconds (- run-time gc-time))
- 'bench/interp (->seconds (- core-time gc-time))
- 'gc (->seconds gc-time))
- port)
- (newline port)))
-
-;;; Return a reporter procedure which prints all results to the file
-;;; FILE, in human-readable form. FILE may be a filename, or a port.
-(define (make-log-reporter file)
- (let ((port (if (output-port? file) file
- (open-output-file file))))
- (lambda (result)
- (print-result port result)
- (force-output port))))
-
-;;; A reporter that reports all results to the user.
-(define (full-reporter result)
- (print-result (current-output-port) result))
-
-;;; Display interesting results of a single benchmark to the given port
-(define (print-user-result port result)
- (let ((name (format-benchmark-name (benchmark-result:name result)))
- (iterations (benchmark-result:iterations result))
- (real-time (benchmark-result:real-time result))
- (run-time (benchmark-result:run-time result))
- (gc-time (benchmark-result:gc-time result))
- (core-time (benchmark-result:core-time result)))
- (write (list name iterations
- 'real (->seconds real-time)
- 'real/iteration (->seconds (/ real-time iterations))
- 'run/iteration (->seconds (/ run-time iterations))
- 'core/iteration (->seconds (/ core-time iterations))
- 'gc (->seconds gc-time))
- port)
- (newline port)))
-
-;;; A reporter that reports interesting results to the user.
-(define (user-reporter result)
- (print-user-result (current-output-port) result))
-
-
-;;;; Initialize the benchmarking system:
-;;;;
-
-(define (calibrate-benchmark-framework)
- (display ";; running guile version ")
- (display (version))
- (newline)
- (display ";; calibrating the benchmarking framework...")
- (newline)
- (parameterize ((current-reporter
- (lambda (result)
- (set! *calibration-result* result)
- (display ";; calibration: ")
- (print-user-result (current-output-port) result))))
- (benchmark "empty initialization benchmark" 10000000 #t)))
-
-(calibrate-benchmark-framework)
-;; -*- Scheme -*-
-;;
-;; A library of dumb functions that may be used to benchmark Guile-VM.
-
-
-;; The comments are from Ludovic, a while ago. The speedups now are much
-;; more significant (all over 2x, sometimes 8x).
-
-(define (fibo x)
- (if (or (= x 1) (= x 2))
- 1
- (+ (fibo (- x 1))
- (fibo (- x 2)))))
-
-(define (g-c-d x y)
- (if (= x y)
- x
- (if (< x y)
- (g-c-d x (- y x))
- (g-c-d (- x y) y))))
-
-(define (loop n)
- ;; This one shows that procedure calls are no faster than within the
- ;; interpreter: the VM yields no performance improvement.
- (if (= 0 n)
- 0
- (loop (1- n))))
-
-;; Disassembly of `loop'
-;;
-;; Disassembly of #<objcode b79bdf28>:
-
-;; nlocs = 0 nexts = 0
-
-;; 0 (make-int8 64) ;; 64
-;; 2 (load-symbol "guile-user") ;; guile-user
-;; 14 (list 0 1) ;; 1 element
-;; 17 (load-symbol "loop") ;; loop
-;; 23 (link-later)
-;; 24 (vector 0 1) ;; 1 element
-;; 27 (make-int8 0) ;; 0
-;; 29 (load-symbol "n") ;; n
-;; 32 (make-false) ;; #f
-;; 33 (make-int8 0) ;; 0
-;; 35 (list 0 3) ;; 3 elements
-;; 38 (list 0 2) ;; 2 elements
-;; 41 (list 0 1) ;; 1 element
-;; 44 (make-int8 5) ;; 5
-;; 46 (make-false) ;; #f
-;; 47 (cons)
-;; 48 (make-int8 18) ;; 18
-;; 50 (make-false) ;; #f
-;; 51 (cons)
-;; 52 (make-int8 20) ;; 20
-;; 54 (make-false) ;; #f
-;; 55 (cons)
-;; 56 (list 0 4) ;; 4 elements
-;; 59 (load-program ##{66}#)
-;; 81 (define "loop")
-;; 87 (variable-set)
-;; 88 (void)
-;; 89 (return)
-
-;; Bytecode ##{66}#\
-
-;; 0 (make-int8 0) ;; 0
-;; 2 (local-ref 0)
-;; 4 (ee?)
-;; 5 (br-if-not 0 3) ;; -> 11
-;; 8 (make-int8 0) ;; 0
-;; 10 (return)
-;; 11 (toplevel-ref 0)
-;; 13 (local-ref 0)
-;; 15 (make-int8 1) ;; 1
-;; 17 (sub)
-;; 18 (tail-call 1)
-
-(define (loopi n)
- ;; Same as `loop'.
- (let loopi ((n n))
- (if (= 0 n)
- 0
- (loopi (1- n)))))
-
-(define (do-loop n)
- ;; Same as `loop' using `do'.
- (do ((i n (1- i)))
- ((= 0 i))
- ;; do nothing
- ))
-
-
-(define (do-cons x)
- ;; This one shows that the built-in `cons' instruction yields a significant
- ;; improvement (speedup: 1.5).
- (let loop ((x x)
- (result '()))
- (if (<= x 0)
- result
- (loop (1- x) (cons x result)))))
-
-(define big-list (iota 500000))
-
-(define (copy-list lst)
- ;; Speedup: 5.9.
- (let loop ((lst lst)
- (result '()))
- (if (null? lst)
- result
- (loop (cdr lst)
- (cons (car lst) result)))))
-
-;; A simple interpreter vs. VM performance comparison tool
-;;
-
-(define-module (measure)
- \:export (measure)
- \:use-module (system vm vm)
- \:use-module (system base compile)
- \:use-module (system base language))
-
-
-(define (time-for-eval sexp eval)
- (let ((before (tms:utime (times))))
- (eval sexp)
- (let ((elapsed (- (tms:utime (times)) before)))
- (format #t "elapsed time: ~a~%" elapsed)
- elapsed)))
-
-(define *scheme* (lookup-language 'scheme))
-
-
-(define (measure . args)
- (if (< (length args) 2)
- (begin
- (format #t "Usage: measure SEXP FILE-TO-LOAD...~%")
- (format #t "~%")
- (format #t "Example: measure '(loop 23424)' lib.scm~%~%")
- (exit 1)))
- (for-each load (cdr args))
- (let* ((sexp (with-input-from-string (car args)
- (lambda ()
- (read))))
- (eval-here (lambda (sexp) (eval sexp (current-module))))
- (proc-name (car sexp))
- (proc-source (procedure-source (eval proc-name (current-module))))
- (% (format #t "proc: ~a~%source: ~a~%" proc-name proc-source))
- (time-interpreted (time-for-eval sexp eval-here))
- (& (if (defined? proc-name)
- (eval `(set! ,proc-name #f) (current-module))
- (format #t "unbound~%")))
- (the-program (compile proc-source))
-
- (time-compiled (time-for-eval `(,proc-name ,@(cdr sexp))
- (lambda (sexp)
- (eval `(begin
- (define ,proc-name
- ,the-program)
- ,sexp)
- (current-module))))))
-
- (format #t "proc: ~a => ~a~%"
- proc-name (eval proc-name (current-module)))
- (format #t "interpreted: ~a~%" time-interpreted)
- (format #t "compiled: ~a~%" time-compiled)
- (format #t "speedup: ~a~%"
- (exact->inexact (/ time-interpreted time-compiled)))
- 0))
-
-(define main measure)
-;;; guile-emacs.scm --- Guile Emacs interface
-
-;; Copyright (C) 2001, 2010 Keisuke Nishida <kxn30@po.cwru.edu>
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free
-;;;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-;;;; 02111-1307 USA
-
-;;; Code:
-
-(use-modules (ice-9 regex))
-(use-modules (ice-9 channel))
-(use-modules (ice-9 session))
-(use-modules (ice-9 documentation))
-
-
-;;;
-;;; Emacs Lisp channel
-;;;
-
-(define (emacs-lisp-channel)
-
- (define (native-type? x)
- (or (integer? x) (symbol? x) (string? x) (pair? x) (vector? x)))
-
- (define (emacs-lisp-print ch val)
- (cond
- ((unspecified? val))
- ((eq? val #t) (channel-print-value ch 't))
- ((or (eq? val #f) (null? val)) (channel-print-value ch 'nil))
- ((native-type? val) (channel-print-value ch val))
- (else (channel-print-token ch val))))
-
- (channel-open (make-object-channel emacs-lisp-print)))
-
-
-;;;
-;;; Scheme channel
-;;;
-
-(define (emacs-scheme-channel)
- (define (print ch val) (channel-print-value ch (object->string val)))
- (channel-open (make-object-channel print)))
-
-
-;;;
-;;; for guile-import and guile-import-module
-;;;
-
-(define (guile-emacs-export-procedure name proc docs)
- (define (procedure-args proc)
- (let ((source (procedure-source proc)))
- (if source
- ;; formals -> emacs args
- (let loop ((formals (cadr source)))
- (cond
- ((null? formals) '())
- ((symbol? formals) `(&rest ,formals))
- (else (cons (car formals) (loop (cdr formals))))))
- ;; arity -> emacs args
- (let* ((arity (procedure-minimum-arity proc))
- (nreqs (car arity))
- (nopts (cadr arity))
- (restp (caddr arity)))
- (define (nsyms n)
- (if (= n 0) '() (cons (gensym "a") (nsyms (1- n)))))
- (append! (nsyms nreqs)
- (if (> nopts 0) (cons '&optional (nsyms nopts)) '())
- (if restp (cons '&rest (nsyms 1)) '()))))))
-
- (define (procedure-call name args)
- (let ((restp (memq '&rest args))
- (args (delq '&rest (delq '&optional args))))
- (if restp
- `('apply ',name ,@args)
- `(',name ,@args))))
-
- (let ((args (procedure-args proc))
- (docs (and docs (object-documentation proc))))
- `(defun ,name ,args
- ,@(if docs (list docs) '())
- (guile-lisp-flat-eval ,@(procedure-call (procedure-name proc) args)))))
-
-(define (guile-emacs-export proc-name func-name docs)
- (let ((proc (module-ref (current-module) proc-name)))
- (guile-emacs-export-procedure func-name proc docs)))
-
-(define (guile-emacs-export-procedures module-name docs)
- (define (module-public-procedures name)
- (hash-fold (lambda (s v d)
- (let ((val (variable-ref v)))
- (if (procedure? val) (acons s val d) d)))
- '() (module-obarray (resolve-interface name))))
- `(progn ,@(map (lambda (n+p)
- (guile-emacs-export-procedure (car n+p) (cdr n+p) docs))
- (module-public-procedures module-name))))
-
-
-;;;
-;;; for guile-scheme-complete-symbol
-;;;
-
-(define (guile-emacs-complete-alist str)
- (sort! (apropos-fold (lambda (module name val data)
- (cons (list (symbol->string name)
- (cond ((procedure? val) " <p>")
- ((macro? val) " <m>")
- (else "")))
- data))
- '() (string-append "^" (regexp-quote str))
- apropos-fold-all)
- (lambda (p1 p2) (string<? (car p1) (car p2)))))
-
-
-;;;
-;;; for guile-scheme-apropos
-;;;
-
-(define (guile-emacs-apropos regexp)
- (with-output-to-string (lambda () (apropos regexp))))
-
-
-;;;
-;;; for guile-scheme-describe
-;;;
-
-(define (guile-emacs-describe sym)
- (object-documentation (eval sym (current-module))))
-
-
-;;;
-;;; Guile 1.4 compatibility
-;;;
-
-(define object->string
- (if (defined? 'object->string)
- object->string
- (lambda (x) (format #f "~S" x))))
-
-;;; guile-emacs.scm ends here
-;;; examples/box-dynamic-module/box-mixed.scm -- Scheme module using some
-;;; functionality from the shared library libbox-module, but do not
-;;; export procedures from the module.
-
-;;; Commentary:
-
-;;; This is the Scheme module box-mixed. It uses some functionality
-;;; from the shared library libbox-module, but does not export it.
-
-;;; Code:
-
-;;; Author: Thomas Wawrzinek
-;;; Date: 2001-06-08
-;;; Changed: 2001-06-14 by martin, some commenting, cleanup and integration.
-
-(define-module (box-mixed))
-
-;; First, load the library.
-;;
-(load-extension "libbox-module" "scm_init_box")
-
-;; Create a list of boxes, each containing one element from ARGS.
-;;
-(define (make-box-list . args)
- (map (lambda (el)
- (let ((b (make-box)))
- (box-set! b el) b))
- args))
-
-;; Map the procedure FUNC over all elements of LST, which must be a
-;; list of boxes. The result is a list of freshly allocated boxes,
-;; each containing the result of an application of FUNC.
-(define (box-map func lst)
- (map (lambda (el)
- (let ((b (make-box)))
- (box-set! b (func (box-ref el)))
- b))
- lst))
-
-;; Export the procedures, so that they can be used by others.
-;;
-(export make-box-list box-map)
-
-;;; End of file.
-;;; examples/box-dynamic-module/box-module.scm -- Scheme module exporting
-;;; some functionality from the shared library libbox-module.
-
-;;; Commentary:
-
-;;; This is the Scheme part of the dynamic library module (box-module).
-;;; When you do a (use-modules (box-module)) in this directory,
-;;; this file gets loaded and will load the compiled extension.
-
-;;; Code:
-
-;;; Author: Martin Grabmueller
-;;; Date: 2001-06-06
-
-(define-module (box-module))
-
-;; First, load the library.
-;;
-(load-extension "libbox-module" "scm_init_box")
-
-;; Then export the procedures which should be visible to module users.
-;;
-(export make-box box-ref box-set!)
-
-;;; End of file.
-;;; examples/modules/module-0.scm -- Module system demo.
-
-;;; Commentary:
-
-;;; Module 0 of the module demo program.
-
-;;; Author: Martin Grabmueller
-;;; Date: 2001-05-29
-
-;;; Code:
-
-(define-module (module-0))
-
-(export foo bar)
-
-(define (foo)
- (display "module-0 foo")
- (newline))
-
-(define (bar)
- (display "module-0 bar")
- (newline))
-
-;;; End of file.
-;;; examples/modules/module-1.scm -- Module system demo.
-
-;;; Commentary:
-
-;;; Module 1 of the module demo program.
-
-;;; Author: Martin Grabmueller
-;;; Date: 2001-05-29
-
-;;; Code:
-
-(define-module (module-1))
-
-(export foo bar)
-
-(define (foo)
- (display "module-1 foo")
- (newline))
-
-(define (bar)
- (display "module-1 bar")
- (newline))
-
-;;; End of file.
-;;; examples/modules/module-2.scm -- Module system demo.
-
-;;; Commentary:
-
-;;; Module 2 of the module demo program.
-
-;;; Author: Martin Grabmueller
-;;; Date: 2001-05-29
-
-;;; Code:
-
-(define-module (module-2))
-
-(export foo bar braz)
-
-(define (foo)
- (display "module-2 foo")
- (newline))
-
-(define (bar)
- (display "module-2 bar")
- (newline))
-
-(define (braz)
- (display "module-2 braz")
- (newline))
-
-;;; End of file.
-;;; examples/safe/evil.scm -- Evil Scheme file to be run in a safe
-;;; environment.
-
-;;; Commentary:
-
-;;; This is an example file to be evaluated by the `safe' program in
-;;; this directory. This program, unlike the `untrusted.scm' (which
-;;; is untrusted, but a really nice fellow though), tries to do evil
-;;; things and will thus break in a safe environment.
-;;;
-;;; *Note* that the files in this directory are only suitable for
-;;; demonstration purposes, if you have to implement safe evaluation
-;;; mechanisms in important environments, you will have to do more
-;;; than shown here -- for example disabling input/output operations.
-
-;;; Author: Martin Grabmueller
-;;; Date: 2001-05-30
-
-;;; Code:
-
-(define passwd (open-input-file "/etc/passwd"))
-
-(let lp ((ch (read-char passwd)))
- (if (not (eof-object? ch))
- (lp (read-char passwd))))
-
-;;; End of file.
-;;; examples/safe/untrusted.scm -- Scheme file to be run in a safe
-;;; environment.
-
-;;; Commentary:
-
-;;; This is an example file to be evaluated by the `safe' program in
-;;; this directory.
-;;;
-;;; *Note* that the files in this directory are only suitable for
-;;; demonstration purposes, if you have to implement safe evaluation
-;;; mechanisms in important environments, you will have to do more
-;;; than shown here -- for example disabling input/output operations.
-
-;;; Author: Martin Grabmueller
-;;; Date: 2001-05-30
-
-;;; Code:
-
-;; fact -- the everlasting factorial function...
-;;
-(define (fact n)
- (if (< n 2)
- 1
- (* n (fact (- n 1)))))
-
-;; Display the factorial of 0..9 to the terminal.
-;;
-(do ((x 0 (+ x 1)))
- ((= x 11))
- (display (fact x))
- (newline))
-
-;;; End of file.
-;;; Commentary:
-
-;;; This is the famous Hello-World-program, written for Guile.
-;;;
-;;; For an advanced version, see the script `hello' in the same
-;;; directory.
-
-;;; Author: Martin Grabmueller
-;;; Date: 2001-05-29
-
-;;; Code:
-
-(display "Hello, World!")
-(newline)
-
-;;; End of file.
-;;; Commentary:
-
-;;; A simple debugging server that responds to all responses with a
-;;; table containing the headers given in the request.
-;;;
-;;; As a novelty, this server uses a little micro-framework to build up
-;;; the response as SXML. Instead of a string, the `respond' helper
-;;; returns a procedure for the body, which allows the `(web server)'
-;;; machinery to collect the output as a bytevector in the desired
-;;; encoding, instead of building an intermediate output string.
-;;;
-;;; In the future this will also allow for chunked transfer-encoding,
-;;; for HTTP/1.1 clients.
-
-;;; Code:
-
-(use-modules (web server)
- (web request)
- (web response)
- (sxml simple))
-
-(define html5-doctype "<!DOCTYPE html>\n")
-(define default-title "Hello hello!")
-
-(define* (templatize #\key (title "No title") (body '((p "No body"))))
- `(html (head (title ,title))
- (body ,@body)))
-
-(define* (respond #\optional body #\key
- (status 200)
- (title default-title)
- (doctype html5-doctype)
- (content-type-params '((charset . "utf-8")))
- (content-type 'text/html)
- (extra-headers '())
- (sxml (and body (templatize #\title title #\body body))))
- (values (build-response
- #\code status
- #\headers `((content-type . (,content-type ,@content-type-params))
- ,@extra-headers))
- (lambda (port)
- (if sxml
- (begin
- (if doctype (display doctype port))
- (sxml->xml sxml port))))))
-
-(define (debug-page request body)
- (respond `((h1 "hello world!")
- (table
- (tr (th "header") (th "value"))
- ,@(map (lambda (pair)
- `(tr (td (tt ,(with-output-to-string
- (lambda () (display (car pair))))))
- (td (tt ,(with-output-to-string
- (lambda ()
- (write (cdr pair))))))))
- (request-headers request))))))
-
-(run-server debug-page)
-;;; Commentary:
-
-;;; A simple web server that responds to all requests with the eponymous
-;;; string. Visit http://localhost:8080 to test.
-
-;;; Code:
-
-(use-modules (web server))
-
-;; A handler receives two values as arguments: the request object, and
-;; the request body. It returns two values also: the response object,
-;; and the response body.
-;;
-;; In this simple example we don't actually access the request object,
-;; but if we wanted to, we would use the procedures from the `(web
-;; request)' module. If there is no body given in the request, the body
-;; argument will be false.
-;;
-;; To create a response object, use the `build-response' procedure from
-;; `(web response)'. Here we take advantage of a shortcut, in which we
-;; return an alist of headers for the response instead of returning a
-;; proper response object. In this case, a response object will be made
-;; for us with a 200 OK status.
-;;
-(define (handler request body)
- (values '((content-type . (text/plain)))
- "Hello, World!"))
-
-(run-server handler)
-;;; Copyright (C) 2008, 2011 Free Software Foundation, Inc.
-;;;
-;;; This program is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public License
-;;; as published by the Free Software Foundation; either version 3, or
-;;; (at your option) any later version.
-;;;
-;;; This program is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;;; GNU Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this software; see the file COPYING.LESSER. If
-;;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(use-modules (ice-9 format)
- (ice-9 rdelim)
- (ice-9 regex)
- (srfi srfi-1)
- (srfi srfi-37)
- (srfi srfi-39))
-
-
-;;;
-;;; Memory usage.
-;;;
-
-(define (memory-mappings pid)
- "Return an list of alists, each of which contains information about a
-memory mapping of process @var{pid}. This information is obtained by reading
-@file{/proc/PID/smaps} on Linux. See `procs(5)' for details."
-
- (define mapping-line-rx
- ;; As of Linux 2.6.32.28, an `smaps' line looks like this:
- ;; "00400000-00401000 r-xp 00000000 fe:00 108264 /home/ludo/soft/bin/guile"
- (make-regexp
- "^([[:xdigit:]]+)-([[:xdigit:]]+) ([rwx-]{3}[ps]) ([[:xdigit:]]+) [[:xdigit:]]{2}:[[:xdigit:]]{2} [0-9]+[[:blank:]]+(.*)$"))
-
- (define rss-line-rx
- (make-regexp
- "^Rss:[[:blank:]]+([[:digit:]]+) kB$"))
-
- (if (not (string-contains %host-type "-linux-"))
- (error "this procedure only works on Linux-based systems" %host-type))
-
- (with-input-from-port (open-input-file (format #f "/proc/~a/smaps" pid))
- (lambda ()
- (let loop ((line (read-line))
- (result '()))
- (if (eof-object? line)
- (reverse result)
- (cond ((regexp-exec mapping-line-rx line)
- =>
- (lambda (match)
- (let ((mapping-start (string->number
- (match:substring match 1)
- 16))
- (mapping-end (string->number
- (match:substring match 2)
- 16))
- (access-bits (match:substring match 3))
- (name (match:substring match 5)))
- (loop (read-line)
- (cons `((mapping-start . ,mapping-start)
- (mapping-end . ,mapping-end)
- (access-bits . ,access-bits)
- (name . ,(if (string=? name "")
- #f
- name)))
- result)))))
- ((regexp-exec rss-line-rx line)
- =>
- (lambda (match)
- (let ((section+ (cons (cons 'rss
- (string->number
- (match:substring match 1)))
- (car result))))
- (loop (read-line)
- (cons section+ (cdr result))))))
- (else
- (loop (read-line) result))))))))
-
-(define (total-heap-size pid)
- "Return a pair representing the total and RSS heap size of PID."
-
- (define heap-or-anon-rx
- (make-regexp "\\[(heap|anon)\\]"))
-
- (define private-mapping-rx
- (make-regexp "^[r-][w-][x-]p$"))
-
- (fold (lambda (heap total+rss)
- (let ((name (assoc-ref heap 'name))
- (perm (assoc-ref heap 'access-bits)))
- ;; Include anonymous private mappings.
- (if (or (and (not name)
- (regexp-exec private-mapping-rx perm))
- (and name
- (regexp-exec heap-or-anon-rx name)))
- (let ((start (assoc-ref heap 'mapping-start))
- (end (assoc-ref heap 'mapping-end))
- (rss (assoc-ref heap 'rss)))
- (cons (+ (car total+rss) (- end start))
- (+ (cdr total+rss) rss)))
- total+rss)))
- '(0 . 0)
- (memory-mappings pid)))
-
-
-(define (display-stats start end)
- (define (->usecs sec+usecs)
- (+ (* 1000000 (car sec+usecs))
- (cdr sec+usecs)))
-
- (let ((usecs (- (->usecs end) (->usecs start)))
- (heap-size (total-heap-size (getpid)))
- (gc-heap-size (assoc-ref (gc-stats) 'heap-size)))
-
- (format #t "execution time: ~6,3f seconds~%"
- (/ usecs 1000000.0))
-
- (and gc-heap-size
- (format #t "GC-reported heap size: ~8d B (~1,2f MiB)~%"
- gc-heap-size
- (/ gc-heap-size 1024.0 1024.0)))
-
- (format #t "heap size: ~8d B (~1,2f MiB)~%"
- (car heap-size)
- (/ (car heap-size) 1024.0 1024.0))
- (format #t "heap RSS: ~8d KiB (~1,2f MiB)~%"
- (cdr heap-size)
- (/ (cdr heap-size) 1024.0))
-;; (system (format #f "cat /proc/~a/smaps" (getpid)))
-;; (system (format #f "exmtool procs | grep -E '^(PID|~a)'" (getpid)))
- ))
-
-
-;;;
-;;; Larceny/Twobit benchmarking compability layer.
-;;;
-
-(define *iteration-count*
- (make-parameter #f))
-
-(define (run-benchmark name . args)
- "A @code{run-benchmark} procedure compatible with Larceny's GC benchmarking
-framework. See
-@url{http://www.ccs.neu.edu/home/will/Twobit/benchmarksAbout.html} for
-details."
-
- (define %concise-invocation?
- ;; This procedure can be called with only two arguments, NAME and
- ;; RUN-MAKER.
- (procedure? (car args)))
-
- (let ((count (or (*iteration-count*)
- (if %concise-invocation? 0 (car args))))
- (run-maker (if %concise-invocation? (car args) (cadr args)))
- (ok? (if %concise-invocation?
- (lambda (result) #t)
- (caddr args)))
- (args (if %concise-invocation? '() (cdddr args))))
- (let loop ((i 0))
- (and (< i count)
- (let ((result (apply run-maker args)))
- (if (not (ok? result))
- (begin
- (format (current-output-port) "invalid result for `~A'~%"
- name)
- (exit 1)))
- (loop (1+ i)))))))
-
-(define (save-directory-excursion directory thunk)
- (let ((previous-dir (getcwd)))
- (dynamic-wind
- (lambda ()
- (chdir directory))
- thunk
- (lambda ()
- (chdir previous-dir)))))
-
-(define (load-larceny-benchmark file)
- "Load the Larceny benchmark from @var{file}."
- (let ((name (let ((base (basename file)))
- (substring base 0 (or (string-rindex base #\.)
- (string-length base)))))
- (module (let ((m (make-module)))
- (beautify-user-module! m)
- (module-use! m (resolve-interface '(ice-9 syncase)))
- m)))
- (save-directory-excursion (dirname file)
- (lambda ()
- (save-module-excursion
- (lambda ()
- (set-current-module module)
- (module-define! module 'run-benchmark run-benchmark)
- (load (basename file))
-
- ;; Invoke the benchmark's entry point.
- (let ((entry (module-ref (current-module)
- (symbol-append (string->symbol name)
- '-benchmark))))
- (entry))))))))
-
-
-
-;;;
-;;; Option processing.
-;;;
-
-(define %options
- (list (option '(#\h "help") #f #f
- (lambda args
- (show-help)
- (exit 0)))
- (option '(#\l "larceny") #f #f
- (lambda (opt name arg result)
- (alist-cons 'larceny? #t result)))
- (option '(#\i "iterations") #t #f
- (lambda (opt name arg result)
- (alist-cons 'iterations (string->number arg) result)))))
-
-(define (show-help)
- (format #t "Usage: gc-profile [OPTIONS] FILE.SCM
-Load FILE.SCM, a Guile Scheme source file, and report its execution time and
-final heap usage.
-
- -h, --help Show this help message
-
- -l, --larceny Provide mechanisms compatible with the Larceny/Twobit
- GC benchmark suite.
- -i, --iterations=COUNT
- Run the given benchmark COUNT times, regardless of the
- iteration count passed to `run-benchmark' (for Larceny
- benchmarks).
-
-Report bugs to <bug-guile@gnu.org>.~%"))
-
-(define (parse-args args)
- (define (leave fmt . args)
- (apply format (current-error-port) (string-append fmt "~%") args)
- (exit 1))
-
- (args-fold args %options
- (lambda (opt name arg result)
- (leave "~A: unrecognized option" opt))
- (lambda (file result)
- (if (pair? (assoc 'input result))
- (leave "~a: only one input file at a time" file)
- (alist-cons 'input file result)))
- '()))
-
-
-;;;
-;;; Main program.
-;;;
-
-(define (main . args)
- (let* ((options (parse-args args))
- (prog (assoc-ref options 'input))
- (load (if (assoc-ref options 'larceny?)
- load-larceny-benchmark
- load)))
-
- (parameterize ((*iteration-count* (assoc-ref options 'iterations)))
- (format #t "running `~a' with Guile ~a...~%" prog (version))
-
- (let ((start (gettimeofday)))
- (dynamic-wind
- (lambda ()
- #t)
- (lambda ()
- (set! quit (lambda args args))
- (load prog))
- (lambda ()
- (let ((end (gettimeofday)))
- (format #t "done~%")
- (display-stats start end))))))))
-; This is adapted from a benchmark written by John Ellis and Pete Kovac
-; of Post Communications.
-; It was modified by Hans Boehm of Silicon Graphics.
-; It was translated into Scheme by William D Clinger of Northeastern Univ;
-; the Scheme version uses (RUN-BENCHMARK <string> <thunk>)
-; Last modified 30 May 1997.
-;
-; This is no substitute for real applications. No actual application
-; is likely to behave in exactly this way. However, this benchmark was
-; designed to be more representative of real applications than other
-; Java GC benchmarks of which we are aware.
-; It attempts to model those properties of allocation requests that
-; are important to current GC techniques.
-; It is designed to be used either to obtain a single overall performance
-; number, or to give a more detailed estimate of how collector
-; performance varies with object lifetimes. It prints the time
-; required to allocate and collect balanced binary trees of various
-; sizes. Smaller trees result in shorter object lifetimes. Each cycle
-; allocates roughly the same amount of memory.
-; Two data structures are kept around during the entire process, so
-; that the measured performance is representative of applications
-; that maintain some live in-memory data. One of these is a tree
-; containing many pointers. The other is a large array containing
-; double precision floating point numbers. Both should be of comparable
-; size.
-;
-; The results are only really meaningful together with a specification
-; of how much memory was used. It is possible to trade memory for
-; better time performance. This benchmark should be run in a 32 MB
-; heap, though we don't currently know how to enforce that uniformly.
-
-; In the Java version, this routine prints the heap size and the amount
-; of free memory. There is no portable way to do this in Scheme; each
-; implementation needs its own version.
-
-(use-modules (ice-9 syncase))
-
-(define (PrintDiagnostics)
- (display " Total memory available= ???????? bytes")
- (display " Free memory= ???????? bytes")
- (newline))
-
-
-
-(define (run-benchmark str thu)
- (display str)
- (thu))
-; Should we implement a Java class as procedures or hygienic macros?
-; Take your pick.
-
-(define-syntax let-class
- (syntax-rules
- ()
-
- ;; Put this rule first to implement a class using procedures.
- ((let-class (((method . args) . method-body) ...) . body)
- (let () (define (method . args) . method-body) ... . body))
-
-
- ;; Put this rule first to implement a class using hygienic macros.
- ((let-class (((method . args) . method-body) ...) . body)
- (letrec-syntax ((method (syntax-rules () ((method . args) (begin . method-body))))
- ...)
- . body))
-
-
- ))
-
-
-(define (gcbench kStretchTreeDepth)
-
- ; Nodes used by a tree of a given size
- (define (TreeSize i)
- (- (expt 2 (+ i 1)) 1))
-
- ; Number of iterations to use for a given tree depth
- (define (NumIters i)
- (quotient (* 2 (TreeSize kStretchTreeDepth))
- (TreeSize i)))
-
- ; Parameters are determined by kStretchTreeDepth.
- ; In Boehm's version the parameters were fixed as follows:
- ; public static final int kStretchTreeDepth = 18; // about 16Mb
- ; public static final int kLongLivedTreeDepth = 16; // about 4Mb
- ; public static final int kArraySize = 500000; // about 4Mb
- ; public static final int kMinTreeDepth = 4;
- ; public static final int kMaxTreeDepth = 16;
- ; In Larceny the storage numbers above would be 12 Mby, 3 Mby, 6 Mby.
-
- (let* ((kLongLivedTreeDepth (- kStretchTreeDepth 2))
- (kArraySize (* 4 (TreeSize kLongLivedTreeDepth)))
- (kMinTreeDepth 4)
- (kMaxTreeDepth kLongLivedTreeDepth))
-
- ; Elements 3 and 4 of the allocated vectors are useless.
-
- (let-class (((make-node l r)
- (let ((v (make-empty-node)))
- (vector-set! v 0 l)
- (vector-set! v 1 r)
- v))
- ((make-empty-node) (make-vector 4 0))
- ((node.left node) (vector-ref node 0))
- ((node.right node) (vector-ref node 1))
- ((node.left-set! node x) (vector-set! node 0 x))
- ((node.right-set! node x) (vector-set! node 1 x)))
-
- ; Build tree top down, assigning to older objects.
- (define (Populate iDepth thisNode)
- (if (<= iDepth 0)
- #f
- (let ((iDepth (- iDepth 1)))
- (node.left-set! thisNode (make-empty-node))
- (node.right-set! thisNode (make-empty-node))
- (Populate iDepth (node.left thisNode))
- (Populate iDepth (node.right thisNode)))))
-
- ; Build tree bottom-up
- (define (MakeTree iDepth)
- (if (<= iDepth 0)
- (make-empty-node)
- (make-node (MakeTree (- iDepth 1))
- (MakeTree (- iDepth 1)))))
-
- (define (TimeConstruction depth)
- (let ((iNumIters (NumIters depth)))
- (display (string-append "Creating "
- (number->string iNumIters)
- " trees of depth "
- (number->string depth)))
- (newline)
- (run-benchmark "GCBench: Top down construction"
- (lambda ()
- (do ((i 0 (+ i 1)))
- ((>= i iNumIters))
- (Populate depth (make-empty-node)))))
- (run-benchmark "GCBench: Bottom up construction"
- (lambda ()
- (do ((i 0 (+ i 1)))
- ((>= i iNumIters))
- (MakeTree depth))))))
-
- (define (main)
- (display "Garbage Collector Test")
- (newline)
- (display (string-append
- " Stretching memory with a binary tree of depth "
- (number->string kStretchTreeDepth)))
- (newline)
- (run-benchmark "GCBench: Main"
- (lambda ()
- ; Stretch the memory space quickly
- (MakeTree kStretchTreeDepth)
-
- ; Create a long lived object
- (display (string-append
- " Creating a long-lived binary tree of depth "
- (number->string kLongLivedTreeDepth)))
- (newline)
- (let ((longLivedTree (make-empty-node)))
- (Populate kLongLivedTreeDepth longLivedTree)
-
- ; Create long-lived array, filling half of it
- (display (string-append
- " Creating a long-lived array of "
- (number->string kArraySize)
- " inexact reals"))
- (newline)
- (let ((array (make-vector kArraySize 0.0)))
- (do ((i 0 (+ i 1)))
- ((>= i (quotient kArraySize 2)))
- (vector-set! array i (/ 1.0 (exact->inexact i))))
- (PrintDiagnostics)
-
- (do ((d kMinTreeDepth (+ d 2)))
- ((> d kMaxTreeDepth))
- (TimeConstruction d))
-
- (if (or (eq? longLivedTree '())
- (let ((n (min 1000
- (- (quotient (vector-length array)
- 2)
- 1))))
- (not (= (vector-ref array n)
- (/ 1.0 (exact->inexact
-n))))))
- (begin (display "Failed") (newline)))
- ; fake reference to LongLivedTree
- ; and array
- ; to keep them from being optimized away
- ))))
- (PrintDiagnostics))
-
- (main))))
-
-(define (gc-benchmark . rest)
- (let ((k (if (null? rest) 18 (car rest))))
- (display "The garbage collector should touch about ")
- (display (expt 2 (- k 13)))
- (display " megabytes of heap storage.")
- (newline)
- (display "The use of more or less memory will skew the results.")
- (newline)
- (run-benchmark (string-append "GCBench" (number->string k))
- (lambda () (gcbench k)))))
-
-
-
-(gc-benchmark )
-(display (gc-stats))
-(set! %load-path (cons (string-append (getenv "HOME") "/src/guile")
- %load-path))
-
-(load "../test-suite/guile-test")
-
-(main `("guile-test"
- "--test-suite" ,(string-append (getenv "HOME")
- "/src/guile/test-suite/tests")
- "--log-file" ",,test-suite.log"))
-;
-; GCOld.sch x.x 00/08/03
-; translated from GCOld.java 2.0a 00/08/23
-;
-; Copyright 2000 Sun Microsystems, Inc. All rights reserved.
-;
-;
-
-; Should be good enough for this benchmark.
-
-(define (newRandom)
- (letrec ((random14
- (lambda (n)
- (set! x (remainder (+ (* a x) c) m))
- (remainder (quotient x 8) n)))
- (a 701)
- (x 1)
- (c 743483)
- (m 524288)
- (loop
- (lambda (q r n)
- (if (zero? q)
- (remainder r n)
- (loop (quotient q 16384)
- (+ (* 16384 r) (random14 16384))
- n)))))
- (lambda (n)
- (if (and (exact? n) (integer? n) (< n 16384))
- (random14 n)
- (loop n (random14 16384) n)))))
-
-; A TreeNode is a record with three fields: left, right, val.
-; The left and right fields contain a TreeNode or 0, and the
-; val field will contain the integer height of the tree.
-
-(define-syntax newTreeNode
- (syntax-rules ()
- ((newTreeNode left right val)
- (vector left right val))
- ((newTreeNode)
- (vector 0 0 0))))
-
-(define-syntax TreeNode.left
- (syntax-rules ()
- ((TreeNode.left node)
- (vector-ref node 0))))
-
-(define-syntax TreeNode.right
- (syntax-rules ()
- ((TreeNode.right node)
- (vector-ref node 1))))
-
-(define-syntax TreeNode.val
- (syntax-rules ()
- ((TreeNode.val node)
- (vector-ref node 2))))
-
-(define-syntax setf
- (syntax-rules (TreeNode.left TreeNode.right TreeNode.val)
- ((setf (TreeNode.left node) x)
- (vector-set! node 0 x))
- ((setf (TreeNode.right node) x)
- (vector-set! node 1 x))
- ((setf (TreeNode.val node) x)
- (vector-set! node 2 x))))
-
-; Args:
-; live-data-size: in megabytes.
-; work: units of mutator non-allocation work per byte allocated,
-; (in unspecified units. This will affect the promotion rate
-; printed at the end of the run: more mutator work per step implies
-; fewer steps per second implies fewer bytes promoted per second.)
-; short/long ratio: ratio of short-lived bytes allocated to long-lived
-; bytes allocated.
-; pointer mutation rate: number of pointer mutations per step.
-; steps: number of steps to do.
-;
-
-(define (GCOld size workUnits promoteRate ptrMutRate steps)
-
- (define (println . args)
- (for-each display args)
- (newline))
-
- ; Rounds an inexact real to two decimal places.
-
- (define (round2 x)
- (/ (round (* 100.0 x)) 100.0))
-
- ; Returns the height of the given tree.
-
- (define (height t)
- (if (eqv? t 0)
- 0
- (+ 1 (max (height (TreeNode.left t))
- (height (TreeNode.right t))))))
-
- ; Returns the length of the shortest path in the given tree.
-
- (define (shortestPath t)
- (if (eqv? t 0)
- 0
- (+ 1 (min (shortestPath (TreeNode.left t))
- (shortestPath (TreeNode.right t))))))
-
- ; Returns the number of nodes in a balanced tree of the given height.
-
- (define (heightToNodes h)
- (- (expt 2 h) 1))
-
- ; Returns the height of the largest balanced tree
- ; that has no more than the given number of nodes.
-
- (define (nodesToHeight nodes)
- (do ((h 1 (+ h 1))
- (n 1 (+ n n)))
- ((> (+ n n -1) nodes)
- (- h 1))))
-
- (let* (
-
- ; Constants.
-
- (null 0) ; Java's null
- (pathBits 65536) ; to generate 16 random bits
-
- (MEG 1000000)
- (INSIGNIFICANT 999) ; this many bytes don't matter
- (bytes/word 4)
- (bytes/node 20) ; bytes per tree node in typical JVM
- (words/dead 100) ; size of young garbage objects
-
- ; Returns the number of bytes in a balanced tree of the given height.
-
- (heightToBytes
- (lambda (h)
- (* bytes/node (heightToNodes h))))
-
- ; Returns the height of the largest balanced tree
- ; that occupies no more than the given number of bytes.
-
- (bytesToHeight
- (lambda (bytes)
- (nodesToHeight (/ bytes bytes/node))))
-
- (treeHeight 14)
- (treeSize (heightToBytes treeHeight))
-
- (msg1 "Usage: java GCOld <size> <work> <ratio> <mutation> <steps>")
- (msg2 " where <size> is the live storage in megabytes")
- (msg3 " <work> is the mutator work per step (arbitrary units)")
- (msg4 " <ratio> is the ratio of short-lived to long-lived allocation")
- (msg5 " <mutation> is the mutations per step")
- (msg6 " <steps> is the number of steps")
-
- ; Counters (and global variables that discourage optimization).
-
- (youngBytes 0)
- (nodes 0)
- (actuallyMut 0)
- (mutatorSum 0)
- (aexport '#())
-
- ; Global variables.
-
- (trees '#())
- (where 0)
- (rnd (newRandom))
-
- )
-
- ; Returns a newly allocated balanced binary tree of height h.
-
- (define (makeTree h)
- (if (zero? h)
- null
- (let ((res (newTreeNode)))
- (set! nodes (+ nodes 1))
- (setf (TreeNode.left res) (makeTree (- h 1)))
- (setf (TreeNode.right res) (makeTree (- h 1)))
- (setf (TreeNode.val res) h)
- res)))
-
- ; Allocates approximately size megabytes of trees and stores
- ; them into a global array.
-
- (define (init)
- ; Each tree will be about a megabyte.
- (let ((ntrees (quotient (* size MEG) treeSize)))
- (set! trees (make-vector ntrees null))
- (println "Allocating " ntrees " trees.")
- (println " (" (* ntrees treeSize) " bytes)")
- (do ((i 0 (+ i 1)))
- ((>= i ntrees))
- (vector-set! trees i (makeTree treeHeight))
- (doYoungGenAlloc (* promoteRate ntrees treeSize) words/dead))
- (println " (" nodes " nodes)")))
-
- ; Confirms that all trees are balanced and have the correct height.
-
- (define (checkTrees)
- (let ((ntrees (vector-length trees)))
- (do ((i 0 (+ i 1)))
- ((>= i ntrees))
- (let* ((t (vector-ref trees i))
- (h1 (height t))
- (h2 (shortestPath t)))
- (if (or (not (= h1 treeHeight))
- (not (= h2 treeHeight)))
- (println "*****BUG: " h1 " " h2))))))
-
- ; Called only by replaceTree (below) and by itself.
-
- (define (replaceTreeWork full partial dir)
- (let ((canGoLeft (and (not (eq? (TreeNode.left full) null))
- (> (TreeNode.val (TreeNode.left full))
- (TreeNode.val partial))))
- (canGoRight (and (not (eq? (TreeNode.right full) null))
- (> (TreeNode.val (TreeNode.right full))
- (TreeNode.val partial)))))
- (cond ((and canGoLeft canGoRight)
- (if dir
- (replaceTreeWork (TreeNode.left full)
- partial
- (not dir))
- (replaceTreeWork (TreeNode.right full)
- partial
- (not dir))))
- ((and (not canGoLeft) (not canGoRight))
- (if dir
- (setf (TreeNode.left full) partial)
- (setf (TreeNode.right full) partial)))
- ((not canGoLeft)
- (setf (TreeNode.left full) partial))
- (else
- (setf (TreeNode.right full) partial)))))
-
- ; Given a balanced tree full and a smaller balanced tree partial,
- ; replaces an appropriate subtree of full by partial, taking care
- ; to preserve the shape of the full tree.
-
- (define (replaceTree full partial)
- (let ((dir (zero? (modulo (TreeNode.val partial) 2))))
- (set! actuallyMut (+ actuallyMut 1))
- (replaceTreeWork full partial dir)))
-
- ; Allocates approximately n bytes of long-lived storage,
- ; replacing oldest existing long-lived storage.
-
- (define (oldGenAlloc n)
- (let ((full (quotient n treeSize))
- (partial (modulo n treeSize)))
- ;(println "In oldGenAlloc, doing "
- ; full
- ; " full trees and one partial tree of size "
- ; partial)
- (do ((i 0 (+ i 1)))
- ((>= i full))
- (vector-set! trees where (makeTree treeHeight))
- (set! where
- (modulo (+ where 1) (vector-length trees))))
- (let loop ((partial partial))
- (if (> partial INSIGNIFICANT)
- (let* ((h (bytesToHeight partial))
- (newTree (makeTree h)))
- (replaceTree (vector-ref trees where) newTree)
- (set! where
- (modulo (+ where 1) (vector-length trees)))
- (loop (- partial (heightToBytes h))))))))
-
- ; Interchanges two randomly selected subtrees (of same size and depth).
-
- (define (oldGenSwapSubtrees)
- ; Randomly pick:
- ; * two tree indices
- ; * A depth
- ; * A path to that depth.
- (let* ((index1 (rnd (vector-length trees)))
- (index2 (rnd (vector-length trees)))
- (depth (rnd treeHeight))
- (path (rnd pathBits))
- (tn1 (vector-ref trees index1))
- (tn2 (vector-ref trees index2)))
- (do ((i 0 (+ i 1)))
- ((>= i depth))
- (if (even? path)
- (begin (set! tn1 (TreeNode.left tn1))
- (set! tn2 (TreeNode.left tn2)))
- (begin (set! tn1 (TreeNode.right tn1))
- (set! tn2 (TreeNode.right tn2))))
- (set! path (quotient path 2)))
- (if (even? path)
- (let ((tmp (TreeNode.left tn1)))
- (setf (TreeNode.left tn1) (TreeNode.left tn2))
- (setf (TreeNode.left tn2) tmp))
- (let ((tmp (TreeNode.right tn1)))
- (setf (TreeNode.right tn1) (TreeNode.right tn2))
- (setf (TreeNode.right tn2) tmp)))
- (set! actuallyMut (+ actuallyMut 2))))
-
- ; Update "n" old-generation pointers.
-
- (define (oldGenMut n)
- (do ((i 0 (+ i 1)))
- ((>= i (quotient n 2)))
- (oldGenSwapSubtrees)))
-
- ; Does the amount of mutator work appropriate for n bytes of young-gen
- ; garbage allocation.
-
- (define (doMutWork n)
- (let ((limit (quotient (* workUnits n) 10)))
- (do ((k 0 (+ k 1))
- (sum 0 (+ sum 1)))
- ((>= k limit)
- ; We don't want dead code elimination to eliminate this loop.
- (set! mutatorSum (+ mutatorSum sum))))))
-
- ; Allocate n bytes of young-gen garbage, in units of "nwords"
- ; words.
-
- (define (doYoungGenAlloc n nwords)
- (let ((nbytes (* nwords bytes/word)))
- (do ((allocated 0 (+ allocated nbytes)))
- ((>= allocated n)
- (set! youngBytes (+ youngBytes allocated)))
- (set! aexport (make-vector nwords 0)))))
-
- ; Allocate "n" bytes of young-gen data; and do the
- ; corresponding amount of old-gen allocation and pointer
- ; mutation.
-
- ; oldGenAlloc may perform some mutations, so this code
- ; takes those mutations into account.
-
- (define (doStep n)
- (let ((mutations actuallyMut))
- (doYoungGenAlloc n words/dead)
- (doMutWork n)
- ; Now do old-gen allocation
- (oldGenAlloc (quotient n promoteRate))
- (oldGenMut (max 0 (- (+ mutations ptrMutRate) actuallyMut)))))
-
- (println size " megabytes")
- (println workUnits " work units per step.")
- (println "promotion ratio is 1:" promoteRate)
- (println "pointer mutation rate is " ptrMutRate)
- (println steps " steps")
-
- (init)
- (checkTrees)
- (set! youngBytes 0)
- (set! nodes 0)
-
- (println "Initialization complete...")
-
- (run-benchmark "GCOld"
- 1
- (lambda ()
- (lambda ()
- (do ((step 0 (+ step 1)))
- ((>= step steps))
- (doStep MEG))))
- (lambda (result) #t))
-
- (checkTrees)
-
- (println "Allocated " steps " Mb of young gen garbage")
- (println " (actually allocated "
- (round2 (/ youngBytes MEG))
- " megabytes)")
- (println "Promoted " (round2 (/ steps promoteRate)) " Mb")
- (println " (actually promoted "
- (round2 (/ (* nodes bytes/node) MEG))
- " megabytes)")
- (if (not (zero? ptrMutRate))
- (println "Mutated " actuallyMut " pointers"))
-
- ; This output serves mainly to discourage optimization.
-
- (+ mutatorSum (vector-length aexport))))
-
-(define (gcold-benchmark . args)
- (define gcold-iters 1)
-
- (GCOld 25 0 10 10 gcold-iters))
-(let loop ((i 10000000))
- (and (> i 0)
- (loop (1- i))))
-
-;;; Copyright (C) 2008, 2009 Free Software Foundation, Inc.
-;;;
-;;; This program is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public License
-;;; as published by the Free Software Foundation; either version 3, or
-;;; (at your option) any later version.
-;;;
-;;; This program is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;;; GNU Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this software; see the file COPYING.LESSER. If
-;;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(use-modules (ice-9 rdelim)
- (ice-9 popen)
- (ice-9 regex)
- (ice-9 format)
- (ice-9 pretty-print)
- (srfi srfi-1)
- (srfi srfi-37))
-
-
-;;;
-;;; Running Guile.
-;;;
-
-(define (run-reference-guile env bench-dir profile-opts bench)
- "Run the ``mainstream'' Guile, i.e., Guile 1.9 with its own GC."
- (open-input-pipe (string-append
- env " "
- bench-dir "/gc-profile.scm " profile-opts
- " \"" bench "\"")))
-
-(define (run-bdwgc-guile env bench-dir profile-opts options bench)
- "Run the Guile port to the Boehm-Demers-Weiser GC (BDW-GC)."
- (let ((fsd (assoc-ref options 'free-space-divisor)))
- (open-input-pipe (string-append env " "
- "GC_FREE_SPACE_DIVISOR="
- (number->string fsd)
-
- (if (or (assoc-ref options 'incremental?)
- (assoc-ref options 'generational?))
- " GC_ENABLE_INCREMENTAL=yes"
- "")
- (if (assoc-ref options 'generational?)
- " GC_PAUSE_TIME_TARGET=999999"
- "")
- (if (assoc-ref options 'parallel?)
- "" ;; let it choose the number of procs
- " GC_MARKERS=1")
- " "
- bench-dir "/gc-profile.scm " profile-opts
- " \"" bench "\""))))
-
-
-;;;
-;;; Extracting performance results.
-;;;
-
-(define (grep regexp input)
- "Read line by line from the @var{input} port and return all matches for
-@var{regexp}."
- (let ((regexp (if (string? regexp) (make-regexp regexp) regexp)))
- (with-input-from-port input
- (lambda ()
- (let loop ((line (read-line))
- (result '()))
- (format #t "> ~A~%" line)
- (if (eof-object? line)
- (reverse result)
- (cond ((regexp-exec regexp line)
- =>
- (lambda (match)
- (loop (read-line)
- (cons match result))))
- (else
- (loop (read-line) result)))))))))
-
-(define (parse-result benchmark-output)
- (let ((result (grep "^(execution time|heap size):[[:blank:]]+([0-9.]+)"
- benchmark-output)))
- (fold (lambda (match result)
- (cond ((equal? (match:substring match 1) "execution time")
- (cons (cons 'execution-time
- (string->number (match:substring match 2)))
- result))
- ((equal? (match:substring match 1) "heap size")
- (cons (cons 'heap-size
- (string->number (match:substring match 2)))
- result))
- (else
- result)))
- '()
- result)))
-
-(define (pretty-print-result benchmark reference bdwgc)
- (define ref-heap (assoc-ref reference 'heap-size))
- (define ref-time (assoc-ref reference 'execution-time))
-
- (define (distance x1 y1 x2 y2)
- ;; Return the distance between (X1,Y1) and (X2,Y2). Y is the heap size,
- ;; in MiB and X is the execution time in seconds.
- (let ((y1 (/ y1 (expt 2 20)))
- (y2 (/ y2 (expt 2 20))))
- (sqrt (+ (expt (- y1 y2) 2)
- (expt (- x1 x2) 2)))))
-
- (define (score time heap)
- ;; Return a score lower than +1.0. The score is positive if the
- ;; distance to the origin of (TIME,HEAP) is smaller than that of
- ;; (REF-TIME,REF-HEAP), negative otherwise.
-
- ;; heap ^ .
- ;; size | . worse
- ;; | . [-]
- ;; | .
- ;; | . . . .ref. . . .
- ;; | .
- ;; | [+] .
- ;; | better .
- ;; 0 +-------------------->
- ;; exec. time
-
- (let ((ref-dist (distance ref-time ref-heap 0 0))
- (dist (distance time heap 0 0)))
- (/ (- ref-dist dist) ref-dist)))
-
- (define (score-string time heap)
- ;; Return a string denoting a bar to illustrate the score of (TIME,HEAP)
- ;; relative to (REF-TIME,REF-HEAP).
- (define %max-width 15)
-
- (let ((s (score time heap)))
- (make-string (inexact->exact (round (* (if (< s 0.0) (- s) s)
- %max-width)))
- (if (< s 0.0)
- #\-
- #\+))))
-
- (define (print-line name result ref?)
- (let ((name (string-pad-right name 23))
- (time (assoc-ref result 'execution-time))
- (heap (assoc-ref result 'heap-size)))
- (format #t "~a ~6,2f (~,2fx) ~7,3f (~,2fx)~A~%"
- name
- (/ heap (expt 2.0 20)) (/ heap ref-heap 1.0)
- time (/ time ref-time 1.0)
- (if (not ref?)
- (string-append " "
- (score-string time heap))
- ""))))
-
- (format #t "benchmark: `~a'~%" benchmark)
- (format #t " heap size (MiB) execution time (s.)~%")
- (print-line "Guile" reference #t)
- (for-each (lambda (bdwgc)
- (let ((name (format #f "BDW-GC, FSD=~a~a"
- (assoc-ref bdwgc 'free-space-divisor)
- (cond ((assoc-ref bdwgc 'incremental?)
- " incr.")
- ((assoc-ref bdwgc 'generational?)
- " gene.")
- ((assoc-ref bdwgc 'parallel?)
- " paral.")
- (else "")))))
- (print-line name bdwgc #f)))
- bdwgc))
-
-(define (print-raw-result benchmark reference bdwgc)
- (pretty-print `(,benchmark
- (reference . ,reference)
- (bdw-gc . ,bdwgc))))
-
-
-
-;;;
-;;; Option processing.
-;;;
-
-(define %options
- (list (option '(#\h "help") #f #f
- (lambda args
- (show-help)
- (exit 0)))
- (option '(#\r "reference") #t #f
- (lambda (opt name arg result)
- (alist-cons 'reference-environment arg
- (alist-delete 'reference-environment result
- eq?))))
- (option '(#\b "bdw-gc") #t #f
- (lambda (opt name arg result)
- (alist-cons 'bdwgc-environment arg
- (alist-delete 'bdwgc-environment result
- eq?))))
- (option '(#\d "benchmark-dir") #t #f
- (lambda (opt name arg result)
- (alist-cons 'benchmark-directory arg
- (alist-delete 'benchmark-directory result
- eq?))))
- (option '(#\p "profile-options") #t #f
- (lambda (opt name arg result)
- (let ((opts (assoc-ref result 'profile-options)))
- (alist-cons 'profile-options
- (string-append opts " " arg)
- (alist-delete 'profile-options result
- eq?)))))
- (option '(#\l "log-file") #t #f
- (lambda (opt name arg result)
- (alist-cons 'log-port (open-output-file arg)
- (alist-delete 'log-port result
- eq?))))
- (option '("raw") #f #f
- (lambda (opt name arg result)
- (alist-cons 'printer print-raw-result
- (alist-delete 'printer result eq?))))
- (option '("load-results") #f #f
- (lambda (opt name arg result)
- (alist-cons 'load-results? #t result)))))
-
-(define %default-options
- `((reference-environment . "GUILE=guile")
- (benchmark-directory . "./gc-benchmarks")
- (log-port . ,(current-output-port))
- (profile-options . "")
- (input . ())
- (printer . ,pretty-print-result)))
-
-(define (show-help)
- (format #t "Usage: run-benchmark [OPTIONS] BENCHMARKS...
-Run BENCHMARKS (a list of Scheme files) and display a performance
-comparison of standard Guile (1.9) and the BDW-GC-based Guile.
-
- -h, --help Show this help message
-
- -r, --reference=ENV
- -b, --bdw-gc=ENV
- Use ENV as the environment necessary to run the
- \"reference\" Guile (1.9) or the BDW-GC-based Guile,
- respectively. At a minimum, ENV should define the
- `GUILE' environment variable. For example:
-
- --reference='GUILE=/foo/bar/guile LD_LIBRARY_PATH=/foo'
-
- -p, --profile-options=OPTS
- Pass OPTS as additional options for `gc-profile.scm'.
- -l, --log-file=FILE
- Save output to FILE instead of the standard output.
-
- --raw Write benchmark results in raw (s-exp) format.
- --load-results
- Load raw (s-exp) results instead of actually running
- the benchmarks.
-
- -d, --benchmark-dir=DIR
- Use DIR as the GC benchmark directory where `gc-profile.scm'
- lives (it is automatically determined by default).
-
-Report bugs to <bug-guile@gnu.org>.~%"))
-
-(define (parse-args args)
- (define (leave fmt . args)
- (apply format (current-error-port) (string-append fmt "~%") args)
- (exit 1))
-
- (args-fold args %options
- (lambda (opt name arg result)
- (leave "~A: unrecognized option" opt))
- (lambda (file result)
- (let ((files (or (assoc-ref result 'input) '())))
- (alist-cons 'input (cons file files)
- (alist-delete 'input result eq?))))
- %default-options))
-
-
-;;;
-;;; The main program.
-;;;
-
-(define (main . args)
- (let* ((args (parse-args args))
- (benchmark-files (assoc-ref args 'input)))
-
- (let* ((log (assoc-ref args 'log-port))
- (bench-dir (assoc-ref args 'benchmark-directory))
- (ref-env (assoc-ref args 'reference-environment))
- (bdwgc-env (or (assoc-ref args 'bdwgc-environment)
- (string-append "GUILE=" bench-dir
- "/../meta/guile")))
- (prof-opts (assoc-ref args 'profile-options))
- (print (assoc-ref args 'printer)))
- (define (run benchmark)
- (let ((ref (parse-result (run-reference-guile ref-env
- bench-dir
- prof-opts
- benchmark)))
- (bdwgc (map (lambda (fsd incremental?
- generational? parallel?)
- (let ((opts
- (list
- (cons 'free-space-divisor fsd)
- (cons 'incremental? incremental?)
- (cons 'generational? generational?)
- (cons 'parallel? parallel?))))
- (append opts
- (parse-result
- (run-bdwgc-guile bdwgc-env
- bench-dir
- prof-opts
- opts
- benchmark)))))
- '( 3 6 9 3 3)
- '(#f #f #f #t #f) ;; incremental
- '(#f #f #f #f #t) ;; generational
- '(#f #f #f #f #f)))) ;; parallel
- `(,benchmark
- (reference . ,ref)
- (bdw-gc . ,bdwgc))))
-
- (define (load-results file)
- (with-input-from-file file
- (lambda ()
- (let loop ((results '()) (o (read)))
- (if (eof-object? o)
- (reverse results)
- (loop (cons o results)
- (read)))))))
-
- (for-each (lambda (result)
- (let ((benchmark (car result))
- (ref (assoc-ref (cdr result) 'reference))
- (bdwgc (assoc-ref (cdr result) 'bdw-gc)))
- (with-output-to-port log
- (lambda ()
- (print benchmark ref bdwgc)
- (newline)
- (force-output)))))
- (if (assoc-ref args 'load-results?)
- (append-map load-results benchmark-files)
- (map run benchmark-files))))))
-;;; From from http://www.ccs.neu.edu/home/will/Twobit/KVW/string.txt .
-; string test
-; (try 100000)
-
-(define s "abcdef")
-
-(define (grow)
- (set! s (string-append "123" s "456" s "789"))
- (set! s (string-append
- (substring s (quotient (string-length s) 2) (string-length s))
- (substring s 0 (+ 1 (quotient (string-length s) 2)))))
- s)
-
-(define (trial n)
- (do ((i 0 (+ i 1)))
- ((> (string-length s) n) (string-length s))
- (grow)))
-
-(define (try n)
- (do ((i 0 (+ i 1)))
- ((>= i 10) (string-length s))
- (set! s "abcdef")
- (trial n)))
-
-(try 50000000);;;; readline.scm --- support functions for command-line editing
-;;;;
-;;;; Copyright (C) 1997, 1999, 2000, 2001, 2002, 2006, 2009, 2010, 2011, 2014 Free Software Foundation, Inc.
-;;;;
-;;;; This program is free software; you can redistribute it and/or modify
-;;;; it under the terms of the GNU General Public License as published by
-;;;; the Free Software Foundation; either version 3, or (at your option)
-;;;; any later version.
-;;;;
-;;;; This program is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;;;; GNU General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU General Public License
-;;;; along with this software; see the file COPYING. If not, write to
-;;;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;;;; Boston, MA 02110-1301 USA
-;;;;
-;;;; Contributed by Daniel Risacher <risacher@worldnet.att.net>.
-;;;; Extensions based upon code by
-;;;; Andrew Archibald <aarchiba@undergrad.math.uwaterloo.ca>.
-
-
-
-(define-module (ice-9 readline)
- #\use-module (ice-9 session)
- #\use-module (ice-9 regex)
- #\use-module (ice-9 buffered-input)
- #\no-backtrace
- #\export (filename-completion-function
- add-history
- read-history
- write-history
- clear-history))
-
-
-
-;;; Dynamically link the glue code for accessing the readline library,
-;;; but only when it isn't already present.
-
-(if (not (provided? 'readline))
- (load-extension "libguilereadline-v-18" "scm_init_readline"))
-
-(if (not (provided? 'readline))
- (scm-error 'misc-error
- #f
- "readline is not provided in this Guile installation"
- '()
- '()))
-
-
-
-;;; Run-time options
-
-(export
- readline-options
- readline-enable
- readline-disable)
-(export-syntax
- readline-set!)
-
-(define-option-interface
- (readline-options-interface
- (readline-options readline-enable readline-disable)
- (readline-set!)))
-
-
-
-;;; MDJ 980513 <djurfeldt@nada.kth.se>:
-;;; There should probably be low-level support instead of this code.
-
-;;; Dirk:FIXME:: If the-readline-port, input-port or output-port are closed,
-;;; guile will enter an endless loop or crash.
-
-(define-once new-input-prompt "")
-(define-once continuation-prompt "")
-(define-once input-port (current-input-port))
-(define-once output-port (current-output-port))
-(define-once read-hook #f)
-
-(define (make-readline-port)
- (let ((history-buffer #f))
- (make-line-buffered-input-port (lambda (continuation?)
- ;; When starting a new read, add
- ;; the previously read expression
- ;; to the history.
- (if (and (not continuation?)
- history-buffer)
- (begin
- (add-history history-buffer)
- (set! history-buffer #f)))
- ;; Set up prompts and read a line.
- (let* ((prompt (if continuation?
- continuation-prompt
- new-input-prompt))
- (str (%readline (if (string? prompt)
- prompt
- (prompt))
- input-port
- output-port
- read-hook)))
- (or (eof-object? str)
- (string=? str "")
- (set! history-buffer
- (if history-buffer
- (string-append history-buffer
- "\n"
- str)
- str)))
- str)))))
-
-;;; We only create one readline port. There's no point in having
-;;; more, since they would all share the tty and history ---
-;;; everything except the prompt. And don't forget the
-;;; compile/load/run phase distinctions. Also, the readline library
-;;; isn't reentrant.
-(define-once the-readline-port #f)
-
-(define-once history-variable "GUILE_HISTORY")
-(define-once history-file
- (string-append (or (getenv "HOME") ".") "/.guile_history"))
-
-(define-public readline-port
- (let ((do (lambda (r/w)
- (if (memq 'history-file (readline-options-interface))
- (r/w (or (getenv history-variable)
- history-file))))))
- (lambda ()
- (if (not the-readline-port)
- (begin
- (do read-history)
- (set! the-readline-port (make-readline-port))
- (add-hook! exit-hook (lambda ()
- (do write-history)
- (clear-history)))))
- the-readline-port)))
-
-;;; The user might try to use readline in his programs. It then
-;;; becomes very uncomfortable that the current-input-port is the
-;;; readline port...
-;;;
-;;; Here, we detect this situation and replace it with the
-;;; underlying port.
-;;;
-;;; %readline is the low-level readline procedure.
-
-(define-public (readline . args)
- (let ((prompt new-input-prompt)
- (inp input-port))
- (cond ((not (null? args))
- (set! prompt (car args))
- (set! args (cdr args))
- (cond ((not (null? args))
- (set! inp (car args))
- (set! args (cdr args))))))
- (apply %readline
- prompt
- (if (eq? inp the-readline-port)
- input-port
- inp)
- args)))
-
-(define-public (set-readline-prompt! p . rest)
- (set! new-input-prompt p)
- (if (not (null? rest))
- (set! continuation-prompt (car rest))))
-
-(define-public (set-readline-input-port! p)
- (cond ((or (not (file-port? p)) (not (input-port? p)))
- (scm-error 'wrong-type-arg "set-readline-input-port!"
- "Not a file input port: ~S" (list p) #f))
- ((port-closed? p)
- (scm-error 'misc-error "set-readline-input-port!"
- "Port not open: ~S" (list p) #f))
- (else
- (set! input-port p))))
-
-(define-public (set-readline-output-port! p)
- (cond ((or (not (file-port? p)) (not (output-port? p)))
- (scm-error 'wrong-type-arg "set-readline-input-port!"
- "Not a file output port: ~S" (list p) #f))
- ((port-closed? p)
- (scm-error 'misc-error "set-readline-output-port!"
- "Port not open: ~S" (list p) #f))
- (else
- (set! output-port p))))
-
-(define-public (set-readline-read-hook! h)
- (set! read-hook h))
-
-(define-public apropos-completion-function
- (let ((completions '()))
- (lambda (text cont?)
- (if (not cont?)
- (set! completions
- (map symbol->string
- (apropos-internal
- (string-append "^" (regexp-quote text))))))
- (if (null? completions)
- #f
- (let ((retval (car completions)))
- (begin (set! completions (cdr completions))
- retval))))))
-
-(if (provided? 'regex)
- (set! *readline-completion-function* apropos-completion-function))
-
-(define-public (with-readline-completion-function completer thunk)
- "With @var{completer} as readline completion function, call @var{thunk}."
- (let ((old-completer *readline-completion-function*))
- (dynamic-wind
- (lambda ()
- (set! *readline-completion-function* completer))
- thunk
- (lambda ()
- (set! *readline-completion-function* old-completer)))))
-
-(define-once readline-repl-reader
- (let ((boot-9-repl-reader repl-reader))
- (lambda* (repl-prompt #\optional (reader (fluid-ref current-reader)))
- (let ((port (current-input-port)))
- (if (eq? port (readline-port))
- (let ((outer-new-input-prompt new-input-prompt)
- (outer-continuation-prompt continuation-prompt)
- (outer-read-hook read-hook))
- (dynamic-wind
- (lambda ()
- (set-buffered-input-continuation?! port #f)
- (set-readline-prompt! repl-prompt "... ")
- (set-readline-read-hook! (lambda ()
- (run-hook before-read-hook))))
- (lambda () ((or reader read) port))
- (lambda ()
- (set-readline-prompt! outer-new-input-prompt
- outer-continuation-prompt)
- (set-readline-read-hook! outer-read-hook))))
- (boot-9-repl-reader repl-prompt reader))))))
-
-(define-public (activate-readline)
- (if (isatty? (current-input-port))
- (begin
- (set-current-input-port (readline-port))
- (set! repl-reader readline-repl-reader)
- (set! (using-readline?) #t))))
-
-(define-public (make-completion-function strings)
- "Construct and return a completion function for a list of strings.
-The returned function is suitable for passing to
-@code{with-readline-completion-function. The argument @var{strings}
-should be a list of strings, where each string is one of the possible
-completions."
- (letrec ((strs '())
- (regexp #f)
- (completer (lambda (text continue?)
- (if continue?
- (if (null? strs)
- #f
- (let ((str (car strs)))
- (set! strs (cdr strs))
- (if (string-match regexp str)
- str
- (completer text #t))))
- (begin
- (set! strs strings)
- (set! regexp
- (string-append "^" (regexp-quote text)))
- (completer text #t))))))
- completer))
-;;; GDB debugging support for Guile.
-;;;
-;;; Copyright 2014, 2015 Free Software Foundation, Inc.
-;;;
-;;; This program is free software; you can redistribute it and/or modify it
-;;; under the terms of the GNU General Public License as published by
-;;; the Free Software Foundation; either version 3 of the License, or (at
-;;; your option) any later version.
-;;;
-;;; This program is distributed in the hope that it will be useful, but
-;;; WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;;; GNU General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU General Public License
-;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-(define-module (guile-gdb)
- #\use-module (system base types)
- #\use-module ((gdb) #\hide (symbol?))
- #\use-module (gdb printing)
- #\use-module (srfi srfi-11)
- #\use-module (ice-9 match)
- #\export (%gdb-memory-backend
- display-vm-frames))
-
-;;; Commentary:
-;;;
-;;; This file defines GDB extensions to pretty-print 'SCM' objects, and
-;;; to walk Guile's virtual machine stack.
-;;;
-;;; This file is installed under a name that follows the convention that
-;;; allows GDB to auto-load it anytime the user is debugging libguile
-;;; (info "(gdb) objfile-gdbdotext file").
-;;;
-;;; Code:
-
-(define (type-name-from-descriptor descriptor-array type-number)
- "Return the name of the type TYPE-NUMBER as seen in DESCRIPTOR-ARRAY, or #f
-if the information is not available."
- (let ((descriptors (lookup-global-symbol descriptor-array)))
- (and descriptors
- (let ((code (type-code (symbol-type descriptors))))
- (or (= TYPE_CODE_ARRAY code)
- (= TYPE_CODE_PTR code)))
- (let* ((type-descr (value-subscript (symbol-value descriptors)
- type-number))
- (name (value-field type-descr "name")))
- (value->string name)))))
-
-(define (scm-value->integer value)
- "Return the integer value of VALUE, which is assumed to be a GDB value
-corresponding to an 'SCM' object."
- (let ((type (type-strip-typedefs (value-type value))))
- (cond ((= (type-code type) TYPE_CODE_UNION)
- ;; SCM_DEBUG_TYPING_STRICTNESS = 2
- (value->integer (value-field (value-field value "n")
- "n")))
- (else
- ;; SCM_DEBUG_TYPING_STRICTNESS = 1
- (value->integer value)))))
-
-(define %gdb-memory-backend
- ;; The GDB back-end to access the inferior's memory.
- (let ((void* (type-pointer (lookup-type "void"))))
- (define (dereference-word address)
- ;; Return the word at ADDRESS.
- (value->integer
- (value-dereference (value-cast (make-value address)
- (type-pointer void*)))))
-
- (define (open address size)
- ;; Return a port to the SIZE bytes starting at ADDRESS.
- (if size
- (open-memory #\start address #\size size)
- (open-memory #\start address)))
-
- (define (type-name kind number)
- ;; Return the type name of KIND type NUMBER.
- (type-name-from-descriptor (case kind
- ((smob) "scm_smobs")
- ((port) "scm_ptobs"))
- number))
-
- (memory-backend dereference-word open type-name)))
-
-
-;;;
-;;; GDB pretty-printer registration.
-;;;
-
-(define (make-scm-pretty-printer-worker obj)
- (define (list->iterator list)
- (make-iterator list list
- (let ((n 0))
- (lambda (iter)
- (match (iterator-progress iter)
- (() (end-of-iteration))
- ((elt . list)
- (set-iterator-progress! iter list)
- (let ((name (format #f "[~a]" n)))
- (set! n (1+ n))
- (cons name (object->string elt)))))))))
- (cond
- ((string? obj)
- (make-pretty-printer-worker
- "string" ; display hint
- (lambda (printer) obj)
- #f))
- ((and (array? obj)
- (match (array-shape obj)
- (((0 _)) #t)
- (_ #f)))
- (make-pretty-printer-worker
- "array" ; display hint
- (lambda (printer)
- (let ((tag (array-type obj)))
- (case tag
- ((#t) "#<vector>")
- ((b) "#<bitvector>")
- (else (format #f "#<~avector>" tag)))))
- (lambda (printer)
- (list->iterator (array->list obj)))))
- ((inferior-struct? obj)
- (make-pretty-printer-worker
- "array" ; display hint
- (lambda (printer)
- (format #f "#<struct ~a>" (inferior-struct-name obj)))
- (lambda (printer)
- (list->iterator (inferior-struct-fields obj)))))
- (else
- (make-pretty-printer-worker
- #f ; display hint
- (lambda (printer)
- (object->string obj))
- #f))))
-
-(define %scm-pretty-printer
- (make-pretty-printer
- "SCM"
- (lambda (pp value)
- (let ((name (type-name (value-type value))))
- (and (and name (string=? name "SCM"))
- (make-scm-pretty-printer-worker
- (scm->object (scm-value->integer value) %gdb-memory-backend)))))))
-
-(define* (register-pretty-printer #\optional objfile)
- (prepend-pretty-printer! objfile %scm-pretty-printer))
-
-(register-pretty-printer)
-
-
-;;;
-;;; VM stack walking.
-;;;
-
-(define (find-vm-engine-frame)
- "Return the bottom-most frame containing a call to the VM engine."
- (define (vm-engine-frame? frame)
- (let ((sym (frame-function frame)))
- (and sym
- (member (symbol-name sym)
- '("vm_debug_engine" "vm_regular_engine")))))
-
- (let loop ((frame (newest-frame)))
- (and frame
- (if (vm-engine-frame? frame)
- frame
- (loop (frame-older frame))))))
-
-(define (vm-stack-pointer)
- "Return the current value of the VM stack pointer or #f."
- (let ((frame (find-vm-engine-frame)))
- (and frame
- (frame-read-var frame "sp"))))
-
-(define (vm-frame-pointer)
- "Return the current value of the VM frame pointer or #f."
- (let ((frame (find-vm-engine-frame)))
- (and frame
- (frame-read-var frame "fp"))))
-
-(define* (display-vm-frames #\optional (port (current-output-port)))
- "Display the VM frames on PORT."
- (define (display-objects start end)
- ;; Display all the objects (arguments and local variables) located
- ;; between START and END.
- (let loop ((number 0)
- (address start))
- (when (and (> start 0) (<= address end))
- (let ((object (dereference-word %gdb-memory-backend address)))
- ;; TODO: Push onto GDB's value history.
- (format port " slot ~a -> ~s~%"
- number (scm->object object %gdb-memory-backend)))
- (loop (+ 1 number) (+ address %word-size)))))
-
- (let loop ((number 0)
- (sp (value->integer (vm-stack-pointer)))
- (fp (value->integer (vm-frame-pointer))))
- (unless (zero? fp)
- (let-values (((ra mvra link proc)
- (vm-frame fp %gdb-memory-backend)))
- (format port "#~a ~s~%" number (scm->object proc %gdb-memory-backend))
- (display-objects fp sp)
- (loop (+ 1 number) (- fp (* 5 %word-size)) link)))))
-
-;; See libguile/frames.h.
-(define* (vm-frame fp #\optional (backend %gdb-memory-backend))
- "Return the components of the stack frame at FP."
- (let ((caller (dereference-word backend (- fp %word-size)))
- (ra (dereference-word backend (- fp (* 2 %word-size))))
- (mvra (dereference-word backend (- fp (* 3 %word-size))))
- (link (dereference-word backend (- fp (* 4 %word-size)))))
- (values ra mvra link caller)))
-
-;;; libguile-2.0-gdb.scm ends here
-;;;; and-let-star.scm --- and-let* syntactic form (SRFI-2) for Guile
-;;;;
-;;;; Copyright (C) 1999, 2001, 2004, 2006, 2013,
-;;;; 2015 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (ice-9 and-let-star)
- \:export-syntax (and-let*))
-
-(define-syntax %and-let*
- (lambda (form)
- (syntax-case form ()
-
- ;; Handle zero-clauses special-case.
- ((_ orig-form () . body)
- #'(begin #t . body))
-
- ;; Reduce clauses down to one regardless of body.
- ((_ orig-form ((var expr) rest . rest*) . body)
- (identifier? #'var)
- #'(let ((var expr))
- (and var (%and-let* orig-form (rest . rest*) . body))))
- ((_ orig-form ((expr) rest . rest*) . body)
- #'(and expr (%and-let* orig-form (rest . rest*) . body)))
- ((_ orig-form (var rest . rest*) . body)
- (identifier? #'var)
- #'(and var (%and-let* orig-form (rest . rest*) . body)))
-
- ;; Handle 1-clause cases without a body.
- ((_ orig-form ((var expr)))
- (identifier? #'var)
- #'expr)
- ((_ orig-form ((expr)))
- #'expr)
- ((_ orig-form (var))
- (identifier? #'var)
- #'var)
-
- ;; Handle 1-clause cases with a body.
- ((_ orig-form ((var expr)) . body)
- (identifier? #'var)
- #'(let ((var expr))
- (and var (begin . body))))
- ((_ orig-form ((expr)) . body)
- #'(and expr (begin . body)))
- ((_ orig-form (var) . body)
- (identifier? #'var)
- #'(and var (begin . body)))
-
- ;; Handle bad clauses.
- ((_ orig-form (bad-clause . rest) . body)
- (syntax-violation 'and-let* "Bad clause" #'orig-form #'bad-clause)))))
-
-(define-syntax and-let*
- (lambda (form)
- (syntax-case form ()
- ((_ (c ...) body ...)
- #`(%and-let* #,form (c ...) body ...)))))
-
-(cond-expand-provide (current-module) '(srfi-2))
-;;; installed-scm-file
-
-;;;; Copyright (C) 1999, 2001, 2004, 2006 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-(define (array-shape a)
- (map (lambda (ind) (if (number? ind) (list 0 (+ -1 ind)) ind))
- (array-dimensions a)))
-;;;; binary-ports.scm --- Binary IO on ports
-
-;;;; Copyright (C) 2009, 2010, 2011, 2013 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Ludovic Courtès <ludo@gnu.org>
-
-;;; Commentary:
-;;;
-;;; The I/O port API of the R6RS is provided by this module. In many areas
-;;; it complements or refines Guile's own historical port API. For instance,
-;;; it allows for binary I/O with bytevectors.
-;;;
-;;; Code:
-
-(define-module (ice-9 binary-ports)
- #\use-module (rnrs bytevectors)
- #\export (eof-object
- open-bytevector-input-port
- make-custom-binary-input-port
- get-u8
- lookahead-u8
- get-bytevector-n
- get-bytevector-n!
- get-bytevector-some
- get-bytevector-all
- get-string-n!
- put-u8
- put-bytevector
- unget-bytevector
- open-bytevector-output-port
- make-custom-binary-output-port))
-
-;; Note that this extension also defines %make-transcoded-port, which is
-;; not exported but is used by (rnrs io ports).
-
-(load-extension (string-append "libguile-" (effective-version))
- "scm_init_r6rs_ports")
-;;; -*- mode: scheme; coding: utf-8; -*-
-
-;;;; Copyright (C) 1995-2014, 2016 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-
-;;; Commentary:
-
-;;; This file is the first thing loaded into Guile. It adds many mundane
-;;; definitions and a few that are interesting.
-;;;
-;;; The module system (hence the hierarchical namespace) are defined in this
-;;; file.
-;;;
-
-;;; Code:
-
-
-
-;; Before compiling, make sure any symbols are resolved in the (guile)
-;; module, the primary location of those symbols, rather than in
-;; (guile-user), the default module that we compile in.
-
-(eval-when (compile)
- (set-current-module (resolve-module '(guile))))
-
-;; Prevent this file being loaded more than once in a session. Just
-;; doesn't make sense!
-(if (current-module)
- (error "re-loading ice-9/boot-9.scm not allowed"))
-
-
-
-;;; {Error handling}
-;;;
-
-;; Define delimited continuation operators, and implement catch and throw in
-;; terms of them.
-
-(define make-prompt-tag
- (lambda* (#\optional (stem "prompt"))
- (gensym stem)))
-
-(define default-prompt-tag
- ;; not sure if we should expose this to the user as a fluid
- (let ((%default-prompt-tag (make-prompt-tag)))
- (lambda ()
- %default-prompt-tag)))
-
-(define (call-with-prompt tag thunk handler)
- (@prompt tag (thunk) handler))
-(define (abort-to-prompt tag . args)
- (@abort tag args))
-
-
-;; Define catch and with-throw-handler, using some common helper routines and a
-;; shared fluid. Hide the helpers in a lexical contour.
-
-(define with-throw-handler #f)
-(let ()
- (define (default-exception-handler k . args)
- (cond
- ((eq? k 'quit)
- (primitive-exit (cond
- ((not (pair? args)) 0)
- ((integer? (car args)) (car args))
- ((not (car args)) 1)
- (else 0))))
- (else
- (format (current-error-port) "guile: uncaught throw to ~a: ~a\n" k args)
- (primitive-exit 1))))
-
- (define %running-exception-handlers (make-fluid '()))
- (define %exception-handler (make-fluid default-exception-handler))
-
- (define (default-throw-handler prompt-tag catch-k)
- (let ((prev (fluid-ref %exception-handler)))
- (lambda (thrown-k . args)
- (if (or (eq? thrown-k catch-k) (eqv? catch-k #t))
- (apply abort-to-prompt prompt-tag thrown-k args)
- (apply prev thrown-k args)))))
-
- (define (custom-throw-handler prompt-tag catch-k pre)
- (let ((prev (fluid-ref %exception-handler)))
- (lambda (thrown-k . args)
- (if (or (eq? thrown-k catch-k) (eqv? catch-k #t))
- (let ((running (fluid-ref %running-exception-handlers)))
- (with-fluids ((%running-exception-handlers (cons pre running)))
- (if (not (memq pre running))
- (apply pre thrown-k args))
- ;; fall through
- (if prompt-tag
- (apply abort-to-prompt prompt-tag thrown-k args)
- (apply prev thrown-k args))))
- (apply prev thrown-k args)))))
-
- (set! catch
- (lambda* (k thunk handler #\optional pre-unwind-handler)
- "Invoke @var{thunk} in the dynamic context of @var{handler} for
-exceptions matching @var{key}. If thunk throws to the symbol
-@var{key}, then @var{handler} is invoked this way:
-@lisp
- (handler key args ...)
-@end lisp
-
-@var{key} is a symbol or @code{#t}.
-
-@var{thunk} takes no arguments. If @var{thunk} returns
-normally, that is the return value of @code{catch}.
-
-Handler is invoked outside the scope of its own @code{catch}.
-If @var{handler} again throws to the same key, a new handler
-from further up the call chain is invoked.
-
-If the key is @code{#t}, then a throw to @emph{any} symbol will
-match this call to @code{catch}.
-
-If a @var{pre-unwind-handler} is given and @var{thunk} throws
-an exception that matches @var{key}, Guile calls the
-@var{pre-unwind-handler} before unwinding the dynamic state and
-invoking the main @var{handler}. @var{pre-unwind-handler} should
-be a procedure with the same signature as @var{handler}, that
-is @code{(lambda (key . args))}. It is typically used to save
-the stack at the point where the exception occurred, but can also
-query other parts of the dynamic state at that point, such as
-fluid values.
-
-A @var{pre-unwind-handler} can exit either normally or non-locally.
-If it exits normally, Guile unwinds the stack and dynamic context
-and then calls the normal (third argument) handler. If it exits
-non-locally, that exit determines the continuation."
- (if (not (or (symbol? k) (eqv? k #t)))
- (scm-error 'wrong-type-arg "catch"
- "Wrong type argument in position ~a: ~a"
- (list 1 k) (list k)))
- (let ((tag (make-prompt-tag "catch")))
- (call-with-prompt
- tag
- (lambda ()
- (with-fluids
- ((%exception-handler
- (if pre-unwind-handler
- (custom-throw-handler tag k pre-unwind-handler)
- (default-throw-handler tag k))))
- (thunk)))
- (lambda (cont k . args)
- (apply handler k args))))))
-
- (set! with-throw-handler
- (lambda (k thunk pre-unwind-handler)
- "Add @var{handler} to the dynamic context as a throw handler
-for key @var{k}, then invoke @var{thunk}."
- (if (not (or (symbol? k) (eqv? k #t)))
- (scm-error 'wrong-type-arg "with-throw-handler"
- "Wrong type argument in position ~a: ~a"
- (list 1 k) (list k)))
- (with-fluids ((%exception-handler
- (custom-throw-handler #f k pre-unwind-handler)))
- (thunk))))
-
- (set! throw
- (lambda (key . args)
- "Invoke the catch form matching @var{key}, passing @var{args} to the
-@var{handler}.
-
-@var{key} is a symbol. It will match catches of the same symbol or of @code{#t}.
-
-If there is no handler at all, Guile prints an error and then exits."
- (if (not (symbol? key))
- ((fluid-ref %exception-handler) 'wrong-type-arg "throw"
- "Wrong type argument in position ~a: ~a" (list 1 key) (list key))
- (apply (fluid-ref %exception-handler) key args)))))
-
-
-
-;;; Boot versions of `map' and `for-each', enough to get the expander
-;;; running, and get the "map" used in eval.scm for with-fluids to work.
-;;;
-(define map
- (case-lambda
- ((f l)
- (let map1 ((l l))
- (if (null? l)
- '()
- (cons (f (car l)) (map1 (cdr l))))))
- ((f l1 l2)
- (let map2 ((l1 l1) (l2 l2))
- (if (null? l1)
- '()
- (cons (f (car l1) (car l2))
- (map2 (cdr l1) (cdr l2))))))
- ((f l1 . rest)
- (let lp ((l1 l1) (rest rest))
- (if (null? l1)
- '()
- (cons (apply f (car l1) (map car rest))
- (lp (cdr l1) (map cdr rest))))))))
-
-(define for-each
- (case-lambda
- ((f l)
- (let for-each1 ((l l))
- (if (pair? l)
- (begin
- (f (car l))
- (for-each1 (cdr l))))))
- ((f l1 l2)
- (let for-each2 ((l1 l1) (l2 l2))
- (if (pair? l1)
- (begin
- (f (car l1) (car l2))
- (for-each2 (cdr l1) (cdr l2))))))
- ((f l1 . rest)
- (let lp ((l1 l1) (rest rest))
- (if (pair? l1)
- (begin
- (apply f (car l1) (map car rest))
- (lp (cdr l1) (map cdr rest))))))))
-
-
-
-;;; {R4RS compliance}
-;;;
-
-(primitive-load-path "ice-9/r4rs")
-
-
-
-;;; {Simple Debugging Tools}
-;;;
-
-;; peek takes any number of arguments, writes them to the
-;; current ouput port, and returns the last argument.
-;; It is handy to wrap around an expression to look at
-;; a value each time is evaluated, e.g.:
-;;
-;; (+ 10 (troublesome-fn))
-;; => (+ 10 (pk 'troublesome-fn-returned (troublesome-fn)))
-;;
-
-(define (peek . stuff)
- (newline)
- (display ";;; ")
- (write stuff)
- (newline)
- (car (last-pair stuff)))
-
-(define pk peek)
-
-(define (warn . stuff)
- (with-output-to-port (current-warning-port)
- (lambda ()
- (newline)
- (display ";;; WARNING ")
- (display stuff)
- (newline)
- (car (last-pair stuff)))))
-
-
-
-;;; {Features}
-;;;
-
-(define (provide sym)
- (if (not (memq sym *features*))
- (set! *features* (cons sym *features*))))
-
-;; Return #t iff FEATURE is available to this Guile interpreter. In SLIB,
-;; provided? also checks to see if the module is available. We should do that
-;; too, but don't.
-
-(define (provided? feature)
- (and (memq feature *features*) #t))
-
-
-
-;;; {Structs}
-;;;
-
-(define (make-struct/no-tail vtable . args)
- (apply make-struct vtable 0 args))
-
-
-
-;; Temporary definition used in the include-from-path expansion;
-;; replaced later.
-
-(define (absolute-file-name? file-name)
- #t)
-
-;;; {and-map and or-map}
-;;;
-;;; (and-map fn lst) is like (and (fn (car lst)) (fn (cadr lst)) (fn...) ...)
-;;; (or-map fn lst) is like (or (fn (car lst)) (fn (cadr lst)) (fn...) ...)
-;;;
-
-;; and-map f l
-;;
-;; Apply f to successive elements of l until exhaustion or f returns #f.
-;; If returning early, return #f. Otherwise, return the last value returned
-;; by f. If f has never been called because l is empty, return #t.
-;;
-(define (and-map f lst)
- (let loop ((result #t)
- (l lst))
- (and result
- (or (and (null? l)
- result)
- (loop (f (car l)) (cdr l))))))
-
-;; or-map f l
-;;
-;; Apply f to successive elements of l until exhaustion or while f returns #f.
-;; If returning early, return the return value of f.
-;;
-(define (or-map f lst)
- (let loop ((result #f)
- (l lst))
- (or result
- (and (not (null? l))
- (loop (f (car l)) (cdr l))))))
-
-
-
-;; let format alias simple-format until the more complete version is loaded
-
-(define format simple-format)
-
-;; this is scheme wrapping the C code so the final pred call is a tail call,
-;; per SRFI-13 spec
-(define string-any
- (lambda* (char_pred s #\optional (start 0) (end (string-length s)))
- (if (and (procedure? char_pred)
- (> end start)
- (<= end (string-length s))) ;; let c-code handle range error
- (or (string-any-c-code char_pred s start (1- end))
- (char_pred (string-ref s (1- end))))
- (string-any-c-code char_pred s start end))))
-
-;; this is scheme wrapping the C code so the final pred call is a tail call,
-;; per SRFI-13 spec
-(define string-every
- (lambda* (char_pred s #\optional (start 0) (end (string-length s)))
- (if (and (procedure? char_pred)
- (> end start)
- (<= end (string-length s))) ;; let c-code handle range error
- (and (string-every-c-code char_pred s start (1- end))
- (char_pred (string-ref s (1- end))))
- (string-every-c-code char_pred s start end))))
-
-;; A variant of string-fill! that we keep for compatability
-;;
-(define (substring-fill! str start end fill)
- (string-fill! str fill start end))
-
-
-
-;; Define a minimal stub of the module API for psyntax, before modules
-;; have booted.
-(define (module-name x)
- '(guile))
-(define (module-add! module sym var)
- (hashq-set! (%get-pre-modules-obarray) sym var))
-(define (module-define! module sym val)
- (let ((v (hashq-ref (%get-pre-modules-obarray) sym)))
- (if v
- (variable-set! v val)
- (module-add! (current-module) sym (make-variable val)))))
-(define (module-ref module sym)
- (let ((v (module-variable module sym)))
- (if v (variable-ref v) (error "badness!" (pk module) (pk sym)))))
-(define module-generate-unique-id!
- (let ((next-id 0))
- (lambda (m)
- (let ((i next-id))
- (set! next-id (+ i 1))
- i))))
-(define module-gensym gensym)
-(define (resolve-module . args)
- #f)
-
-;; API provided by psyntax
-(define syntax-violation #f)
-(define datum->syntax #f)
-(define syntax->datum #f)
-(define syntax-source #f)
-(define identifier? #f)
-(define generate-temporaries #f)
-(define bound-identifier=? #f)
-(define free-identifier=? #f)
-
-;; $sc-dispatch is an implementation detail of psyntax. It is used by
-;; expanded macros, to dispatch an input against a set of patterns.
-(define $sc-dispatch #f)
-
-;; Load it up!
-(primitive-load-path "ice-9/psyntax-pp")
-;; The binding for `macroexpand' has now been overridden, making psyntax the
-;; expander now.
-
-(define-syntax and
- (syntax-rules ()
- ((_) #t)
- ((_ x) x)
- ;; Avoid ellipsis, which would lead to quadratic expansion time.
- ((_ x . y) (if x (and . y) #f))))
-
-(define-syntax or
- (syntax-rules ()
- ((_) #f)
- ((_ x) x)
- ;; Avoid ellipsis, which would lead to quadratic expansion time.
- ((_ x . y) (let ((t x)) (if t t (or . y))))))
-
-(include-from-path "ice-9/quasisyntax")
-
-(define-syntax-rule (when test stmt stmt* ...)
- (if test (begin stmt stmt* ...)))
-
-(define-syntax-rule (unless test stmt stmt* ...)
- (if (not test) (begin stmt stmt* ...)))
-
-(define-syntax cond
- (lambda (whole-expr)
- (define (fold f seed xs)
- (let loop ((xs xs) (seed seed))
- (if (null? xs) seed
- (loop (cdr xs) (f (car xs) seed)))))
- (define (reverse-map f xs)
- (fold (lambda (x seed) (cons (f x) seed))
- '() xs))
- (syntax-case whole-expr ()
- ((_ clause clauses ...)
- #`(begin
- #,@(fold (lambda (clause-builder tail)
- (clause-builder tail))
- #'()
- (reverse-map
- (lambda (clause)
- (define* (bad-clause #\optional (msg "invalid clause"))
- (syntax-violation 'cond msg whole-expr clause))
- (syntax-case clause (=> else)
- ((else e e* ...)
- (lambda (tail)
- (if (null? tail)
- #'((begin e e* ...))
- (bad-clause "else must be the last clause"))))
- ((else . _) (bad-clause))
- ((test => receiver)
- (lambda (tail)
- #`((let ((t test))
- (if t
- (receiver t)
- #,@tail)))))
- ((test => receiver ...)
- (bad-clause "wrong number of receiver expressions"))
- ((generator guard => receiver)
- (lambda (tail)
- #`((call-with-values (lambda () generator)
- (lambda vals
- (if (apply guard vals)
- (apply receiver vals)
- #,@tail))))))
- ((generator guard => receiver ...)
- (bad-clause "wrong number of receiver expressions"))
- ((test)
- (lambda (tail)
- #`((let ((t test))
- (if t t #,@tail)))))
- ((test e e* ...)
- (lambda (tail)
- #`((if test
- (begin e e* ...)
- #,@tail))))
- (_ (bad-clause))))
- #'(clause clauses ...))))))))
-
-(define-syntax case
- (lambda (whole-expr)
- (define (fold f seed xs)
- (let loop ((xs xs) (seed seed))
- (if (null? xs) seed
- (loop (cdr xs) (f (car xs) seed)))))
- (define (fold2 f a b xs)
- (let loop ((xs xs) (a a) (b b))
- (if (null? xs) (values a b)
- (call-with-values
- (lambda () (f (car xs) a b))
- (lambda (a b)
- (loop (cdr xs) a b))))))
- (define (reverse-map-with-seed f seed xs)
- (fold2 (lambda (x ys seed)
- (call-with-values
- (lambda () (f x seed))
- (lambda (y seed)
- (values (cons y ys) seed))))
- '() seed xs))
- (syntax-case whole-expr ()
- ((_ expr clause clauses ...)
- (with-syntax ((key #'key))
- #`(let ((key expr))
- #,@(fold
- (lambda (clause-builder tail)
- (clause-builder tail))
- #'()
- (reverse-map-with-seed
- (lambda (clause seen)
- (define* (bad-clause #\optional (msg "invalid clause"))
- (syntax-violation 'case msg whole-expr clause))
- (syntax-case clause ()
- ((test . rest)
- (with-syntax
- ((clause-expr
- (syntax-case #'rest (=>)
- ((=> receiver) #'(receiver key))
- ((=> receiver ...)
- (bad-clause
- "wrong number of receiver expressions"))
- ((e e* ...) #'(begin e e* ...))
- (_ (bad-clause)))))
- (syntax-case #'test (else)
- ((datums ...)
- (let ((seen
- (fold
- (lambda (datum seen)
- (define (warn-datum type)
- ((@ (system base message)
- warning)
- type
- (append (source-properties datum)
- (source-properties
- (syntax->datum #'test)))
- datum
- (syntax->datum clause)
- (syntax->datum whole-expr)))
- (when (memv datum seen)
- (warn-datum 'duplicate-case-datum))
- (when (or (pair? datum) (array? datum))
- (warn-datum 'bad-case-datum))
- (cons datum seen))
- seen
- (map syntax->datum #'(datums ...)))))
- (values (lambda (tail)
- #`((if (memv key '(datums ...))
- clause-expr
- #,@tail)))
- seen)))
- (else (values (lambda (tail)
- (if (null? tail)
- #'(clause-expr)
- (bad-clause
- "else must be the last clause")))
- seen))
- (_ (bad-clause)))))
- (_ (bad-clause))))
- '() #'(clause clauses ...)))))))))
-
-(define-syntax do
- (syntax-rules ()
- ((do ((var init step ...) ...)
- (test expr ...)
- command ...)
- (letrec
- ((loop
- (lambda (var ...)
- (if test
- (begin
- (if #f #f)
- expr ...)
- (begin
- command
- ...
- (loop (do "step" var step ...)
- ...))))))
- (loop init ...)))
- ((do "step" x)
- x)
- ((do "step" x y)
- y)))
-
-;; XXX FIXME: When 'call-with-values' is fixed to no longer do automatic
-;; truncation of values (in 2.2 ?), then this hack can be removed.
-(define (%define-values-arity-error)
- (throw 'wrong-number-of-args
- #f
- "define-values: wrong number of return values returned by expression"
- '()
- #f))
-
-(define-syntax define-values
- (lambda (orig-form)
- (syntax-case orig-form ()
- ((_ () expr)
- ;; XXX Work around the lack of hygienic top-level identifiers
- (with-syntax (((dummy) (generate-temporaries '(dummy))))
- #`(define dummy
- (call-with-values (lambda () expr)
- (case-lambda
- (() #f)
- (_ (%define-values-arity-error)))))))
- ((_ (var) expr)
- (identifier? #'var)
- #`(define var
- (call-with-values (lambda () expr)
- (case-lambda
- ((v) v)
- (_ (%define-values-arity-error))))))
- ((_ (var0 ... varn) expr)
- (and-map identifier? #'(var0 ... varn))
- ;; XXX Work around the lack of hygienic toplevel identifiers
- (with-syntax (((dummy) (generate-temporaries '(dummy))))
- #`(begin
- ;; Avoid mutating the user-visible variables
- (define dummy
- (call-with-values (lambda () expr)
- (case-lambda
- ((var0 ... varn)
- (list var0 ... varn))
- (_ (%define-values-arity-error)))))
- (define var0
- (let ((v (car dummy)))
- (set! dummy (cdr dummy))
- v))
- ...
- (define varn
- (let ((v (car dummy)))
- (set! dummy #f) ; blackhole dummy
- v)))))
- ((_ var expr)
- (identifier? #'var)
- #'(define var
- (call-with-values (lambda () expr)
- list)))
- ((_ (var0 ... . varn) expr)
- (and-map identifier? #'(var0 ... varn))
- ;; XXX Work around the lack of hygienic toplevel identifiers
- (with-syntax (((dummy) (generate-temporaries '(dummy))))
- #`(begin
- ;; Avoid mutating the user-visible variables
- (define dummy
- (call-with-values (lambda () expr)
- (case-lambda
- ((var0 ... . varn)
- (list var0 ... varn))
- (_ (%define-values-arity-error)))))
- (define var0
- (let ((v (car dummy)))
- (set! dummy (cdr dummy))
- v))
- ...
- (define varn
- (let ((v (car dummy)))
- (set! dummy #f) ; blackhole dummy
- v))))))))
-
-(define-syntax-rule (delay exp)
- (make-promise (lambda () exp)))
-
-(define-syntax current-source-location
- (lambda (x)
- (syntax-case x ()
- ((_)
- (with-syntax ((s (datum->syntax x (syntax-source x))))
- #''s)))))
-
-;; We provide this accessor out of convenience. current-line and
-;; current-column aren't so interesting, because they distort what they
-;; are measuring; better to use syntax-source from a macro.
-;;
-(define-syntax current-filename
- (lambda (x)
- "A macro that expands to the current filename: the filename that
-the (current-filename) form appears in. Expands to #f if this
-information is unavailable."
- (false-if-exception
- (canonicalize-path (assq-ref (syntax-source x) 'filename)))))
-
-(define-syntax-rule (define-once sym val)
- (define sym
- (if (module-locally-bound? (current-module) 'sym) sym val)))
-
-;;; The real versions of `map' and `for-each', with cycle detection, and
-;;; that use reverse! instead of recursion in the case of `map'.
-;;;
-(define map
- (case-lambda
- ((f l)
- (let map1 ((hare l) (tortoise l) (move? #f) (out '()))
- (if (pair? hare)
- (if move?
- (if (eq? tortoise hare)
- (scm-error 'wrong-type-arg "map" "Circular list: ~S"
- (list l) #f)
- (map1 (cdr hare) (cdr tortoise) #f
- (cons (f (car hare)) out)))
- (map1 (cdr hare) tortoise #t
- (cons (f (car hare)) out)))
- (if (null? hare)
- (reverse! out)
- (scm-error 'wrong-type-arg "map" "Not a list: ~S"
- (list l) #f)))))
-
- ((f l1 l2)
- (let map2 ((h1 l1) (h2 l2) (t1 l1) (t2 l2) (move? #f) (out '()))
- (cond
- ((pair? h1)
- (cond
- ((not (pair? h2))
- (scm-error 'wrong-type-arg "map"
- (if (list? h2)
- "List of wrong length: ~S"
- "Not a list: ~S")
- (list l2) #f))
- ((not move?)
- (map2 (cdr h1) (cdr h2) t1 t2 #t
- (cons (f (car h1) (car h2)) out)))
- ((eq? t1 h1)
- (scm-error 'wrong-type-arg "map" "Circular list: ~S"
- (list l1) #f))
- ((eq? t2 h2)
- (scm-error 'wrong-type-arg "map" "Circular list: ~S"
- (list l2) #f))
- (else
- (map2 (cdr h1) (cdr h2) (cdr t1) (cdr t2) #f
- (cons (f (car h1) (car h2)) out)))))
-
- ((and (null? h1) (null? h2))
- (reverse! out))
-
- ((null? h1)
- (scm-error 'wrong-type-arg "map"
- (if (list? h2)
- "List of wrong length: ~S"
- "Not a list: ~S")
- (list l2) #f))
- (else
- (scm-error 'wrong-type-arg "map"
- "Not a list: ~S"
- (list l1) #f)))))
-
- ((f l1 . rest)
- (let ((len (length l1)))
- (let mapn ((rest rest))
- (or (null? rest)
- (if (= (length (car rest)) len)
- (mapn (cdr rest))
- (scm-error 'wrong-type-arg "map" "List of wrong length: ~S"
- (list (car rest)) #f)))))
- (let mapn ((l1 l1) (rest rest) (out '()))
- (if (null? l1)
- (reverse! out)
- (mapn (cdr l1) (map cdr rest)
- (cons (apply f (car l1) (map car rest)) out)))))))
-
-(define map-in-order map)
-
-(define for-each
- (case-lambda
- ((f l)
- (let for-each1 ((hare l) (tortoise l))
- (if (pair? hare)
- (begin
- (f (car hare))
- (let ((hare (cdr hare)))
- (if (pair? hare)
- (begin
- (when (eq? tortoise hare)
- (scm-error 'wrong-type-arg "for-each" "Circular list: ~S"
- (list l) #f))
- (f (car hare))
- (for-each1 (cdr hare) (cdr tortoise))))))
- (if (not (null? hare))
- (scm-error 'wrong-type-arg "for-each" "Not a list: ~S"
- (list l) #f)))))
-
- ((f l1 l2)
- (let for-each2 ((h1 l1) (h2 l2) (t1 l1) (t2 l2) (move? #f))
- (cond
- ((and (pair? h1) (pair? h2))
- (cond
- ((not move?)
- (f (car h1) (car h2))
- (for-each2 (cdr h1) (cdr h2) t1 t2 #t))
- ((eq? t1 h1)
- (scm-error 'wrong-type-arg "for-each" "Circular list: ~S"
- (list l1) #f))
- ((eq? t2 h2)
- (scm-error 'wrong-type-arg "for-each" "Circular list: ~S"
- (list l2) #f))
- (else
- (f (car h1) (car h2))
- (for-each2 (cdr h1) (cdr h2) (cdr t1) (cdr t2) #f))))
-
- ((if (null? h1)
- (or (null? h2) (pair? h2))
- (and (pair? h1) (null? h2)))
- (if #f #f))
-
- ((list? h1)
- (scm-error 'wrong-type-arg "for-each" "Unexpected tail: ~S"
- (list h2) #f))
- (else
- (scm-error 'wrong-type-arg "for-each" "Unexpected tail: ~S"
- (list h1) #f)))))
-
- ((f l1 . rest)
- (let ((len (length l1)))
- (let for-eachn ((rest rest))
- (or (null? rest)
- (if (= (length (car rest)) len)
- (for-eachn (cdr rest))
- (scm-error 'wrong-type-arg "for-each" "List of wrong length: ~S"
- (list (car rest)) #f)))))
-
- (let for-eachn ((l1 l1) (rest rest))
- (if (pair? l1)
- (begin
- (apply f (car l1) (map car rest))
- (for-eachn (cdr l1) (map cdr rest))))))))
-
-
-
-
-;;;
-;;; Enhanced file opening procedures
-;;;
-
-(define* (open-input-file
- file #\key (binary #f) (encoding #f) (guess-encoding #f))
- "Takes a string naming an existing file and returns an input port
-capable of delivering characters from the file. If the file
-cannot be opened, an error is signalled."
- (open-file file (if binary "rb" "r")
- #\encoding encoding
- #\guess-encoding guess-encoding))
-
-(define* (open-output-file file #\key (binary #f) (encoding #f))
- "Takes a string naming an output file to be created and returns an
-output port capable of writing characters to a new file by that
-name. If the file cannot be opened, an error is signalled. If a
-file with the given name already exists, the effect is unspecified."
- (open-file file (if binary "wb" "w")
- #\encoding encoding))
-
-(define* (call-with-input-file
- file proc #\key (binary #f) (encoding #f) (guess-encoding #f))
- "PROC should be a procedure of one argument, and FILE should be a
-string naming a file. The file must
-already exist. These procedures call PROC
-with one argument: the port obtained by opening the named file for
-input or output. If the file cannot be opened, an error is
-signalled. If the procedure returns, then the port is closed
-automatically and the values yielded by the procedure are returned.
-If the procedure does not return, then the port will not be closed
-automatically unless it is possible to prove that the port will
-never again be used for a read or write operation."
- (let ((p (open-input-file file
- #\binary binary
- #\encoding encoding
- #\guess-encoding guess-encoding)))
- (call-with-values
- (lambda () (proc p))
- (lambda vals
- (close-input-port p)
- (apply values vals)))))
-
-(define* (call-with-output-file file proc #\key (binary #f) (encoding #f))
- "PROC should be a procedure of one argument, and FILE should be a
-string naming a file. The behaviour is unspecified if the file
-already exists. These procedures call PROC
-with one argument: the port obtained by opening the named file for
-input or output. If the file cannot be opened, an error is
-signalled. If the procedure returns, then the port is closed
-automatically and the values yielded by the procedure are returned.
-If the procedure does not return, then the port will not be closed
-automatically unless it is possible to prove that the port will
-never again be used for a read or write operation."
- (let ((p (open-output-file file #\binary binary #\encoding encoding)))
- (call-with-values
- (lambda () (proc p))
- (lambda vals
- (close-output-port p)
- (apply values vals)))))
-
-(define* (with-input-from-file
- file thunk #\key (binary #f) (encoding #f) (guess-encoding #f))
- "THUNK must be a procedure of no arguments, and FILE must be a
-string naming a file. The file must already exist. The file is opened for
-input, an input port connected to it is made
-the default value returned by `current-input-port',
-and the THUNK is called with no arguments.
-When the THUNK returns, the port is closed and the previous
-default is restored. Returns the values yielded by THUNK. If an
-escape procedure is used to escape from the continuation of these
-procedures, their behavior is implementation dependent."
- (call-with-input-file file
- (lambda (p) (with-input-from-port p thunk))
- #\binary binary
- #\encoding encoding
- #\guess-encoding guess-encoding))
-
-(define* (with-output-to-file file thunk #\key (binary #f) (encoding #f))
- "THUNK must be a procedure of no arguments, and FILE must be a
-string naming a file. The effect is unspecified if the file already exists.
-The file is opened for output, an output port connected to it is made
-the default value returned by `current-output-port',
-and the THUNK is called with no arguments.
-When the THUNK returns, the port is closed and the previous
-default is restored. Returns the values yielded by THUNK. If an
-escape procedure is used to escape from the continuation of these
-procedures, their behavior is implementation dependent."
- (call-with-output-file file
- (lambda (p) (with-output-to-port p thunk))
- #\binary binary
- #\encoding encoding))
-
-(define* (with-error-to-file file thunk #\key (binary #f) (encoding #f))
- "THUNK must be a procedure of no arguments, and FILE must be a
-string naming a file. The effect is unspecified if the file already exists.
-The file is opened for output, an output port connected to it is made
-the default value returned by `current-error-port',
-and the THUNK is called with no arguments.
-When the THUNK returns, the port is closed and the previous
-default is restored. Returns the values yielded by THUNK. If an
-escape procedure is used to escape from the continuation of these
-procedures, their behavior is implementation dependent."
- (call-with-output-file file
- (lambda (p) (with-error-to-port p thunk))
- #\binary binary
- #\encoding encoding))
-
-
-
-;;;
-;;; Extensible exception printing.
-;;;
-
-(define set-exception-printer! #f)
-;; There is already a definition of print-exception from backtrace.c
-;; that we will override.
-
-(let ((exception-printers '()))
- (define (print-location frame port)
- (let ((source (and=> frame frame-source)))
- ;; source := (addr . (filename . (line . column)))
- (if source
- (let ((filename (or (cadr source) "<unnamed port>"))
- (line (caddr source))
- (col (cdddr source)))
- (format port "~a:~a:~a: " filename (1+ line) col))
- (format port "ERROR: "))))
-
- (set! set-exception-printer!
- (lambda (key proc)
- (set! exception-printers (acons key proc exception-printers))))
-
- (set! print-exception
- (lambda (port frame key args)
- (define (default-printer)
- (format port "Throw to key `~a' with args `~s'." key args))
-
- (if frame
- (let ((proc (frame-procedure frame)))
- (print-location frame port)
- (format port "In procedure ~a:\n"
- (or (false-if-exception (procedure-name proc))
- proc))))
-
- (print-location frame port)
- (catch #t
- (lambda ()
- (let ((printer (assq-ref exception-printers key)))
- (if printer
- (printer port key args default-printer)
- (default-printer))))
- (lambda (k . args)
- (format port "Error while printing exception.")))
- (newline port)
- (force-output port))))
-
-;;;
-;;; Printers for those keys thrown by Guile.
-;;;
-(let ()
- (define (scm-error-printer port key args default-printer)
- ;; Abuse case-lambda as a pattern matcher, given that we don't have
- ;; ice-9 match at this point.
- (apply (case-lambda
- ((subr msg args . rest)
- (if subr
- (format port "In procedure ~a: " subr))
- (apply format port msg (or args '())))
- (_ (default-printer)))
- args))
-
- (define (syntax-error-printer port key args default-printer)
- (apply (case-lambda
- ((who what where form subform . extra)
- (format port "Syntax error:\n")
- (if where
- (let ((file (or (assq-ref where 'filename) "unknown file"))
- (line (and=> (assq-ref where 'line) 1+))
- (col (assq-ref where 'column)))
- (format port "~a:~a:~a: " file line col))
- (format port "unknown location: "))
- (if who
- (format port "~a: " who))
- (format port "~a" what)
- (if subform
- (format port " in subform ~s of ~s" subform form)
- (if form
- (format port " in form ~s" form))))
- (_ (default-printer)))
- args))
-
- (define (keyword-error-printer port key args default-printer)
- (let ((message (cadr args))
- (faulty (car (cadddr args)))) ; I won't do it again, I promise.
- (format port "~a: ~s" message faulty)))
-
- (define (getaddrinfo-error-printer port key args default-printer)
- (format port "In procedure getaddrinfo: ~a" (gai-strerror (car args))))
-
- (set-exception-printer! 'goops-error scm-error-printer)
- (set-exception-printer! 'host-not-found scm-error-printer)
- (set-exception-printer! 'keyword-argument-error keyword-error-printer)
- (set-exception-printer! 'misc-error scm-error-printer)
- (set-exception-printer! 'no-data scm-error-printer)
- (set-exception-printer! 'no-recovery scm-error-printer)
- (set-exception-printer! 'null-pointer-error scm-error-printer)
- (set-exception-printer! 'out-of-range scm-error-printer)
- (set-exception-printer! 'program-error scm-error-printer)
- (set-exception-printer! 'read-error scm-error-printer)
- (set-exception-printer! 'regular-expression-syntax scm-error-printer)
- (set-exception-printer! 'signal scm-error-printer)
- (set-exception-printer! 'stack-overflow scm-error-printer)
- (set-exception-printer! 'system-error scm-error-printer)
- (set-exception-printer! 'try-again scm-error-printer)
- (set-exception-printer! 'unbound-variable scm-error-printer)
- (set-exception-printer! 'wrong-number-of-args scm-error-printer)
- (set-exception-printer! 'wrong-type-arg scm-error-printer)
-
- (set-exception-printer! 'syntax-error syntax-error-printer)
-
- (set-exception-printer! 'getaddrinfo-error getaddrinfo-error-printer))
-
-
-
-
-;;; {Defmacros}
-;;;
-
-(define-syntax define-macro
- (lambda (x)
- "Define a defmacro."
- (syntax-case x ()
- ((_ (macro . args) doc body1 body ...)
- (string? (syntax->datum #'doc))
- #'(define-macro macro doc (lambda args body1 body ...)))
- ((_ (macro . args) body ...)
- #'(define-macro macro #f (lambda args body ...)))
- ((_ macro transformer)
- #'(define-macro macro #f transformer))
- ((_ macro doc transformer)
- (or (string? (syntax->datum #'doc))
- (not (syntax->datum #'doc)))
- #'(define-syntax macro
- (lambda (y)
- doc
- #((macro-type . defmacro)
- (defmacro-args args))
- (syntax-case y ()
- ((_ . args)
- (let ((v (syntax->datum #'args)))
- (datum->syntax y (apply transformer v)))))))))))
-
-(define-syntax defmacro
- (lambda (x)
- "Define a defmacro, with the old lispy defun syntax."
- (syntax-case x ()
- ((_ macro args doc body1 body ...)
- (string? (syntax->datum #'doc))
- #'(define-macro macro doc (lambda args body1 body ...)))
- ((_ macro args body ...)
- #'(define-macro macro #f (lambda args body ...))))))
-
-(provide 'defmacro)
-
-
-
-;;; {Deprecation}
-;;;
-
-(define-syntax begin-deprecated
- (lambda (x)
- (syntax-case x ()
- ((_ form form* ...)
- (if (include-deprecated-features)
- #'(begin form form* ...)
- #'(begin))))))
-
-
-
-;;; {Trivial Functions}
-;;;
-
-(define (identity x) x)
-
-(define (compose proc . rest)
- "Compose PROC with the procedures in REST, such that the last one in
-REST is applied first and PROC last, and return the resulting procedure.
-The given procedures must have compatible arity."
- (if (null? rest)
- proc
- (let ((g (apply compose rest)))
- (lambda args
- (call-with-values (lambda () (apply g args)) proc)))))
-
-(define (negate proc)
- "Return a procedure with the same arity as PROC that returns the `not'
-of PROC's result."
- (lambda args
- (not (apply proc args))))
-
-(define (const value)
- "Return a procedure that accepts any number of arguments and returns
-VALUE."
- (lambda _
- value))
-
-(define (and=> value procedure)
- "When VALUE is #f, return #f. Otherwise, return (PROC VALUE)."
- (and value (procedure value)))
-
-(define call/cc call-with-current-continuation)
-
-(define-syntax false-if-exception
- (syntax-rules ()
- ((false-if-exception expr)
- (catch #t
- (lambda () expr)
- (lambda args #f)))
- ((false-if-exception expr #\warning template arg ...)
- (catch #t
- (lambda () expr)
- (lambda (key . args)
- (for-each (lambda (s)
- (if (not (string-null? s))
- (format (current-warning-port) ";;; ~a\n" s)))
- (string-split
- (call-with-output-string
- (lambda (port)
- (format port template arg ...)
- (print-exception port #f key args)))
- #\newline))
- #f)))))
-
-
-
-;;; {General Properties}
-;;;
-
-;; Properties are a lispy way to associate random info with random objects.
-;; Traditionally properties are implemented as an alist or a plist actually
-;; pertaining to the object in question.
-;;
-;; These "object properties" have the advantage that they can be associated with
-;; any object, even if the object has no plist. Object properties are good when
-;; you are extending pre-existing objects in unexpected ways. They also present
-;; a pleasing, uniform procedure-with-setter interface. But if you have a data
-;; type that always has properties, it's often still best to store those
-;; properties within the object itself.
-
-(define (make-object-property)
- (define-syntax-rule (with-mutex lock exp)
- (dynamic-wind (lambda () (lock-mutex lock))
- (lambda () exp)
- (lambda () (unlock-mutex lock))))
- (let ((prop (make-weak-key-hash-table))
- (lock (make-mutex)))
- (make-procedure-with-setter
- (lambda (obj) (with-mutex lock (hashq-ref prop obj)))
- (lambda (obj val) (with-mutex lock (hashq-set! prop obj val))))))
-
-
-
-
-;;; {Symbol Properties}
-;;;
-
-;;; Symbol properties are something you see in old Lisp code. In most current
-;;; Guile code, symbols are not used as a data structure -- they are used as
-;;; keys into other data structures.
-
-(define (symbol-property sym prop)
- (let ((pair (assoc prop (symbol-pref sym))))
- (and pair (cdr pair))))
-
-(define (set-symbol-property! sym prop val)
- (let ((pair (assoc prop (symbol-pref sym))))
- (if pair
- (set-cdr! pair val)
- (symbol-pset! sym (acons prop val (symbol-pref sym))))))
-
-(define (symbol-property-remove! sym prop)
- (let ((pair (assoc prop (symbol-pref sym))))
- (if pair
- (symbol-pset! sym (delq! pair (symbol-pref sym))))))
-
-
-
-;;; {Arrays}
-;;;
-
-(define (array-shape a)
- (map (lambda (ind) (if (number? ind) (list 0 (+ -1 ind)) ind))
- (array-dimensions a)))
-
-
-
-;;; {Keywords}
-;;;
-
-;;; It's much better if you can use lambda* / define*, of course.
-
-(define (kw-arg-ref args kw)
- (let ((rem (member kw args)))
- (and rem (pair? (cdr rem)) (cadr rem))))
-
-
-
-;;; {Structs}
-;;;
-
-(define (struct-layout s)
- (struct-ref (struct-vtable s) vtable-index-layout))
-
-
-
-;;; {Records}
-;;;
-
-;; Printing records: by default, records are printed as
-;;
-;; #<type-name field1: val1 field2: val2 ...>
-;;
-;; You can change that by giving a custom printing function to
-;; MAKE-RECORD-TYPE (after the list of field symbols). This function
-;; will be called like
-;;
-;; (<printer> object port)
-;;
-;; It should print OBJECT to PORT.
-
-(define (inherit-print-state old-port new-port)
- (if (get-print-state old-port)
- (port-with-print-state new-port (get-print-state old-port))
- new-port))
-
-;; 0: type-name, 1: fields, 2: constructor
-(define record-type-vtable
- (let ((s (make-vtable (string-append standard-vtable-fields "prprpw")
- (lambda (s p)
- (display "#<record-type " p)
- (display (record-type-name s) p)
- (display ">" p)))))
- (set-struct-vtable-name! s 'record-type)
- s))
-
-(define (record-type? obj)
- (and (struct? obj) (eq? record-type-vtable (struct-vtable obj))))
-
-(define* (make-record-type type-name fields #\optional printer)
- ;; Pre-generate constructors for nfields < 20.
- (define-syntax make-constructor
- (lambda (x)
- (define *max-static-argument-count* 20)
- (define (make-formals n)
- (let lp ((i 0))
- (if (< i n)
- (cons (datum->syntax
- x
- (string->symbol
- (string (integer->char (+ (char->integer #\a) i)))))
- (lp (1+ i)))
- '())))
- (syntax-case x ()
- ((_ rtd exp) (not (identifier? #'exp))
- #'(let ((n exp))
- (make-constructor rtd n)))
- ((_ rtd nfields)
- #`(case nfields
- #,@(let lp ((n 0))
- (if (< n *max-static-argument-count*)
- (cons (with-syntax (((formal ...) (make-formals n))
- (n n))
- #'((n)
- (lambda (formal ...)
- (make-struct rtd 0 formal ...))))
- (lp (1+ n)))
- '()))
- (else
- (lambda args
- (if (= (length args) nfields)
- (apply make-struct rtd 0 args)
- (scm-error 'wrong-number-of-args
- (format #f "make-~a" type-name)
- "Wrong number of arguments" '() #f)))))))))
-
- (define (default-record-printer s p)
- (display "#<" p)
- (display (record-type-name (record-type-descriptor s)) p)
- (let loop ((fields (record-type-fields (record-type-descriptor s)))
- (off 0))
- (cond
- ((not (null? fields))
- (display " " p)
- (display (car fields) p)
- (display ": " p)
- (display (struct-ref s off) p)
- (loop (cdr fields) (+ 1 off)))))
- (display ">" p))
-
- (let ((rtd (make-struct record-type-vtable 0
- (make-struct-layout
- (apply string-append
- (map (lambda (f) "pw") fields)))
- (or printer default-record-printer)
- type-name
- (copy-tree fields))))
- (struct-set! rtd (+ vtable-offset-user 2)
- (make-constructor rtd (length fields)))
- ;; Temporary solution: Associate a name to the record type descriptor
- ;; so that the object system can create a wrapper class for it.
- (set-struct-vtable-name! rtd (if (symbol? type-name)
- type-name
- (string->symbol type-name)))
- rtd))
-
-(define (record-type-name obj)
- (if (record-type? obj)
- (struct-ref obj vtable-offset-user)
- (error 'not-a-record-type obj)))
-
-(define (record-type-fields obj)
- (if (record-type? obj)
- (struct-ref obj (+ 1 vtable-offset-user))
- (error 'not-a-record-type obj)))
-
-(define* (record-constructor rtd #\optional field-names)
- (if (not field-names)
- (struct-ref rtd (+ 2 vtable-offset-user))
- (primitive-eval
- `(lambda ,field-names
- (make-struct ',rtd 0 ,@(map (lambda (f)
- (if (memq f field-names)
- f
- #f))
- (record-type-fields rtd)))))))
-
-(define (record-predicate rtd)
- (lambda (obj) (and (struct? obj) (eq? rtd (struct-vtable obj)))))
-
-(define (%record-type-error rtd obj) ;; private helper
- (or (eq? rtd (record-type-descriptor obj))
- (scm-error 'wrong-type-arg "%record-type-check"
- "Wrong type record (want `~S'): ~S"
- (list (record-type-name rtd) obj)
- #f)))
-
-(define (record-accessor rtd field-name)
- (let ((pos (list-index (record-type-fields rtd) field-name)))
- (if (not pos)
- (error 'no-such-field field-name))
- (lambda (obj)
- (if (eq? (struct-vtable obj) rtd)
- (struct-ref obj pos)
- (%record-type-error rtd obj)))))
-
-(define (record-modifier rtd field-name)
- (let ((pos (list-index (record-type-fields rtd) field-name)))
- (if (not pos)
- (error 'no-such-field field-name))
- (lambda (obj val)
- (if (eq? (struct-vtable obj) rtd)
- (struct-set! obj pos val)
- (%record-type-error rtd obj)))))
-
-(define (record? obj)
- (and (struct? obj) (record-type? (struct-vtable obj))))
-
-(define (record-type-descriptor obj)
- (if (struct? obj)
- (struct-vtable obj)
- (error 'not-a-record obj)))
-
-(provide 'record)
-
-
-
-;;; {Booleans}
-;;;
-
-(define (->bool x) (not (not x)))
-
-
-
-;;; {Symbols}
-;;;
-
-(define (symbol-append . args)
- (string->symbol (apply string-append (map symbol->string args))))
-
-(define (list->symbol . args)
- (string->symbol (apply list->string args)))
-
-(define (symbol . args)
- (string->symbol (apply string args)))
-
-
-
-;;; {Lists}
-;;;
-
-(define (list-index l k)
- (let loop ((n 0)
- (l l))
- (and (not (null? l))
- (if (eq? (car l) k)
- n
- (loop (+ n 1) (cdr l))))))
-
-
-
-;; Load `posix.scm' even when not (provided? 'posix) so that we get the
-;; `stat' accessors.
-(primitive-load-path "ice-9/posix")
-
-(if (provided? 'socket)
- (primitive-load-path "ice-9/networking"))
-
-;; For reference, Emacs file-exists-p uses stat in this same way.
-(define file-exists?
- (if (provided? 'posix)
- (lambda (str)
- (->bool (stat str #f)))
- (lambda (str)
- (let ((port (catch 'system-error (lambda () (open-file str OPEN_READ))
- (lambda args #f))))
- (if port (begin (close-port port) #t)
- #f)))))
-
-(define file-is-directory?
- (if (provided? 'posix)
- (lambda (str)
- (eq? (stat:type (stat str)) 'directory))
- (lambda (str)
- (let ((port (catch 'system-error
- (lambda () (open-file (string-append str "/.")
- OPEN_READ))
- (lambda args #f))))
- (if port (begin (close-port port) #t)
- #f)))))
-
-(define (system-error-errno args)
- (if (eq? (car args) 'system-error)
- (car (list-ref args 4))
- #f))
-
-
-
-;;; {Error Handling}
-;;;
-
-(define error
- (case-lambda
- (()
- (scm-error 'misc-error #f "?" #f #f))
- ((message . args)
- (let ((msg (string-join (cons "~A" (make-list (length args) "~S")))))
- (scm-error 'misc-error #f msg (cons message args) #f)))))
-
-
-
-;;; {Time Structures}
-;;;
-
-(define (tm:sec obj) (vector-ref obj 0))
-(define (tm:min obj) (vector-ref obj 1))
-(define (tm:hour obj) (vector-ref obj 2))
-(define (tm:mday obj) (vector-ref obj 3))
-(define (tm:mon obj) (vector-ref obj 4))
-(define (tm:year obj) (vector-ref obj 5))
-(define (tm:wday obj) (vector-ref obj 6))
-(define (tm:yday obj) (vector-ref obj 7))
-(define (tm:isdst obj) (vector-ref obj 8))
-(define (tm:gmtoff obj) (vector-ref obj 9))
-(define (tm:zone obj) (vector-ref obj 10))
-
-(define (set-tm:sec obj val) (vector-set! obj 0 val))
-(define (set-tm:min obj val) (vector-set! obj 1 val))
-(define (set-tm:hour obj val) (vector-set! obj 2 val))
-(define (set-tm:mday obj val) (vector-set! obj 3 val))
-(define (set-tm:mon obj val) (vector-set! obj 4 val))
-(define (set-tm:year obj val) (vector-set! obj 5 val))
-(define (set-tm:wday obj val) (vector-set! obj 6 val))
-(define (set-tm:yday obj val) (vector-set! obj 7 val))
-(define (set-tm:isdst obj val) (vector-set! obj 8 val))
-(define (set-tm:gmtoff obj val) (vector-set! obj 9 val))
-(define (set-tm:zone obj val) (vector-set! obj 10 val))
-
-(define (tms:clock obj) (vector-ref obj 0))
-(define (tms:utime obj) (vector-ref obj 1))
-(define (tms:stime obj) (vector-ref obj 2))
-(define (tms:cutime obj) (vector-ref obj 3))
-(define (tms:cstime obj) (vector-ref obj 4))
-
-
-
-;;; {File Descriptors and Ports}
-;;;
-
-(define file-position ftell)
-(define* (file-set-position port offset #\optional (whence SEEK_SET))
- (seek port offset whence))
-
-(define (move->fdes fd/port fd)
- (cond ((integer? fd/port)
- (dup->fdes fd/port fd)
- (close fd/port)
- fd)
- (else
- (primitive-move->fdes fd/port fd)
- (set-port-revealed! fd/port 1)
- fd/port)))
-
-(define (release-port-handle port)
- (let ((revealed (port-revealed port)))
- (if (> revealed 0)
- (set-port-revealed! port (- revealed 1)))))
-
-(define dup->port
- (case-lambda
- ((port/fd mode)
- (fdopen (dup->fdes port/fd) mode))
- ((port/fd mode new-fd)
- (let ((port (fdopen (dup->fdes port/fd new-fd) mode)))
- (set-port-revealed! port 1)
- port))))
-
-(define dup->inport
- (case-lambda
- ((port/fd)
- (dup->port port/fd "r"))
- ((port/fd new-fd)
- (dup->port port/fd "r" new-fd))))
-
-(define dup->outport
- (case-lambda
- ((port/fd)
- (dup->port port/fd "w"))
- ((port/fd new-fd)
- (dup->port port/fd "w" new-fd))))
-
-(define dup
- (case-lambda
- ((port/fd)
- (if (integer? port/fd)
- (dup->fdes port/fd)
- (dup->port port/fd (port-mode port/fd))))
- ((port/fd new-fd)
- (if (integer? port/fd)
- (dup->fdes port/fd new-fd)
- (dup->port port/fd (port-mode port/fd) new-fd)))))
-
-(define (duplicate-port port modes)
- (dup->port port modes))
-
-(define (fdes->inport fdes)
- (let loop ((rest-ports (fdes->ports fdes)))
- (cond ((null? rest-ports)
- (let ((result (fdopen fdes "r")))
- (set-port-revealed! result 1)
- result))
- ((input-port? (car rest-ports))
- (set-port-revealed! (car rest-ports)
- (+ (port-revealed (car rest-ports)) 1))
- (car rest-ports))
- (else
- (loop (cdr rest-ports))))))
-
-(define (fdes->outport fdes)
- (let loop ((rest-ports (fdes->ports fdes)))
- (cond ((null? rest-ports)
- (let ((result (fdopen fdes "w")))
- (set-port-revealed! result 1)
- result))
- ((output-port? (car rest-ports))
- (set-port-revealed! (car rest-ports)
- (+ (port-revealed (car rest-ports)) 1))
- (car rest-ports))
- (else
- (loop (cdr rest-ports))))))
-
-(define (port->fdes port)
- (set-port-revealed! port (+ (port-revealed port) 1))
- (fileno port))
-
-(define (setenv name value)
- (if value
- (putenv (string-append name "=" value))
- (putenv name)))
-
-(define (unsetenv name)
- "Remove the entry for NAME from the environment."
- (putenv name))
-
-
-
-;;; {Load Paths}
-;;;
-
-(let-syntax ((compile-time-case
- (lambda (stx)
- (syntax-case stx ()
- ((_ exp clauses ...)
- (let ((val (primitive-eval (syntax->datum #'exp))))
- (let next-clause ((clauses #'(clauses ...)))
- (syntax-case clauses (else)
- (()
- (syntax-violation 'compile-time-case
- "all clauses failed to match" stx))
- (((else form ...))
- #'(begin form ...))
- ((((k ...) form ...) clauses ...)
- (if (memv val (syntax->datum #'(k ...)))
- #'(begin form ...)
- (next-clause #'(clauses ...))))))))))))
- ;; emacs: (put 'compile-time-case 'scheme-indent-function 1)
- (compile-time-case (system-file-name-convention)
- ((posix)
- (define (file-name-separator? c)
- (char=? c #\/))
-
- (define file-name-separator-string "/")
-
- (define (absolute-file-name? file-name)
- (string-prefix? "/" file-name)))
-
- ((windows)
- (define (file-name-separator? c)
- (or (char=? c #\/)
- (char=? c #\\)))
-
- (define file-name-separator-string "/")
-
- (define (absolute-file-name? file-name)
- (define (file-name-separator-at-index? idx)
- (and (> (string-length file-name) idx)
- (file-name-separator? (string-ref file-name idx))))
- (define (unc-file-name?)
- ;; Universal Naming Convention (UNC) file-names start with \\,
- ;; and are always absolute. See:
- ;; http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#fully_qualified_vs._relative_paths
- (and (file-name-separator-at-index? 0)
- (file-name-separator-at-index? 1)))
- (define (has-drive-specifier?)
- (and (>= (string-length file-name) 2)
- (let ((drive (string-ref file-name 0)))
- (or (char<=? #\a drive #\z)
- (char<=? #\A drive #\Z)))
- (eqv? (string-ref file-name 1) #\:)))
- (or (unc-file-name?)
- (if (has-drive-specifier?)
- (file-name-separator-at-index? 2)
- (file-name-separator-at-index? 0)))))))
-
-(define (in-vicinity vicinity file)
- (let ((tail (let ((len (string-length vicinity)))
- (if (zero? len)
- #f
- (string-ref vicinity (- len 1))))))
- (string-append vicinity
- (if (or (not tail) (file-name-separator? tail))
- ""
- file-name-separator-string)
- file)))
-
-
-
-;;; {Help for scm_shell}
-;;;
-;;; The argument-processing code used by Guile-based shells generates
-;;; Scheme code based on the argument list. This page contains help
-;;; functions for the code it generates.
-;;;
-
-(define (command-line) (program-arguments))
-
-;; This is mostly for the internal use of the code generated by
-;; scm_compile_shell_switches.
-
-(define (load-user-init)
- (let* ((home (or (getenv "HOME")
- (false-if-exception (passwd:dir (getpwuid (getuid))))
- file-name-separator-string)) ;; fallback for cygwin etc.
- (init-file (in-vicinity home ".guile")))
- (if (file-exists? init-file)
- (primitive-load init-file))))
-
-
-
-;;; {The interpreter stack}
-;;;
-
-;; %stacks defined in stacks.c
-(define (%start-stack tag thunk)
- (let ((prompt-tag (make-prompt-tag "start-stack")))
- (call-with-prompt
- prompt-tag
- (lambda ()
- (with-fluids ((%stacks (acons tag prompt-tag
- (or (fluid-ref %stacks) '()))))
- (thunk)))
- (lambda (k . args)
- (%start-stack tag (lambda () (apply k args)))))))
-
-(define-syntax-rule (start-stack tag exp)
- (%start-stack tag (lambda () exp)))
-
-
-
-;;; {Loading by paths}
-;;;
-
-;;; Load a Scheme source file named NAME, searching for it in the
-;;; directories listed in %load-path, and applying each of the file
-;;; name extensions listed in %load-extensions.
-(define (load-from-path name)
- (start-stack 'load-stack
- (primitive-load-path name)))
-
-(define-syntax-rule (add-to-load-path elt)
- "Add ELT to Guile's load path, at compile-time and at run-time."
- (eval-when (expand load eval)
- (set! %load-path (cons elt (delete elt %load-path)))))
-
-(define %load-verbosely #f)
-(define (assert-load-verbosity v) (set! %load-verbosely v))
-
-(define (%load-announce file)
- (if %load-verbosely
- (with-output-to-port (current-warning-port)
- (lambda ()
- (display ";;; ")
- (display "loading ")
- (display file)
- (newline)
- (force-output)))))
-
-(set! %load-hook %load-announce)
-
-
-
-;;; {Reader Extensions}
-;;;
-;;; Reader code for various "#c" forms.
-;;;
-
-(define read-eval? (make-fluid #f))
-(read-hash-extend #\.
- (lambda (c port)
- (if (fluid-ref read-eval?)
- (eval (read port) (interaction-environment))
- (error
- "#. read expansion found and read-eval? is #f."))))
-
-
-
-;;; {Low Level Modules}
-;;;
-;;; These are the low level data structures for modules.
-;;;
-;;; Every module object is of the type 'module-type', which is a record
-;;; consisting of the following members:
-;;;
-;;; - eval-closure: A deprecated field, to be removed in Guile 2.2.
-;;;
-;;; - obarray: a hash table that maps symbols to variable objects. In this
-;;; hash table, the definitions are found that are local to the module (that
-;;; is, not imported from other modules). When looking up bindings in the
-;;; module, this hash table is searched first.
-;;;
-;;; - binder: either #f or a function taking a module and a symbol argument.
-;;; If it is a function it is called after the obarray has been
-;;; unsuccessfully searched for a binding. It then can provide bindings
-;;; that would otherwise not be found locally in the module.
-;;;
-;;; - uses: a list of modules from which non-local bindings can be inherited.
-;;; These modules are the third place queried for bindings after the obarray
-;;; has been unsuccessfully searched and the binder function did not deliver
-;;; a result either.
-;;;
-;;; - transformer: either #f or a function taking a scheme expression as
-;;; delivered by read. If it is a function, it will be called to perform
-;;; syntax transformations (e. g. makro expansion) on the given scheme
-;;; expression. The output of the transformer function will then be passed
-;;; to Guile's internal memoizer. This means that the output must be valid
-;;; scheme code. The only exception is, that the output may make use of the
-;;; syntax extensions provided to identify the modules that a binding
-;;; belongs to.
-;;;
-;;; - name: the name of the module. This is used for all kinds of printing
-;;; outputs. In certain places the module name also serves as a way of
-;;; identification. When adding a module to the uses list of another
-;;; module, it is made sure that the new uses list will not contain two
-;;; modules of the same name.
-;;;
-;;; - kind: classification of the kind of module. The value is (currently?)
-;;; only used for printing. It has no influence on how a module is treated.
-;;; Currently the following values are used when setting the module kind:
-;;; 'module, 'directory, 'interface, 'custom-interface. If no explicit kind
-;;; is set, it defaults to 'module.
-;;;
-;;; - duplicates-handlers: a list of procedures that get called to make a
-;;; choice between two duplicate bindings when name clashes occur. See the
-;;; `duplicate-handlers' global variable below.
-;;;
-;;; - observers: a list of procedures that get called when the module is
-;;; modified.
-;;;
-;;; - weak-observers: a weak-key hash table of procedures that get called
-;;; when the module is modified. See `module-observe-weak' for details.
-;;;
-;;; In addition, the module may (must?) contain a binding for
-;;; `%module-public-interface'. This variable should be bound to a module
-;;; representing the exported interface of a module. See the
-;;; `module-public-interface' and `module-export!' procedures.
-;;;
-;;; !!! warning: The interface to lazy binder procedures is going
-;;; to be changed in an incompatible way to permit all the basic
-;;; module ops to be virtualized.
-;;;
-;;; (make-module size use-list lazy-binding-proc) => module
-;;; module-{obarray,uses,binder}[|-set!]
-;;; (module? obj) => [#t|#f]
-;;; (module-locally-bound? module symbol) => [#t|#f]
-;;; (module-bound? module symbol) => [#t|#f]
-;;; (module-symbol-locally-interned? module symbol) => [#t|#f]
-;;; (module-symbol-interned? module symbol) => [#t|#f]
-;;; (module-local-variable module symbol) => [#<variable ...> | #f]
-;;; (module-variable module symbol) => [#<variable ...> | #f]
-;;; (module-symbol-binding module symbol opt-value)
-;;; => [ <obj> | opt-value | an error occurs ]
-;;; (module-make-local-var! module symbol) => #<variable...>
-;;; (module-add! module symbol var) => unspecified
-;;; (module-remove! module symbol) => unspecified
-;;; (module-for-each proc module) => unspecified
-;;; (make-scm-module) => module ; a lazy copy of the symhash module
-;;; (set-current-module module) => unspecified
-;;; (current-module) => #<module...>
-;;;
-;;;
-
-
-
-;;; {Printing Modules}
-;;;
-
-;; This is how modules are printed. You can re-define it.
-(define (%print-module mod port)
- (display "#<" port)
- (display (or (module-kind mod) "module") port)
- (display " " port)
- (display (module-name mod) port)
- (display " " port)
- (display (number->string (object-address mod) 16) port)
- (display ">" port))
-
-(letrec-syntax
- ;; Locally extend the syntax to allow record accessors to be defined at
- ;; compile-time. Cache the rtd locally to the constructor, the getters and
- ;; the setters, in order to allow for redefinition of the record type; not
- ;; relevant in the case of modules, but perhaps if we make this public, it
- ;; could matter.
-
- ((define-record-type
- (lambda (x)
- (define (make-id scope . fragments)
- (datum->syntax #'scope
- (apply symbol-append
- (map (lambda (x)
- (if (symbol? x) x (syntax->datum x)))
- fragments))))
-
- (define (getter rtd type-name field slot)
- #`(define #,(make-id rtd type-name '- field)
- (let ((rtd #,rtd))
- (lambda (#,type-name)
- (if (eq? (struct-vtable #,type-name) rtd)
- (struct-ref #,type-name #,slot)
- (%record-type-error rtd #,type-name))))))
-
- (define (setter rtd type-name field slot)
- #`(define #,(make-id rtd 'set- type-name '- field '!)
- (let ((rtd #,rtd))
- (lambda (#,type-name val)
- (if (eq? (struct-vtable #,type-name) rtd)
- (struct-set! #,type-name #,slot val)
- (%record-type-error rtd #,type-name))))))
-
- (define (accessors rtd type-name fields n exp)
- (syntax-case fields ()
- (() exp)
- (((field #\no-accessors) field* ...) (identifier? #'field)
- (accessors rtd type-name #'(field* ...) (1+ n)
- exp))
- (((field #\no-setter) field* ...) (identifier? #'field)
- (accessors rtd type-name #'(field* ...) (1+ n)
- #`(begin #,exp
- #,(getter rtd type-name #'field n))))
- (((field #\no-getter) field* ...) (identifier? #'field)
- (accessors rtd type-name #'(field* ...) (1+ n)
- #`(begin #,exp
- #,(setter rtd type-name #'field n))))
- ((field field* ...) (identifier? #'field)
- (accessors rtd type-name #'(field* ...) (1+ n)
- #`(begin #,exp
- #,(getter rtd type-name #'field n)
- #,(setter rtd type-name #'field n))))))
-
- (define (predicate rtd type-name fields exp)
- (accessors
- rtd type-name fields 0
- #`(begin
- #,exp
- (define (#,(make-id rtd type-name '?) obj)
- (and (struct? obj) (eq? (struct-vtable obj) #,rtd))))))
-
- (define (field-list fields)
- (syntax-case fields ()
- (() '())
- (((f . opts) . rest) (identifier? #'f)
- (cons #'f (field-list #'rest)))
- ((f . rest) (identifier? #'f)
- (cons #'f (field-list #'rest)))))
-
- (define (constructor rtd type-name fields exp)
- (let ((ctor (make-id rtd type-name '-constructor))
- (args (field-list fields)))
- (predicate rtd type-name fields
- #`(begin #,exp
- (define #,ctor
- (let ((rtd #,rtd))
- (lambda #,args
- (make-struct rtd 0 #,@args))))
- (struct-set! #,rtd (+ vtable-offset-user 2)
- #,ctor)))))
-
- (define (type type-name printer fields)
- (define (make-layout)
- (let lp ((fields fields) (slots '()))
- (syntax-case fields ()
- (() (datum->syntax #'here
- (make-struct-layout
- (apply string-append slots))))
- ((_ . rest) (lp #'rest (cons "pw" slots))))))
-
- (let ((rtd (make-id type-name type-name '-type)))
- (constructor rtd type-name fields
- #`(begin
- (define #,rtd
- (make-struct record-type-vtable 0
- '#,(make-layout)
- #,printer
- '#,type-name
- '#,(field-list fields)))
- (set-struct-vtable-name! #,rtd '#,type-name)))))
-
- (syntax-case x ()
- ((_ type-name printer (field ...))
- (type #'type-name #'printer #'(field ...)))))))
-
- ;; module-type
- ;;
- ;; A module is characterized by an obarray in which local symbols
- ;; are interned, a list of modules, "uses", from which non-local
- ;; bindings can be inherited, and an optional lazy-binder which
- ;; is a (CLOSURE module symbol) which, as a last resort, can provide
- ;; bindings that would otherwise not be found locally in the module.
- ;;
- ;; NOTE: If you change the set of fields or their order, you also need to
- ;; change the constants in libguile/modules.h.
- ;;
- ;; NOTE: The getter `module-transfomer' is defined libguile/modules.c.
- ;; NOTE: The getter `module-name' is defined later, due to boot reasons.
- ;; NOTE: The getter `module-public-interface' is used in libguile/modules.c.
- ;;
- (define-record-type module
- (lambda (obj port) (%print-module obj port))
- (obarray
- uses
- binder
- eval-closure
- (transformer #\no-getter)
- (name #\no-getter)
- kind
- duplicates-handlers
- (import-obarray #\no-setter)
- observers
- (weak-observers #\no-setter)
- version
- submodules
- submodule-binder
- public-interface
- filename
- next-unique-id)))
-
-
-;; make-module &opt size uses binder
-;;
-;; Create a new module, perhaps with a particular size of obarray,
-;; initial uses list, or binding procedure.
-;;
-(define* (make-module #\optional (size 31) (uses '()) (binder #f))
- (define %default-import-size
- ;; Typical number of imported bindings actually used by a module.
- 600)
-
- (if (not (integer? size))
- (error "Illegal size to make-module." size))
- (if (not (and (list? uses)
- (and-map module? uses)))
- (error "Incorrect use list." uses))
- (if (and binder (not (procedure? binder)))
- (error
- "Lazy-binder expected to be a procedure or #f." binder))
-
- (module-constructor (make-hash-table size)
- uses binder #f macroexpand
- #f #f #f
- (make-hash-table %default-import-size)
- '()
- (make-weak-key-hash-table 31) #f
- (make-hash-table 7) #f #f #f 0))
-
-
-
-
-;;; {Observer protocol}
-;;;
-
-(define (module-observe module proc)
- (set-module-observers! module (cons proc (module-observers module)))
- (cons module proc))
-
-(define* (module-observe-weak module observer-id #\optional (proc observer-id))
- ;; Register PROC as an observer of MODULE under name OBSERVER-ID (which can
- ;; be any Scheme object). PROC is invoked and passed MODULE any time
- ;; MODULE is modified. PROC gets unregistered when OBSERVER-ID gets GC'd
- ;; (thus, it is never unregistered if OBSERVER-ID is an immediate value,
- ;; for instance).
-
- ;; The two-argument version is kept for backward compatibility: when called
- ;; with two arguments, the observer gets unregistered when closure PROC
- ;; gets GC'd (making it impossible to use an anonymous lambda for PROC).
- (hashq-set! (module-weak-observers module) observer-id proc))
-
-(define (module-unobserve token)
- (let ((module (car token))
- (id (cdr token)))
- (if (integer? id)
- (hash-remove! (module-weak-observers module) id)
- (set-module-observers! module (delq1! id (module-observers module)))))
- *unspecified*)
-
-(define module-defer-observers #f)
-(define module-defer-observers-mutex (make-mutex 'recursive))
-(define module-defer-observers-table (make-hash-table))
-
-(define (module-modified m)
- (if module-defer-observers
- (hash-set! module-defer-observers-table m #t)
- (module-call-observers m)))
-
-;;; This function can be used to delay calls to observers so that they
-;;; can be called once only in the face of massive updating of modules.
-;;;
-(define (call-with-deferred-observers thunk)
- (dynamic-wind
- (lambda ()
- (lock-mutex module-defer-observers-mutex)
- (set! module-defer-observers #t))
- thunk
- (lambda ()
- (set! module-defer-observers #f)
- (hash-for-each (lambda (m dummy)
- (module-call-observers m))
- module-defer-observers-table)
- (hash-clear! module-defer-observers-table)
- (unlock-mutex module-defer-observers-mutex))))
-
-(define (module-call-observers m)
- (for-each (lambda (proc) (proc m)) (module-observers m))
-
- ;; We assume that weak observers don't (un)register themselves as they are
- ;; called since this would preclude proper iteration over the hash table
- ;; elements.
- (hash-for-each (lambda (id proc) (proc m)) (module-weak-observers m)))
-
-
-
-;;; {Module Searching in General}
-;;;
-;;; We sometimes want to look for properties of a symbol
-;;; just within the obarray of one module. If the property
-;;; holds, then it is said to hold ``locally'' as in, ``The symbol
-;;; DISPLAY is locally rebound in the module `safe-guile'.''
-;;;
-;;;
-;;; Other times, we want to test for a symbol property in the obarray
-;;; of M and, if it is not found there, try each of the modules in the
-;;; uses list of M. This is the normal way of testing for some
-;;; property, so we state these properties without qualification as
-;;; in: ``The symbol 'fnord is interned in module M because it is
-;;; interned locally in module M2 which is a member of the uses list
-;;; of M.''
-;;;
-
-;; module-search fn m
-;;
-;; return the first non-#f result of FN applied to M and then to
-;; the modules in the uses of m, and so on recursively. If all applications
-;; return #f, then so does this function.
-;;
-(define (module-search fn m v)
- (define (loop pos)
- (and (pair? pos)
- (or (module-search fn (car pos) v)
- (loop (cdr pos)))))
- (or (fn m v)
- (loop (module-uses m))))
-
-
-;;; {Is a symbol bound in a module?}
-;;;
-;;; Symbol S in Module M is bound if S is interned in M and if the binding
-;;; of S in M has been set to some well-defined value.
-;;;
-
-;; module-locally-bound? module symbol
-;;
-;; Is a symbol bound (interned and defined) locally in a given module?
-;;
-(define (module-locally-bound? m v)
- (let ((var (module-local-variable m v)))
- (and var
- (variable-bound? var))))
-
-;; module-bound? module symbol
-;;
-;; Is a symbol bound (interned and defined) anywhere in a given module
-;; or its uses?
-;;
-(define (module-bound? m v)
- (let ((var (module-variable m v)))
- (and var
- (variable-bound? var))))
-
-;;; {Is a symbol interned in a module?}
-;;;
-;;; Symbol S in Module M is interned if S occurs in
-;;; of S in M has been set to some well-defined value.
-;;;
-;;; It is possible to intern a symbol in a module without providing
-;;; an initial binding for the corresponding variable. This is done
-;;; with:
-;;; (module-add! module symbol (make-undefined-variable))
-;;;
-;;; In that case, the symbol is interned in the module, but not
-;;; bound there. The unbound symbol shadows any binding for that
-;;; symbol that might otherwise be inherited from a member of the uses list.
-;;;
-
-(define (module-obarray-get-handle ob key)
- ((if (symbol? key) hashq-get-handle hash-get-handle) ob key))
-
-(define (module-obarray-ref ob key)
- ((if (symbol? key) hashq-ref hash-ref) ob key))
-
-(define (module-obarray-set! ob key val)
- ((if (symbol? key) hashq-set! hash-set!) ob key val))
-
-(define (module-obarray-remove! ob key)
- ((if (symbol? key) hashq-remove! hash-remove!) ob key))
-
-;; module-symbol-locally-interned? module symbol
-;;
-;; is a symbol interned (not neccessarily defined) locally in a given module
-;; or its uses? Interned symbols shadow inherited bindings even if
-;; they are not themselves bound to a defined value.
-;;
-(define (module-symbol-locally-interned? m v)
- (not (not (module-obarray-get-handle (module-obarray m) v))))
-
-;; module-symbol-interned? module symbol
-;;
-;; is a symbol interned (not neccessarily defined) anywhere in a given module
-;; or its uses? Interned symbols shadow inherited bindings even if
-;; they are not themselves bound to a defined value.
-;;
-(define (module-symbol-interned? m v)
- (module-search module-symbol-locally-interned? m v))
-
-
-;;; {Mapping modules x symbols --> variables}
-;;;
-
-;; module-local-variable module symbol
-;; return the local variable associated with a MODULE and SYMBOL.
-;;
-;;; This function is very important. It is the only function that can
-;;; return a variable from a module other than the mutators that store
-;;; new variables in modules. Therefore, this function is the location
-;;; of the "lazy binder" hack.
-;;;
-;;; If symbol is defined in MODULE, and if the definition binds symbol
-;;; to a variable, return that variable object.
-;;;
-;;; If the symbols is not found at first, but the module has a lazy binder,
-;;; then try the binder.
-;;;
-;;; If the symbol is not found at all, return #f.
-;;;
-;;; (This is now written in C, see `modules.c'.)
-;;;
-
-;;; {Mapping modules x symbols --> bindings}
-;;;
-;;; These are similar to the mapping to variables, except that the
-;;; variable is dereferenced.
-;;;
-
-;; module-symbol-binding module symbol opt-value
-;;
-;; return the binding of a variable specified by name within
-;; a given module, signalling an error if the variable is unbound.
-;; If the OPT-VALUE is passed, then instead of signalling an error,
-;; return OPT-VALUE.
-;;
-(define (module-symbol-local-binding m v . opt-val)
- (let ((var (module-local-variable m v)))
- (if (and var (variable-bound? var))
- (variable-ref var)
- (if (not (null? opt-val))
- (car opt-val)
- (error "Locally unbound variable." v)))))
-
-;; module-symbol-binding module symbol opt-value
-;;
-;; return the binding of a variable specified by name within
-;; a given module, signalling an error if the variable is unbound.
-;; If the OPT-VALUE is passed, then instead of signalling an error,
-;; return OPT-VALUE.
-;;
-(define (module-symbol-binding m v . opt-val)
- (let ((var (module-variable m v)))
- (if (and var (variable-bound? var))
- (variable-ref var)
- (if (not (null? opt-val))
- (car opt-val)
- (error "Unbound variable." v)))))
-
-
-
-
-;;; {Adding Variables to Modules}
-;;;
-
-;; module-make-local-var! module symbol
-;;
-;; ensure a variable for V in the local namespace of M.
-;; If no variable was already there, then create a new and uninitialzied
-;; variable.
-;;
-;; This function is used in modules.c.
-;;
-(define (module-make-local-var! m v)
- (or (let ((b (module-obarray-ref (module-obarray m) v)))
- (and (variable? b)
- (begin
- ;; Mark as modified since this function is called when
- ;; the standard eval closure defines a binding
- (module-modified m)
- b)))
-
- ;; Create a new local variable.
- (let ((local-var (make-undefined-variable)))
- (module-add! m v local-var)
- local-var)))
-
-;; module-ensure-local-variable! module symbol
-;;
-;; Ensure that there is a local variable in MODULE for SYMBOL. If
-;; there is no binding for SYMBOL, create a new uninitialized
-;; variable. Return the local variable.
-;;
-(define (module-ensure-local-variable! module symbol)
- (or (module-local-variable module symbol)
- (let ((var (make-undefined-variable)))
- (module-add! module symbol var)
- var)))
-
-;; module-add! module symbol var
-;;
-;; ensure a particular variable for V in the local namespace of M.
-;;
-(define (module-add! m v var)
- (if (not (variable? var))
- (error "Bad variable to module-add!" var))
- (if (not (symbol? v))
- (error "Bad symbol to module-add!" v))
- (module-obarray-set! (module-obarray m) v var)
- (module-modified m))
-
-;; module-remove!
-;;
-;; make sure that a symbol is undefined in the local namespace of M.
-;;
-(define (module-remove! m v)
- (module-obarray-remove! (module-obarray m) v)
- (module-modified m))
-
-(define (module-clear! m)
- (hash-clear! (module-obarray m))
- (module-modified m))
-
-;; MODULE-FOR-EACH -- exported
-;;
-;; Call PROC on each symbol in MODULE, with arguments of (SYMBOL VARIABLE).
-;;
-(define (module-for-each proc module)
- (hash-for-each proc (module-obarray module)))
-
-(define (module-map proc module)
- (hash-map->list proc (module-obarray module)))
-
-;; Submodules
-;;
-;; Modules exist in a separate namespace from values, because you generally do
-;; not want the name of a submodule, which you might not even use, to collide
-;; with local variables that happen to be named the same as the submodule.
-;;
-(define (module-ref-submodule module name)
- (or (hashq-ref (module-submodules module) name)
- (and (module-submodule-binder module)
- ((module-submodule-binder module) module name))))
-
-(define (module-define-submodule! module name submodule)
- (hashq-set! (module-submodules module) name submodule))
-
-;; It used to be, however, that module names were also present in the
-;; value namespace. When we enable deprecated code, we preserve this
-;; legacy behavior.
-;;
-;; These shims are defined here instead of in deprecated.scm because we
-;; need their definitions before loading other modules.
-;;
-(begin-deprecated
- (define (module-ref-submodule module name)
- (or (hashq-ref (module-submodules module) name)
- (and (module-submodule-binder module)
- ((module-submodule-binder module) module name))
- (let ((var (module-local-variable module name)))
- (and var (variable-bound? var) (module? (variable-ref var))
- (begin
- (warn "module" module "not in submodules table")
- (variable-ref var))))))
-
- (define (module-define-submodule! module name submodule)
- (let ((var (module-local-variable module name)))
- (if (and var
- (or (not (variable-bound? var))
- (not (module? (variable-ref var)))))
- (warn "defining module" module ": not overriding local definition" var)
- (module-define! module name submodule)))
- (hashq-set! (module-submodules module) name submodule)))
-
-
-
-;;; {Module-based Loading}
-;;;
-
-(define (save-module-excursion thunk)
- (let ((inner-module (current-module))
- (outer-module #f))
- (dynamic-wind (lambda ()
- (set! outer-module (current-module))
- (set-current-module inner-module)
- (set! inner-module #f))
- thunk
- (lambda ()
- (set! inner-module (current-module))
- (set-current-module outer-module)
- (set! outer-module #f)))))
-
-
-
-;;; {MODULE-REF -- exported}
-;;;
-
-;; Returns the value of a variable called NAME in MODULE or any of its
-;; used modules. If there is no such variable, then if the optional third
-;; argument DEFAULT is present, it is returned; otherwise an error is signaled.
-;;
-(define (module-ref module name . rest)
- (let ((variable (module-variable module name)))
- (if (and variable (variable-bound? variable))
- (variable-ref variable)
- (if (null? rest)
- (error "No variable named" name 'in module)
- (car rest) ; default value
- ))))
-
-;; MODULE-SET! -- exported
-;;
-;; Sets the variable called NAME in MODULE (or in a module that MODULE uses)
-;; to VALUE; if there is no such variable, an error is signaled.
-;;
-(define (module-set! module name value)
- (let ((variable (module-variable module name)))
- (if variable
- (variable-set! variable value)
- (error "No variable named" name 'in module))))
-
-;; MODULE-DEFINE! -- exported
-;;
-;; Sets the variable called NAME in MODULE to VALUE; if there is no such
-;; variable, it is added first.
-;;
-(define (module-define! module name value)
- (let ((variable (module-local-variable module name)))
- (if variable
- (begin
- (variable-set! variable value)
- (module-modified module))
- (let ((variable (make-variable value)))
- (module-add! module name variable)))))
-
-;; MODULE-DEFINED? -- exported
-;;
-;; Return #t iff NAME is defined in MODULE (or in a module that MODULE
-;; uses)
-;;
-(define (module-defined? module name)
- (let ((variable (module-variable module name)))
- (and variable (variable-bound? variable))))
-
-;; MODULE-USE! module interface
-;;
-;; Add INTERFACE to the list of interfaces used by MODULE.
-;;
-(define (module-use! module interface)
- (if (not (or (eq? module interface)
- (memq interface (module-uses module))))
- (begin
- ;; Newly used modules must be appended rather than consed, so that
- ;; `module-variable' traverses the use list starting from the first
- ;; used module.
- (set-module-uses! module (append (module-uses module)
- (list interface)))
- (hash-clear! (module-import-obarray module))
- (module-modified module))))
-
-;; MODULE-USE-INTERFACES! module interfaces
-;;
-;; Same as MODULE-USE!, but only notifies module observers after all
-;; interfaces are added to the inports list.
-;;
-(define (module-use-interfaces! module interfaces)
- (let* ((cur (module-uses module))
- (new (let lp ((in interfaces) (out '()))
- (if (null? in)
- (reverse out)
- (lp (cdr in)
- (let ((iface (car in)))
- (if (or (memq iface cur) (memq iface out))
- out
- (cons iface out))))))))
- (set-module-uses! module (append cur new))
- (hash-clear! (module-import-obarray module))
- (module-modified module)))
-
-
-
-;;; {Recursive Namespaces}
-;;;
-;;; A hierarchical namespace emerges if we consider some module to be
-;;; root, and submodules of that module to be nested namespaces.
-;;;
-;;; The routines here manage variable names in hierarchical namespace.
-;;; Each variable name is a list of elements, looked up in successively nested
-;;; modules.
-;;;
-;;; (nested-ref some-root-module '(foo bar baz))
-;;; => <value of a variable named baz in the submodule bar of
-;;; the submodule foo of some-root-module>
-;;;
-;;;
-;;; There are:
-;;;
-;;; ;; a-root is a module
-;;; ;; name is a list of symbols
-;;;
-;;; nested-ref a-root name
-;;; nested-set! a-root name val
-;;; nested-define! a-root name val
-;;; nested-remove! a-root name
-;;;
-;;; These functions manipulate values in namespaces. For referencing the
-;;; namespaces themselves, use the following:
-;;;
-;;; nested-ref-module a-root name
-;;; nested-define-module! a-root name mod
-;;;
-;;; (current-module) is a natural choice for a root so for convenience there are
-;;; also:
-;;;
-;;; local-ref name == nested-ref (current-module) name
-;;; local-set! name val == nested-set! (current-module) name val
-;;; local-define name val == nested-define! (current-module) name val
-;;; local-remove name == nested-remove! (current-module) name
-;;; local-ref-module name == nested-ref-module (current-module) name
-;;; local-define-module! name m == nested-define-module! (current-module) name m
-;;;
-
-
-(define (nested-ref root names)
- (if (null? names)
- root
- (let loop ((cur root)
- (head (car names))
- (tail (cdr names)))
- (if (null? tail)
- (module-ref cur head #f)
- (let ((cur (module-ref-submodule cur head)))
- (and cur
- (loop cur (car tail) (cdr tail))))))))
-
-(define (nested-set! root names val)
- (let loop ((cur root)
- (head (car names))
- (tail (cdr names)))
- (if (null? tail)
- (module-set! cur head val)
- (let ((cur (module-ref-submodule cur head)))
- (if (not cur)
- (error "failed to resolve module" names)
- (loop cur (car tail) (cdr tail)))))))
-
-(define (nested-define! root names val)
- (let loop ((cur root)
- (head (car names))
- (tail (cdr names)))
- (if (null? tail)
- (module-define! cur head val)
- (let ((cur (module-ref-submodule cur head)))
- (if (not cur)
- (error "failed to resolve module" names)
- (loop cur (car tail) (cdr tail)))))))
-
-(define (nested-remove! root names)
- (let loop ((cur root)
- (head (car names))
- (tail (cdr names)))
- (if (null? tail)
- (module-remove! cur head)
- (let ((cur (module-ref-submodule cur head)))
- (if (not cur)
- (error "failed to resolve module" names)
- (loop cur (car tail) (cdr tail)))))))
-
-
-(define (nested-ref-module root names)
- (let loop ((cur root)
- (names names))
- (if (null? names)
- cur
- (let ((cur (module-ref-submodule cur (car names))))
- (and cur
- (loop cur (cdr names)))))))
-
-(define (nested-define-module! root names module)
- (if (null? names)
- (error "can't redefine root module" root module)
- (let loop ((cur root)
- (head (car names))
- (tail (cdr names)))
- (if (null? tail)
- (module-define-submodule! cur head module)
- (let ((cur (or (module-ref-submodule cur head)
- (let ((m (make-module 31)))
- (set-module-kind! m 'directory)
- (set-module-name! m (append (module-name cur)
- (list head)))
- (module-define-submodule! cur head m)
- m))))
- (loop cur (car tail) (cdr tail)))))))
-
-
-(define (local-ref names)
- (nested-ref (current-module) names))
-
-(define (local-set! names val)
- (nested-set! (current-module) names val))
-
-(define (local-define names val)
- (nested-define! (current-module) names val))
-
-(define (local-remove names)
- (nested-remove! (current-module) names))
-
-(define (local-ref-module names)
- (nested-ref-module (current-module) names))
-
-(define (local-define-module names mod)
- (nested-define-module! (current-module) names mod))
-
-
-
-
-
-;;; {The (guile) module}
-;;;
-;;; The standard module, which has the core Guile bindings. Also called the
-;;; "root module", as it is imported by many other modules, but it is not
-;;; necessarily the root of anything; and indeed, the module named '() might be
-;;; better thought of as a root.
-;;;
-
-;; The root module uses the pre-modules-obarray as its obarray. This
-;; special obarray accumulates all bindings that have been established
-;; before the module system is fully booted.
-;;
-;; (The obarray continues to be used by code that has been closed over
-;; before the module system has been booted.)
-;;
-(define the-root-module
- (let ((m (make-module 0)))
- (set-module-obarray! m (%get-pre-modules-obarray))
- (set-module-name! m '(guile))
-
- ;; Inherit next-unique-id from preliminary stub of
- ;; %module-get-next-unique-id! defined above.
- (set-module-next-unique-id! m (module-generate-unique-id! #f))
-
- m))
-
-;; The root interface is a module that uses the same obarray as the
-;; root module. It does not allow new definitions, tho.
-;;
-(define the-scm-module
- (let ((m (make-module 0)))
- (set-module-obarray! m (%get-pre-modules-obarray))
- (set-module-name! m '(guile))
- (set-module-kind! m 'interface)
-
- ;; In Guile 1.8 and earlier M was its own public interface.
- (set-module-public-interface! m m)
-
- m))
-
-(set-module-public-interface! the-root-module the-scm-module)
-
-
-
-;; Now that we have a root module, even though modules aren't fully booted,
-;; expand the definition of resolve-module.
-;;
-(define (resolve-module name . args)
- (if (equal? name '(guile))
- the-root-module
- (error "unexpected module to resolve during module boot" name)))
-
-(define (module-generate-unique-id! m)
- (let ((i (module-next-unique-id m)))
- (set-module-next-unique-id! m (+ i 1))
- i))
-
-;; Cheat. These bindings are needed by modules.c, but we don't want
-;; to move their real definition here because that would be unnatural.
-;;
-(define define-module* #f)
-(define process-use-modules #f)
-(define module-export! #f)
-(define default-duplicate-binding-procedures #f)
-
-;; This boots the module system. All bindings needed by modules.c
-;; must have been defined by now.
-;;
-(set-current-module the-root-module)
-
-
-
-
-;; Now that modules are booted, give module-name its final definition.
-;;
-(define module-name
- (let ((accessor (record-accessor module-type 'name)))
- (lambda (mod)
- (or (accessor mod)
- (let ((name (list (gensym))))
- ;; Name MOD and bind it in the module root so that it's visible to
- ;; `resolve-module'. This is important as `psyntax' stores module
- ;; names and relies on being able to `resolve-module' them.
- (set-module-name! mod name)
- (nested-define-module! (resolve-module '() #f) name mod)
- (accessor mod))))))
-
-(define* (module-gensym #\optional (id " mg") (m (current-module)))
- "Return a fresh symbol in the context of module M, based on ID (a
-string or symbol). As long as M is a valid module, this procedure is
-deterministic."
- (define (->string number)
- (number->string number 16))
-
- (if m
- (string->symbol
- (string-append id "-"
- (->string (hash (module-name m) most-positive-fixnum))
- "-"
- (->string (module-generate-unique-id! m))))
- (gensym id)))
-
-(define (make-modules-in module name)
- (or (nested-ref-module module name)
- (let ((m (make-module 31)))
- (set-module-kind! m 'directory)
- (set-module-name! m (append (module-name module) name))
- (nested-define-module! module name m)
- m)))
-
-(define (beautify-user-module! module)
- (let ((interface (module-public-interface module)))
- (if (or (not interface)
- (eq? interface module))
- (let ((interface (make-module 31)))
- (set-module-name! interface (module-name module))
- (set-module-version! interface (module-version module))
- (set-module-kind! interface 'interface)
- (set-module-public-interface! module interface))))
- (if (and (not (memq the-scm-module (module-uses module)))
- (not (eq? module the-root-module)))
- ;; Import the default set of bindings (from the SCM module) in MODULE.
- (module-use! module the-scm-module)))
-
-(define (version-matches? version-ref target)
- (define (sub-versions-match? v-refs t)
- (define (sub-version-matches? v-ref t)
- (let ((matches? (lambda (v) (sub-version-matches? v t))))
- (cond
- ((number? v-ref) (eqv? v-ref t))
- ((list? v-ref)
- (case (car v-ref)
- ((>=) (>= t (cadr v-ref)))
- ((<=) (<= t (cadr v-ref)))
- ((and) (and-map matches? (cdr v-ref)))
- ((or) (or-map matches? (cdr v-ref)))
- ((not) (not (matches? (cadr v-ref))))
- (else (error "Invalid sub-version reference" v-ref))))
- (else (error "Invalid sub-version reference" v-ref)))))
- (or (null? v-refs)
- (and (not (null? t))
- (sub-version-matches? (car v-refs) (car t))
- (sub-versions-match? (cdr v-refs) (cdr t)))))
-
- (let ((matches? (lambda (v) (version-matches? v target))))
- (or (null? version-ref)
- (case (car version-ref)
- ((and) (and-map matches? (cdr version-ref)))
- ((or) (or-map matches? (cdr version-ref)))
- ((not) (not (matches? (cadr version-ref))))
- (else (sub-versions-match? version-ref target))))))
-
-(define (make-fresh-user-module)
- (let ((m (make-module)))
- (beautify-user-module! m)
- m))
-
-;; NOTE: This binding is used in libguile/modules.c.
-;;
-(define resolve-module
- (let ((root (make-module)))
- (set-module-name! root '())
- ;; Define the-root-module as '(guile).
- (module-define-submodule! root 'guile the-root-module)
-
- (lambda* (name #\optional (autoload #t) (version #f) #\key (ensure #t))
- (let ((already (nested-ref-module root name)))
- (cond
- ((and already
- (or (not autoload) (module-public-interface already)))
- ;; A hit, a palpable hit.
- (if (and version
- (not (version-matches? version (module-version already))))
- (error "incompatible module version already loaded" name))
- already)
- (autoload
- ;; Try to autoload the module, and recurse.
- (try-load-module name version)
- (resolve-module name #f #\ensure ensure))
- (else
- ;; No module found (or if one was, it had no public interface), and
- ;; we're not autoloading. Make an empty module if #\ensure is true.
- (or already
- (and ensure
- (make-modules-in root name)))))))))
-
-
-(define (try-load-module name version)
- (try-module-autoload name version))
-
-(define (reload-module m)
- "Revisit the source file corresponding to the module @var{m}."
- (let ((f (module-filename m)))
- (if f
- (save-module-excursion
- (lambda ()
- ;; Re-set the initial environment, as in try-module-autoload.
- (set-current-module (make-fresh-user-module))
- (primitive-load-path f)
- m))
- ;; Though we could guess, we *should* know it.
- (error "unknown file name for module" m))))
-
-(define (purify-module! module)
- "Removes bindings in MODULE which are inherited from the (guile) module."
- (let ((use-list (module-uses module)))
- (if (and (pair? use-list)
- (eq? (car (last-pair use-list)) the-scm-module))
- (set-module-uses! module (reverse (cdr (reverse use-list)))))))
-
-;; Return a module that is an interface to the module designated by
-;; NAME.
-;;
-;; `resolve-interface' takes four keyword arguments:
-;;
-;; #\select SELECTION
-;;
-;; SELECTION is a list of binding-specs to be imported; A binding-spec
-;; is either a symbol or a pair of symbols (ORIG . SEEN), where ORIG
-;; is the name in the used module and SEEN is the name in the using
-;; module. Note that SEEN is also passed through RENAMER, below. The
-;; default is to select all bindings. If you specify no selection but
-;; a renamer, only the bindings that already exist in the used module
-;; are made available in the interface. Bindings that are added later
-;; are not picked up.
-;;
-;; #\hide BINDINGS
-;;
-;; BINDINGS is a list of bindings which should not be imported.
-;;
-;; #\prefix PREFIX
-;;
-;; PREFIX is a symbol that will be appended to each exported name.
-;; The default is to not perform any renaming.
-;;
-;; #\renamer RENAMER
-;;
-;; RENAMER is a procedure that takes a symbol and returns its new
-;; name. The default is not perform any renaming.
-;;
-;; Signal "no code for module" error if module name is not resolvable
-;; or its public interface is not available. Signal "no binding"
-;; error if selected binding does not exist in the used module.
-;;
-(define* (resolve-interface name #\key
- (select #f)
- (hide '())
- (prefix #f)
- (renamer (if prefix
- (symbol-prefix-proc prefix)
- identity))
- version)
- (let* ((module (resolve-module name #t version #\ensure #f))
- (public-i (and module (module-public-interface module))))
- (unless public-i
- (error "no code for module" name))
- (if (and (not select) (null? hide) (eq? renamer identity))
- public-i
- (let ((selection (or select (module-map (lambda (sym var) sym)
- public-i)))
- (custom-i (make-module 31)))
- (set-module-kind! custom-i 'custom-interface)
- (set-module-name! custom-i name)
- ;; XXX - should use a lazy binder so that changes to the
- ;; used module are picked up automatically.
- (for-each (lambda (bspec)
- (let* ((direct? (symbol? bspec))
- (orig (if direct? bspec (car bspec)))
- (seen (if direct? bspec (cdr bspec)))
- (var (or (module-local-variable public-i orig)
- (module-local-variable module orig)
- (error
- ;; fixme: format manually for now
- (simple-format
- #f "no binding `~A' in module ~A"
- orig name)))))
- (if (memq orig hide)
- (set! hide (delq! orig hide))
- (module-add! custom-i
- (renamer seen)
- var))))
- selection)
- ;; Check that we are not hiding bindings which don't exist
- (for-each (lambda (binding)
- (if (not (module-local-variable public-i binding))
- (error
- (simple-format
- #f "no binding `~A' to hide in module ~A"
- binding name))))
- hide)
- custom-i))))
-
-(define (symbol-prefix-proc prefix)
- (lambda (symbol)
- (symbol-append prefix symbol)))
-
-;; This function is called from "modules.c". If you change it, be
-;; sure to update "modules.c" as well.
-
-(define* (define-module* name
- #\key filename pure version (duplicates '())
- (imports '()) (exports '()) (replacements '())
- (re-exports '()) (autoloads '()) transformer)
- (define (list-of pred l)
- (or (null? l)
- (and (pair? l) (pred (car l)) (list-of pred (cdr l)))))
- (define (valid-export? x)
- (or (symbol? x) (and (pair? x) (symbol? (car x)) (symbol? (cdr x)))))
- (define (valid-autoload? x)
- (and (pair? x) (list-of symbol? (car x)) (list-of symbol? (cdr x))))
-
- (define (resolve-imports imports)
- (define (resolve-import import-spec)
- (if (list? import-spec)
- (apply resolve-interface import-spec)
- (error "unexpected use-module specification" import-spec)))
- (let lp ((imports imports) (out '()))
- (cond
- ((null? imports) (reverse! out))
- ((pair? imports)
- (lp (cdr imports)
- (cons (resolve-import (car imports)) out)))
- (else (error "unexpected tail of imports list" imports)))))
-
- ;; We could add a #\no-check arg, set by the define-module macro, if
- ;; these checks are taking too much time.
- ;;
- (let ((module (resolve-module name #f)))
- (beautify-user-module! module)
- (if filename
- (set-module-filename! module filename))
- (if pure
- (purify-module! module))
- (if version
- (begin
- (if (not (list-of integer? version))
- (error "expected list of integers for version"))
- (set-module-version! module version)
- (set-module-version! (module-public-interface module) version)))
- (let ((imports (resolve-imports imports)))
- (call-with-deferred-observers
- (lambda ()
- (if (pair? imports)
- (module-use-interfaces! module imports))
- (if (list-of valid-export? exports)
- (if (pair? exports)
- (module-export! module exports))
- (error "expected exports to be a list of symbols or symbol pairs"))
- (if (list-of valid-export? replacements)
- (if (pair? replacements)
- (module-replace! module replacements))
- (error "expected replacements to be a list of symbols or symbol pairs"))
- (if (list-of valid-export? re-exports)
- (if (pair? re-exports)
- (module-re-export! module re-exports))
- (error "expected re-exports to be a list of symbols or symbol pairs"))
- ;; FIXME
- (if (not (null? autoloads))
- (apply module-autoload! module autoloads))
- ;; Wait until modules have been loaded to resolve duplicates
- ;; handlers.
- (if (pair? duplicates)
- (let ((handlers (lookup-duplicates-handlers duplicates)))
- (set-module-duplicates-handlers! module handlers))))))
-
- (if transformer
- (if (and (pair? transformer) (list-of symbol? transformer))
- (let ((iface (resolve-interface transformer))
- (sym (car (last-pair transformer))))
- (set-module-transformer! module (module-ref iface sym)))
- (error "expected transformer to be a module name" transformer)))
-
- (run-hook module-defined-hook module)
- module))
-
-;; `module-defined-hook' is a hook that is run whenever a new module
-;; is defined. Its members are called with one argument, the new
-;; module.
-(define module-defined-hook (make-hook 1))
-
-
-
-;;; {Autoload}
-;;;
-
-(define (make-autoload-interface module name bindings)
- (let ((b (lambda (a sym definep)
- (false-if-exception
- (and (memq sym bindings)
- (let ((i (module-public-interface (resolve-module name))))
- (if (not i)
- (error "missing interface for module" name))
- (let ((autoload (memq a (module-uses module))))
- ;; Replace autoload-interface with actual interface if
- ;; that has not happened yet.
- (if (pair? autoload)
- (set-car! autoload i)))
- (module-local-variable i sym)))
- #\warning "Failed to autoload ~a in ~a:\n" sym name))))
- (module-constructor (make-hash-table 0) '() b #f #f name 'autoload #f
- (make-hash-table 0) '() (make-weak-value-hash-table 31) #f
- (make-hash-table 0) #f #f #f 0)))
-
-(define (module-autoload! module . args)
- "Have @var{module} automatically load the module named @var{name} when one
-of the symbols listed in @var{bindings} is looked up. @var{args} should be a
-list of module-name/binding-list pairs, e.g., as in @code{(module-autoload!
-module '(ice-9 q) '(make-q q-length))}."
- (let loop ((args args))
- (cond ((null? args)
- #t)
- ((null? (cdr args))
- (error "invalid name+binding autoload list" args))
- (else
- (let ((name (car args))
- (bindings (cadr args)))
- (module-use! module (make-autoload-interface module
- name bindings))
- (loop (cddr args)))))))
-
-
-
-
-;;; {Autoloading modules}
-;;;
-
-;;; XXX FIXME autoloads-in-progress and autoloads-done
-;;; are not handled in a thread-safe way.
-
-(define autoloads-in-progress '())
-
-;; This function is called from scm_load_scheme_module in
-;; "deprecated.c". Please do not change its interface.
-;;
-(define* (try-module-autoload module-name #\optional version)
- "Try to load a module of the given name. If it is not found, return
-#f. Otherwise return #t. May raise an exception if a file is found,
-but it fails to load."
- (let* ((reverse-name (reverse module-name))
- (name (symbol->string (car reverse-name)))
- (dir-hint-module-name (reverse (cdr reverse-name)))
- (dir-hint (apply string-append
- (map (lambda (elt)
- (string-append (symbol->string elt)
- file-name-separator-string))
- dir-hint-module-name))))
- (resolve-module dir-hint-module-name #f)
- (and (not (autoload-done-or-in-progress? dir-hint name))
- (let ((didit #f))
- (dynamic-wind
- (lambda () (autoload-in-progress! dir-hint name))
- (lambda ()
- (with-fluids ((current-reader #f))
- (save-module-excursion
- (lambda ()
- (define (call/ec proc)
- (let ((tag (make-prompt-tag)))
- (call-with-prompt
- tag
- (lambda ()
- (proc (lambda () (abort-to-prompt tag))))
- (lambda (k) (values)))))
- ;; The initial environment when loading a module is a fresh
- ;; user module.
- (set-current-module (make-fresh-user-module))
- ;; Here we could allow some other search strategy (other than
- ;; primitive-load-path), for example using versions encoded
- ;; into the file system -- but then we would have to figure
- ;; out how to locate the compiled file, do auto-compilation,
- ;; etc. Punt for now, and don't use versions when locating
- ;; the file.
- (call/ec
- (lambda (abort)
- (primitive-load-path (in-vicinity dir-hint name)
- abort)
- (set! didit #t)))))))
- (lambda () (set-autoloaded! dir-hint name didit)))
- didit))))
-
-
-
-;;; {Dynamic linking of modules}
-;;;
-
-(define autoloads-done '((guile . guile)))
-
-(define (autoload-done-or-in-progress? p m)
- (let ((n (cons p m)))
- (->bool (or (member n autoloads-done)
- (member n autoloads-in-progress)))))
-
-(define (autoload-done! p m)
- (let ((n (cons p m)))
- (set! autoloads-in-progress
- (delete! n autoloads-in-progress))
- (or (member n autoloads-done)
- (set! autoloads-done (cons n autoloads-done)))))
-
-(define (autoload-in-progress! p m)
- (let ((n (cons p m)))
- (set! autoloads-done
- (delete! n autoloads-done))
- (set! autoloads-in-progress (cons n autoloads-in-progress))))
-
-(define (set-autoloaded! p m done?)
- (if done?
- (autoload-done! p m)
- (let ((n (cons p m)))
- (set! autoloads-done (delete! n autoloads-done))
- (set! autoloads-in-progress (delete! n autoloads-in-progress)))))
-
-
-
-;;; {Run-time options}
-;;;
-
-(define-syntax define-option-interface
- (syntax-rules ()
- ((_ (interface (options enable disable) (option-set!)))
- (begin
- (define options
- (case-lambda
- (() (interface))
- ((arg)
- (if (list? arg)
- (begin (interface arg) (interface))
- (for-each
- (lambda (option)
- (apply (lambda (name value documentation)
- (display name)
- (let ((len (string-length (symbol->string name))))
- (when (< len 16)
- (display #\tab)
- (when (< len 8)
- (display #\tab))))
- (display #\tab)
- (display value)
- (display #\tab)
- (display documentation)
- (newline))
- option))
- (interface #t))))))
- (define (enable . flags)
- (interface (append flags (interface)))
- (interface))
- (define (disable . flags)
- (let ((options (interface)))
- (for-each (lambda (flag) (set! options (delq! flag options)))
- flags)
- (interface options)
- (interface)))
- (define-syntax-rule (option-set! opt val)
- (eval-when (expand load eval)
- (options (append (options) (list 'opt val)))))))))
-
-(define-option-interface
- (debug-options-interface
- (debug-options debug-enable debug-disable)
- (debug-set!)))
-
-(define-option-interface
- (read-options-interface
- (read-options read-enable read-disable)
- (read-set!)))
-
-(define-option-interface
- (print-options-interface
- (print-options print-enable print-disable)
- (print-set!)))
-
-
-
-;;; {The Unspecified Value}
-;;;
-;;; Currently Guile represents unspecified values via one particular value,
-;;; which may be obtained by evaluating (if #f #f). It would be nice in the
-;;; future if we could replace this with a return of 0 values, though.
-;;;
-
-(define-syntax *unspecified*
- (identifier-syntax (if #f #f)))
-
-(define (unspecified? v) (eq? v *unspecified*))
-
-
-
-
-;;; {Parameters}
-;;;
-
-(define <parameter>
- ;; Three fields: the procedure itself, the fluid, and the converter.
- (make-struct <applicable-struct-vtable> 0 'pwprpr))
-(set-struct-vtable-name! <parameter> '<parameter>)
-
-(define* (make-parameter init #\optional (conv (lambda (x) x)))
- "Make a new parameter.
-
-A parameter is a dynamically bound value, accessed through a procedure.
-To access the current value, apply the procedure with no arguments:
-
- (define p (make-parameter 10))
- (p) => 10
-
-To provide a new value for the parameter in a dynamic extent, use
-`parameterize':
-
- (parameterize ((p 20))
- (p)) => 20
- (p) => 10
-
-The value outside of the dynamic extent of the body is unaffected. To
-update the current value, apply it to one argument:
-
- (p 20) => 10
- (p) => 20
-
-As you can see, the call that updates a parameter returns its previous
-value.
-
-All values for the parameter are first run through the CONV procedure,
-including INIT, the initial value. The default CONV procedure is the
-identity procedure. CONV is commonly used to ensure some set of
-invariants on the values that a parameter may have."
- (let ((fluid (make-fluid (conv init))))
- (make-struct <parameter> 0
- (case-lambda
- (() (fluid-ref fluid))
- ((x) (let ((prev (fluid-ref fluid)))
- (fluid-set! fluid (conv x))
- prev)))
- fluid conv)))
-
-(define* (fluid->parameter fluid #\optional (conv (lambda (x) x)))
- "Make a parameter that wraps a fluid.
-
-The value of the parameter will be the same as the value of the fluid.
-If the parameter is rebound in some dynamic extent, perhaps via
-`parameterize', the new value will be run through the optional CONV
-procedure, as with any parameter. Note that unlike `make-parameter',
-CONV is not applied to the initial value."
- (make-struct <parameter> 0
- (case-lambda
- (() (fluid-ref fluid))
- ((x) (let ((prev (fluid-ref fluid)))
- (fluid-set! fluid (conv x))
- prev)))
- fluid conv))
-
-(define (parameter? x)
- (and (struct? x) (eq? (struct-vtable x) <parameter>)))
-
-(define (parameter-fluid p)
- (if (parameter? p)
- (struct-ref p 1)
- (scm-error 'wrong-type-arg "parameter-fluid"
- "Not a parameter: ~S" (list p) #f)))
-
-(define (parameter-converter p)
- (if (parameter? p)
- (struct-ref p 2)
- (scm-error 'wrong-type-arg "parameter-fluid"
- "Not a parameter: ~S" (list p) #f)))
-
-(define-syntax parameterize
- (lambda (x)
- (syntax-case x ()
- ((_ ((param value) ...) body body* ...)
- (with-syntax (((p ...) (generate-temporaries #'(param ...))))
- #'(let ((p param) ...)
- (if (not (parameter? p))
- (scm-error 'wrong-type-arg "parameterize"
- "Not a parameter: ~S" (list p) #f))
- ...
- (with-fluids (((struct-ref p 1) ((struct-ref p 2) value))
- ...)
- body body* ...)))))))
-
-
-;;;
-;;; Current ports as parameters.
-;;;
-
-(let ()
- (define-syntax-rule (port-parameterize! binding fluid predicate msg)
- (begin
- (set! binding (fluid->parameter (module-ref (current-module) 'fluid)
- (lambda (x)
- (if (predicate x) x
- (error msg x)))))
- (module-remove! (current-module) 'fluid)))
-
- (port-parameterize! current-input-port %current-input-port-fluid
- input-port? "expected an input port")
- (port-parameterize! current-output-port %current-output-port-fluid
- output-port? "expected an output port")
- (port-parameterize! current-error-port %current-error-port-fluid
- output-port? "expected an output port")
- (port-parameterize! current-warning-port %current-warning-port-fluid
- output-port? "expected an output port"))
-
-
-
-;;;
-;;; Languages.
-;;;
-
-;; The language can be a symbolic name or a <language> object from
-;; (system base language).
-;;
-(define current-language (make-parameter 'scheme))
-
-
-
-
-;;; {Running Repls}
-;;;
-
-(define *repl-stack* (make-fluid '()))
-
-;; Programs can call `batch-mode?' to see if they are running as part of a
-;; script or if they are running interactively. REPL implementations ensure that
-;; `batch-mode?' returns #f during their extent.
-;;
-(define (batch-mode?)
- (null? (fluid-ref *repl-stack*)))
-
-;; Programs can re-enter batch mode, for example after a fork, by calling
-;; `ensure-batch-mode!'. It's not a great interface, though; it would be better
-;; to abort to the outermost prompt, and call a thunk there.
-;;
-(define (ensure-batch-mode!)
- (set! batch-mode? (lambda () #t)))
-
-(define (quit . args)
- (apply throw 'quit args))
-
-(define exit quit)
-
-(define (gc-run-time)
- (cdr (assq 'gc-time-taken (gc-stats))))
-
-(define abort-hook (make-hook))
-(define before-error-hook (make-hook))
-(define after-error-hook (make-hook))
-(define before-backtrace-hook (make-hook))
-(define after-backtrace-hook (make-hook))
-
-(define before-read-hook (make-hook))
-(define after-read-hook (make-hook))
-(define before-eval-hook (make-hook 1))
-(define after-eval-hook (make-hook 1))
-(define before-print-hook (make-hook 1))
-(define after-print-hook (make-hook 1))
-
-;;; This hook is run at the very end of an interactive session.
-;;;
-(define exit-hook (make-hook))
-
-;;; The default repl-reader function. We may override this if we've
-;;; the readline library.
-(define repl-reader
- (lambda* (prompt #\optional (reader (fluid-ref current-reader)))
- (if (not (char-ready?))
- (begin
- (display (if (string? prompt) prompt (prompt)))
- ;; An interesting situation. The printer resets the column to
- ;; 0 by printing a newline, but we then advance it by printing
- ;; the prompt. However the port-column of the output port
- ;; does not typically correspond with the actual column on the
- ;; screen, because the input is echoed back! Since the
- ;; input is line-buffered and thus ends with a newline, the
- ;; output will really start on column zero. So, here we zero
- ;; it out. See bug 9664.
- ;;
- ;; Note that for similar reasons, the output-line will not
- ;; reflect the actual line on the screen. But given the
- ;; possibility of multiline input, the fix is not as
- ;; straightforward, so we don't bother.
- ;;
- ;; Also note that the readline implementation papers over
- ;; these concerns, because it's readline itself printing the
- ;; prompt, and not Guile.
- (set-port-column! (current-output-port) 0)))
- (force-output)
- (run-hook before-read-hook)
- ((or reader read) (current-input-port))))
-
-
-
-
-;;; {IOTA functions: generating lists of numbers}
-;;;
-
-(define (iota n)
- (let loop ((count (1- n)) (result '()))
- (if (< count 0) result
- (loop (1- count) (cons count result)))))
-
-
-
-;;; {While}
-;;;
-;;; with `continue' and `break'.
-;;;
-
-;; The inliner will remove the prompts at compile-time if it finds that
-;; `continue' or `break' are not used.
-;;
-(define-syntax while
- (lambda (x)
- (syntax-case x ()
- ((while cond body ...)
- #`(let ((break-tag (make-prompt-tag "break"))
- (continue-tag (make-prompt-tag "continue")))
- (call-with-prompt
- break-tag
- (lambda ()
- (define-syntax #,(datum->syntax #'while 'break)
- (lambda (x)
- (syntax-case x ()
- ((_ arg (... ...))
- #'(abort-to-prompt break-tag arg (... ...)))
- (_
- #'(lambda args
- (apply abort-to-prompt break-tag args))))))
- (let lp ()
- (call-with-prompt
- continue-tag
- (lambda ()
- (define-syntax #,(datum->syntax #'while 'continue)
- (lambda (x)
- (syntax-case x ()
- ((_)
- #'(abort-to-prompt continue-tag))
- ((_ . args)
- (syntax-violation 'continue "too many arguments" x))
- (_
- #'(lambda ()
- (abort-to-prompt continue-tag))))))
- (do () ((not cond) #f) body ...))
- (lambda (k) (lp)))))
- (lambda (k . args)
- (if (null? args)
- #t
- (apply values args)))))))))
-
-
-
-
-;;; {Module System Macros}
-;;;
-
-;; Return a list of expressions that evaluate to the appropriate
-;; arguments for resolve-interface according to SPEC.
-
-(eval-when (expand)
- (if (memq 'prefix (read-options))
- (error "boot-9 must be compiled with #:kw, not :kw")))
-
-(define (keyword-like-symbol->keyword sym)
- (symbol->keyword (string->symbol (substring (symbol->string sym) 1))))
-
-(define-syntax define-module
- (lambda (x)
- (define (keyword-like? stx)
- (let ((dat (syntax->datum stx)))
- (and (symbol? dat)
- (eqv? (string-ref (symbol->string dat) 0) #\:))))
- (define (->keyword sym)
- (symbol->keyword (string->symbol (substring (symbol->string sym) 1))))
-
- (define (parse-iface args)
- (let loop ((in args) (out '()))
- (syntax-case in ()
- (() (reverse! out))
- ;; The user wanted #\foo, but wrote :foo. Fix it.
- ((sym . in) (keyword-like? #'sym)
- (loop #`(#,(->keyword (syntax->datum #'sym)) . in) out))
- ((kw . in) (not (keyword? (syntax->datum #'kw)))
- (syntax-violation 'define-module "expected keyword arg" x #'kw))
- ((#\renamer renamer . in)
- (loop #'in (cons* #',renamer #\renamer out)))
- ((kw val . in)
- (loop #'in (cons* #'val #'kw out))))))
-
- (define (parse args imp exp rex rep aut)
- ;; Just quote everything except #\use-module and #\use-syntax. We
- ;; need to know about all arguments regardless since we want to turn
- ;; symbols that look like keywords into real keywords, and the
- ;; keyword args in a define-module form are not regular
- ;; (i.e. no-backtrace doesn't take a value).
- (syntax-case args ()
- (()
- (let ((imp (if (null? imp) '() #`(#\imports `#,imp)))
- (exp (if (null? exp) '() #`(#\exports '#,exp)))
- (rex (if (null? rex) '() #`(#\re-exports '#,rex)))
- (rep (if (null? rep) '() #`(#\replacements '#,rep)))
- (aut (if (null? aut) '() #`(#\autoloads '#,aut))))
- #`(#,@imp #,@exp #,@rex #,@rep #,@aut)))
- ;; The user wanted #\foo, but wrote :foo. Fix it.
- ((sym . args) (keyword-like? #'sym)
- (parse #`(#,(->keyword (syntax->datum #'sym)) . args)
- imp exp rex rep aut))
- ((kw . args) (not (keyword? (syntax->datum #'kw)))
- (syntax-violation 'define-module "expected keyword arg" x #'kw))
- ((#\no-backtrace . args)
- ;; Ignore this one.
- (parse #'args imp exp rex rep aut))
- ((#\pure . args)
- #`(#\pure #t . #,(parse #'args imp exp rex rep aut)))
- ((kw)
- (syntax-violation 'define-module "keyword arg without value" x #'kw))
- ((#\version (v ...) . args)
- #`(#\version '(v ...) . #,(parse #'args imp exp rex rep aut)))
- ((#\duplicates (d ...) . args)
- #`(#\duplicates '(d ...) . #,(parse #'args imp exp rex rep aut)))
- ((#\filename f . args)
- #`(#\filename 'f . #,(parse #'args imp exp rex rep aut)))
- ((#\use-module (name name* ...) . args)
- (and (and-map symbol? (syntax->datum #'(name name* ...))))
- (parse #'args #`(#,@imp ((name name* ...))) exp rex rep aut))
- ((#\use-syntax (name name* ...) . args)
- (and (and-map symbol? (syntax->datum #'(name name* ...))))
- #`(#\transformer '(name name* ...)
- . #,(parse #'args #`(#,@imp ((name name* ...))) exp rex rep aut)))
- ((#\use-module ((name name* ...) arg ...) . args)
- (and (and-map symbol? (syntax->datum #'(name name* ...))))
- (parse #'args
- #`(#,@imp ((name name* ...) #,@(parse-iface #'(arg ...))))
- exp rex rep aut))
- ((#\export (ex ...) . args)
- (parse #'args imp #`(#,@exp ex ...) rex rep aut))
- ((#\export-syntax (ex ...) . args)
- (parse #'args imp #`(#,@exp ex ...) rex rep aut))
- ((#\re-export (re ...) . args)
- (parse #'args imp exp #`(#,@rex re ...) rep aut))
- ((#\re-export-syntax (re ...) . args)
- (parse #'args imp exp #`(#,@rex re ...) rep aut))
- ((#\replace (r ...) . args)
- (parse #'args imp exp rex #`(#,@rep r ...) aut))
- ((#\replace-syntax (r ...) . args)
- (parse #'args imp exp rex #`(#,@rep r ...) aut))
- ((#\autoload name bindings . args)
- (parse #'args imp exp rex rep #`(#,@aut name bindings)))
- ((kw val . args)
- (syntax-violation 'define-module "unknown keyword or bad argument"
- #'kw #'val))))
-
- (syntax-case x ()
- ((_ (name name* ...) arg ...)
- (and-map symbol? (syntax->datum #'(name name* ...)))
- (with-syntax (((quoted-arg ...)
- (parse #'(arg ...) '() '() '() '() '()))
- ;; Ideally the filename is either a string or #f;
- ;; this hack is to work around a case in which
- ;; port-filename returns a symbol (`socket') for
- ;; sockets.
- (filename (let ((f (assq-ref (or (syntax-source x) '())
- 'filename)))
- (and (string? f) f))))
- #'(eval-when (expand load eval)
- (let ((m (define-module* '(name name* ...)
- #\filename filename quoted-arg ...)))
- (set-current-module m)
- m)))))))
-
-;; The guts of the use-modules macro. Add the interfaces of the named
-;; modules to the use-list of the current module, in order.
-
-;; This function is called by "modules.c". If you change it, be sure
-;; to change scm_c_use_module as well.
-
-(define (process-use-modules module-interface-args)
- (let ((interfaces (map (lambda (mif-args)
- (or (apply resolve-interface mif-args)
- (error "no such module" mif-args)))
- module-interface-args)))
- (call-with-deferred-observers
- (lambda ()
- (module-use-interfaces! (current-module) interfaces)))))
-
-(define-syntax use-modules
- (lambda (x)
- (define (keyword-like? stx)
- (let ((dat (syntax->datum stx)))
- (and (symbol? dat)
- (eqv? (string-ref (symbol->string dat) 0) #\:))))
- (define (->keyword sym)
- (symbol->keyword (string->symbol (substring (symbol->string sym) 1))))
-
- (define (quotify-iface args)
- (let loop ((in args) (out '()))
- (syntax-case in ()
- (() (reverse! out))
- ;; The user wanted #\foo, but wrote :foo. Fix it.
- ((sym . in) (keyword-like? #'sym)
- (loop #`(#,(->keyword (syntax->datum #'sym)) . in) out))
- ((kw . in) (not (keyword? (syntax->datum #'kw)))
- (syntax-violation 'define-module "expected keyword arg" x #'kw))
- ((#\renamer renamer . in)
- (loop #'in (cons* #'renamer #\renamer out)))
- ((kw val . in)
- (loop #'in (cons* #''val #'kw out))))))
-
- (define (quotify specs)
- (let lp ((in specs) (out '()))
- (syntax-case in ()
- (() (reverse out))
- (((name name* ...) . in)
- (and-map symbol? (syntax->datum #'(name name* ...)))
- (lp #'in (cons #''((name name* ...)) out)))
- ((((name name* ...) arg ...) . in)
- (and-map symbol? (syntax->datum #'(name name* ...)))
- (with-syntax (((quoted-arg ...) (quotify-iface #'(arg ...))))
- (lp #'in (cons #`(list '(name name* ...) quoted-arg ...)
- out)))))))
-
- (syntax-case x ()
- ((_ spec ...)
- (with-syntax (((quoted-args ...) (quotify #'(spec ...))))
- #'(eval-when (expand load eval)
- (process-use-modules (list quoted-args ...))
- *unspecified*))))))
-
-(define-syntax-rule (use-syntax spec ...)
- (begin
- (eval-when (expand load eval)
- (issue-deprecation-warning
- "`use-syntax' is deprecated. Please contact guile-devel for more info."))
- (use-modules spec ...)))
-
-(include-from-path "ice-9/r6rs-libraries")
-
-(define-syntax-rule (define-private foo bar)
- (define foo bar))
-
-(define-syntax define-public
- (syntax-rules ()
- ((_ (name . args) . body)
- (begin
- (define (name . args) . body)
- (export name)))
- ((_ name val)
- (begin
- (define name val)
- (export name)))))
-
-(define-syntax-rule (defmacro-public name args body ...)
- (begin
- (defmacro name args body ...)
- (export-syntax name)))
-
-;; And now for the most important macro.
-(define-syntax-rule (lumbum formals body ...)
- (lambda formals body ...))
-
-
-;; Export a local variable
-
-;; This function is called from "modules.c". If you change it, be
-;; sure to update "modules.c" as well.
-
-(define (module-export! m names)
- (let ((public-i (module-public-interface m)))
- (for-each (lambda (name)
- (let* ((internal-name (if (pair? name) (car name) name))
- (external-name (if (pair? name) (cdr name) name))
- (var (module-ensure-local-variable! m internal-name)))
- (module-add! public-i external-name var)))
- names)))
-
-(define (module-replace! m names)
- (let ((public-i (module-public-interface m)))
- (for-each (lambda (name)
- (let* ((internal-name (if (pair? name) (car name) name))
- (external-name (if (pair? name) (cdr name) name))
- (var (module-ensure-local-variable! m internal-name)))
- ;; FIXME: use a bit on variables instead of object
- ;; properties.
- (set-object-property! var 'replace #t)
- (module-add! public-i external-name var)))
- names)))
-
-;; Export all local variables from a module
-;;
-(define (module-export-all! mod)
- (define (fresh-interface!)
- (let ((iface (make-module)))
- (set-module-name! iface (module-name mod))
- (set-module-version! iface (module-version mod))
- (set-module-kind! iface 'interface)
- (set-module-public-interface! mod iface)
- iface))
- (let ((iface (or (module-public-interface mod)
- (fresh-interface!))))
- (set-module-obarray! iface (module-obarray mod))))
-
-;; Re-export a imported variable
-;;
-(define (module-re-export! m names)
- (let ((public-i (module-public-interface m)))
- (for-each (lambda (name)
- (let* ((internal-name (if (pair? name) (car name) name))
- (external-name (if (pair? name) (cdr name) name))
- (var (module-variable m internal-name)))
- (cond ((not var)
- (error "Undefined variable:" internal-name))
- ((eq? var (module-local-variable m internal-name))
- (error "re-exporting local variable:" internal-name))
- (else
- (module-add! public-i external-name var)))))
- names)))
-
-(define-syntax-rule (export name ...)
- (eval-when (expand load eval)
- (call-with-deferred-observers
- (lambda ()
- (module-export! (current-module) '(name ...))))))
-
-(define-syntax-rule (re-export name ...)
- (eval-when (expand load eval)
- (call-with-deferred-observers
- (lambda ()
- (module-re-export! (current-module) '(name ...))))))
-
-(define-syntax-rule (export! name ...)
- (eval-when (expand load eval)
- (call-with-deferred-observers
- (lambda ()
- (module-replace! (current-module) '(name ...))))))
-
-(define-syntax-rule (export-syntax name ...)
- (export name ...))
-
-(define-syntax-rule (re-export-syntax name ...)
- (re-export name ...))
-
-
-
-;;; {Parameters}
-;;;
-
-(define* (make-mutable-parameter init #\optional (converter identity))
- (let ((fluid (make-fluid (converter init))))
- (case-lambda
- (() (fluid-ref fluid))
- ((val) (fluid-set! fluid (converter val))))))
-
-
-
-
-;;; {Handling of duplicate imported bindings}
-;;;
-
-;; Duplicate handlers take the following arguments:
-;;
-;; module importing module
-;; name conflicting name
-;; int1 old interface where name occurs
-;; val1 value of binding in old interface
-;; int2 new interface where name occurs
-;; val2 value of binding in new interface
-;; var previous resolution or #f
-;; val value of previous resolution
-;;
-;; A duplicate handler can take three alternative actions:
-;;
-;; 1. return #f => leave responsibility to next handler
-;; 2. exit with an error
-;; 3. return a variable resolving the conflict
-;;
-
-(define duplicate-handlers
- (let ((m (make-module 7)))
-
- (define (check module name int1 val1 int2 val2 var val)
- (scm-error 'misc-error
- #f
- "~A: `~A' imported from both ~A and ~A"
- (list (module-name module)
- name
- (module-name int1)
- (module-name int2))
- #f))
-
- (define (warn module name int1 val1 int2 val2 var val)
- (format (current-warning-port)
- "WARNING: ~A: `~A' imported from both ~A and ~A\n"
- (module-name module)
- name
- (module-name int1)
- (module-name int2))
- #f)
-
- (define (replace module name int1 val1 int2 val2 var val)
- (let ((old (or (and var (object-property var 'replace) var)
- (module-variable int1 name)))
- (new (module-variable int2 name)))
- (if (object-property old 'replace)
- (and (or (eq? old new)
- (not (object-property new 'replace)))
- old)
- (and (object-property new 'replace)
- new))))
-
- (define (warn-override-core module name int1 val1 int2 val2 var val)
- (and (eq? int1 the-scm-module)
- (begin
- (format (current-warning-port)
- "WARNING: ~A: imported module ~A overrides core binding `~A'\n"
- (module-name module)
- (module-name int2)
- name)
- (module-local-variable int2 name))))
-
- (define (first module name int1 val1 int2 val2 var val)
- (or var (module-local-variable int1 name)))
-
- (define (last module name int1 val1 int2 val2 var val)
- (module-local-variable int2 name))
-
- (define (noop module name int1 val1 int2 val2 var val)
- #f)
-
- (set-module-name! m 'duplicate-handlers)
- (set-module-kind! m 'interface)
- (module-define! m 'check check)
- (module-define! m 'warn warn)
- (module-define! m 'replace replace)
- (module-define! m 'warn-override-core warn-override-core)
- (module-define! m 'first first)
- (module-define! m 'last last)
- (module-define! m 'merge-generics noop)
- (module-define! m 'merge-accessors noop)
- m))
-
-(define (lookup-duplicates-handlers handler-names)
- (and handler-names
- (map (lambda (handler-name)
- (or (module-symbol-local-binding
- duplicate-handlers handler-name #f)
- (error "invalid duplicate handler name:"
- handler-name)))
- (if (list? handler-names)
- handler-names
- (list handler-names)))))
-
-(define default-duplicate-binding-procedures
- (make-mutable-parameter #f))
-
-(define default-duplicate-binding-handler
- (make-mutable-parameter '(replace warn-override-core warn last)
- (lambda (handler-names)
- (default-duplicate-binding-procedures
- (lookup-duplicates-handlers handler-names))
- handler-names)))
-
-
-
-;;; {`load'.}
-;;;
-;;; Load is tricky when combined with relative file names, compilation,
-;;; and the file system. If a file name is relative, what is it
-;;; relative to? The name of the source file at the time it was
-;;; compiled? The name of the compiled file? What if both or either
-;;; were installed? And how do you get that information? Tricky, I
-;;; say.
-;;;
-;;; To get around all of this, we're going to do something nasty, and
-;;; turn `load' into a macro. That way it can know the name of the
-;;; source file with respect to which it was invoked, so it can resolve
-;;; relative file names with respect to the original source file.
-;;;
-;;; There is an exception, and that is that if the source file was in
-;;; the load path when it was compiled, instead of looking up against
-;;; the absolute source location, we load-from-path against the relative
-;;; source location.
-;;;
-
-(define %auto-compilation-options
- ;; Default `compile-file' option when auto-compiling.
- '(#\warnings (unbound-variable arity-mismatch format
- duplicate-case-datum bad-case-datum)))
-
-(define* (load-in-vicinity dir file-name #\optional reader)
- "Load source file FILE-NAME in vicinity of directory DIR. Use a
-pre-compiled version of FILE-NAME when available, and auto-compile one
-when none is available, reading FILE-NAME with READER."
-
- ;; The auto-compilation code will residualize a .go file in the cache
- ;; dir: by default, $HOME/.cache/guile/2.0/ccache/PATH.go. This
- ;; function determines the PATH to use as a key into the compilation
- ;; cache.
- (define (canonical->suffix canon)
- (cond
- ((and (not (string-null? canon))
- (file-name-separator? (string-ref canon 0)))
- canon)
- ((and (eq? (system-file-name-convention) 'windows)
- (absolute-file-name? canon))
- ;; An absolute file name that doesn't start with a separator
- ;; starts with a drive component. Transform the drive component
- ;; to a file name element: c:\foo -> \c\foo.
- (string-append file-name-separator-string
- (substring canon 0 1)
- (substring canon 2)))
- (else canon)))
-
- (define compiled-extension
- ;; File name extension of compiled files.
- (cond ((or (null? %load-compiled-extensions)
- (string-null? (car %load-compiled-extensions)))
- (warn "invalid %load-compiled-extensions"
- %load-compiled-extensions)
- ".go")
- (else (car %load-compiled-extensions))))
-
- (define (more-recent? stat1 stat2)
- ;; Return #t when STAT1 has an mtime greater than that of STAT2.
- (or (> (stat:mtime stat1) (stat:mtime stat2))
- (and (= (stat:mtime stat1) (stat:mtime stat2))
- (>= (stat:mtimensec stat1)
- (stat:mtimensec stat2)))))
-
- (define (fallback-file-name canon-file-name)
- ;; Return the in-cache compiled file name for source file
- ;; CANON-FILE-NAME.
-
- ;; FIXME: would probably be better just to append
- ;; SHA1(canon-file-name) to the %compile-fallback-path, to avoid
- ;; deep directory stats.
- (and %compile-fallback-path
- (string-append %compile-fallback-path
- (canonical->suffix canon-file-name)
- compiled-extension)))
-
- (define (compile file)
- ;; Compile source FILE, lazily loading the compiler.
- ((module-ref (resolve-interface '(system base compile))
- 'compile-file)
- file
- #\opts %auto-compilation-options
- #\env (current-module)))
-
- (define (load-thunk-from-file file)
- (let ((objcode (resolve-interface '(system vm objcode)))
- (program (resolve-interface '(system vm program))))
- ((module-ref program 'make-program)
- ((module-ref objcode 'load-objcode) file))))
-
- ;; Returns a thunk loaded from the .go file corresponding to `name'.
- ;; Does not search load paths, only the fallback path. If the .go
- ;; file is missing or out of date, and auto-compilation is enabled,
- ;; will try auto-compilation, just as primitive-load-path does
- ;; internally. primitive-load is unaffected. Returns #f if
- ;; auto-compilation failed or was disabled.
- ;;
- ;; NB: Unless we need to compile the file, this function should not
- ;; cause (system base compile) to be loaded up. For that reason
- ;; compiled-file-name partially duplicates functionality from (system
- ;; base compile).
-
- (define (fresh-compiled-thunk name scmstat go-file-name)
- ;; Return GO-FILE-NAME after making sure that it contains a freshly
- ;; compiled version of source file NAME with stat SCMSTAT; return #f
- ;; on failure.
- (false-if-exception
- (let ((gostat (and (not %fresh-auto-compile)
- (stat go-file-name #f))))
- (if (and gostat (more-recent? gostat scmstat))
- (load-thunk-from-file go-file-name)
- (begin
- (when gostat
- (format (current-warning-port)
- ";;; note: source file ~a\n;;; newer than compiled ~a\n"
- name go-file-name))
- (cond
- (%load-should-auto-compile
- (%warn-auto-compilation-enabled)
- (format (current-warning-port) ";;; compiling ~a\n" name)
- (let ((cfn (compile name)))
- (format (current-warning-port) ";;; compiled ~a\n" cfn)
- (load-thunk-from-file cfn)))
- (else #f)))))
- #\warning "WARNING: compilation of ~a failed:\n" name))
-
- (define (sans-extension file)
- (let ((dot (string-rindex file #\.)))
- (if dot
- (substring file 0 dot)
- file)))
-
- (define (load-absolute abs-file-name)
- ;; Load from ABS-FILE-NAME, using a compiled file or auto-compiling
- ;; if needed.
- (define scmstat
- (false-if-exception
- (stat abs-file-name)
- #\warning "Stat of ~a failed:\n" abs-file-name))
-
- (define (pre-compiled)
- (or-map
- (lambda (dir)
- (or-map
- (lambda (ext)
- (let ((candidate (string-append (in-vicinity dir file-name) ext)))
- (let ((gostat (stat candidate #f)))
- (and gostat
- (more-recent? gostat scmstat)
- (false-if-exception
- (load-thunk-from-file candidate)
- #\warning "WARNING: failed to load compiled file ~a:\n"
- candidate)))))
- %load-compiled-extensions))
- %load-compiled-path))
-
- (define (fallback)
- (and=> (false-if-exception (canonicalize-path abs-file-name))
- (lambda (canon)
- (and=> (fallback-file-name canon)
- (lambda (go-file-name)
- (fresh-compiled-thunk abs-file-name
- scmstat
- go-file-name))))))
-
- (let ((compiled (and scmstat (or (pre-compiled) (fallback)))))
- (if compiled
- (begin
- (if %load-hook
- (%load-hook abs-file-name))
- (compiled))
- (start-stack 'load-stack
- (primitive-load abs-file-name)))))
-
- (save-module-excursion
- (lambda ()
- (with-fluids ((current-reader reader)
- (%file-port-name-canonicalization 'relative))
- (cond
- ((absolute-file-name? file-name)
- (load-absolute file-name))
- ((absolute-file-name? dir)
- (load-absolute (in-vicinity dir file-name)))
- (else
- (load-from-path (in-vicinity dir file-name))))))))
-
-(define-syntax load
- (make-variable-transformer
- (lambda (x)
- (let* ((src (syntax-source x))
- (file (and src (assq-ref src 'filename)))
- (dir (and (string? file) (dirname file))))
- (syntax-case x ()
- ((_ arg ...)
- #`(load-in-vicinity #,(or dir #'(getcwd)) arg ...))
- (id
- (identifier? #'id)
- #`(lambda args
- (apply load-in-vicinity #,(or dir #'(getcwd)) args))))))))
-
-
-
-;;; {`cond-expand' for SRFI-0 support.}
-;;;
-;;; This syntactic form expands into different commands or
-;;; definitions, depending on the features provided by the Scheme
-;;; implementation.
-;;;
-;;; Syntax:
-;;;
-;;; <cond-expand>
-;;; --> (cond-expand <cond-expand-clause>+)
-;;; | (cond-expand <cond-expand-clause>* (else <command-or-definition>))
-;;; <cond-expand-clause>
-;;; --> (<feature-requirement> <command-or-definition>*)
-;;; <feature-requirement>
-;;; --> <feature-identifier>
-;;; | (and <feature-requirement>*)
-;;; | (or <feature-requirement>*)
-;;; | (not <feature-requirement>)
-;;; <feature-identifier>
-;;; --> <a symbol which is the name or alias of a SRFI>
-;;;
-;;; Additionally, this implementation provides the
-;;; <feature-identifier>s `guile' and `r5rs', so that programs can
-;;; determine the implementation type and the supported standard.
-;;;
-;;; Remember to update the features list when adding more SRFIs.
-;;;
-
-(define %cond-expand-features
- ;; This should contain only features that are present in core Guile,
- ;; before loading any modules. Modular features are handled by
- ;; placing 'cond-expand-provide' in the relevant module.
- '(guile
- guile-2
- r5rs
- srfi-0 ;; cond-expand itself
- srfi-4 ;; homogeneous numeric vectors
- ;; We omit srfi-6 because the 'open-input-string' etc in Guile
- ;; core are not conformant with SRFI-6; they expose details
- ;; of the binary I/O model and may fail to support some characters.
- srfi-13 ;; string library
- srfi-14 ;; character sets
- srfi-16 ;; case-lambda
- srfi-23 ;; `error` procedure
- srfi-30 ;; nested multi-line comments
- srfi-39 ;; parameterize
- srfi-46 ;; basic syntax-rules extensions
- srfi-55 ;; require-extension
- srfi-61 ;; general cond clause
- srfi-62 ;; s-expression comments
- srfi-87 ;; => in case clauses
- srfi-105 ;; curly infix expressions
- ))
-
-;; This table maps module public interfaces to the list of features.
-;;
-(define %cond-expand-table (make-hash-table 31))
-
-;; Add one or more features to the `cond-expand' feature list of the
-;; module `module'.
-;;
-(define (cond-expand-provide module features)
- (let ((mod (module-public-interface module)))
- (and mod
- (hashq-set! %cond-expand-table mod
- (append (hashq-ref %cond-expand-table mod '())
- features)))))
-
-(define-syntax cond-expand
- (lambda (x)
- (define (module-has-feature? mod sym)
- (or-map (lambda (mod)
- (memq sym (hashq-ref %cond-expand-table mod '())))
- (module-uses mod)))
-
- (define (condition-matches? condition)
- (syntax-case condition (and or not)
- ((and c ...)
- (and-map condition-matches? #'(c ...)))
- ((or c ...)
- (or-map condition-matches? #'(c ...)))
- ((not c)
- (if (condition-matches? #'c) #f #t))
- (c
- (identifier? #'c)
- (let ((sym (syntax->datum #'c)))
- (if (memq sym %cond-expand-features)
- #t
- (module-has-feature? (current-module) sym))))))
-
- (define (match clauses alternate)
- (syntax-case clauses ()
- (((condition form ...) . rest)
- (if (condition-matches? #'condition)
- #'(begin form ...)
- (match #'rest alternate)))
- (() (alternate))))
-
- (syntax-case x (else)
- ((_ clause ... (else form ...))
- (match #'(clause ...)
- (lambda ()
- #'(begin form ...))))
- ((_ clause ...)
- (match #'(clause ...)
- (lambda ()
- (syntax-violation 'cond-expand "unfulfilled cond-expand" x)))))))
-
-;; This procedure gets called from the startup code with a list of
-;; numbers, which are the numbers of the SRFIs to be loaded on startup.
-;;
-(define (use-srfis srfis)
- (process-use-modules
- (map (lambda (num)
- (list (list 'srfi (string->symbol
- (string-append "srfi-" (number->string num))))))
- srfis)))
-
-
-
-;;; srfi-55: require-extension
-;;;
-
-(define-syntax require-extension
- (lambda (x)
- (syntax-case x (srfi)
- ((_ (srfi n ...))
- (and-map integer? (syntax->datum #'(n ...)))
- (with-syntax
- (((srfi-n ...)
- (map (lambda (n)
- (datum->syntax x (symbol-append 'srfi- n)))
- (map string->symbol
- (map number->string (syntax->datum #'(n ...)))))))
- #'(use-modules (srfi srfi-n) ...)))
- ((_ (type arg ...))
- (identifier? #'type)
- (syntax-violation 'require-extension "Not a recognized extension type"
- x)))))
-
-
-;;; Defining transparently inlinable procedures
-;;;
-
-(define-syntax define-inlinable
- ;; Define a macro and a procedure such that direct calls are inlined, via
- ;; the macro expansion, whereas references in non-call contexts refer to
- ;; the procedure. Inspired by the `define-integrable' macro by Dybvig et al.
- (lambda (x)
- ;; Use a space in the prefix to avoid potential -Wunused-toplevel
- ;; warning
- (define prefix (string->symbol "% "))
- (define (make-procedure-name name)
- (datum->syntax name
- (symbol-append prefix (syntax->datum name)
- '-procedure)))
-
- (syntax-case x ()
- ((_ (name formals ...) body ...)
- (identifier? #'name)
- (with-syntax ((proc-name (make-procedure-name #'name))
- ((args ...) (generate-temporaries #'(formals ...))))
- #`(begin
- (define (proc-name formals ...)
- (syntax-parameterize ((name (identifier-syntax proc-name)))
- body ...))
- (define-syntax-parameter name
- (lambda (x)
- (syntax-case x ()
- ((_ args ...)
- #'((syntax-parameterize ((name (identifier-syntax proc-name)))
- (lambda (formals ...)
- body ...))
- args ...))
- ((_ a (... ...))
- (syntax-violation 'name "Wrong number of arguments" x))
- (_
- (identifier? x)
- #'proc-name))))))))))
-
-
-
-(define using-readline?
- (let ((using-readline? (make-fluid)))
- (make-procedure-with-setter
- (lambda () (fluid-ref using-readline?))
- (lambda (v) (fluid-set! using-readline? v)))))
-
-
-
-;;; {Deprecated stuff}
-;;;
-
-(begin-deprecated
- (module-use! the-scm-module (resolve-interface '(ice-9 deprecated))))
-
-
-
-;;; SRFI-4 in the default environment. FIXME: we should figure out how
-;;; to deprecate this.
-;;;
-
-;; FIXME:
-(module-use! the-scm-module (resolve-interface '(srfi srfi-4)))
-
-
-
-;;; A few identifiers that need to be defined in this file are really
-;;; internal implementation details. We shove them off into internal
-;;; modules, removing them from the (guile) module.
-;;;
-
-(define-module (system syntax))
-
-(let ()
- (define (steal-bindings! from to ids)
- (for-each
- (lambda (sym)
- (let ((v (module-local-variable from sym)))
- (module-remove! from sym)
- (module-add! to sym v)))
- ids)
- (module-export! to ids))
-
- (steal-bindings! the-root-module (resolve-module '(system syntax))
- '(syntax-local-binding
- syntax-module
- syntax-locally-bound-identifiers
- syntax-session-id)))
-
-
-
-
-;;; Place the user in the guile-user module.
-;;;
-
-;; Set filename to #f to prevent reload.
-(define-module (guile-user)
- #\autoload (system base compile) (compile compile-file)
- #\filename #f)
-
-;; Remain in the `(guile)' module at compilation-time so that the
-;; `-Wunused-toplevel' warning works as expected.
-(eval-when (compile) (set-current-module the-root-module))
-
-;;; boot-9.scm ends here
-;;;; buffered-input.scm --- construct a port from a buffered input reader
-;;;;
-;;;; Copyright (C) 2001, 2006, 2010 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (ice-9 buffered-input)
- #\export (make-buffered-input-port
- make-line-buffered-input-port
- set-buffered-input-continuation?!))
-
-;; @code{buffered-input-continuation?} is a property of the ports
-;; created by @code{make-line-buffered-input-port} that stores the
-;; read continuation flag for each such port.
-(define buffered-input-continuation? (make-object-property))
-
-(define (set-buffered-input-continuation?! port val)
- "Set the read continuation flag for @var{port} to @var{val}.
-
-See @code{make-buffered-input-port} for the meaning and use of this
-flag."
- (set! (buffered-input-continuation? port) val))
-
-(define (make-buffered-input-port reader)
- "Construct a line-buffered input port from the specified @var{reader}.
-@var{reader} should be a procedure of one argument that somehow reads
-a chunk of input and returns it as a string.
-
-The port created by @code{make-buffered-input-port} does @emph{not}
-interpolate any additional characters between the strings returned by
-@var{reader}.
-
-@var{reader} should take a boolean @var{continuation?} argument.
-@var{continuation?} indicates whether @var{reader} is being called to
-start a logically new read operation (in which case
-@var{continuation?} is @code{#f}) or to continue a read operation for
-which some input has already been read (in which case
-@var{continuation?} is @code{#t}). Some @var{reader} implementations
-use the @var{continuation?} argument to determine what prompt to
-display to the user.
-
-The new/continuation distinction is largely an application-level
-concept: @code{set-buffered-input-continuation?!} allows an
-application to specify when a read operation is considered to be new.
-But note that if there is non-whitespace data already buffered in the
-port when a new read operation starts, this data will be read before
-the first call to @var{reader}, and so @var{reader} will be called
-with @var{continuation?} set to @code{#t}."
- (let ((read-string "")
- (string-index 0))
- (letrec ((get-character
- (lambda ()
- (if (< string-index (string-length read-string))
- ;; Read a char.
- (let ((res (string-ref read-string string-index)))
- (set! string-index (+ 1 string-index))
- (if (not (char-whitespace? res))
- (set! (buffered-input-continuation? port) #t))
- res)
- ;; Fill the buffer.
- (let ((x (reader (buffered-input-continuation? port))))
- (cond
- ((eof-object? x)
- ;; Don't buffer the EOF object.
- x)
- (else
- (set! read-string x)
- (set! string-index 0)
- (get-character)))))))
- (input-waiting
- (lambda ()
- (- (string-length read-string) string-index)))
- (port #f))
- (set! port (make-soft-port (vector #f #f #f get-character #f input-waiting) "r"))
- (set! (buffered-input-continuation? port) #f)
- port)))
-
-(define (make-line-buffered-input-port reader)
- "Construct a line-buffered input port from the specified @var{reader}.
-@var{reader} should be a procedure of one argument that somehow reads
-a line of input and returns it as a string @emph{without} the
-terminating newline character.
-
-The port created by @code{make-line-buffered-input-port} automatically
-interpolates a newline character after each string returned by
-@var{reader}.
-
-@var{reader} should take a boolean @var{continuation?} argument. For
-the meaning and use of this argument, see
-@code{make-buffered-input-port}."
- (make-buffered-input-port (lambda (continuation?)
- (let ((str (reader continuation?)))
- (if (eof-object? str)
- str
- (string-append str "\n"))))))
-
-;;; buffered-input.scm ends here
-;;;; calling.scm --- Calling Conventions
-;;;;
-;;;; Copyright (C) 1995, 1996, 1997, 2000, 2001, 2006 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-(define-module (ice-9 calling)
- \:export-syntax (with-excursion-function
- with-getter-and-setter
- with-getter
- with-delegating-getter-and-setter
- with-excursion-getter-and-setter
- with-configuration-getter-and-setter
- with-delegating-configuration-getter-and-setter
- let-with-configuration-getter-and-setter))
-
-;;;;
-;;;
-;;; This file contains a number of macros that support
-;;; common calling conventions.
-
-;;;
-;;; with-excursion-function <vars> proc
-;;; <vars> is an unevaluated list of names that are bound in the caller.
-;;; proc is a procedure, called:
-;;; (proc excursion)
-;;;
-;;; excursion is a procedure isolates all changes to <vars>
-;;; in the dynamic scope of the call to proc. In other words,
-;;; the values of <vars> are saved when proc is entered, and when
-;;; proc returns, those values are restored. Values are also restored
-;;; entering and leaving the call to proc non-locally, such as using
-;;; call-with-current-continuation, error, or throw.
-;;;
-(defmacro with-excursion-function (vars proc)
- `(,proc ,(excursion-function-syntax vars)))
-
-
-
-;;; with-getter-and-setter <vars> proc
-;;; <vars> is an unevaluated list of names that are bound in the caller.
-;;; proc is a procedure, called:
-;;; (proc getter setter)
-;;;
-;;; getter and setter are procedures used to access
-;;; or modify <vars>.
-;;;
-;;; setter, called with keywords arguments, modifies the named
-;;; values. If "foo" and "bar" are among <vars>, then:
-;;;
-;;; (setter :foo 1 :bar 2)
-;;; == (set! foo 1 bar 2)
-;;;
-;;; getter, called with just keywords, returns
-;;; a list of the corresponding values. For example,
-;;; if "foo" and "bar" are among the <vars>, then
-;;;
-;;; (getter :foo :bar)
-;;; => (<value-of-foo> <value-of-bar>)
-;;;
-;;; getter, called with no arguments, returns a list of all accepted
-;;; keywords and the corresponding values. If "foo" and "bar" are
-;;; the *only* <vars>, then:
-;;;
-;;; (getter)
-;;; => (\:foo <value-of-bar> :bar <value-of-foo>)
-;;;
-;;; The unusual calling sequence of a getter supports too handy
-;;; idioms:
-;;;
-;;; (apply setter (getter)) ;; save and restore
-;;;
-;;; (apply-to-args (getter :foo :bar) ;; fetch and bind
-;;; (lambda (foo bar) ....))
-;;;
-;;; ;; [ "apply-to-args" is just like two-argument "apply" except that it
-;;; ;; takes its arguments in a different order.
-;;;
-;;;
-(defmacro with-getter-and-setter (vars proc)
- `(,proc ,@ (getter-and-setter-syntax vars)))
-
-;;; with-getter vars proc
-;;; A short-hand for a call to with-getter-and-setter.
-;;; The procedure is called:
-;;; (proc getter)
-;;;
-(defmacro with-getter (vars proc)
- `(,proc ,(car (getter-and-setter-syntax vars))))
-
-
-;;; with-delegating-getter-and-setter <vars> get-delegate set-delegate proc
-;;; Compose getters and setters.
-;;;
-;;; <vars> is an unevaluated list of names that are bound in the caller.
-;;;
-;;; get-delegate is called by the new getter to extend the set of
-;;; gettable variables beyond just <vars>
-;;; set-delegate is called by the new setter to extend the set of
-;;; gettable variables beyond just <vars>
-;;;
-;;; proc is a procedure that is called
-;;; (proc getter setter)
-;;;
-(defmacro with-delegating-getter-and-setter (vars get-delegate set-delegate proc)
- `(,proc ,@ (delegating-getter-and-setter-syntax vars get-delegate set-delegate)))
-
-
-;;; with-excursion-getter-and-setter <vars> proc
-;;; <vars> is an unevaluated list of names that are bound in the caller.
-;;; proc is called:
-;;;
-;;; (proc excursion getter setter)
-;;;
-;;; See also:
-;;; with-getter-and-setter
-;;; with-excursion-function
-;;;
-(defmacro with-excursion-getter-and-setter (vars proc)
- `(,proc ,(excursion-function-syntax vars)
- ,@ (getter-and-setter-syntax vars)))
-
-
-(define (excursion-function-syntax vars)
- (let ((saved-value-names (map gensym vars))
- (tmp-var-name (gensym "temp"))
- (swap-fn-name (gensym "swap"))
- (thunk-name (gensym "thunk")))
- `(lambda (,thunk-name)
- (letrec ((,tmp-var-name #f)
- (,swap-fn-name
- (lambda () ,@ (map (lambda (n sn)
- `(begin (set! ,tmp-var-name ,n)
- (set! ,n ,sn)
- (set! ,sn ,tmp-var-name)))
- vars saved-value-names)))
- ,@ (map (lambda (sn n) `(,sn ,n)) saved-value-names vars))
- (dynamic-wind
- ,swap-fn-name
- ,thunk-name
- ,swap-fn-name)))))
-
-
-(define (getter-and-setter-syntax vars)
- (let ((args-name (gensym "args"))
- (an-arg-name (gensym "an-arg"))
- (new-val-name (gensym "new-value"))
- (loop-name (gensym "loop"))
- (kws (map symbol->keyword vars)))
- (list `(lambda ,args-name
- (let ,loop-name ((,args-name ,args-name))
- (if (null? ,args-name)
- ,(if (null? kws)
- ''()
- `(let ((all-vals (,loop-name ',kws)))
- (let ,loop-name ((vals all-vals)
- (kws ',kws))
- (if (null? vals)
- '()
- `(,(car kws) ,(car vals) ,@(,loop-name (cdr vals) (cdr kws)))))))
- (map (lambda (,an-arg-name)
- (case ,an-arg-name
- ,@ (append
- (map (lambda (kw v) `((,kw) ,v)) kws vars)
- `((else (throw 'bad-get-option ,an-arg-name))))))
- ,args-name))))
-
- `(lambda ,args-name
- (let ,loop-name ((,args-name ,args-name))
- (or (null? ,args-name)
- (null? (cdr ,args-name))
- (let ((,an-arg-name (car ,args-name))
- (,new-val-name (cadr ,args-name)))
- (case ,an-arg-name
- ,@ (append
- (map (lambda (kw v) `((,kw) (set! ,v ,new-val-name))) kws vars)
- `((else (throw 'bad-set-option ,an-arg-name)))))
- (,loop-name (cddr ,args-name)))))))))
-
-(define (delegating-getter-and-setter-syntax vars get-delegate set-delegate)
- (let ((args-name (gensym "args"))
- (an-arg-name (gensym "an-arg"))
- (new-val-name (gensym "new-value"))
- (loop-name (gensym "loop"))
- (kws (map symbol->keyword vars)))
- (list `(lambda ,args-name
- (let ,loop-name ((,args-name ,args-name))
- (if (null? ,args-name)
- (append!
- ,(if (null? kws)
- ''()
- `(let ((all-vals (,loop-name ',kws)))
- (let ,loop-name ((vals all-vals)
- (kws ',kws))
- (if (null? vals)
- '()
- `(,(car kws) ,(car vals) ,@(,loop-name (cdr vals) (cdr kws)))))))
- (,get-delegate))
- (map (lambda (,an-arg-name)
- (case ,an-arg-name
- ,@ (append
- (map (lambda (kw v) `((,kw) ,v)) kws vars)
- `((else (car (,get-delegate ,an-arg-name)))))))
- ,args-name))))
-
- `(lambda ,args-name
- (let ,loop-name ((,args-name ,args-name))
- (or (null? ,args-name)
- (null? (cdr ,args-name))
- (let ((,an-arg-name (car ,args-name))
- (,new-val-name (cadr ,args-name)))
- (case ,an-arg-name
- ,@ (append
- (map (lambda (kw v) `((,kw) (set! ,v ,new-val-name))) kws vars)
- `((else (,set-delegate ,an-arg-name ,new-val-name)))))
- (,loop-name (cddr ,args-name)))))))))
-
-
-
-
-;;; with-configuration-getter-and-setter <vars-etc> proc
-;;;
-;;; Create a getter and setter that can trigger arbitrary computation.
-;;;
-;;; <vars-etc> is a list of variable specifiers, explained below.
-;;; proc is called:
-;;;
-;;; (proc getter setter)
-;;;
-;;; Each element of the <vars-etc> list is of the form:
-;;;
-;;; (<var> getter-hook setter-hook)
-;;;
-;;; Both hook elements are evaluated; the variable name is not.
-;;; Either hook may be #f or procedure.
-;;;
-;;; A getter hook is a thunk that returns a value for the corresponding
-;;; variable. If omitted (#f is passed), the binding of <var> is
-;;; returned.
-;;;
-;;; A setter hook is a procedure of one argument that accepts a new value
-;;; for the corresponding variable. If omitted, the binding of <var>
-;;; is simply set using set!.
-;;;
-(defmacro with-configuration-getter-and-setter (vars-etc proc)
- `((lambda (simpler-get simpler-set body-proc)
- (with-delegating-getter-and-setter ()
- simpler-get simpler-set body-proc))
-
- (lambda (kw)
- (case kw
- ,@(map (lambda (v) `((,(symbol->keyword (car v)))
- ,(cond
- ((cadr v) => list)
- (else `(list ,(car v))))))
- vars-etc)))
-
- (lambda (kw new-val)
- (case kw
- ,@(map (lambda (v) `((,(symbol->keyword (car v)))
- ,(cond
- ((caddr v) => (lambda (proc) `(,proc new-val)))
- (else `(set! ,(car v) new-val)))))
- vars-etc)))
-
- ,proc))
-
-(defmacro with-delegating-configuration-getter-and-setter (vars-etc delegate-get delegate-set proc)
- `((lambda (simpler-get simpler-set body-proc)
- (with-delegating-getter-and-setter ()
- simpler-get simpler-set body-proc))
-
- (lambda (kw)
- (case kw
- ,@(append! (map (lambda (v) `((,(symbol->keyword (car v)))
- ,(cond
- ((cadr v) => list)
- (else `(list ,(car v))))))
- vars-etc)
- `((else (,delegate-get kw))))))
-
- (lambda (kw new-val)
- (case kw
- ,@(append! (map (lambda (v) `((,(symbol->keyword (car v)))
- ,(cond
- ((caddr v) => (lambda (proc) `(,proc new-val)))
- (else `(set! ,(car v) new-val)))))
- vars-etc)
- `((else (,delegate-set kw new-val))))))
-
- ,proc))
-
-
-;;; let-configuration-getter-and-setter <vars-etc> proc
-;;;
-;;; This procedure is like with-configuration-getter-and-setter (q.v.)
-;;; except that each element of <vars-etc> is:
-;;;
-;;; (<var> initial-value getter-hook setter-hook)
-;;;
-;;; Unlike with-configuration-getter-and-setter, let-configuration-getter-and-setter
-;;; introduces bindings for the variables named in <vars-etc>.
-;;; It is short-hand for:
-;;;
-;;; (let ((<var1> initial-value-1)
-;;; (<var2> initial-value-2)
-;;; ...)
-;;; (with-configuration-getter-and-setter ((<var1> v1-get v1-set) ...) proc))
-;;;
-(defmacro let-with-configuration-getter-and-setter (vars-etc proc)
- `(let ,(map (lambda (v) `(,(car v) ,(cadr v))) vars-etc)
- (with-configuration-getter-and-setter ,(map (lambda (v) `(,(car v) ,(caddr v) ,(cadddr v))) vars-etc)
- ,proc)))
-;;; Guile object channel
-
-;; Copyright (C) 2001, 2006, 2009, 2010 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Commentary:
-
-;; Now you can use Guile's modules in Emacs Lisp like this:
-;;
-;; (guile-import current-module)
-;; (guile-import module-ref)
-;;
-;; (setq assq (module-ref (current-module) 'assq))
-;; => ("<guile>" %%1%% . "#<primitive-procedure assq>")
-;;
-;; (guile-use-modules (ice-9 documentation))
-;;
-;; (object-documentation assq)
-;; =>
-;; " - primitive: assq key alist
-;; - primitive: assv key alist
-;; - primitive: assoc key alist
-;; Fetches the entry in ALIST that is associated with KEY. To decide
-;; whether the argument KEY matches a particular entry in ALIST,
-;; `assq' compares keys with `eq?', `assv' uses `eqv?' and `assoc'
-;; uses `equal?'. If KEY cannot be found in ALIST (according to
-;; whichever equality predicate is in use), then `#f' is returned.
-;; These functions return the entire alist entry found (i.e. both the
-;; key and the value)."
-;;
-;; Probably we can use GTK in Emacs Lisp. Can anybody try it?
-;;
-;; I have also implemented Guile Scheme mode and Scheme Interaction mode.
-;; Just put the following lines in your ~/.emacs:
-;;
-;; (require 'guile-scheme)
-;; (setq initial-major-mode 'scheme-interaction-mode)
-;;
-;; Currently, the following commands are available:
-;;
-;; M-TAB guile-scheme-complete-symbol
-;; M-C-x guile-scheme-eval-define
-;; C-x C-e guile-scheme-eval-last-sexp
-;; C-c C-b guile-scheme-eval-buffer
-;; C-c C-r guile-scheme-eval-region
-;; C-c : guile-scheme-eval-expression
-;;
-;; I'll write more commands soon, or if you want to hack, please take
-;; a look at the following files:
-;;
-;; guile-core/ice-9/channel.scm ;; object channel
-;; guile-core/emacs/guile.el ;; object adapter
-;; guile-core/emacs/guile-emacs.scm ;; Guile <-> Emacs channels
-;; guile-core/emacs/guile-scheme.el ;; Guile Scheme mode
-;;
-;; As always, there are more than one bugs ;)
-
-;;; Code:
-
-(define-module (ice-9 channel)
- \:export (make-object-channel
- channel-open
- channel-print-value
- channel-print-token))
-
-;;;
-;;; Channel type
-;;;
-
-(define channel-type
- (make-record-type 'channel '(stdin stdout printer token-module)))
-
-(define make-channel (record-constructor channel-type))
-
-(define (make-object-channel printer)
- (make-channel (current-input-port)
- (current-output-port)
- printer
- (make-module)))
-
-(define channel-stdin (record-accessor channel-type 'stdin))
-(define channel-stdout (record-accessor channel-type 'stdout))
-(define channel-printer (record-accessor channel-type 'printer))
-(define channel-token-module (record-accessor channel-type 'token-module))
-
-;;;
-;;; Channel
-;;;
-
-(define (channel-open ch)
- (let ((stdin (channel-stdin ch))
- (stdout (channel-stdout ch))
- (printer (channel-printer ch))
- (token-module (channel-token-module ch)))
- (let loop ()
- (catch #t
- (lambda ()
- (channel:prompt stdout)
- (let ((cmd (read stdin)))
- (if (eof-object? cmd)
- (throw 'quit)
- (case cmd
- ((eval)
- (module-use! (current-module) token-module)
- (printer ch (eval (read stdin) (current-module))))
- ((destroy)
- (let ((token (read stdin)))
- (if (module-defined? token-module token)
- (module-remove! token-module token)
- (channel:error stdout "Invalid token: ~S" token))))
- ((quit)
- (throw 'quit))
- (else
- (channel:error stdout "Unknown command: ~S" cmd)))))
- (loop))
- (lambda (key . args)
- (case key
- ((quit) (throw 'quit))
- (else
- (format stdout "exception = ~S\n"
- (list key (apply format #f (cadr args) (caddr args))))
- (loop))))))))
-
-(define (channel-print-value ch val)
- (format (channel-stdout ch) "value = ~S\n" val))
-
-(define (channel-print-token ch val)
- (let* ((token (symbol-append (gensym "%%") '%%))
- (pair (cons token (object->string val))))
- (format (channel-stdout ch) "token = ~S\n" pair)
- (module-define! (channel-token-module ch) token val)))
-
-(define (channel:prompt port)
- (display "channel> " port)
- (force-output port))
-
-(define (channel:error port msg . args)
- (display "ERROR: " port)
- (apply format port msg args)
- (newline port))
-
-;;;
-;;; Guile 1.4 compatibility
-;;;
-
-(define guile:eval eval)
-(define eval
- (if (= (car (procedure-minimum-arity guile:eval)) 1)
- (lambda (x e) (guile:eval x e))
- guile:eval))
-
-(define object->string
- (if (defined? 'object->string)
- object->string
- (lambda (x) (format #f "~S" x))))
-
-;;; channel.scm ends here
-;;; Parsing Guile's command-line
-
-;;; Copyright (C) 1994-1998, 2000-2016 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-;;;
-;;; Please be careful not to load up other modules in this file, unless
-;;; they are explicitly requested. Loading modules currently imposes a
-;;; speed penalty of a few stats, an mmap, and some allocation, which
-;;; can range from 1 to 20ms, depending on the state of your disk cache.
-;;; Since `compile-shell-switches' is called even for the most transient
-;;; of command-line programs, we need to keep it lean.
-;;;
-;;; Generally speaking, the goal is for Guile to boot and execute simple
-;;; expressions like "1" within 20ms or less, measured using system time
-;;; from the time of the `guile' invocation to exit.
-;;;
-
-(define-module (ice-9 command-line)
- #\autoload (system vm vm) (set-default-vm-engine! set-vm-engine! the-vm)
- #\export (compile-shell-switches
- version-etc
- *GPLv3+*
- *LGPLv3+*
- emit-bug-reporting-address))
-
-;; An initial stab at i18n.
-(define _ gettext)
-
-(define *GPLv3+*
- (_ "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
-This is free software: you are free to change and redistribute it.
-There is NO WARRANTY, to the extent permitted by law."))
-
-(define *LGPLv3+*
- (_ "License LGPLv3+: GNU LGPL 3 or later <http://gnu.org/licenses/lgpl.html>.
-This is free software: you are free to change and redistribute it.
-There is NO WARRANTY, to the extent permitted by law."))
-
-;; Display the --version information in the
-;; standard way: command and package names, package version, followed
-;; by a short license notice and a list of up to 10 author names.
-;; If COMMAND_NAME is NULL, the PACKAGE is asumed to be the name of
-;; the program. The formats are therefore:
-;; PACKAGE VERSION
-;; or
-;; COMMAND_NAME (PACKAGE) VERSION.
-;;
-;; Based on the version-etc gnulib module.
-;;
-(define* (version-etc package version #\key
- (port (current-output-port))
- ;; FIXME: authors
- (copyright-year 2016)
- (copyright-holder "Free Software Foundation, Inc.")
- (copyright (format #f "Copyright (C) ~a ~a"
- copyright-year copyright-holder))
- (license *GPLv3+*)
- command-name
- packager packager-version)
- (if command-name
- (format port "~a (~a) ~a\n" command-name package version)
- (format port "~a ~a\n" package version))
-
- (if packager
- (if packager-version
- (format port (_ "Packaged by ~a (~a)\n") packager packager-version)
- (format port (_ "Packaged by ~a\n") packager)))
-
- (display copyright port)
- (newline port)
- (newline port)
- (display license port)
- (newline port))
-
-
-;; Display the usual `Report bugs to' stanza.
-;;
-(define* (emit-bug-reporting-address package bug-address #\key
- (port (current-output-port))
- (url (string-append
- "http://www.gnu.org/software/"
- package
- "/"))
- packager packager-bug-address)
- (format port (_ "\nReport bugs to: ~a\n") bug-address)
- (if (and packager packager-bug-address)
- (format port (_ "Report ~a bugs to: ~a\n") packager packager-bug-address))
- (format port (_ "~a home page: <~a>\n") package url)
- (format port
- (_ "General help using GNU software: <http://www.gnu.org/gethelp/>\n")))
-
-(define *usage*
- (_ "Evaluate code with Guile, interactively or from a script.
-
- [-s] FILE load source code from FILE, and exit
- -c EXPR evalute expression EXPR, and exit
- -- stop scanning arguments; run interactively
-
-The above switches stop argument processing, and pass all
-remaining arguments as the value of (command-line).
-If FILE begins with `-' the -s switch is mandatory.
-
- -L DIRECTORY add DIRECTORY to the front of the module load path
- -C DIRECTORY like -L, but for compiled files
- -x EXTENSION add EXTENSION to the front of the load extensions
- -l FILE load source code from FILE
- -e FUNCTION after reading script, apply FUNCTION to
- command line arguments
- --language=LANG change language; default: scheme
- -ds do -s script at this point
- --debug start with the \"debugging\" VM engine
- --no-debug start with the normal VM engine (backtraces but
- no breakpoints); default is --debug for interactive
- use, but not for `-s' and `-c'.
- --auto-compile compile source files automatically
- --fresh-auto-compile invalidate auto-compilation cache
- --no-auto-compile disable automatic source file compilation;
- default is to enable auto-compilation of source
- files.
- --listen[=P] listen on a local port or a path for REPL clients;
- if P is not given, the default is local port 37146
- -q inhibit loading of user init file
- --use-srfi=LS load SRFI modules for the SRFIs in LS,
- which is a list of numbers like \"2,13,14\"
- -h, --help display this help and exit
- -v, --version display version information and exit
- \\ read arguments from following script lines"))
-
-
-(define* (shell-usage name fatal? #\optional fmt . args)
- (let ((port (if fatal?
- (current-error-port)
- (current-output-port))))
- (when fmt
- (apply format port fmt args)
- (newline port))
-
- (format port (_ "Usage: ~a [OPTION]... [FILE]...\n") name)
- (display *usage* port)
- (newline port)
-
- (emit-bug-reporting-address
- "GNU Guile" "bug-guile@gnu.org"
- #\port port
- #\url "http://www.gnu.org/software/guile/"
- #\packager (assq-ref %guile-build-info 'packager)
- #\packager-bug-address
- (assq-ref %guile-build-info 'packager-bug-address))
-
- (if fatal?
- (exit 1))))
-
-;; Try to avoid loading (ice-9 eval-string) and (system base compile) if
-;; possible.
-(define (eval-string/lang str)
- (case (current-language)
- ((scheme)
- (call-with-input-string
- str
- (lambda (port)
- (let lp ()
- (let ((exp (read port)))
- (if (not (eof-object? exp))
- (begin
- (eval exp (current-module))
- (lp))))))))
- (else
- ((module-ref (resolve-module '(ice-9 eval-string)) 'eval-string) str))))
-
-(define (load/lang f)
- (case (current-language)
- ((scheme)
- (load-in-vicinity (getcwd) f))
- (else
- ((module-ref (resolve-module '(system base compile)) 'compile-file)
- f #\to 'value))))
-
-(define* (compile-shell-switches args #\optional (usage-name "guile"))
- (let ((arg0 "guile")
- (script-cell #f)
- (entry-point #f)
- (user-load-path '())
- (user-load-compiled-path '())
- (user-extensions '())
- (interactive? #t)
- (inhibit-user-init? #f)
- (turn-on-debugging? #f)
- (turn-off-debugging? #f))
-
- (define (error fmt . args)
- (apply shell-usage usage-name #t
- (string-append "error: " fmt "~%") args))
-
- (define (parse args out)
- (cond
- ((null? args)
- (finish args out))
- (else
- (let ((arg (car args))
- (args (cdr args)))
- (cond
- ((not (string-prefix? "-" arg)) ; foo
- ;; If we specified the -ds option, script-cell is the cdr of
- ;; an expression like (load #f). We replace the car (i.e.,
- ;; the #f) with the script name.
- (set! arg0 arg)
- (set! interactive? #f)
- (if script-cell
- (begin
- (set-car! script-cell arg0)
- (finish args out))
- (finish args
- (cons `((@@ (ice-9 command-line) load/lang) ,arg0)
- out))))
-
- ((string=? arg "-s") ; foo
- (if (null? args)
- (error "missing argument to `-s' switch"))
- (set! arg0 (car args))
- (set! interactive? #f)
- (if script-cell
- (begin
- (set-car! script-cell arg0)
- (finish (cdr args) out))
- (finish (cdr args)
- (cons `((@@ (ice-9 command-line) load/lang) ,arg0)
- out))))
-
- ((string=? arg "-c") ; evaluate expr
- (if (null? args)
- (error "missing argument to `-c' switch"))
- (set! interactive? #f)
- (finish (cdr args)
- (cons `((@@ (ice-9 command-line) eval-string/lang)
- ,(car args))
- out)))
-
- ((string=? arg "--") ; end args go interactive
- (finish args out))
-
- ((string=? arg "-l") ; load a file
- (if (null? args)
- (error "missing argument to `-l' switch"))
- (parse (cdr args)
- (cons `((@@ (ice-9 command-line) load/lang) ,(car args))
- out)))
-
- ((string=? arg "-L") ; add to %load-path
- (if (null? args)
- (error "missing argument to `-L' switch"))
- (set! user-load-path (cons (car args) user-load-path))
- (parse (cdr args)
- out))
-
- ((string=? arg "-C") ; add to %load-compiled-path
- (if (null? args)
- (error "missing argument to `-C' switch"))
- (set! user-load-compiled-path
- (cons (car args) user-load-compiled-path))
- (parse (cdr args)
- out))
-
- ((string=? arg "-x") ; add to %load-extensions
- (if (null? args)
- (error "missing argument to `-x' switch"))
- (set! user-extensions (cons (car args) user-extensions))
- (parse (cdr args)
- out))
-
- ((string=? arg "-e") ; entry point
- (if (null? args)
- (error "missing argument to `-e' switch"))
- (let* ((port (open-input-string (car args)))
- (arg1 (read port))
- (arg2 (read port)))
- ;; Recognize syntax of certain versions of guile 1.4 and
- ;; transform to (@ MODULE-NAME FUNC).
- (set! entry-point
- (cond
- ((not (eof-object? arg2))
- `(@ ,arg1 ,arg2))
- ((and (pair? arg1)
- (not (memq (car arg1) '(@ @@)))
- (and-map symbol? arg1))
- `(@ ,arg1 main))
- (else
- arg1))))
- (parse (cdr args)
- out))
-
- ((string-prefix? "--language=" arg) ; language
- (parse args
- (cons `(current-language
- ',(string->symbol
- (substring arg (string-length "--language="))))
- out)))
-
- ((string=? "--language" arg) ; language
- (when (null? args)
- (error "missing argument to `--language' option"))
- (parse (cdr args)
- (cons `(current-language ',(string->symbol (car args)))
- out)))
-
- ((string=? arg "-ds") ; do script here
- ;; We put a dummy "load" expression, and let the -s put the
- ;; filename in.
- (when script-cell
- (error "the -ds switch may only be specified once"))
- (set! script-cell (list #f))
- (parse args
- (acons '(@@ (ice-9 command-line) load/lang)
- script-cell
- out)))
-
- ((string=? arg "--debug")
- (set! turn-on-debugging? #t)
- (set! turn-off-debugging? #f)
- (parse args out))
-
- ((string=? arg "--no-debug")
- (set! turn-off-debugging? #t)
- (set! turn-on-debugging? #f)
- (parse args out))
-
- ;; Do auto-compile on/off now, because the form itself might
- ;; need this decision.
- ((string=? arg "--auto-compile")
- (set! %load-should-auto-compile #t)
- (parse args out))
-
- ((string=? arg "--fresh-auto-compile")
- (set! %load-should-auto-compile #t)
- (set! %fresh-auto-compile #t)
- (parse args out))
-
- ((string=? arg "--no-auto-compile")
- (set! %load-should-auto-compile #f)
- (parse args out))
-
- ((string=? arg "-q") ; don't load user init
- (set! inhibit-user-init? #t)
- (parse args out))
-
- ((string-prefix? "--use-srfi=" arg)
- (let ((srfis (map (lambda (x)
- (let ((n (string->number x)))
- (if (and n (exact? n) (integer? n) (>= n 0))
- n
- (error "invalid SRFI specification"))))
- (string-split (substring arg 11) #\,))))
- (if (null? srfis)
- (error "invalid SRFI specification"))
- (parse args
- (cons `(use-srfis ',srfis) out))))
-
- ((string=? arg "--listen") ; start a repl server
- (parse args
- (cons '((@@ (system repl server) spawn-server)) out)))
-
- ((string-prefix? "--listen=" arg) ; start a repl server
- (parse
- args
- (cons
- (let ((where (substring arg 9)))
- (cond
- ((string->number where) ; --listen=PORT
- => (lambda (port)
- (if (and (integer? port) (exact? port) (>= port 0))
- `((@@ (system repl server) spawn-server)
- ((@@ (system repl server) make-tcp-server-socket) #\port ,port))
- (error "invalid port for --listen"))))
- ((string-prefix? "/" where) ; --listen=/PATH/TO/SOCKET
- `((@@ (system repl server) spawn-server)
- ((@@ (system repl server) make-unix-domain-server-socket) #\path ,where)))
- (else
- (error "unknown argument to --listen"))))
- out)))
-
- ((or (string=? arg "-h") (string=? arg "--help"))
- (shell-usage usage-name #f)
- (exit 0))
-
- ((or (string=? arg "-v") (string=? arg "--version"))
- (version-etc "GNU Guile" (version)
- #\license *LGPLv3+*
- #\command-name "guile"
- #\packager (assq-ref %guile-build-info 'packager)
- #\packager-version
- (assq-ref %guile-build-info 'packager-version))
- (exit 0))
-
- (else
- (error "unrecognized switch ~a" arg)))))))
-
- (define (finish args out)
- ;; Check to make sure the -ds got a -s.
- (when (and script-cell (not (car script-cell)))
- (error "the `-ds' switch requires the use of `-s' as well"))
-
- ;; Make any remaining arguments available to the
- ;; script/command/whatever.
- (set-program-arguments (cons arg0 args))
-
- ;; If debugging was requested, or we are interactive and debugging
- ;; was not explicitly turned off, use the debug engine.
- (if (or turn-on-debugging?
- (and interactive? (not turn-off-debugging?)))
- (begin
- (set-default-vm-engine! 'debug)
- (set-vm-engine! (the-vm) 'debug)))
-
- ;; Return this value.
- `(;; It would be nice not to load up (ice-9 control), but the
- ;; default-prompt-handler is nontrivial.
- (@ (ice-9 control) %)
- (begin
- ;; If we didn't end with a -c or a -s and didn't supply a -q, load
- ;; the user's customization file.
- ,@(if (and interactive? (not inhibit-user-init?))
- '((load-user-init))
- '())
-
- ;; Use-specified extensions.
- ,@(map (lambda (ext)
- `(set! %load-extensions (cons ,ext %load-extensions)))
- user-extensions)
-
- ;; Add the user-specified load paths here, so they won't be in
- ;; effect during the loading of the user's customization file.
- ,@(map (lambda (path)
- `(set! %load-path (cons ,path %load-path)))
- user-load-path)
- ,@(map (lambda (path)
- `(set! %load-compiled-path
- (cons ,path %load-compiled-path)))
- user-load-compiled-path)
-
- ;; Put accumulated actions in their correct order.
- ,@(reverse! out)
-
- ;; Handle the `-e' switch, if it was specified.
- ,@(if entry-point
- `((,entry-point (command-line)))
- '())
- ,(if interactive?
- ;; If we didn't end with a -c or a -s, start the
- ;; repl.
- '((@ (ice-9 top-repl) top-repl))
- ;; Otherwise, after doing all the other actions
- ;; prescribed by the command line, quit.
- '(quit)))))
-
- (if (pair? args)
- (begin
- (set! arg0 (car args))
- (let ((slash (string-rindex arg0 #\/)))
- (set! usage-name
- (if slash (substring arg0 (1+ slash)) arg0)))
- (parse (cdr args) '()))
- (parse args '()))))
-;;;; common-list.scm --- COMMON LISP list functions for Scheme
-;;;;
-;;;; Copyright (C) 1995, 1996, 1997, 2001, 2006 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary:
-
-;; These procedures are exported:
-;; (adjoin e l)
-;; (union l1 l2)
-;; (intersection l1 l2)
-;; (set-difference l1 l2)
-;; (reduce-init p init l)
-;; (reduce p l)
-;; (some pred l . rest)
-;; (every pred l . rest)
-;; (notany pred . ls)
-;; (notevery pred . ls)
-;; (count-if pred l)
-;; (find-if pred l)
-;; (member-if pred l)
-;; (remove-if pred l)
-;; (remove-if-not pred l)
-;; (delete-if! pred l)
-;; (delete-if-not! pred l)
-;; (butlast lst n)
-;; (and? . args)
-;; (or? . args)
-;; (has-duplicates? lst)
-;; (pick p l)
-;; (pick-mappings p l)
-;; (uniq l)
-;;
-;; See docstrings for each procedure for more info. See also module
-;; `(srfi srfi-1)' for a complete list handling library.
-
-;;; Code:
-
-(define-module (ice-9 common-list)
- \:export (adjoin union intersection set-difference reduce-init reduce
- some every notany notevery count-if find-if member-if remove-if
- remove-if-not delete-if! delete-if-not! butlast and? or?
- has-duplicates? pick pick-mappings uniq))
-
-;;"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.
-
-(define (adjoin e l)
- "Return list L, possibly with element E added if it is not already in L."
- (if (memq e l) l (cons e l)))
-
-(define (union l1 l2)
- "Return a new list that is the union of L1 and L2.
-Elements that occur in both lists occur only once in
-the result list."
- (cond ((null? l1) l2)
- ((null? l2) l1)
- (else (union (cdr l1) (adjoin (car l1) l2)))))
-
-(define (intersection l1 l2)
- "Return a new list that is the intersection of L1 and L2.
-Only elements that occur in both lists occur in the result list."
- (if (null? l2) l2
- (let loop ((l1 l1) (result '()))
- (cond ((null? l1) (reverse! result))
- ((memv (car l1) l2) (loop (cdr l1) (cons (car l1) result)))
- (else (loop (cdr l1) result))))))
-
-(define (set-difference l1 l2)
- "Return elements from list L1 that are not in list L2."
- (let loop ((l1 l1) (result '()))
- (cond ((null? l1) (reverse! result))
- ((memv (car l1) l2) (loop (cdr l1) result))
- (else (loop (cdr l1) (cons (car l1) result))))))
-
-(define (reduce-init p init l)
- "Same as `reduce' except it implicitly inserts INIT at the start of L."
- (if (null? l)
- init
- (reduce-init p (p init (car l)) (cdr l))))
-
-(define (reduce p l)
- "Combine all the elements of sequence L using a binary operation P.
-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."
- (cond ((null? l) l)
- ((null? (cdr l)) (car l))
- (else (reduce-init p (car l) (cdr l)))))
-
-(define (some pred l . rest)
- "PRED is a boolean function of as many arguments as there are list
-arguments to `some', i.e., L plus any optional arguments. PRED is
-applied to successive elements of the list arguments in order. As soon
-as one of these applications returns a true value, return that value.
-If no application returns a true value, return #f.
-All the lists should have the same length."
- (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 (every pred l . rest)
- "Return #t iff every application of PRED to L, etc., returns #t.
-Analogous to `some' except it returns #t if every application of
-PRED is #t and #f otherwise."
- (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 (notany pred . ls)
- "Return #t iff every application of PRED to L, etc., returns #f.
-Analogous to some but returns #t if no application of PRED returns a
-true value or #f as soon as any one does."
- (not (apply some pred ls)))
-
-(define (notevery pred . ls)
- "Return #t iff there is an application of PRED to L, etc., that returns #f.
-Analogous to some but returns #t as soon as an application of PRED returns #f,
-or #f otherwise."
- (not (apply every pred ls)))
-
-(define (count-if pred l)
- "Return the number of elements in L for which (PRED element) returns true."
- (let loop ((n 0) (l l))
- (cond ((null? l) n)
- ((pred (car l)) (loop (+ n 1) (cdr l)))
- (else (loop n (cdr l))))))
-
-(define (find-if pred l)
- "Search for the first element in L for which (PRED element) returns true.
-If found, return that element, otherwise return #f."
- (cond ((null? l) #f)
- ((pred (car l)) (car l))
- (else (find-if pred (cdr l)))))
-
-(define (member-if pred l)
- "Return the first sublist of L for whose car PRED is true."
- (cond ((null? l) #f)
- ((pred (car l)) l)
- (else (member-if pred (cdr l)))))
-
-(define (remove-if pred l)
- "Remove all elements from L where (PRED element) is true.
-Return everything that's left."
- (let loop ((l l) (result '()))
- (cond ((null? l) (reverse! result))
- ((pred (car l)) (loop (cdr l) result))
- (else (loop (cdr l) (cons (car l) result))))))
-
-(define (remove-if-not pred l)
- "Remove all elements from L where (PRED element) is #f.
-Return everything that's left."
- (let loop ((l l) (result '()))
- (cond ((null? l) (reverse! result))
- ((not (pred (car l))) (loop (cdr l) result))
- (else (loop (cdr l) (cons (car l) result))))))
-
-(define (delete-if! pred l)
- "Destructive version of `remove-if'."
- (let delete-if ((l l))
- (cond ((null? l) '())
- ((pred (car l)) (delete-if (cdr l)))
- (else
- (set-cdr! l (delete-if (cdr l)))
- l))))
-
-(define (delete-if-not! pred l)
- "Destructive version of `remove-if-not'."
- (let delete-if-not ((l l))
- (cond ((null? l) '())
- ((not (pred (car l))) (delete-if-not (cdr l)))
- (else
- (set-cdr! l (delete-if-not (cdr l)))
- l))))
-
-(define (butlast lst n)
- "Return all but the last N elements of LST."
- (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)
- (error "negative argument to butlast" n)
- l))))
-
-(define (and? . args)
- "Return #t iff all of ARGS are true."
- (cond ((null? args) #t)
- ((car args) (apply and? (cdr args)))
- (else #f)))
-
-(define (or? . args)
- "Return #t iff any of ARGS is true."
- (cond ((null? args) #f)
- ((car args) #t)
- (else (apply or? (cdr args)))))
-
-(define (has-duplicates? lst)
- "Return #t iff 2 members of LST are equal?, else #f."
- (cond ((null? lst) #f)
- ((member (car lst) (cdr lst)) #t)
- (else (has-duplicates? (cdr lst)))))
-
-(define (pick p l)
- "Apply P to each element of L, returning a list of elts
-for which P returns a non-#f value."
- (let loop ((s '())
- (l l))
- (cond
- ((null? l) s)
- ((p (car l)) (loop (cons (car l) s) (cdr l)))
- (else (loop s (cdr l))))))
-
-(define (pick-mappings p l)
- "Apply P to each element of L, returning a list of the
-non-#f return values of P."
- (let loop ((s '())
- (l l))
- (cond
- ((null? l) s)
- ((p (car l)) => (lambda (mapping) (loop (cons mapping s) (cdr l))))
- (else (loop s (cdr l))))))
-
-(define (uniq l)
- "Return a list containing elements of L, with duplicates removed."
- (let loop ((acc '())
- (l l))
- (if (null? l)
- (reverse! acc)
- (loop (if (memq (car l) acc)
- acc
- (cons (car l) acc))
- (cdr l)))))
-
-;;; common-list.scm ends here
-;;; -*- mode: scheme; coding: utf-8; -*-
-;;;
-;;; Copyright (C) 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(use-modules (language tree-il)
- (language tree-il primitives)
- (language tree-il canonicalize)
- (srfi srfi-1)
- (ice-9 pretty-print)
- (system syntax))
-
-;; Minimize a syntax-object such that it can no longer be used as the
-;; first argument to 'datum->syntax', but is otherwise equivalent.
-(define (squeeze-syntax-object! syn)
- (define (ensure-list x) (if (vector? x) (vector->list x) x))
- (let ((x (vector-ref syn 1))
- (wrap (vector-ref syn 2))
- (mod (vector-ref syn 3)))
- (let ((marks (car wrap))
- (subst (cdr wrap)))
- (define (set-wrap! marks subst)
- (vector-set! syn 2 (cons marks subst)))
- (cond
- ((symbol? x)
- (let loop ((marks marks) (subst subst))
- (cond
- ((null? subst) (set-wrap! marks subst) syn)
- ((eq? 'shift (car subst)) (loop (cdr marks) (cdr subst)))
- ((find (lambda (entry) (and (eq? x (car entry))
- (equal? marks (cadr entry))))
- (apply map list (map ensure-list
- (cdr (vector->list (car subst))))))
- => (lambda (entry)
- (set-wrap! marks
- (list (list->vector
- (cons 'ribcage
- (map vector entry)))))
- syn))
- (else (loop marks (cdr subst))))))
- ((or (pair? x) (vector? x))
- syn)
- (else x)))))
-
-(define (squeeze-constant! x)
- (define (syntax-object? x)
- (and (vector? x)
- (= 4 (vector-length x))
- (eq? 'syntax-object (vector-ref x 0))))
- (cond ((syntax-object? x)
- (squeeze-syntax-object! x))
- ((pair? x)
- (set-car! x (squeeze-constant! (car x)))
- (set-cdr! x (squeeze-constant! (cdr x)))
- x)
- ((vector? x)
- (for-each (lambda (i)
- (vector-set! x i (squeeze-constant! (vector-ref x i))))
- (iota (vector-length x)))
- x)
- (else x)))
-
-(define (squeeze-tree-il! x)
- (post-order! (lambda (x)
- (if (const? x)
- (set! (const-exp x)
- (squeeze-constant! (const-exp x))))
- #f)
- x))
-
-;; Avoid gratuitous churn in psyntax-pp.scm due to the marks and labels
-;; changing session identifiers.
-(set! syntax-session-id (lambda () "*"))
-
-(let ((source (list-ref (command-line) 1))
- (target (list-ref (command-line) 2)))
- (let ((in (open-input-file source))
- (out (open-output-file (string-append target ".tmp"))))
- (write '(eval-when (compile) (set-current-module (resolve-module '(guile))))
- out)
- (newline out)
- (let loop ((x (read in)))
- (if (eof-object? x)
- (begin
- (close-port out)
- (close-port in))
- (begin
- (pretty-print (tree-il->scheme
- (squeeze-tree-il!
- (canonicalize!
- (resolve-primitives!
- (macroexpand x 'c '(compile load eval))
- (current-module))))
- (current-module)
- (list #\avoid-lambda? #f
- #\use-case? #f
- #\strip-numeric-suffixes? #t
- #\use-derived-syntax?
- (and (pair? x)
- (eq? 'let (car x)))))
- out #\width 120 #\max-expr-width 70)
- (newline out)
- (loop (read in))))))
- (system (format #f "mv -f ~s.tmp ~s" target target)))
-;;; Beyond call/cc
-
-;; Copyright (C) 2010, 2011, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (ice-9 control)
- #\re-export (call-with-prompt abort-to-prompt
- default-prompt-tag make-prompt-tag)
- #\export (% abort shift reset shift* reset*
- call-with-escape-continuation call/ec
- let-escape-continuation let/ec))
-
-(define (abort . args)
- (apply abort-to-prompt (default-prompt-tag) args))
-
-(define-syntax %
- (syntax-rules ()
- ((_ expr)
- (call-with-prompt (default-prompt-tag)
- (lambda () expr)
- default-prompt-handler))
- ((_ expr handler)
- (call-with-prompt (default-prompt-tag)
- (lambda () expr)
- handler))
- ((_ tag expr handler)
- (call-with-prompt tag
- (lambda () expr)
- handler))))
-
-;; Each prompt tag has a type -- an expected set of arguments, and an unwritten
-;; contract of what its handler will do on an abort. In the case of the default
-;; prompt tag, we could choose to return values, exit nonlocally, or punt to the
-;; user.
-;;
-;; We choose the latter, by requiring that the user return one value, a
-;; procedure, to an abort to the prompt tag. That argument is then invoked with
-;; the continuation as an argument, within a reinstated default prompt. In this
-;; way the return value(s) from a default prompt are under the user's control.
-(define (default-prompt-handler k proc)
- (% (default-prompt-tag)
- (proc k)
- default-prompt-handler))
-
-;; Kindly provided by Wolfgang J Moeller <wjm@heenes.com>, modelled
-;; after the ones by Oleg Kiselyov in
-;; http://okmij.org/ftp/Scheme/delim-control-n.scm, which are in the
-;; public domain, as noted at the top of http://okmij.org/ftp/.
-;;
-(define-syntax-rule (reset . body)
- (call-with-prompt (default-prompt-tag)
- (lambda () . body)
- (lambda (cont f) (f cont))))
-
-(define-syntax-rule (shift var . body)
- (abort-to-prompt (default-prompt-tag)
- (lambda (cont)
- ((lambda (var) (reset . body))
- (lambda vals (reset (apply cont vals)))))))
-
-(define (reset* thunk)
- (reset (thunk)))
-
-(define (shift* fc)
- (shift c (fc c)))
-
-(define (call-with-escape-continuation proc)
- "Call PROC with an escape continuation."
- (let ((tag (list 'call/ec)))
- (call-with-prompt tag
- (lambda ()
- (proc (lambda args
- (apply abort-to-prompt tag args))))
- (lambda (_ . args)
- (apply values args)))))
-
-(define call/ec call-with-escape-continuation)
-
-(define-syntax-rule (let-escape-continuation k body ...)
- "Bind K to an escape continuation within the lexical extent of BODY."
- (let ((tag (list 'let/ec)))
- (call-with-prompt tag
- (lambda ()
- (let ((k (lambda args
- (apply abort-to-prompt tag args))))
- body ...))
- (lambda (_ . results)
- (apply values results)))))
-
-(define-syntax-rule (let/ec k body ...)
- (let-escape-continuation k body ...))
-;;; Copyright (C) 2010, 2013 Free Software Foundation, Inc.
-;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (ice-9 curried-definitions)
- #\replace ((cdefine . define)
- (cdefine* . define*)
- define-public
- define*-public))
-
-(define-syntax cdefine
- (syntax-rules ()
- ((_ (head . rest) body body* ...)
- (cdefine head
- (lambda rest body body* ...)))
- ((_ name val)
- (define name val))))
-
-(define-syntax cdefine*
- (syntax-rules ()
- ((_ (head . rest) body body* ...)
- (cdefine* head
- (lambda* rest body body* ...)))
- ((_ name val)
- (define* name val))))
-
-(define-syntax define-public
- (syntax-rules ()
- ((_ (head . rest) body body* ...)
- (define-public head
- (lambda rest body body* ...)))
- ((_ name val)
- (begin
- (define name val)
- (export name)))))
-
-(define-syntax define*-public
- (syntax-rules ()
- ((_ (head . rest) body body* ...)
- (define*-public head
- (lambda* rest body body* ...)))
- ((_ name val)
- (begin
- (define* name val)
- (export name)))))
-;;;; Copyright (C) 1996, 1997, 1998, 1999, 2001, 2006, 2010 Free Software Foundation
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-;;;; The author can be reached at djurfeldt@nada.kth.se
-;;;; Mikael Djurfeldt, SANS/NADA KTH, 10044 STOCKHOLM, SWEDEN
-;;;;
-
-
-(define-module (ice-9 debug))
-
-(issue-deprecation-warning
- "(ice-9 debug) is deprecated. Use (system vm trace) for tracing.")
-;;;; Copyright (C) 2003, 2005, 2006, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-(define-module (ice-9 deprecated)
- #\export (substring-move-left! substring-move-right!
- dynamic-maybe-call dynamic-maybe-link
- try-module-linked try-module-dynamic-link
- list* feature? eval-case unmemoize-expr
- $asinh
- $acosh
- $atanh
- $sqrt
- $abs
- $exp
- $expt
- $log
- $sin
- $cos
- $tan
- $asin
- $acos
- $atan
- $sinh
- $cosh
- $tanh
- closure?
- %nil
- @bind
- bad-throw
- error-catching-loop
- error-catching-repl
- scm-style-repl
- apply-to-args
- has-suffix?
- scheme-file-suffix
- get-option
- for-next-option
- display-usage-report
- transform-usage-lambda
- collect
- assert-repl-silence
- assert-repl-print-unspecified
- assert-repl-verbosity
- set-repl-prompt!
- set-batch-mode?!
- repl
- pre-unwind-handler-dispatch
- default-pre-unwind-handler
- handle-system-error
- stack-saved?
- the-last-stack
- save-stack
- named-module-use!
- top-repl
- turn-on-debugging
- read-hash-procedures
- process-define-module
- fluid-let-syntax
- set-system-module!
- char-code-limit
- generalized-vector?
- generalized-vector-length
- generalized-vector-ref
- generalized-vector-set!
- generalized-vector->list))
-
-
-;;;; Deprecated definitions.
-
-(define substring-move-left!
- (lambda args
- (issue-deprecation-warning
- "`substring-move-left!' is deprecated. Use `substring-move!' instead.")
- (apply substring-move! args)))
-(define substring-move-right!
- (lambda args
- (issue-deprecation-warning
- "`substring-move-right!' is deprecated. Use `substring-move!' instead.")
- (apply substring-move! args)))
-
-
-
-;; This method of dynamically linking Guile Extensions is deprecated.
-;; Use `load-extension' explicitly from Scheme code instead.
-
-(define (split-c-module-name str)
- (let loop ((rev '())
- (start 0)
- (pos 0)
- (end (string-length str)))
- (cond
- ((= pos end)
- (reverse (cons (string->symbol (substring str start pos)) rev)))
- ((eq? (string-ref str pos) #\space)
- (loop (cons (string->symbol (substring str start pos)) rev)
- (+ pos 1)
- (+ pos 1)
- end))
- (else
- (loop rev start (+ pos 1) end)))))
-
-(define (convert-c-registered-modules dynobj)
- (let ((res (map (lambda (c)
- (list (split-c-module-name (car c)) (cdr c) dynobj))
- (c-registered-modules))))
- (c-clear-registered-modules)
- res))
-
-(define registered-modules '())
-
-(define (register-modules dynobj)
- (set! registered-modules
- (append! (convert-c-registered-modules dynobj)
- registered-modules)))
-
-(define (warn-autoload-deprecation modname)
- (issue-deprecation-warning
- "Autoloading of compiled code modules is deprecated."
- "Write a Scheme file instead that uses `load-extension'.")
- (issue-deprecation-warning
- (simple-format #f "(You just autoloaded module ~S.)" modname)))
-
-(define (init-dynamic-module modname)
- ;; Register any linked modules which have been registered on the C level
- (register-modules #f)
- (or-map (lambda (modinfo)
- (if (equal? (car modinfo) modname)
- (begin
- (warn-autoload-deprecation modname)
- (set! registered-modules (delq! modinfo registered-modules))
- (let ((mod (resolve-module modname #f)))
- (save-module-excursion
- (lambda ()
- (set-current-module mod)
- (set-module-public-interface! mod mod)
- (dynamic-call (cadr modinfo) (caddr modinfo))
- ))
- #t))
- #f))
- registered-modules))
-
-(define (dynamic-maybe-call name dynobj)
- (issue-deprecation-warning
- "`dynamic-maybe-call' is deprecated. "
- "Wrap `dynamic-call' in a `false-if-exception' yourself.")
- (false-if-exception (dynamic-call name dynobj)))
-
-
-(define (dynamic-maybe-link filename)
- (issue-deprecation-warning
- "`dynamic-maybe-link' is deprecated. "
- "Wrap `dynamic-link' in a `false-if-exception' yourself.")
- (false-if-exception (dynamic-link filename)))
-
-(define (find-and-link-dynamic-module module-name)
- (define (make-init-name mod-name)
- (string-append "scm_init"
- (list->string (map (lambda (c)
- (if (or (char-alphabetic? c)
- (char-numeric? c))
- c
- #\_))
- (string->list mod-name)))
- "_module"))
-
- ;; Put the subdirectory for this module in the car of SUBDIR-AND-LIBNAME,
- ;; and the `libname' (the name of the module prepended by `lib') in the cdr
- ;; field. For example, if MODULE-NAME is the list (inet tcp-ip udp), then
- ;; SUBDIR-AND-LIBNAME will be the pair ("inet/tcp-ip" . "libudp").
- (let ((subdir-and-libname
- (let loop ((dirs "")
- (syms module-name))
- (if (null? (cdr syms))
- (cons dirs (string-append "lib" (symbol->string (car syms))))
- (loop (string-append dirs (symbol->string (car syms)) "/")
- (cdr syms)))))
- (init (make-init-name (apply string-append
- (map (lambda (s)
- (string-append "_"
- (symbol->string s)))
- module-name)))))
- (let ((subdir (car subdir-and-libname))
- (libname (cdr subdir-and-libname)))
-
- ;; Now look in each dir in %LOAD-PATH for `subdir/libfoo.la'. If that
- ;; file exists, fetch the dlname from that file and attempt to link
- ;; against it. If `subdir/libfoo.la' does not exist, or does not seem
- ;; to name any shared library, look for `subdir/libfoo.so' instead and
- ;; link against that.
- (let check-dirs ((dir-list %load-path))
- (if (null? dir-list)
- #f
- (let* ((dir (in-vicinity (car dir-list) subdir))
- (sharlib-full
- (or (try-using-libtool-name dir libname)
- (try-using-sharlib-name dir libname))))
- (if (and sharlib-full (file-exists? sharlib-full))
- (link-dynamic-module sharlib-full init)
- (check-dirs (cdr dir-list)))))))))
-
-(define (try-using-libtool-name libdir libname)
- (let ((libtool-filename (in-vicinity libdir
- (string-append libname ".la"))))
- (and (file-exists? libtool-filename)
- libtool-filename)))
-
-(define (try-using-sharlib-name libdir libname)
- (in-vicinity libdir (string-append libname ".so")))
-
-(define (link-dynamic-module filename initname)
- ;; Register any linked modules which have been registered on the C level
- (register-modules #f)
- (let ((dynobj (dynamic-link filename)))
- (dynamic-call initname dynobj)
- (register-modules dynobj)))
-
-(define (try-module-linked module-name)
- (issue-deprecation-warning
- "`try-module-linked' is deprecated."
- "See the manual for how more on C extensions.")
- (init-dynamic-module module-name))
-
-(define (try-module-dynamic-link module-name)
- (issue-deprecation-warning
- "`try-module-dynamic-link' is deprecated."
- "See the manual for how more on C extensions.")
- (and (find-and-link-dynamic-module module-name)
- (init-dynamic-module module-name)))
-
-
-(define (list* . args)
- (issue-deprecation-warning "'list*' is deprecated. Use 'cons*' instead.")
- (apply cons* args))
-
-(define (feature? sym)
- (issue-deprecation-warning
- "`feature?' is deprecated. Use `provided?' instead.")
- (provided? sym))
-
-(define-macro (eval-case . clauses)
- (issue-deprecation-warning
- "`eval-case' is deprecated. Use `eval-when' instead.")
- ;; Practically speaking, eval-case only had load-toplevel and else as
- ;; conditions.
- (cond
- ((assoc-ref clauses '(load-toplevel))
- => (lambda (exps)
- ;; the *unspecified so that non-toplevel definitions will be
- ;; caught
- `(begin *unspecified* . ,exps)))
- ((assoc-ref clauses 'else)
- => (lambda (exps)
- `(begin *unspecified* . ,exps)))
- (else
- `(begin))))
-
-;; The strange prototype system for uniform arrays has been
-;; deprecated.
-(read-hash-extend
- #\y
- (lambda (c port)
- (issue-deprecation-warning
- "The `#y' bytevector syntax is deprecated. Use `#s8' instead.")
- (let ((x (read port)))
- (cond
- ((list? x) (list->s8vector x))
- (else (error "#y needs to be followed by a list" x))))))
-
-(define (unmemoize-expr . args)
- (issue-deprecation-warning
- "`unmemoize-expr' is deprecated. Use `unmemoize-expression' instead.")
- (apply unmemoize-expression args))
-
-(define ($asinh z)
- (issue-deprecation-warning
- "`$asinh' is deprecated. Use `asinh' instead.")
- (asinh z))
-(define ($acosh z)
- (issue-deprecation-warning
- "`$acosh' is deprecated. Use `acosh' instead.")
- (acosh z))
-(define ($atanh z)
- (issue-deprecation-warning
- "`$atanh' is deprecated. Use `atanh' instead.")
- (atanh z))
-(define ($sqrt z)
- (issue-deprecation-warning
- "`$sqrt' is deprecated. Use `sqrt' instead.")
- (sqrt z))
-(define ($abs z)
- (issue-deprecation-warning
- "`$abs' is deprecated. Use `abs' instead.")
- (abs z))
-(define ($exp z)
- (issue-deprecation-warning
- "`$exp' is deprecated. Use `exp' instead.")
- (exp z))
-(define ($expt z1 z2)
- (issue-deprecation-warning
- "`$expt' is deprecated. Use `expt' instead.")
- (expt z1 z2))
-(define ($log z)
- (issue-deprecation-warning
- "`$log' is deprecated. Use `log' instead.")
- (log z))
-(define ($sin z)
- (issue-deprecation-warning
- "`$sin' is deprecated. Use `sin' instead.")
- (sin z))
-(define ($cos z)
- (issue-deprecation-warning
- "`$cos' is deprecated. Use `cos' instead.")
- (cos z))
-(define ($tan z)
- (issue-deprecation-warning
- "`$tan' is deprecated. Use `tan' instead.")
- (tan z))
-(define ($asin z)
- (issue-deprecation-warning
- "`$asin' is deprecated. Use `asin' instead.")
- (asin z))
-(define ($acos z)
- (issue-deprecation-warning
- "`$acos' is deprecated. Use `acos' instead.")
- (acos z))
-(define ($atan z)
- (issue-deprecation-warning
- "`$atan' is deprecated. Use `atan' instead.")
- (atan z))
-(define ($sinh z)
- (issue-deprecation-warning
- "`$sinh' is deprecated. Use `sinh' instead.")
- (sinh z))
-(define ($cosh z)
- (issue-deprecation-warning
- "`$cosh' is deprecated. Use `cosh' instead.")
- (cosh z))
-(define ($tanh z)
- (issue-deprecation-warning
- "`$tanh' is deprecated. Use `tanh' instead.")
- (tanh z))
-
-(define (closure? x)
- (issue-deprecation-warning
- "`closure?' is deprecated. Use `procedure?' instead.")
- (procedure? x))
-
-(define %nil #nil)
-
-;;; @bind is used by the old elisp code as a dynamic scoping mechanism.
-;;; Please let the Guile developers know if you are using this macro.
-;;;
-(define-syntax @bind
- (lambda (x)
- (define (bound-member id ids)
- (cond ((null? ids) #f)
- ((bound-identifier=? id (car ids)) #t)
- ((bound-member (car ids) (cdr ids)))))
-
- (issue-deprecation-warning
- "`@bind' is deprecated. Use `with-fluids' instead.")
-
- (syntax-case x ()
- ((_ () b0 b1 ...)
- #'(let () b0 b1 ...))
- ((_ ((id val) ...) b0 b1 ...)
- (and-map identifier? #'(id ...))
- (if (let lp ((ids #'(id ...)))
- (cond ((null? ids) #f)
- ((bound-member (car ids) (cdr ids)) #t)
- (else (lp (cdr ids)))))
- (syntax-violation '@bind "duplicate bound identifier" x)
- (with-syntax (((old-v ...) (generate-temporaries #'(id ...)))
- ((v ...) (generate-temporaries #'(id ...))))
- #'(let ((old-v id) ...
- (v val) ...)
- (dynamic-wind
- (lambda ()
- (set! id v) ...)
- (lambda () b0 b1 ...)
- (lambda ()
- (set! id old-v) ...)))))))))
-
-;; There are deprecated definitions for module-ref-submodule and
-;; module-define-submodule! in boot-9.scm.
-
-;; Define (%app) and (%app modules), and have (app) alias (%app). This
-;; side-effects the-root-module, both to the submodules table and (through
-;; module-define-submodule! above) the obarray.
-;;
-(let ((%app (make-module 31)))
- (set-module-name! %app '(%app))
- (module-define-submodule! the-root-module '%app %app)
- (module-define-submodule! the-root-module 'app %app)
- (module-define-submodule! %app 'modules (resolve-module '() #f)))
-
-;; Allow code that poked %module-public-interface to keep on working.
-;;
-(set! module-public-interface
- (let ((getter module-public-interface))
- (lambda (mod)
- (or (getter mod)
- (cond
- ((and=> (module-local-variable mod '%module-public-interface)
- variable-ref)
- => (lambda (iface)
- (issue-deprecation-warning
-"Setting a module's public interface via munging %module-public-interface is
-deprecated. Use set-module-public-interface! instead.")
- (set-module-public-interface! mod iface)
- iface))
- (else #f))))))
-
-(set! set-module-public-interface!
- (let ((setter set-module-public-interface!))
- (lambda (mod iface)
- (setter mod iface)
- (module-define! mod '%module-public-interface iface))))
-
-(define (bad-throw key . args)
- (issue-deprecation-warning
- "`bad-throw' in the default environment is deprecated.
-Find it in the `(ice-9 scm-style-repl)' module instead.")
- (apply (@ (ice-9 scm-style-repl) bad-throw) key args))
-
-(define (error-catching-loop thunk)
- (issue-deprecation-warning
- "`error-catching-loop' in the default environment is deprecated.
-Find it in the `(ice-9 scm-style-repl)' module instead.")
- ((@ (ice-9 scm-style-repl) error-catching-loop) thunk))
-
-(define (error-catching-repl r e p)
- (issue-deprecation-warning
- "`error-catching-repl' in the default environment is deprecated.
-Find it in the `(ice-9 scm-style-repl)' module instead.")
- ((@ (ice-9 scm-style-repl) error-catching-repl) r e p))
-
-(define (scm-style-repl)
- (issue-deprecation-warning
- "`scm-style-repl' in the default environment is deprecated.
-Find it in the `(ice-9 scm-style-repl)' module instead, or
-better yet, use the repl from `(system repl repl)'.")
- ((@ (ice-9 scm-style-repl) scm-style-repl)))
-
-
-;;; Apply-to-args had the following comment attached to it in boot-9, but it's
-;;; wrong-headed: in the mentioned case, a point should either be a record or
-;;; multiple values.
-;;;
-;;; apply-to-args is functionally redundant with apply and, worse,
-;;; is less general than apply since it only takes two arguments.
-;;;
-;;; On the other hand, apply-to-args is a syntacticly convenient way to
-;;; perform binding in many circumstances when the "let" family of
-;;; of forms don't cut it. E.g.:
-;;;
-;;; (apply-to-args (return-3d-mouse-coords)
-;;; (lambda (x y z)
-;;; ...))
-;;;
-
-(define (apply-to-args args fn)
- (issue-deprecation-warning
- "`apply-to-args' is deprecated. Include a local copy in your program.")
- (apply fn args))
-
-(define (has-suffix? str suffix)
- (issue-deprecation-warning
- "`has-suffix?' is deprecated. Use `string-suffix?' instead (args reversed).")
- (string-suffix? suffix str))
-
-(define scheme-file-suffix
- (lambda ()
- (issue-deprecation-warning
- "`scheme-file-suffix' is deprecated. Use `%load-extensions' instead.")
- ".scm"))
-
-
-
-;;; {Command Line Options}
-;;;
-
-(define (get-option argv kw-opts kw-args return)
- (issue-deprecation-warning
- "`get-option' is deprecated. Use `(ice-9 getopt-long)' instead.")
- (cond
- ((null? argv)
- (return #f #f argv))
-
- ((or (not (eq? #\- (string-ref (car argv) 0)))
- (eq? (string-length (car argv)) 1))
- (return 'normal-arg (car argv) (cdr argv)))
-
- ((eq? #\- (string-ref (car argv) 1))
- (let* ((kw-arg-pos (or (string-index (car argv) #\=)
- (string-length (car argv))))
- (kw (symbol->keyword (substring (car argv) 2 kw-arg-pos)))
- (kw-opt? (member kw kw-opts))
- (kw-arg? (member kw kw-args))
- (arg (or (and (not (eq? kw-arg-pos (string-length (car argv))))
- (substring (car argv)
- (+ kw-arg-pos 1)
- (string-length (car argv))))
- (and kw-arg?
- (begin (set! argv (cdr argv)) (car argv))))))
- (if (or kw-opt? kw-arg?)
- (return kw arg (cdr argv))
- (return 'usage-error kw (cdr argv)))))
-
- (else
- (let* ((char (substring (car argv) 1 2))
- (kw (symbol->keyword char)))
- (cond
-
- ((member kw kw-opts)
- (let* ((rest-car (substring (car argv) 2 (string-length (car argv))))
- (new-argv (if (= 0 (string-length rest-car))
- (cdr argv)
- (cons (string-append "-" rest-car) (cdr argv)))))
- (return kw #f new-argv)))
-
- ((member kw kw-args)
- (let* ((rest-car (substring (car argv) 2 (string-length (car argv))))
- (arg (if (= 0 (string-length rest-car))
- (cadr argv)
- rest-car))
- (new-argv (if (= 0 (string-length rest-car))
- (cddr argv)
- (cdr argv))))
- (return kw arg new-argv)))
-
- (else (return 'usage-error kw argv)))))))
-
-(define (for-next-option proc argv kw-opts kw-args)
- (issue-deprecation-warning
- "`for-next-option' is deprecated. Use `(ice-9 getopt-long)' instead.")
- (let loop ((argv argv))
- (get-option argv kw-opts kw-args
- (lambda (opt opt-arg argv)
- (and opt (proc opt opt-arg argv loop))))))
-
-(define (display-usage-report kw-desc)
- (issue-deprecation-warning
- "`display-usage-report' is deprecated. Use `(ice-9 getopt-long)' instead.")
- (for-each
- (lambda (kw)
- (or (eq? (car kw) #t)
- (eq? (car kw) 'else)
- (let* ((opt-desc kw)
- (help (cadr opt-desc))
- (opts (car opt-desc))
- (opts-proper (if (string? (car opts)) (cdr opts) opts))
- (arg-name (if (string? (car opts))
- (string-append "<" (car opts) ">")
- ""))
- (left-part (string-append
- (with-output-to-string
- (lambda ()
- (map (lambda (x) (display (keyword->symbol x)) (display " "))
- opts-proper)))
- arg-name))
- (middle-part (if (and (< (string-length left-part) 30)
- (< (string-length help) 40))
- (make-string (- 30 (string-length left-part)) #\space)
- "\n\t")))
- (display left-part)
- (display middle-part)
- (display help)
- (newline))))
- kw-desc))
-
-(define (transform-usage-lambda cases)
- (issue-deprecation-warning
- "`display-usage-report' is deprecated. Use `(ice-9 getopt-long)' instead.")
- (let* ((raw-usage (delq! 'else (map car cases)))
- (usage-sans-specials (map (lambda (x)
- (or (and (not (list? x)) x)
- (and (symbol? (car x)) #t)
- (and (boolean? (car x)) #t)
- x))
- raw-usage))
- (usage-desc (delq! #t usage-sans-specials))
- (kw-desc (map car usage-desc))
- (kw-opts (apply append (map (lambda (x) (and (not (string? (car x))) x)) kw-desc)))
- (kw-args (apply append (map (lambda (x) (and (string? (car x)) (cdr x))) kw-desc)))
- (transmogrified-cases (map (lambda (case)
- (cons (let ((opts (car case)))
- (if (or (boolean? opts) (eq? 'else opts))
- opts
- (cond
- ((symbol? (car opts)) opts)
- ((boolean? (car opts)) opts)
- ((string? (caar opts)) (cdar opts))
- (else (car opts)))))
- (cdr case)))
- cases)))
- `(let ((%display-usage (lambda () (display-usage-report ',usage-desc))))
- (lambda (%argv)
- (let %next-arg ((%argv %argv))
- (get-option %argv
- ',kw-opts
- ',kw-args
- (lambda (%opt %arg %new-argv)
- (case %opt
- ,@ transmogrified-cases))))))))
-
-
-
-;;; {collect}
-;;;
-;;; Similar to `begin' but returns a list of the results of all constituent
-;;; forms instead of the result of the last form.
-;;;
-
-(define-syntax collect
- (lambda (x)
- (issue-deprecation-warning
- "`collect' is deprecated. Define it yourself.")
- (syntax-case x ()
- ((_) #''())
- ((_ x x* ...)
- #'(let ((val x))
- (cons val (collect x* ...)))))))
-
-
-
-
-(define (assert-repl-silence v)
- (issue-deprecation-warning
- "`assert-repl-silence' has moved to `(ice-9 scm-style-repl)'.")
- ((@ (ice-9 scm-style-repl) assert-repl-silence) v))
-
-(define (assert-repl-print-unspecified v)
- (issue-deprecation-warning
- "`assert-repl-print-unspecified' has moved to `(ice-9 scm-style-repl)'.")
- ((@ (ice-9 scm-style-repl) assert-repl-print-unspecified) v))
-
-(define (assert-repl-verbosity v)
- (issue-deprecation-warning
- "`assert-repl-verbosity' has moved to `(ice-9 scm-style-repl)'.")
- ((@ (ice-9 scm-style-repl) assert-repl-verbosity) v))
-
-(define (set-repl-prompt! v)
- (issue-deprecation-warning
- "`set-repl-prompt!' is deprecated. Use `repl-default-prompt-set!' from
-the `(system repl common)' module.")
- ;; Avoid @, as when bootstrapping it will cause the (system repl common)
- ;; module to be loaded at expansion time, which eventually loads srfi-1, but
- ;; that fails due to an unbuilt supporting lib... grrrrrrrrr.
- ((module-ref (resolve-interface '(system repl common))
- 'repl-default-prompt-set!)
- v))
-
-(define (set-batch-mode?! arg)
- (cond
- (arg
- (issue-deprecation-warning
- "`set-batch-mode?!' is deprecated. Use `ensure-batch-mode!' instead.")
- (ensure-batch-mode!))
- (else
- (issue-deprecation-warning
- "`set-batch-mode?!' with an argument of `#f' is deprecated. Use the
-`*repl-stack*' fluid instead.")
- #t)))
-
-(define (repl read evaler print)
- (issue-deprecation-warning
- "`repl' is deprecated. Define it yourself.")
- (let loop ((source (read (current-input-port))))
- (print (evaler source))
- (loop (read (current-input-port)))))
-
-(define (pre-unwind-handler-dispatch key . args)
- (issue-deprecation-warning
- "`pre-unwind-handler-dispatch' is deprecated. Use
-`default-pre-unwind-handler' from `(ice-9 scm-style-repl)' directly.")
- (apply (@ (ice-9 scm-style-repl) default-pre-unwind-handler) key args))
-
-(define (default-pre-unwind-handler key . args)
- (issue-deprecation-warning
- "`default-pre-unwind-handler' is deprecated. Use it from
-`(ice-9 scm-style-repl)' if you need it.")
- (apply (@ (ice-9 scm-style-repl) default-pre-unwind-handler) key args))
-
-(define (handle-system-error key . args)
- (issue-deprecation-warning
- "`handle-system-error' is deprecated. Use it from
-`(ice-9 scm-style-repl)' if you need it.")
- (apply (@ (ice-9 scm-style-repl) handle-system-error) key args))
-
-(define-syntax stack-saved?
- (make-variable-transformer
- (lambda (x)
- (issue-deprecation-warning
- "`stack-saved?' is deprecated. Use it from
-`(ice-9 save-stack)' if you need it.")
- (syntax-case x (set!)
- ((set! id val)
- (identifier? #'id)
- #'(set! (@ (ice-9 save-stack) stack-saved?) val))
- (id
- (identifier? #'id)
- #'(@ (ice-9 save-stack) stack-saved?))))))
-
-(define-syntax the-last-stack
- (lambda (x)
- (issue-deprecation-warning
- "`the-last-stack' is deprecated. Use it from `(ice-9 save-stack)'
-if you need it.")
- (syntax-case x ()
- (id
- (identifier? #'id)
- #'(@ (ice-9 save-stack) the-last-stack)))))
-
-(define (save-stack . args)
- (issue-deprecation-warning
- "`save-stack' is deprecated. Use it from `(ice-9 save-stack)' if you need
-it.")
- (apply (@ (ice-9 save-stack) save-stack) args))
-
-(define (named-module-use! user usee)
- (issue-deprecation-warning
- "`named-module-use!' is deprecated. Define it yourself if you need it.")
- (module-use! (resolve-module user) (resolve-interface usee)))
-
-(define (top-repl)
- (issue-deprecation-warning
- "`top-repl' has moved to the `(ice-9 top-repl)' module.")
- ((module-ref (resolve-module '(ice-9 top-repl)) 'top-repl)))
-
-(set! debug-enable
- (let ((debug-enable debug-enable))
- (lambda opts
- (if (memq 'debug opts)
- (begin
- (issue-deprecation-warning
- "`(debug-enable 'debug)' is obsolete and has no effect."
- "Remove it from your code.")
- (apply debug-enable (delq 'debug opts)))
- (apply debug-enable opts)))))
-
-(define (turn-on-debugging)
- (issue-deprecation-warning
- "`(turn-on-debugging)' is obsolete and usually has no effect."
- "Debugging capabilities are present by default.")
- (debug-enable 'backtrace)
- (read-enable 'positions))
-
-(define (read-hash-procedures-warning)
- (issue-deprecation-warning
- "`read-hash-procedures' is deprecated."
- "Use the fluid `%read-hash-procedures' instead."))
-
-(define-syntax read-hash-procedures
- (identifier-syntax
- (_
- (begin (read-hash-procedures-warning)
- (fluid-ref %read-hash-procedures)))
- ((set! _ expr)
- (begin (read-hash-procedures-warning)
- (fluid-set! %read-hash-procedures expr)))))
-
-(define (process-define-module args)
- (define (missing kw)
- (error "missing argument to define-module keyword" kw))
- (define (unrecognized arg)
- (error "unrecognized define-module argument" arg))
-
- (issue-deprecation-warning
- "`process-define-module' is deprecated. Use `define-module*' instead.")
-
- (let ((name (car args))
- (filename #f)
- (pure? #f)
- (version #f)
- (system? #f)
- (duplicates '())
- (transformer #f))
- (let loop ((kws (cdr args))
- (imports '())
- (exports '())
- (re-exports '())
- (replacements '())
- (autoloads '()))
- (if (null? kws)
- (define-module* name
- #\filename filename #\pure pure? #\version version
- #\duplicates duplicates #\transformer transformer
- #\imports (reverse! imports)
- #\exports exports
- #\re-exports re-exports
- #\replacements replacements
- #\autoloads autoloads)
- (case (car kws)
- ((#\use-module #\use-syntax)
- (or (pair? (cdr kws))
- (missing (car kws)))
- (cond
- ((equal? (cadr kws) '(ice-9 syncase))
- (issue-deprecation-warning
- "(ice-9 syncase) is deprecated. Support for syntax-case is now in Guile core.")
- (loop (cddr kws)
- imports exports re-exports replacements autoloads))
- (else
- (let ((iface-spec (cadr kws)))
- (if (eq? (car kws) #\use-syntax)
- (set! transformer iface-spec))
- (loop (cddr kws)
- (cons iface-spec imports) exports re-exports
- replacements autoloads)))))
- ((#\autoload)
- (or (and (pair? (cdr kws)) (pair? (cddr kws)))
- (missing (car kws)))
- (let ((name (cadr kws))
- (bindings (caddr kws)))
- (loop (cdddr kws)
- imports exports re-exports
- replacements (cons* name bindings autoloads))))
- ((#\no-backtrace)
- ;; FIXME: deprecate?
- (set! system? #t)
- (loop (cdr kws)
- imports exports re-exports replacements autoloads))
- ((#\pure)
- (set! pure? #t)
- (loop (cdr kws)
- imports exports re-exports replacements autoloads))
- ((#\version)
- (or (pair? (cdr kws))
- (missing (car kws)))
- (set! version (cadr kws))
- (loop (cddr kws)
- imports exports re-exports replacements autoloads))
- ((#\duplicates)
- (if (not (pair? (cdr kws)))
- (missing (car kws)))
- (set! duplicates (cadr kws))
- (loop (cddr kws)
- imports exports re-exports replacements autoloads))
- ((#\export #\export-syntax)
- (or (pair? (cdr kws))
- (missing (car kws)))
- (loop (cddr kws)
- imports (append exports (cadr kws)) re-exports
- replacements autoloads))
- ((#\re-export #\re-export-syntax)
- (or (pair? (cdr kws))
- (missing (car kws)))
- (loop (cddr kws)
- imports exports (append re-exports (cadr kws))
- replacements autoloads))
- ((#\replace #\replace-syntax)
- (or (pair? (cdr kws))
- (missing (car kws)))
- (loop (cddr kws)
- imports exports re-exports
- (append replacements (cadr kws)) autoloads))
- ((#\filename)
- (or (pair? (cdr kws))
- (missing (car kws)))
- (set! filename (cadr kws))
- (loop (cddr kws)
- imports exports re-exports replacements autoloads))
- (else
- (unrecognized kws)))))))
-
-(define-syntax fluid-let-syntax
- (lambda (x)
- (issue-deprecation-warning
- "`fluid-let-syntax' is deprecated. Use syntax parameters instead.")
- (syntax-case x ()
- ((_ ((k v) ...) body0 body ...)
- #'(syntax-parameterize ((k v) ...)
- body0 body ...)))))
-
-(define (close-io-port port)
- (issue-deprecation-warning
- "`close-io-port' is deprecated. Use `close-port' instead.")
- (close-port port))
-
-(define (set-system-module! m s)
- (issue-deprecation-warning
- "`set-system-module!' is deprecated. There is no need to use it.")
- (set-procedure-property! (module-eval-closure m) 'system-module s))
-
-(set! module-eval-closure
- (lambda (m)
- (issue-deprecation-warning
- "`module-eval-closure' is deprecated. Use module-variable or module-define! instead.")
- (standard-eval-closure m)))
-
-;; Legacy definition. We can't make it identifier-syntax yet though,
-;; because compiled code might rely on it.
-(define char-code-limit 256)
-;;;; Copyright (C) 2000,2001, 2002, 2003, 2006, 2009, 2010 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary:
-
-;; * This module exports:
-;;
-;; file-commentary -- a procedure that returns a file's "commentary"
-;;
-;; documentation-files -- a search-list of files using the Guile
-;; Documentation Format Version 2.
-;;
-;; search-documentation-files -- a procedure that takes NAME (a symbol)
-;; and searches `documentation-files' for
-;; associated documentation. optional
-;; arg FILES is a list of filenames to use
-;; instead of `documentation-files'.
-;;
-;; object-documentation -- a procedure that returns its arg's docstring
-;;
-;; * Guile Documentation Format
-;;
-;; Here is the complete and authoritative documentation for the Guile
-;; Documentation Format Version 2:
-;;
-;; HEADER
-;; ^LPROC1
-;; DOCUMENTATION1
-;;
-;; ^LPROC2
-;; DOCUMENTATION2
-;;
-;; ^L...
-;;
-;; The HEADER is completely ignored. The "^L" are formfeeds. PROC1, PROC2
-;; and so on are symbols that name the element documented. DOCUMENTATION1,
-;; DOCUMENTATION2 and so on are the related documentation, w/o any further
-;; formatting. Note that there are two newlines before the next formfeed;
-;; these are discarded when the documentation is read in.
-;;
-;; (Version 1, corresponding to guile-1.4 and prior, is documented as being
-;; not documented anywhere except by this embarrassingly circular comment.)
-;;
-;; * File Commentary
-;;
-;; A file's commentary is the body of text found between comments
-;; ;;; Commentary:
-;; and
-;; ;;; Code:
-;; both of which must be at the beginning of the line. In the result string,
-;; semicolons at the beginning of each line are discarded.
-;;
-;; You can specify to `file-commentary' alternate begin and end strings, and
-;; scrub procedure. Use #t to get default values. For example:
-;;
-;; (file-commentary "documentation.scm")
-;; You should see this text!
-;;
-;; (file-commentary "documentation.scm" "^;;; Code:" "ends here$")
-;; You should see the rest of this file.
-;;
-;; (file-commentary "documentation.scm" #t #t string-upcase)
-;; You should see this text very loudly (note semicolons untouched).
-
-;;; Code:
-
-(define-module (ice-9 documentation)
- \:use-module (ice-9 rdelim)
- \:export (file-commentary
- documentation-files search-documentation-files
- object-documentation)
- \:autoload (ice-9 regex) (match:suffix)
- \:no-backtrace)
-
-
-;;
-;; commentary extraction
-;;
-
-(define (file-commentary filename . cust) ; (IN-LINE-RE AFTER-LINE-RE SCRUB)
-
- ;; These are constants but are not at the top level because the repl in
- ;; boot-9.scm loads session.scm which in turn loads this file, and we want
- ;; that to work even even when regexps are not available (ie. make-regexp
- ;; doesn't exist), as for instance is the case on mingw.
- ;;
- (define default-in-line-re (make-regexp "^;;; Commentary:"))
- (define default-after-line-re (make-regexp "^;;; Code:"))
- (define default-scrub (let ((dirt (make-regexp "^;+")))
- (lambda (line)
- (let ((m (regexp-exec dirt line)))
- (if m (match:suffix m) line)))))
-
- ;; fixme: might be cleaner to use optargs here...
- (let ((in-line-re (if (> 1 (length cust))
- default-in-line-re
- (let ((v (car cust)))
- (cond ((regexp? v) v)
- ((string? v) (make-regexp v))
- (else default-in-line-re)))))
- (after-line-re (if (> 2 (length cust))
- default-after-line-re
- (let ((v (cadr cust)))
- (cond ((regexp? v) v)
- ((string? v) (make-regexp v))
- (else default-after-line-re)))))
- (scrub (if (> 3 (length cust))
- default-scrub
- (let ((v (caddr cust)))
- (cond ((procedure? v) v)
- (else default-scrub))))))
- (call-with-input-file filename
- (lambda (port)
- (let loop ((line (read-delimited "\n" port))
- (doc "")
- (parse-state 'before))
- (if (or (eof-object? line) (eq? 'after parse-state))
- doc
- (let ((new-state
- (cond ((regexp-exec in-line-re line) 'in)
- ((regexp-exec after-line-re line) 'after)
- (else parse-state))))
- (if (eq? 'after new-state)
- doc
- (loop (read-delimited "\n" port)
- (if (and (eq? 'in new-state) (eq? 'in parse-state))
- (string-append doc (scrub line) "\n")
- doc)
- new-state)))))))))
-
-
-
-;;
-;; documentation-files is the list of places to look for documentation
-;;
-(define documentation-files
- (map (lambda (vicinity)
- (in-vicinity (vicinity) "guile-procedures.txt"))
- (list %library-dir
- %package-data-dir
- %site-dir
- (lambda () "."))))
-
-(define entry-delimiter "\f")
-
-(define (find-documentation-in-file name file)
- (and (file-exists? file)
- (call-with-input-file file
- (lambda (port)
- (let ((name (symbol->string name)))
- (let ((len (string-length name)))
- (read-delimited entry-delimiter port) ;skip to first entry
- (let loop ((entry (read-delimited entry-delimiter port)))
- (cond ((eof-object? entry) #f)
- ;; match?
- ((and ;; large enough?
- (>= (string-length entry) len)
- ;; matching name?
- (string=? (substring entry 0 len) name)
- ;; terminated?
- (memq (string-ref entry len) '(#\newline)))
- ;; cut away name tag and extra surrounding newlines
- (substring entry (+ len 2) (- (string-length entry) 2)))
- (else (loop (read-delimited entry-delimiter port)))))))))))
-
-(define (search-documentation-files name . files)
- (or-map (lambda (file)
- (find-documentation-in-file name file))
- (cond ((null? files) documentation-files)
- (else files))))
-
-(define (object-documentation object)
- "Return the docstring for OBJECT.
-OBJECT can be a procedure, macro or any object that has its
-`documentation' property set."
- (or (and (procedure? object)
- (procedure-documentation object))
- (object-property object 'documentation)
- (and (macro? object)
- (object-documentation (macro-transformer object)))
- (and (procedure? object)
- (procedure-name object)
- (let ((docstring (search-documentation-files
- (procedure-name object))))
- (if docstring
- (set-procedure-property! object 'documentation docstring))
- docstring))))
-
-;;; documentation.scm ends here
-;;; Evaluating code from users
-
-;;; Copyright (C) 2011, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (ice-9 eval-string)
- #\use-module (system base compile)
- #\use-module (system base language)
- #\use-module (system vm program)
- #\replace (eval-string))
-
-(define (ensure-language x)
- (if (language? x)
- x
- (lookup-language x)))
-
-(define* (read-and-eval port #\key (lang (current-language)))
- (parameterize ((current-language (ensure-language lang)))
- (define (read)
- ((language-reader (current-language)) port (current-module)))
- (define (eval exp)
- ((language-evaluator (current-language)) exp (current-module)))
-
- (let ((exp (read)))
- (if (eof-object? exp)
- ;; The behavior of read-and-compile and of the old
- ;; eval-string.
- *unspecified*
- (let lp ((exp exp))
- (call-with-values
- (lambda () (eval exp))
- (lambda vals
- (let ((next (read)))
- (cond
- ((eof-object? next)
- (apply values vals))
- (else
- (lp next)))))))))))
-
-(define* (eval-string str #\key
- (module (current-module))
- (file #f)
- (line #f)
- (column #f)
- (lang (current-language))
- (compile? #f))
- (define (maybe-with-module module thunk)
- (if module
- (save-module-excursion
- (lambda ()
- (set-current-module module)
- (thunk)))
- (thunk)))
-
- (let ((lang (ensure-language lang)))
- (call-with-input-string
- str
- (lambda (port)
- (maybe-with-module
- module
- (lambda ()
- (if module
- (set-current-module module))
- (if file
- (set-port-filename! port file))
- (if line
- (set-port-line! port line))
- (if column
- (set-port-column! port line))
-
- (if (or compile? (not (language-evaluator lang)))
- ((make-program (read-and-compile port #\from lang #\to 'objcode)))
- (read-and-eval port #\lang lang))))))))
-;;; -*- mode: scheme; coding: utf-8; -*-
-
-;;;; Copyright (C) 2009, 2010, 2012, 2013 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-
-;;; Commentary:
-
-;;; Scheme eval, written in Scheme.
-;;;
-;;; Expressions are first expanded, by the syntax expander (i.e.
-;;; psyntax), then memoized into internal forms. The evaluator itself
-;;; only operates on the internal forms ("memoized expressions").
-;;;
-;;; Environments are represented as linked lists of the form (VAL ... .
-;;; MOD). If MOD is #f, it means the environment was captured before
-;;; modules were booted. If MOD is the literal value '(), we are
-;;; evaluating at the top level, and so should track changes to the
-;;; current module.
-;;;
-;;; Evaluate this in Emacs to make code indentation work right:
-;;;
-;;; (put 'memoized-expression-case 'scheme-indent-function 1)
-;;;
-
-;;; Code:
-
-
-
-(eval-when (compile)
- (define-syntax capture-env
- (syntax-rules ()
- ((_ (exp ...))
- (let ((env (exp ...)))
- (capture-env env)))
- ((_ env)
- (if (null? env)
- (current-module)
- (if (not env)
- ;; the and current-module checks that modules are booted,
- ;; and thus the-root-module is defined
- (and (current-module) the-root-module)
- env)))))
-
- ;; Fast case for procedures with fixed arities.
- (define-syntax make-fixed-closure
- (lambda (x)
- (define *max-static-argument-count* 8)
- (define (make-formals n)
- (map (lambda (i)
- (datum->syntax
- x
- (string->symbol
- (string (integer->char (+ (char->integer #\a) i))))))
- (iota n)))
- (syntax-case x ()
- ((_ eval nreq body env) (not (identifier? #'env))
- #'(let ((e env))
- (make-fixed-closure eval nreq body e)))
- ((_ eval nreq body env)
- #`(case nreq
- #,@(map (lambda (nreq)
- (let ((formals (make-formals nreq)))
- #`((#,nreq)
- (lambda (#,@formals)
- (eval body
- (cons* #,@(reverse formals) env))))))
- (iota *max-static-argument-count*))
- (else
- #,(let ((formals (make-formals *max-static-argument-count*)))
- #`(lambda (#,@formals . more)
- (let lp ((new-env (cons* #,@(reverse formals) env))
- (nreq (- nreq #,*max-static-argument-count*))
- (args more))
- (if (zero? nreq)
- (eval body
- (if (null? args)
- new-env
- (scm-error 'wrong-number-of-args
- "eval" "Wrong number of arguments"
- '() #f)))
- (if (null? args)
- (scm-error 'wrong-number-of-args
- "eval" "Wrong number of arguments"
- '() #f)
- (lp (cons (car args) new-env)
- (1- nreq)
- (cdr args)))))))))))))
-
- (define-syntax call
- (lambda (x)
- (define *max-static-call-count* 4)
- (syntax-case x ()
- ((_ eval proc nargs args env) (identifier? #'env)
- #`(case nargs
- #,@(map (lambda (nargs)
- #`((#,nargs)
- (proc
- #,@(map
- (lambda (n)
- (let lp ((n n) (args #'args))
- (if (zero? n)
- #`(eval (car #,args) env)
- (lp (1- n) #`(cdr #,args)))))
- (iota nargs)))))
- (iota *max-static-call-count*))
- (else
- (apply proc
- #,@(map
- (lambda (n)
- (let lp ((n n) (args #'args))
- (if (zero? n)
- #`(eval (car #,args) env)
- (lp (1- n) #`(cdr #,args)))))
- (iota *max-static-call-count*))
- (let lp ((exps #,(let lp ((n *max-static-call-count*)
- (args #'args))
- (if (zero? n)
- args
- (lp (1- n) #`(cdr #,args)))))
- (args '()))
- (if (null? exps)
- (reverse args)
- (lp (cdr exps)
- (cons (eval (car exps) env) args)))))))))))
-
- ;; This macro could be more straightforward if the compiler had better
- ;; copy propagation. As it is we do some copy propagation by hand.
- (define-syntax mx-bind
- (lambda (x)
- (syntax-case x ()
- ((_ data () body)
- #'body)
- ((_ data (a . b) body) (and (identifier? #'a) (identifier? #'b))
- #'(let ((a (car data))
- (b (cdr data)))
- body))
- ((_ data (a . b) body) (identifier? #'a)
- #'(let ((a (car data))
- (xb (cdr data)))
- (mx-bind xb b body)))
- ((_ data (a . b) body)
- #'(let ((xa (car data))
- (xb (cdr data)))
- (mx-bind xa a (mx-bind xb b body))))
- ((_ data v body) (identifier? #'v)
- #'(let ((v data))
- body)))))
-
- ;; The resulting nested if statements will be an O(n) dispatch. Once
- ;; we compile `case' effectively, this situation will improve.
- (define-syntax mx-match
- (lambda (x)
- (syntax-case x (quote)
- ((_ mx data tag)
- #'(error "what" mx))
- ((_ mx data tag (('type pat) body) c* ...)
- #`(if (eqv? tag #,(or (memoized-typecode (syntax->datum #'type))
- (error "not a typecode" #'type)))
- (mx-bind data pat body)
- (mx-match mx data tag c* ...))))))
-
- (define-syntax memoized-expression-case
- (lambda (x)
- (syntax-case x ()
- ((_ mx c ...)
- #'(let ((tag (memoized-expression-typecode mx))
- (data (memoized-expression-data mx)))
- (mx-match mx data tag c ...)))))))
-
-
-;;;
-;;; On 18 Feb 2010, I did a profile of how often the various memoized expression
-;;; types occur when getting to a prompt on a fresh build. Here are the numbers
-;;; I got:
-;;;
-;;; lexical-ref: 32933054
-;;; call: 20281547
-;;; toplevel-ref: 13228724
-;;; if: 9156156
-;;; quote: 6610137
-;;; let: 2619707
-;;; lambda: 1010921
-;;; begin: 948945
-;;; lexical-set: 509862
-;;; call-with-values: 139668
-;;; apply: 49402
-;;; module-ref: 14468
-;;; define: 1259
-;;; toplevel-set: 328
-;;; dynwind: 162
-;;; with-fluids: 0
-;;; call/cc: 0
-;;; module-set: 0
-;;;
-;;; So until we compile `case' into a computed goto, we'll order the clauses in
-;;; `eval' in this order, to put the most frequent cases first.
-;;;
-
-(define primitive-eval
- (let ()
- ;; We pre-generate procedures with fixed arities, up to some number of
- ;; arguments; see make-fixed-closure above.
-
- ;; A unique marker for unbound keywords.
- (define unbound-arg (list 'unbound-arg))
-
- ;; Procedures with rest, optional, or keyword arguments, potentially with
- ;; multiple arities, as with case-lambda.
- (define (make-general-closure env body nreq rest? nopt kw inits alt)
- (define alt-proc
- (and alt ; (body docstring nreq ...)
- (let* ((body (car alt))
- (spec (cddr alt))
- (nreq (car spec))
- (rest (if (null? (cdr spec)) #f (cadr spec)))
- (tail (and (pair? (cdr spec)) (pair? (cddr spec)) (cddr spec)))
- (nopt (if tail (car tail) 0))
- (kw (and tail (cadr tail)))
- (inits (if tail (caddr tail) '()))
- (alt (and tail (cadddr tail))))
- (make-general-closure env body nreq rest nopt kw inits alt))))
- (define (set-procedure-arity! proc)
- (let lp ((alt alt) (nreq nreq) (nopt nopt) (rest? rest?))
- (if (not alt)
- (begin
- (set-procedure-property! proc 'arglist
- (list nreq
- nopt
- (if kw (cdr kw) '())
- (and kw (car kw))
- (and rest? '_)))
- (set-procedure-minimum-arity! proc nreq nopt rest?))
- (let* ((spec (cddr alt))
- (nreq* (car spec))
- (rest?* (if (null? (cdr spec)) #f (cadr spec)))
- (tail (and (pair? (cdr spec)) (pair? (cddr spec)) (cddr spec)))
- (nopt* (if tail (car tail) 0))
- (alt* (and tail (cadddr tail))))
- (if (or (< nreq* nreq)
- (and (= nreq* nreq)
- (if rest?
- (and rest?* (> nopt* nopt))
- (or rest?* (> nopt* nopt)))))
- (lp alt* nreq* nopt* rest?*)
- (lp alt* nreq nopt rest?)))))
- proc)
- (set-procedure-arity!
- (lambda %args
- (let lp ((env env)
- (nreq* nreq)
- (args %args))
- (if (> nreq* 0)
- ;; First, bind required arguments.
- (if (null? args)
- (if alt
- (apply alt-proc %args)
- (scm-error 'wrong-number-of-args
- "eval" "Wrong number of arguments"
- '() #f))
- (lp (cons (car args) env)
- (1- nreq*)
- (cdr args)))
- ;; Move on to optional arguments.
- (if (not kw)
- ;; Without keywords, bind optionals from arguments.
- (let lp ((env env)
- (nopt nopt)
- (args args)
- (inits inits))
- (if (zero? nopt)
- (if rest?
- (eval body (cons args env))
- (if (null? args)
- (eval body env)
- (if alt
- (apply alt-proc %args)
- (scm-error 'wrong-number-of-args
- "eval" "Wrong number of arguments"
- '() #f))))
- (if (null? args)
- (lp (cons (eval (car inits) env) env)
- (1- nopt) args (cdr inits))
- (lp (cons (car args) env)
- (1- nopt) (cdr args) (cdr inits)))))
- (let lp ((env env)
- (nopt* nopt)
- (args args)
- (inits inits))
- (cond
- ;; With keywords, we stop binding optionals at the
- ;; first keyword.
- ((> nopt* 0)
- (if (or (null? args) (keyword? (car args)))
- (lp (cons (eval (car inits) env) env)
- (1- nopt*) args (cdr inits))
- (lp (cons (car args) env)
- (1- nopt*) (cdr args) (cdr inits))))
- ;; Finished with optionals.
- ((and alt (pair? args) (not (keyword? (car args)))
- (not rest?))
- ;; Too many positional args, no #\rest arg,
- ;; and we have an alternate.
- (apply alt-proc %args))
- (else
- (let* ((aok (car kw))
- (kw (cdr kw))
- (kw-base (+ nopt nreq (if rest? 1 0)))
- (imax (let lp ((imax (1- kw-base)) (kw kw))
- (if (null? kw)
- imax
- (lp (max (cdar kw) imax)
- (cdr kw)))))
- ;; Fill in kwargs with "undefined" vals.
- (env (let lp ((i kw-base)
- ;; Also, here we bind the rest
- ;; arg, if any.
- (env (if rest?
- (cons args env)
- env)))
- (if (<= i imax)
- (lp (1+ i) (cons unbound-arg env))
- env))))
- ;; Now scan args for keywords.
- (let lp ((args args))
- (cond
- ((pair? args)
- (cond
- ((keyword? (car args))
- (let ((k (car args))
- (args (cdr args)))
- (cond
- ((assq k kw)
- => (lambda (kw-pair)
- ;; Found a known keyword; set its value.
- (if (pair? args)
- (let ((v (car args))
- (args (cdr args)))
- (list-set! env
- (- imax (cdr kw-pair))
- v)
- (lp args))
- (scm-error 'keyword-argument-error
- "eval"
- "Keyword argument has no value"
- '() (list k)))))
- ;; Otherwise unknown keyword.
- (aok
- (lp (if (pair? args) (cdr args) args)))
- (else
- (scm-error 'keyword-argument-error
- "eval" "Unrecognized keyword"
- '() (list k))))))
- (rest?
- ;; Be lenient parsing rest args.
- (lp (cdr args)))
- (else
- (scm-error 'keyword-argument-error
- "eval" "Invalid keyword"
- '() (list (car args))))))
- (else
- ;; Finished parsing keywords. Fill in
- ;; uninitialized kwargs by evalling init
- ;; expressions in their appropriate
- ;; environment.
- (let lp ((i (- imax kw-base))
- (inits inits))
- (if (pair? inits)
- (let ((tail (list-tail env i)))
- (if (eq? (car tail) unbound-arg)
- (set-car! tail
- (eval (car inits)
- (cdr tail))))
- (lp (1- i) (cdr inits)))
- ;; Finally, eval the body.
- (eval body env)))))
- )))))))))))
-
- ;; The "engine". EXP is a memoized expression.
- (define (eval exp env)
- (memoized-expression-case exp
- (('lexical-ref n)
- (list-ref env n))
-
- (('call (f nargs . args))
- (let ((proc (eval f env)))
- (call eval proc nargs args env)))
-
- (('toplevel-ref var-or-sym)
- (variable-ref
- (if (variable? var-or-sym)
- var-or-sym
- (memoize-variable-access! exp
- (capture-env (if (pair? env)
- (cdr (last-pair env))
- env))))))
-
- (('if (test consequent . alternate))
- (if (eval test env)
- (eval consequent env)
- (eval alternate env)))
-
- (('quote x)
- x)
-
- (('let (inits . body))
- (let lp ((inits inits) (new-env (capture-env env)))
- (if (null? inits)
- (eval body new-env)
- (lp (cdr inits)
- (cons (eval (car inits) env) new-env)))))
-
- (('lambda (body docstring nreq . tail))
- (let ((proc
- (if (null? tail)
- (make-fixed-closure eval nreq body (capture-env env))
- (if (null? (cdr tail))
- (make-general-closure (capture-env env) body
- nreq (car tail)
- 0 #f '() #f)
- (apply make-general-closure (capture-env env)
- body nreq tail)))))
- (when docstring
- (set-procedure-property! proc 'documentation docstring))
- proc))
-
- (('begin (first . rest))
- (let lp ((first first) (rest rest))
- (if (null? rest)
- (eval first env)
- (begin
- (eval first env)
- (lp (car rest) (cdr rest))))))
-
- (('lexical-set! (n . x))
- (let ((val (eval x env)))
- (list-set! env n val)))
-
- (('call-with-values (producer . consumer))
- (call-with-values (eval producer env)
- (eval consumer env)))
-
- (('apply (f args))
- (apply (eval f env) (eval args env)))
-
- (('module-ref var-or-spec)
- (variable-ref
- (if (variable? var-or-spec)
- var-or-spec
- (memoize-variable-access! exp #f))))
-
- (('define (name . x))
- (let ((x (eval x env)))
- (if (and (procedure? x) (not (procedure-property x 'name)))
- (set-procedure-property! x 'name name))
- (define! name x)
- (if #f #f)))
-
- (('toplevel-set! (var-or-sym . x))
- (variable-set!
- (if (variable? var-or-sym)
- var-or-sym
- (memoize-variable-access! exp
- (capture-env (if (pair? env)
- (cdr (last-pair env))
- env))))
- (eval x env)))
-
- (('dynwind (in exp . out))
- (dynamic-wind (eval in env)
- (lambda () (eval exp env))
- (eval out env)))
-
- (('with-fluids (fluids vals . exp))
- (let* ((fluids (map (lambda (x) (eval x env)) fluids))
- (vals (map (lambda (x) (eval x env)) vals)))
- (let lp ((fluids fluids) (vals vals))
- (if (null? fluids)
- (eval exp env)
- (with-fluids (((car fluids) (car vals)))
- (lp (cdr fluids) (cdr vals)))))))
-
- (('prompt (tag exp . handler))
- (@prompt (eval tag env)
- (eval exp env)
- (eval handler env)))
-
- (('call/cc proc)
- (call/cc (eval proc env)))
-
- (('module-set! (x . var-or-spec))
- (variable-set!
- (if (variable? var-or-spec)
- var-or-spec
- (memoize-variable-access! exp #f))
- (eval x env)))))
-
- ;; primitive-eval
- (lambda (exp)
- "Evaluate @var{exp} in the current module."
- (eval
- (memoize-expression
- (if (macroexpanded? exp)
- exp
- ((module-transformer (current-module)) exp)))
- '()))))
-;;;; Copyright (C) 1996, 1998, 1999, 2001, 2006 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary:
-
-;; This module is documented in the Guile Reference Manual.
-;; Briefly, these are exported:
-;; procedures: expect-select, expect-regexec
-;; variables: expect-port, expect-timeout, expect-timeout-proc,
-;; expect-eof-proc, expect-char-proc,
-;; expect-strings-compile-flags, expect-strings-exec-flags,
-;; macros: expect, expect-strings
-
-;;; Code:
-
-(define-module (ice-9 expect)
- \:use-module (ice-9 regex)
- \:export-syntax (expect expect-strings)
- \:export (expect-port expect-timeout expect-timeout-proc
- expect-eof-proc expect-char-proc expect-strings-compile-flags
- expect-strings-exec-flags expect-select expect-regexec))
-
-;;; Expect: a macro for selecting actions based on what it reads from a port.
-;;; The idea is from Don Libes' expect based on Tcl.
-;;; This version by Gary Houston incorporating ideas from Aubrey Jaffer.
-
-
-(define expect-port #f)
-(define expect-timeout #f)
-(define expect-timeout-proc #f)
-(define expect-eof-proc #f)
-(define expect-char-proc #f)
-
-;;; expect: each test is a procedure which is applied to the accumulating
-;;; string.
-(defmacro expect clauses
- (let ((s (gensym))
- (c (gensym))
- (port (gensym))
- (timeout (gensym)))
- `(let ((,s "")
- (,port (or expect-port (current-input-port)))
- ;; when timeout occurs, in floating point seconds.
- (,timeout (if expect-timeout
- (let* ((secs-usecs (gettimeofday)))
- (+ (car secs-usecs)
- expect-timeout
- (/ (cdr secs-usecs)
- 1000000))) ; one million.
- #f)))
- (let next-char ()
- (if (and expect-timeout
- (not (expect-select ,port ,timeout)))
- (if expect-timeout-proc
- (expect-timeout-proc ,s)
- #f)
- (let ((,c (read-char ,port)))
- (if expect-char-proc
- (expect-char-proc ,c))
- (if (not (eof-object? ,c))
- (set! ,s (string-append ,s (string ,c))))
- (cond
- ;; this expands to clauses where the car invokes the
- ;; match proc and the cdr is the return value from expect
- ;; if the proc matched.
- ,@(let next-expr ((tests (map car clauses))
- (exprs (map cdr clauses))
- (body '()))
- (cond
- ((null? tests)
- (reverse body))
- (else
- (next-expr
- (cdr tests)
- (cdr exprs)
- (cons
- `((,(car tests) ,s (eof-object? ,c))
- ,@(cond ((null? (car exprs))
- '())
- ((eq? (caar exprs) '=>)
- (if (not (= (length (car exprs))
- 2))
- (scm-error 'misc-error
- "expect"
- "bad recipient: ~S"
- (list (car exprs))
- #f)
- `((apply ,(cadar exprs)
- (,(car tests) ,s ,port)))))
- (else
- (car exprs))))
- body)))))
- ;; if none of the clauses matched the current string.
- (else (cond ((eof-object? ,c)
- (if expect-eof-proc
- (expect-eof-proc ,s)
- #f))
- (else
- (next-char)))))))))))
-
-
-(define expect-strings-compile-flags regexp/newline)
-(define expect-strings-exec-flags regexp/noteol)
-
-;;; the regexec front-end to expect:
-;;; each test must evaluate to a regular expression.
-(defmacro expect-strings clauses
- `(let ,@(let next-test ((tests (map car clauses))
- (exprs (map cdr clauses))
- (defs '())
- (body '()))
- (cond ((null? tests)
- (list (reverse defs) `(expect ,@(reverse body))))
- (else
- (let ((rxname (gensym)))
- (next-test (cdr tests)
- (cdr exprs)
- (cons `(,rxname (make-regexp
- ,(car tests)
- expect-strings-compile-flags))
- defs)
- (cons `((lambda (s eof?)
- (expect-regexec ,rxname s eof?))
- ,@(car exprs))
- body))))))))
-
-;;; simplified select: returns #t if input is waiting or #f if timed out or
-;;; select was interrupted by a signal.
-;;; timeout is an absolute time in floating point seconds.
-(define (expect-select port timeout)
- (let* ((secs-usecs (gettimeofday))
- (relative (- timeout
- (car secs-usecs)
- (/ (cdr secs-usecs)
- 1000000)))) ; one million.
- (and (> relative 0)
- (pair? (car (select (list port) '() '()
- relative))))))
-
-;;; match a string against a regexp, returning a list of strings (required
-;;; by the => syntax) or #f. called once each time a character is added
-;;; to s (eof? will be #f), and once when eof is reached (with eof? #t).
-(define (expect-regexec rx s eof?)
- ;; if expect-strings-exec-flags contains regexp/noteol,
- ;; remove it for the eof test.
- (let* ((flags (if (and eof?
- (logand expect-strings-exec-flags regexp/noteol))
- (logxor expect-strings-exec-flags regexp/noteol)
- expect-strings-exec-flags))
- (match (regexp-exec rx s 0 flags)))
- (if match
- (do ((i (- (match:count match) 1) (- i 1))
- (result '() (cons (match:substring match i) result)))
- ((< i 0) result))
- #f)))
-
-;;; expect.scm ends here
-;;;; "format.scm" Common LISP text output formatter for SLIB
-;;; Copyright (C) 2010, 2011, 2012 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;
-
-;;; This code was orignally in the public domain.
-;;;
-;;; Written 1992-1994 by Dirk Lutzebaeck (lutzeb@cs.tu-berlin.de).
-;;;
-;;; Authors of the version from SLIB (< 1.4) were Ken Dickey and Aubrey
-;;; Jaffer.
-;;;
-;;; Assimilated into Guile May 1999.
-;;;
-;;; Please don't bother the original authors with bug reports, though;
-;;; send them to bug-guile@gnu.org.
-;;;
-
-(define-module (ice-9 format)
- #\autoload (ice-9 pretty-print) (pretty-print truncated-print)
- #\autoload (ice-9 i18n) (%global-locale number->locale-string)
- #\replace (format))
-
-(define format:version "3.0")
-
-(define (format destination format-string . format-args)
- (if (not (string? format-string))
- (error "format: expected a string for format string" format-string))
-
- (let* ((port
- (cond
- ((not destination)
- ;; Use a Unicode-capable output string port.
- (with-fluids ((%default-port-encoding "UTF-8"))
- (open-output-string)))
- ((boolean? destination) (current-output-port)) ; boolean but not false
- ((output-port? destination) destination)
- ((number? destination)
- (issue-deprecation-warning
- "Passing a number to format as the port is deprecated."
- "Pass (current-error-port) instead.")
- (current-error-port))
- (else
- (error "format: bad destination `~a'" destination))))
-
- (output-col (or (port-column port) 0))
-
- (flush-output? #f))
-
- (define format:case-conversion #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 port
-
- (define (format:out-str str)
- (if format:case-conversion
- (display (format:case-conversion str) port)
- (display str port))
- (set! output-col
- (+ output-col (string-length str))))
-
- (define (format:out-char ch)
- (if format:case-conversion
- (display (format:case-conversion (string ch))
- port)
- (write-char ch port))
- (set! output-col
- (if (char=? ch #\newline)
- 0
- (+ output-col 1))))
-
- ;;(define (format:out-substr str i n) ; this allocates a new string
- ;; (display (substring str i n) port)
- ;; (set! output-col (+ output-col n)))
-
- (define (format:out-substr str i n)
- (do ((k i (+ k 1)))
- ((= k n))
- (write-char (string-ref str k) port))
- (set! output-col (+ output-col (- n i))))
-
- ;;(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 port))
- (set! output-col (+ output-col n)))
-
- ;; format's user error handler
-
- (define (format:error . args) ; never returns!
- (let ((port (current-error-port)))
- (set! format:error format:intern-error)
- (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 ~})~% "
- destination
- (substring format-string 0 format:pos)
- (substring format-string format:pos
- (string-length format-string))
- (list-head format-args format:arg-pos)
- (list-tail format-args format:arg-pos))
- (apply format port args)
- (newline port)
- (set! format:error format:error-save)
- (format:abort)))
-
- (define (format:intern-error . args)
- ;;if something goes wrong in format:error
- (display "FORMAT: INTERNAL ERROR IN FORMAT:ERROR!") (newline)
- (display " destination: ") (write destination) (newline)
- (display " format string: ") (write format-string) (newline)
- (display " format args: ") (write format-args) (newline)
- (display " error args: ") (write args) (newline)
- (set! format:error format:error-save)
- (format:abort))
-
- (define format:error-save format:error)
-
- (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))
- ((#\H) ; Localized number
- (let* ((num (next-arg))
- (locale (case modifier
- ((colon) (next-arg))
- (else %global-locale)))
- (argc (length params))
- (width (format:par params argc 0 #f "width"))
- (decimals (format:par params argc 1 #t "decimals"))
- (padchar (integer->char
- (format:par params argc 2 format:space-ch
- "padchar")))
- (str (number->locale-string num decimals
- locale)))
- (format:out-str (if (and width
- (< (string-length str) width))
- (string-pad str width padchar)
- str)))
- (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
- (format:out-fixed modifier (next-arg) params)
- (anychar-dispatch))
- ((#\E) ; Exponential floating-point
- (format:out-expon modifier (next-arg) params)
- (anychar-dispatch))
- ((#\G) ; General floating-point
- (format:out-general modifier (next-arg) params)
- (anychar-dispatch))
- ((#\$) ; Dollars floating-point
- (format:out-dollar modifier (next-arg) params)
- (anychar-dispatch))
- ((#\I) ; Complex numbers
- (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 (object->string 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
- (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! output-col 0)
- (anychar-dispatch))
- ((#\&) ; Fresh line
- (if (one-positive-integer? params)
- (begin
- (if (> (car params) 0)
- (format:out-fill (- (car params)
- (if (>
- output-col
- 0) 0 1))
- #\newline))
- (set! output-col 0))
- (if (> 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) #\tab)
- (format:out-char #\tab))
- (anychar-dispatch))
- ((#\|) ; Page seperator
- (if (one-positive-integer? params)
- (format:out-fill (car params) #\page)
- (format:out-char #\page))
- (set! output-col 0)
- (anychar-dispatch))
- ((#\T) ; Tabulate
- (format:tabulate modifier params)
- (anychar-dispatch))
- ((#\Y) ; Structured print
- (let ((width (if (one-positive-integer? params)
- (car params)
- 79)))
- (case modifier
- ((at)
- (format:out-str
- (call-with-output-string
- (lambda (p)
- (truncated-print (next-arg) p
- #\width width)))))
- ((colon-at)
- (format:out-str
- (call-with-output-string
- (lambda (p)
- (truncated-print (next-arg) p
- #\width
- (max (- width
- output-col)
- 1))))))
- ((colon)
- (format:error "illegal modifier in ~~?"))
- (else
- (pretty-print (next-arg) port
- #\width width)
- (set! 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! 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")))
- (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)
- (and max-iterations
- (>= 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)
- (and max-iterations
- (>= 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)
- (and max-iterations
- (>= 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)
- (and max-iterations
- (>= 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 (memq modifier '(at colon-at))
- (format:error "double `@' modifier"))
- (set! modifier (if (eq? modifier 'colon) 'colon-at 'at))
- (tilde-dispatch))
- ((#\:) ; `:' modifier
- (if (memq modifier '(colon colon-at))
- (format:error "double `:' modifier"))
- (set! modifier (if (eq? modifier 'at) 'colon-at '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 param-value-found (format:error "misplaced '#'"))
- (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
-
- ;; when format:read-proof is true, format:obj->str will wrap
- ;; result strings starting with "#<" in an extra pair of double
- ;; quotes.
-
- (define format:read-proof #f)
-
- ;; format:obj->str returns a R4RS representation as a string of
- ;; an arbitrary scheme object.
-
- (define (format:obj->str obj slashify)
- (let ((res (if slashify
- (object->string obj)
- (call-with-output-string (lambda (p) (display obj p))))))
- (if (and format:read-proof (string-prefix? "#<" res))
- (object->string res)
- res)))
-
- (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 (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 (+ output-col colnum)))
- ((>= c col)
- (- c output-col))))
- padch))
- (else ; absolute tabulation
- (format:out-fill
- (cond
- ((< output-col colnum)
- (- colnum output-col))
- ((= colinc 0)
- 0)
- (else
- (do ((c colnum (+ c colinc)))
- ((>= c output-col)
- (- c 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 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 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 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 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 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 inf and nan.
-
- (define (format:out-inf-nan number width digits edigits overch padch)
- ;; inf and nan are always printed exactly as "+inf.0", "-inf.0" or
- ;; "+nan.0", suitably justified in their field. We insist on
- ;; printing this exact form so that the numbers can be read back in.
- (let* ((str (number->string number))
- (len (string-length str))
- (dot (string-index str #\.))
- (digits (+ (or digits 0)
- (if edigits (+ edigits 2) 0))))
- (if (and width overch (< width len))
- (format:out-fill width (integer->char overch))
- (let* ((leftpad (if width
- (max (- width (max len (+ dot 1 digits))) 0)
- 0))
- (rightpad (if width
- (max (- width leftpad len) 0)
- 0))
- (padch (integer->char (or padch format:space-ch))))
- (format:out-fill leftpad padch)
- (format:out-str str)
- (format:out-fill rightpad padch)))))
-
- ;; 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)))
-
- (cond
- ((and (number? number)
- (or (inf? number) (nan? number)))
- (format:out-inf-nan number width digits #f overch padch))
-
- (digits
- (format:parse-float 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)))
-
- (else
- (format:parse-float 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)))
-
- (cond
- ((and (number? number)
- (or (inf? number) (nan? number)))
- (format:out-inf-nan number width digits edigits overch padch))
-
- (digits ; fixed precision
-
- (let ((digits (if (> scale 0)
- (if (< scale (+ digits 2))
- (+ (- digits scale) 1)
- 0)
- digits)))
- (format:parse-float 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)))))
-
- (else
- (format:parse-float 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)))
- (cond
- ((and (number? number)
- (or (inf? number) (nan? number)))
- ;; FIXME: this isn't right.
- (format:out-inf-nan number width digits edigits overch padch))
- (else
- (format:parse-float 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)))
-
- (cond
- ((and (number? number)
- (or (inf? number) (nan? number)))
- (format:out-inf-nan number width digits #f #f padch))
-
- (else
- (format:parse-float 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 400) ; 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 fixed? scale)
- (let ((num-str (if (string? num)
- num
- (number->string (exact->inexact num)))))
- (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! format:fn-dot (- format:fn-dot left-zeros))
- (set! left-zeros 0))
- (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) #\E))
- (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
-
- (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)))))))))
-
- ;; Aborts the program when a formatting error occures. This is a null
- ;; argument closure to jump to the interpreters toplevel continuation.
-
- (define (format:abort) (error "error in format"))
-
- (let ((arg-pos (format:format-work format-string format-args))
- (arg-len (length format-args)))
- (cond
- ((> 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 flush-output?
- (force-output port))
- (if destination
- #t
- (let ((str (get-output-string port)))
- (close-port port)
- str)))))))
-
-(begin-deprecated
- (set! format
- (let ((format format))
- (case-lambda
- ((destination format-string . args)
- (if (string? destination)
- (begin
- (issue-deprecation-warning
- "Omitting the destination on a call to format is deprecated."
- "Pass #f as the destination, before the format string.")
- (apply format #f destination format-string args))
- (apply format destination format-string args)))
- ((deprecated-format-string-only)
- (issue-deprecation-warning
- "Omitting the destination port on a call to format is deprecated."
- "Pass #f as the destination port, before the format string.")
- (format #f deprecated-format-string-only))))))
-
-
-;; Thanks to Shuji Narazaki
-(module-set! the-root-module 'format format)
-;;;; ftw.scm --- file system tree walk
-
-;;;; Copyright (C) 2002, 2003, 2006, 2011, 2012, 2014, 2016 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Thien-Thi Nguyen <ttn@gnu.org>
-
-;;; Commentary:
-
-;; Two procedures are provided: `ftw' and `nftw'.
-
-;; NOTE: The following description was adapted from the GNU libc info page, w/
-;; significant modifications for a more "Schemey" interface. Most noticible
-;; are the inlining of `struct FTW *' parameters `base' and `level' and the
-;; omission of `descriptors' parameters.
-
-;; * Types
-;;
-;; The X/Open specification defines two procedures to process whole
-;; hierarchies of directories and the contained files. Both procedures
-;; of this `ftw' family take as one of the arguments a callback procedure
-;; which must be of these types.
-;;
-;; - Data Type: __ftw_proc_t
-;; (lambda (filename statinfo flag) ...) => status
-;;
-;; Type for callback procedures given to the `ftw' procedure. The
-;; first parameter is a filename, the second parameter is the
-;; vector value as returned by calling `stat' on FILENAME.
-;;
-;; The last parameter is a symbol giving more information about
-;; FILENAM. It can have one of the following values:
-;;
-;; `regular'
-;; The current item is a normal file or files which do not fit
-;; into one of the following categories. This means
-;; especially special files, sockets etc.
-;;
-;; `directory'
-;; The current item is a directory.
-;;
-;; `invalid-stat'
-;; The `stat' call to fill the object pointed to by the second
-;; parameter failed and so the information is invalid.
-;;
-;; `directory-not-readable'
-;; The item is a directory which cannot be read.
-;;
-;; `symlink'
-;; The item is a symbolic link. Since symbolic links are
-;; normally followed seeing this value in a `ftw' callback
-;; procedure means the referenced file does not exist. The
-;; situation for `nftw' is different.
-;;
-;; - Data Type: __nftw_proc_t
-;; (lambda (filename statinfo flag base level) ...) => status
-;;
-;; The first three arguments have the same as for the
-;; `__ftw_proc_t' type. A difference is that for the third
-;; argument some additional values are defined to allow finer
-;; differentiation:
-;;
-;; `directory-processed'
-;; The current item is a directory and all subdirectories have
-;; already been visited and reported. This flag is returned
-;; instead of `directory' if the `depth' flag is given to
-;; `nftw' (see below).
-;;
-;; `stale-symlink'
-;; The current item is a stale symbolic link. The file it
-;; points to does not exist.
-;;
-;; The last two parameters are described below. They contain
-;; information to help interpret FILENAME and give some information
-;; about current state of the traversal of the directory hierarchy.
-;;
-;; `base'
-;; The value specifies which part of the filename argument
-;; given in the first parameter to the callback procedure is
-;; the name of the file. The rest of the string is the path
-;; to locate the file. This information is especially
-;; important if the `chdir' flag for `nftw' was set since then
-;; the current directory is the one the current item is found
-;; in.
-;;
-;; `level'
-;; While processing the directory the procedures tracks how
-;; many directories have been examined to find the current
-;; item. This nesting level is 0 for the item given starting
-;; item (file or directory) and is incremented by one for each
-;; entered directory.
-;;
-;; * Procedure: (ftw filename proc . options)
-;; Do a file system tree walk starting at FILENAME using PROC.
-;;
-;; The `ftw' procedure calls the callback procedure given in the
-;; parameter PROC for every item which is found in the directory
-;; specified by FILENAME and all directories below. The procedure
-;; follows symbolic links if necessary but does not process an item
-;; twice. If FILENAME names no directory this item is the only
-;; object reported by calling the callback procedure.
-;;
-;; The filename given to the callback procedure is constructed by
-;; taking the FILENAME parameter and appending the names of all
-;; passed directories and then the local file name. So the
-;; callback procedure can use this parameter to access the file.
-;; Before the callback procedure is called `ftw' calls `stat' for
-;; this file and passes the information up to the callback
-;; procedure. If this `stat' call was not successful the failure is
-;; indicated by setting the flag argument of the callback procedure
-;; to `invalid-stat'. Otherwise the flag is set according to the
-;; description given in the description of `__ftw_proc_t' above.
-;;
-;; The callback procedure is expected to return non-#f to indicate
-;; that no error occurred and the processing should be continued.
-;; If an error occurred in the callback procedure or the call to
-;; `ftw' shall return immediately the callback procedure can return
-;; #f. This is the only correct way to stop the procedure. The
-;; program must not use `throw' or similar techniques to continue
-;; the program in another place. [Can we relax this? --ttn]
-;;
-;; The return value of the `ftw' procedure is #t if all callback
-;; procedure calls returned #t and all actions performed by the
-;; `ftw' succeeded. If some procedure call failed (other than
-;; calling `stat' on an item) the procedure returns #f. If a
-;; callback procedure returns a value other than #t this value is
-;; returned as the return value of `ftw'.
-;;
-;; * Procedure: (nftw filename proc . control-flags)
-;; Do a new-style file system tree walk starting at FILENAME using PROC.
-;; Various optional CONTROL-FLAGS alter the default behavior.
-;;
-;; The `nftw' procedures works like the `ftw' procedures. It calls
-;; the callback procedure PROC for all items it finds in the
-;; directory FILENAME and below.
-;;
-;; The differences are that for one the callback procedure is of a
-;; different type. It takes also `base' and `level' parameters as
-;; described above.
-;;
-;; The second difference is that `nftw' takes additional optional
-;; arguments which are zero or more of the following symbols:
-;;
-;; physical'
-;; While traversing the directory symbolic links are not
-;; followed. I.e., if this flag is given symbolic links are
-;; reported using the `symlink' value for the type parameter
-;; to the callback procedure. Please note that if this flag is
-;; used the appearance of `symlink' in a callback procedure
-;; does not mean the referenced file does not exist. To
-;; indicate this the extra value `stale-symlink' exists.
-;;
-;; mount'
-;; The callback procedure is only called for items which are on
-;; the same mounted file system as the directory given as the
-;; FILENAME parameter to `nftw'.
-;;
-;; chdir'
-;; If this flag is given the current working directory is
-;; changed to the directory containing the reported object
-;; before the callback procedure is called.
-;;
-;; depth'
-;; If this option is given the procedure visits first all files
-;; and subdirectories before the callback procedure is called
-;; for the directory itself (depth-first processing). This
-;; also means the type flag given to the callback procedure is
-;; `directory-processed' and not `directory'.
-;;
-;; The return value is computed in the same way as for `ftw'.
-;; `nftw' returns #t if no failure occurred in `nftw' and all
-;; callback procedure call return values are also #t. For internal
-;; errors such as memory problems the error `ftw-error' is thrown.
-;; If the return value of a callback invocation is not #t this
-;; very same value is returned.
-
-;;; Code:
-
-(define-module (ice-9 ftw)
- #\use-module (ice-9 match)
- #\use-module (ice-9 vlist)
- #\use-module (srfi srfi-1)
- #\autoload (ice-9 i18n) (string-locale<?)
- #\export (ftw nftw
- file-system-fold
- file-system-tree
- scandir))
-
-(define (directory-files dir)
- (let ((dir-stream (opendir dir)))
- (let loop ((new (readdir dir-stream))
- (acc '()))
- (if (eof-object? new)
- (begin
- (closedir dir-stream)
- acc)
- (loop (readdir dir-stream)
- (if (or (string=? "." new) ;;; ignore
- (string=? ".." new)) ;;; ignore
- acc
- (cons new acc)))))))
-
-(define (pathify . nodes)
- (let loop ((nodes nodes)
- (result ""))
- (if (null? nodes)
- (or (and (string=? "" result) "")
- (substring result 1 (string-length result)))
- (loop (cdr nodes) (string-append result "/" (car nodes))))))
-
-(define (abs? filename)
- (char=? #\/ (string-ref filename 0)))
-
-;; `visited?-proc' returns a test procedure VISITED? which when called as
-;; (VISITED? stat-obj) returns #f the first time a distinct file is seen,
-;; then #t on any subsequent sighting of it.
-;;
-;; stat:dev and stat:ino together uniquely identify a file (see "Attribute
-;; Meanings" in the glibc manual). Often there'll be just one dev, and
-;; usually there's just a handful mounted, so the strategy here is a small
-;; hash table indexed by dev, containing hash tables indexed by ino.
-;;
-;; It'd be possible to make a pair (dev . ino) and use that as the key to a
-;; single hash table. It'd use an extra pair for every file visited, but
-;; might be a little faster if it meant less scheme code.
-;;
-(define (visited?-proc size)
- (let ((dev-hash (make-hash-table 7)))
- (lambda (s)
- (and s
- (let ((ino-hash (hashv-ref dev-hash (stat:dev s)))
- (ino (stat:ino s)))
- (or ino-hash
- (begin
- (set! ino-hash (make-hash-table size))
- (hashv-set! dev-hash (stat:dev s) ino-hash)))
- (or (hashv-ref ino-hash ino)
- (begin
- (hashv-set! ino-hash ino #t)
- #f)))))))
-
-(define (stat-dir-readable?-proc uid gid)
- (let ((uid (getuid))
- (gid (getgid)))
- (lambda (s)
- (let* ((perms (stat:perms s))
- (perms-bit-set? (lambda (mask)
- (not (= 0 (logand mask perms))))))
- (or (zero? uid)
- (and (= uid (stat:uid s))
- (perms-bit-set? #o400))
- (and (= gid (stat:gid s))
- (perms-bit-set? #o040))
- (perms-bit-set? #o004))))))
-
-(define (stat&flag-proc dir-readable? . control-flags)
- (let* ((directory-flag (if (memq 'depth control-flags)
- 'directory-processed
- 'directory))
- (stale-symlink-flag (if (memq 'nftw-style control-flags)
- 'stale-symlink
- 'symlink))
- (physical? (memq 'physical control-flags))
- (easy-flag (lambda (s)
- (let ((type (stat:type s)))
- (if (eq? 'directory type)
- (if (dir-readable? s)
- directory-flag
- 'directory-not-readable)
- 'regular)))))
- (lambda (name)
- (let ((s (false-if-exception (lstat name))))
- (cond ((not s)
- (values s 'invalid-stat))
- ((eq? 'symlink (stat:type s))
- (let ((s-follow (false-if-exception (stat name))))
- (cond ((not s-follow)
- (values s stale-symlink-flag))
- ((and s-follow physical?)
- (values s 'symlink))
- ((and s-follow (not physical?))
- (values s-follow (easy-flag s-follow))))))
- (else (values s (easy-flag s))))))))
-
-(define (clean name)
- (let ((last-char-index (1- (string-length name))))
- (if (char=? #\/ (string-ref name last-char-index))
- (substring name 0 last-char-index)
- name)))
-
-(define (ftw filename proc . options)
- (let* ((visited? (visited?-proc (cond ((memq 'hash-size options) => cadr)
- (else 211))))
- (stat&flag (stat&flag-proc
- (stat-dir-readable?-proc (getuid) (getgid)))))
- (letrec ((go (lambda (fullname)
- (call-with-values (lambda () (stat&flag fullname))
- (lambda (s flag)
- (or (visited? s)
- (let ((ret (proc fullname s flag))) ; callback
- (or (eq? #t ret)
- (throw 'ftw-early-exit ret))
- (and (eq? 'directory flag)
- (for-each
- (lambda (child)
- (go (pathify fullname child)))
- (directory-files fullname)))
- #t)))))))
- (catch 'ftw-early-exit
- (lambda () (go (clean filename)))
- (lambda (key val) val)))))
-
-(define (nftw filename proc . control-flags)
- (let* ((od (getcwd)) ; orig dir
- (odev (let ((s (false-if-exception (lstat filename))))
- (if s (stat:dev s) -1)))
- (same-dev? (if (memq 'mount control-flags)
- (lambda (s) (= (stat:dev s) odev))
- (lambda (s) #t)))
- (base-sub (lambda (name base) (substring name 0 base)))
- (maybe-cd (if (memq 'chdir control-flags)
- (if (abs? filename)
- (lambda (fullname base)
- (or (= 0 base)
- (chdir (base-sub fullname base))))
- (lambda (fullname base)
- (chdir
- (pathify od (base-sub fullname base)))))
- (lambda (fullname base) #t)))
- (maybe-cd-back (if (memq 'chdir control-flags)
- (lambda () (chdir od))
- (lambda () #t)))
- (depth-first? (memq 'depth control-flags))
- (visited? (visited?-proc
- (cond ((memq 'hash-size control-flags) => cadr)
- (else 211))))
- (has-kids? (if depth-first?
- (lambda (flag) (eq? flag 'directory-processed))
- (lambda (flag) (eq? flag 'directory))))
- (stat&flag (apply stat&flag-proc
- (stat-dir-readable?-proc (getuid) (getgid))
- (cons 'nftw-style control-flags))))
- (letrec ((go (lambda (fullname base level)
- (call-with-values (lambda () (stat&flag fullname))
- (lambda (s flag)
- (letrec ((self (lambda ()
- (maybe-cd fullname base)
- ;; the callback
- (let ((ret (proc fullname s flag
- base level)))
- (maybe-cd-back)
- (or (eq? #t ret)
- (throw 'nftw-early-exit ret)))))
- (kids (lambda ()
- (and (has-kids? flag)
- (for-each
- (lambda (child)
- (go (pathify fullname child)
- (1+ (string-length
- fullname))
- (1+ level)))
- (directory-files fullname))))))
- (or (visited? s)
- (not (same-dev? s))
- (if depth-first?
- (begin (kids) (self))
- (begin (self) (kids)))))))
- #t)))
- (let ((ret (catch 'nftw-early-exit
- (lambda () (go (clean filename) 0 0))
- (lambda (key val) val))))
- (chdir od)
- ret))))
-
-
-;;;
-;;; `file-system-fold' & co.
-;;;
-
-(define-syntax-rule (errno-if-exception expr)
- (catch 'system-error
- (lambda ()
- expr)
- (lambda args
- (system-error-errno args))))
-
-(define* (file-system-fold enter? leaf down up skip error init file-name
- #\optional (stat lstat))
- "Traverse the directory at FILE-NAME, recursively. Enter
-sub-directories only when (ENTER? PATH STAT RESULT) returns true. When
-a sub-directory is entered, call (DOWN PATH STAT RESULT), where PATH is
-the path of the sub-directory and STAT the result of (stat PATH); when
-it is left, call (UP PATH STAT RESULT). For each file in a directory,
-call (LEAF PATH STAT RESULT). When ENTER? returns false, call (SKIP
-PATH STAT RESULT). When an `opendir' or STAT call raises an exception,
-call (ERROR PATH STAT ERRNO RESULT), with ERRNO being the operating
-system error number that was raised.
-
-Return the result of these successive applications.
-When FILE-NAME names a flat file, (LEAF PATH STAT INIT) is returned.
-The optional STAT parameter defaults to `lstat'."
-
- (define (mark v s)
- (vhash-cons (cons (stat:dev s) (stat:ino s)) #t v))
-
- (define (visited? v s)
- (vhash-assoc (cons (stat:dev s) (stat:ino s)) v))
-
- (let loop ((name file-name)
- (path "")
- (dir-stat (errno-if-exception (stat file-name)))
- (result init)
- (visited vlist-null))
-
- (define full-name
- (if (string=? path "")
- name
- (string-append path "/" name)))
-
- (cond
- ((integer? dir-stat)
- ;; FILE-NAME is not readable.
- (error full-name #f dir-stat result))
- ((visited? visited dir-stat)
- (values result visited))
- ((eq? 'directory (stat:type dir-stat)) ; true except perhaps the 1st time
- (if (enter? full-name dir-stat result)
- (let ((dir (errno-if-exception (opendir full-name)))
- (visited (mark visited dir-stat)))
- (cond
- ((directory-stream? dir)
- (let liip ((entry (readdir dir))
- (result (down full-name dir-stat result))
- (subdirs '()))
- (cond ((eof-object? entry)
- (begin
- (closedir dir)
- (let ((r+v
- (fold (lambda (subdir result+visited)
- (call-with-values
- (lambda ()
- (loop (car subdir)
- full-name
- (cdr subdir)
- (car result+visited)
- (cdr result+visited)))
- cons))
- (cons result visited)
- subdirs)))
- (values (up full-name dir-stat (car r+v))
- (cdr r+v)))))
- ((or (string=? entry ".")
- (string=? entry ".."))
- (liip (readdir dir)
- result
- subdirs))
- (else
- (let* ((child (string-append full-name "/" entry))
- (st (errno-if-exception (stat child))))
- (if (integer? st) ; CHILD is a dangling symlink?
- (liip (readdir dir)
- (error child #f st result)
- subdirs)
- (if (eq? (stat:type st) 'directory)
- (liip (readdir dir)
- result
- (alist-cons entry st subdirs))
- (liip (readdir dir)
- (leaf child st result)
- subdirs))))))))
- (else
- ;; Directory FULL-NAME not readable, but it is stat'able.
- (values (error full-name dir-stat dir result)
- visited))))
- (values (skip full-name dir-stat result)
- (mark visited dir-stat))))
- (else
- ;; Caller passed a FILE-NAME that names a flat file, not a directory.
- (leaf full-name dir-stat result)))))
-
-(define* (file-system-tree file-name
- #\optional (enter? (lambda (n s) #t))
- (stat lstat))
- "Return a tree of the form (FILE-NAME STAT CHILDREN ...) where STAT is
-the result of (STAT FILE-NAME) and CHILDREN are similar structures for
-each file contained in FILE-NAME when it designates a directory. The
-optional ENTER? predicate is invoked as (ENTER? NAME STAT) and should
-return true to allow recursion into directory NAME; the default value is
-a procedure that always returns #t. When a directory does not match
-ENTER?, it nonetheless appears in the resulting tree, only with zero
-children. The optional STAT parameter defaults to `lstat'. Return #f
-when FILE-NAME is not readable."
- (define (enter?* name stat result)
- (enter? name stat))
- (define (leaf name stat result)
- (match result
- (((siblings ...) rest ...)
- (cons (alist-cons (basename name) (cons stat '()) siblings)
- rest))))
- (define (down name stat result)
- (cons '() result))
- (define (up name stat result)
- (match result
- (((children ...) (siblings ...) rest ...)
- (cons (alist-cons (basename name) (cons stat children)
- siblings)
- rest))))
- (define skip ; keep an entry for skipped directories
- leaf)
- (define (error name stat errno result)
- (if (string=? name file-name)
- result
- (leaf name stat result)))
-
- (match (file-system-fold enter?* leaf down up skip error '(())
- file-name stat)
- (((tree)) tree)
- ((()) #f))) ; FILE-NAME is unreadable
-
-(define* (scandir name #\optional (select? (const #t))
- (entry<? string-locale<?))
- "Return the list of the names of files contained in directory NAME
-that match predicate SELECT? (by default, all files.) The returned list
-of file names is sorted according to ENTRY<?, which defaults to
-`string-locale<?'. Return #f when NAME is unreadable or is not a
-directory."
-
- ;; This procedure is implemented in terms of 'readdir' instead of
- ;; 'file-system-fold' to avoid the extra 'stat' call that the latter
- ;; makes for each entry.
-
- (define (opendir* directory)
- (catch 'system-error
- (lambda ()
- (opendir directory))
- (const #f)))
-
- (and=> (opendir* name)
- (lambda (stream)
- (let loop ((entry (readdir stream))
- (files '()))
- (if (eof-object? entry)
- (begin
- (closedir stream)
- (sort files entry<?))
- (loop (readdir stream)
- (if (select? entry)
- (cons entry files)
- files)))))))
-
-;;; ftw.scm ends here
-;;; -*- mode: scheme; coding: utf-8; -*-
-;;;
-;;; Copyright (C) 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (ice-9 futures)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-9)
- #\use-module (srfi srfi-9 gnu)
- #\use-module (srfi srfi-11)
- #\use-module (ice-9 q)
- #\use-module (ice-9 match)
- #\use-module (ice-9 control)
- #\export (future make-future future? touch))
-
-;;; Author: Ludovic Courtès <ludo@gnu.org>
-;;;
-;;; Commentary:
-;;;
-;;; This module provides an implementation of futures, a mechanism for
-;;; fine-grain parallelism. Futures were first described by Henry Baker
-;;; in ``The Incremental Garbage Collection of Processes'', 1977, and
-;;; then implemented in MultiLisp (an implicit variant thereof, i.e.,
-;;; without `touch'.)
-;;;
-;;; This modules uses a fixed thread pool, normally one per CPU core.
-;;; Futures are off-loaded to these threads, when they are idle.
-;;;
-;;; Code:
-
-
-;;;
-;;; Futures.
-;;;
-
-(define-record-type <future>
- (%make-future thunk state mutex completion)
- future?
- (thunk future-thunk set-future-thunk!)
- (state future-state set-future-state!) ; done | started | queued
- (result future-result set-future-result!)
- (mutex future-mutex)
- (completion future-completion)) ; completion cond. var.
-
-(set-record-type-printer!
- <future>
- (lambda (future port)
- (simple-format port "#<future ~a ~a ~s>"
- (number->string (object-address future) 16)
- (future-state future)
- (future-thunk future))))
-
-(define (make-future thunk)
- "Return a new future for THUNK. Execution may start at any point
-concurrently, or it can start at the time when the returned future is
-touched."
- (create-workers!)
- (let ((future (%make-future thunk 'queued
- (make-mutex) (make-condition-variable))))
- (register-future! future)
- future))
-
-
-;;;
-;;; Future queues.
-;;;
-
-;; Global queue of pending futures.
-;; TODO: Use per-worker queues to reduce contention.
-(define %futures (make-q))
-
-;; Lock for %FUTURES and %FUTURES-WAITING.
-(define %futures-mutex (make-mutex))
-(define %futures-available (make-condition-variable))
-
-;; A mapping of nested futures to futures waiting for them to complete.
-(define %futures-waiting '())
-
-;; Nesting level of futures. Incremented each time a future is touched
-;; from within a future.
-(define %nesting-level (make-parameter 0))
-
-;; Maximum nesting level. The point is to avoid stack overflows when
-;; nested futures are executed on the same stack. See
-;; <http://bugs.gnu.org/13188>.
-(define %max-nesting-level 200)
-
-(define-syntax-rule (with-mutex m e0 e1 ...)
- ;; Copied from (ice-9 threads) to avoid circular dependency.
- (let ((x m))
- (dynamic-wind
- (lambda () (lock-mutex x))
- (lambda () (begin e0 e1 ...))
- (lambda () (unlock-mutex x)))))
-
-(define %future-prompt
- ;; The prompt futures abort to when they want to wait for another
- ;; future.
- (make-prompt-tag))
-
-
-(define (register-future! future)
- ;; Register FUTURE as being processable.
- (lock-mutex %futures-mutex)
- (enq! %futures future)
- (signal-condition-variable %futures-available)
- (unlock-mutex %futures-mutex))
-
-(define (process-future! future)
- "Process FUTURE. When FUTURE completes, return #t and update its
-result; otherwise, when FUTURE touches a nested future that has not
-completed yet, then suspend it and return #f. Suspending a future
-consists in capturing its continuation, marking it as `queued', and
-adding it to the waiter queue."
- (let/ec return
- (let* ((suspend
- (lambda (cont future-to-wait)
- ;; FUTURE wishes to wait for the completion of FUTURE-TO-WAIT.
- ;; At this point, FUTURE is unlocked and in `started' state,
- ;; and FUTURE-TO-WAIT is unlocked.
- (with-mutex %futures-mutex
- (with-mutex (future-mutex future)
- (set-future-thunk! future cont)
- (set-future-state! future 'queued))
-
- (with-mutex (future-mutex future-to-wait)
- ;; If FUTURE-TO-WAIT completed in the meantime, then
- ;; reschedule FUTURE directly; otherwise, add it to the
- ;; waiter queue.
- (if (eq? 'done (future-state future-to-wait))
- (begin
- (enq! %futures future)
- (signal-condition-variable %futures-available))
- (set! %futures-waiting
- (alist-cons future-to-wait future
- %futures-waiting))))
-
- (return #f))))
- (thunk (lambda ()
- (call-with-prompt %future-prompt
- (lambda ()
- (parameterize ((%nesting-level
- (1+ (%nesting-level))))
- ((future-thunk future))))
- suspend))))
- (set-future-result! future
- (catch #t
- (lambda ()
- (call-with-values thunk
- (lambda results
- (lambda ()
- (apply values results)))))
- (lambda args
- (lambda ()
- (apply throw args)))))
- #t)))
-
-(define (process-one-future)
- "Attempt to pick one future from the queue and process it."
- ;; %FUTURES-MUTEX must be locked on entry, and is locked on exit.
- (or (q-empty? %futures)
- (let ((future (deq! %futures)))
- (lock-mutex (future-mutex future))
- (case (future-state future)
- ((done started)
- ;; Nothing to do.
- (unlock-mutex (future-mutex future)))
- (else
- ;; Do the actual work.
-
- ;; We want to release %FUTURES-MUTEX so that other workers can
- ;; progress. However, to avoid deadlocks, we have to unlock
- ;; FUTURE as well, to preserve lock ordering.
- (unlock-mutex (future-mutex future))
- (unlock-mutex %futures-mutex)
-
- (lock-mutex (future-mutex future))
- (if (eq? (future-state future) 'queued) ; lost the race?
- (begin ; no, so let's process it
- (set-future-state! future 'started)
- (unlock-mutex (future-mutex future))
-
- (let ((done? (process-future! future)))
- (when done?
- (with-mutex %futures-mutex
- (with-mutex (future-mutex future)
- (set-future-state! future 'done)
- (notify-completion future))))))
- (unlock-mutex (future-mutex future))) ; yes
-
- (lock-mutex %futures-mutex))))))
-
-(define (process-futures)
- "Continuously process futures from the queue."
- (lock-mutex %futures-mutex)
- (let loop ()
- (when (q-empty? %futures)
- (wait-condition-variable %futures-available
- %futures-mutex))
-
- (process-one-future)
- (loop)))
-
-(define (notify-completion future)
- "Notify futures and callers waiting that FUTURE completed."
- ;; FUTURE and %FUTURES-MUTEX are locked.
- (broadcast-condition-variable (future-completion future))
- (let-values (((waiting remaining)
- (partition (match-lambda ; TODO: optimize
- ((waitee . _)
- (eq? waitee future)))
- %futures-waiting)))
- (set! %futures-waiting remaining)
- (for-each (match-lambda
- ((_ . waiter)
- (enq! %futures waiter)))
- waiting)))
-
-(define (touch future)
- "Return the result of FUTURE, computing it if not already done."
- (define (work)
- ;; Do some work while waiting for FUTURE to complete.
- (lock-mutex %futures-mutex)
- (if (q-empty? %futures)
- (begin
- (unlock-mutex %futures-mutex)
- (with-mutex (future-mutex future)
- (unless (eq? 'done (future-state future))
- (wait-condition-variable (future-completion future)
- (future-mutex future)))))
- (begin
- (process-one-future)
- (unlock-mutex %futures-mutex))))
-
- (let loop ()
- (lock-mutex (future-mutex future))
- (case (future-state future)
- ((done)
- (unlock-mutex (future-mutex future)))
- ((started)
- (unlock-mutex (future-mutex future))
- (if (> (%nesting-level) 0)
- (abort-to-prompt %future-prompt future)
- (begin
- (work)
- (loop))))
- (else ; queued
- (unlock-mutex (future-mutex future))
- (if (> (%nesting-level) %max-nesting-level)
- (abort-to-prompt %future-prompt future)
- (work))
- (loop))))
- ((future-result future)))
-
-
-;;;
-;;; Workers.
-;;;
-
-(define %worker-count
- (if (provided? 'threads)
- (- (current-processor-count) 1)
- 0))
-
-;; A dock of workers that stay here forever.
-
-;; TODO
-;; 1. Allow the pool to be shrunk, as in libgomp (though that we'd
-;; need semaphores, which aren't yet in libguile!).
-;; 2. Provide a `worker-count' fluid.
-(define %workers '())
-
-(define (%create-workers!)
- (with-mutex
- %futures-mutex
- ;; Setting 'create-workers!' to a no-op is an optimization, but it is
- ;; still possible for '%create-workers!' to be called more than once
- ;; from different threads. Therefore, to avoid creating %workers more
- ;; than once (and thus creating too many threads), we check to make
- ;; sure %workers is empty within the critical section.
- (when (null? %workers)
- (set! %workers
- (unfold (lambda (i) (>= i %worker-count))
- (lambda (i) (call-with-new-thread process-futures))
- 1+
- 0))
- (set! create-workers! (lambda () #t)))))
-
-(define create-workers!
- (lambda () (%create-workers!)))
-
-
-;;;
-;;; Syntax.
-;;;
-
-(define-syntax-rule (future body)
- "Return a new future for BODY."
- (make-future (lambda () body)))
-
-;;; Local Variables:
-;;; eval: (put 'with-mutex 'scheme-indent-function 1)
-;;; End:
-;;; gap-buffer.scm --- String buffer that supports point
-
-;;; Copyright (C) 2002, 2003, 2006 Free Software Foundation, Inc.
-;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;
-
-;;; Author: Thien-Thi Nguyen <ttn@gnu.org>
-
-;;; Commentary:
-
-;; A gap buffer is a structure that models a string but allows relatively
-;; efficient insertion of text somewhere in the middle. The insertion
-;; location is called `point' with minimum value 1, and a maximum value of the
-;; length of the string (which is not fixed).
-;;
-;; Specifically, we allocate a continuous buffer of characters that is
-;; composed of the BEFORE, the GAP and the AFTER (reading L->R), like so:
-;;
-;; +--- POINT
-;; v
-;; +--------------------+--------------------+--------------------+
-;; | BEFORE | GAP | AFTER |
-;; +--------------------+--------------------+--------------------+
-;;
-;; <----- bef-sz ----->|<----- gap-sz ----->|<----- aft-sz ----->
-;;
-;; <-------------------| usr-sz |------------------->
-;;
-;; <-------------------------- all-sz -------------------------->
-;;
-;; This diagram also shows how the different sizes are computed, and the
-;; location of POINT. Note that the user-visible buffer size `usr-sz' does
-;; NOT include the GAP, while the allocation `all-sz' DOES.
-;;
-;; The consequence of this arrangement is that "moving point" is simply a
-;; matter of kicking characters across the GAP, while insertion can be viewed
-;; as filling up the gap, increasing `bef-sz' and decreasing `gap-sz'. When
-;; `gap-sz' falls below some threshold, we reallocate with a larger `all-sz'.
-;;
-;; In the implementation, we actually keep track of the AFTER start offset
-;; `aft-ofs' since it is used more often than `gap-sz'. In fact, most of the
-;; variables in the diagram are for conceptualization only.
-;;
-;; A gap buffer port is a soft port (see Guile manual) that wraps a gap
-;; buffer. Character and string writes, as well as character reads, are
-;; supported. Flushing and closing are not supported.
-;;
-;; These procedures are exported:
-;; (gb? OBJ)
-;; (make-gap-buffer . INIT)
-;; (gb-point GB)
-;; (gb-point-min GB)
-;; (gb-point-max GB)
-;; (gb-insert-string! GB STRING)
-;; (gb-insert-char! GB CHAR)
-;; (gb-delete-char! GB COUNT)
-;; (gb-goto-char GB LOCATION)
-;; (gb->string GB)
-;; (gb-filter! GB STRING-PROC)
-;; (gb->lines GB)
-;; (gb-filter-lines! GB LINES-PROC)
-;; (make-gap-buffer-port GB)
-;;
-;; INIT is an optional port or a string. COUNT and LOCATION are integers.
-;; STRING-PROC is a procedure that takes and returns a string. LINES-PROC is
-;; a procedure that takes and returns a list of strings, each representing a
-;; line of text (newlines are stripped and added back automatically).
-;;
-;; (The term and concept of "gap buffer" are borrowed from Emacs. We will
-;; gladly return them when libemacs.so is available. ;-)
-;;
-;; Notes:
-;; - overrun errors are suppressed silently
-
-;;; Code:
-
-(define-module (ice-9 gap-buffer)
- \:autoload (srfi srfi-13) (string-join)
- \:export (gb?
- make-gap-buffer
- gb-point
- gb-point-min
- gb-point-max
- gb-insert-string!
- gb-insert-char!
- gb-delete-char!
- gb-erase!
- gb-goto-char
- gb->string
- gb-filter!
- gb->lines
- gb-filter-lines!
- make-gap-buffer-port))
-
-(define gap-buffer
- (make-record-type 'gap-buffer
- '(s ; the buffer, a string
- all-sz ; total allocation
- gap-ofs ; GAP starts, aka (1- point)
- aft-ofs ; AFTER starts
- )))
-
-(define gb? (record-predicate gap-buffer))
-
-(define s\: (record-accessor gap-buffer 's))
-(define all-sz\: (record-accessor gap-buffer 'all-sz))
-(define gap-ofs\: (record-accessor gap-buffer 'gap-ofs))
-(define aft-ofs\: (record-accessor gap-buffer 'aft-ofs))
-
-(define s! (record-modifier gap-buffer 's))
-(define all-sz! (record-modifier gap-buffer 'all-sz))
-(define gap-ofs! (record-modifier gap-buffer 'gap-ofs))
-(define aft-ofs! (record-modifier gap-buffer 'aft-ofs))
-
-;; todo: expose
-(define default-initial-allocation 128)
-(define default-chunk-size 128)
-(define default-realloc-threshold 32)
-
-(define (round-up n)
- (* default-chunk-size (+ 1 (quotient n default-chunk-size))))
-
-(define new (record-constructor gap-buffer '()))
-
-(define (realloc gb inc)
- (let* ((old-s (s\: gb))
- (all-sz (all-sz\: gb))
- (new-sz (+ all-sz inc))
- (gap-ofs (gap-ofs\: gb))
- (aft-ofs (aft-ofs\: gb))
- (new-s (make-string new-sz))
- (new-aft-ofs (+ aft-ofs inc)))
- (substring-move! old-s 0 gap-ofs new-s 0)
- (substring-move! old-s aft-ofs all-sz new-s new-aft-ofs)
- (s! gb new-s)
- (all-sz! gb new-sz)
- (aft-ofs! gb new-aft-ofs)))
-
-(define (make-gap-buffer . init) ; port/string
- (let ((gb (new)))
- (cond ((null? init)
- (s! gb (make-string default-initial-allocation))
- (all-sz! gb default-initial-allocation)
- (gap-ofs! gb 0)
- (aft-ofs! gb default-initial-allocation))
- (else (let ((jam! (lambda (string len)
- (let ((alloc (round-up len)))
- (s! gb (make-string alloc))
- (all-sz! gb alloc)
- (substring-move! string 0 len (s\: gb) 0)
- (gap-ofs! gb len)
- (aft-ofs! gb alloc))))
- (v (car init)))
- (cond ((port? v)
- (let ((next (lambda () (read-char v))))
- (let loop ((c (next)) (acc '()) (len 0))
- (if (eof-object? c)
- (jam! (list->string (reverse acc)) len)
- (loop (next) (cons c acc) (1+ len))))))
- ((string? v)
- (jam! v (string-length v)))
- (else (error "bad init type"))))))
- gb))
-
-(define (gb-point gb)
- (1+ (gap-ofs\: gb)))
-
-(define (gb-point-min gb) 1) ; no narrowing (for now)
-
-(define (gb-point-max gb)
- (1+ (- (all-sz\: gb) (- (aft-ofs\: gb) (gap-ofs\: gb)))))
-
-(define (insert-prep gb len)
- (let* ((gap-ofs (gap-ofs\: gb))
- (aft-ofs (aft-ofs\: gb))
- (slack (- (- aft-ofs gap-ofs) len)))
- (and (< slack default-realloc-threshold)
- (realloc gb (round-up (- slack))))
- gap-ofs))
-
-(define (gb-insert-string! gb string)
- (let* ((len (string-length string))
- (gap-ofs (insert-prep gb len)))
- (substring-move! string 0 len (s\: gb) gap-ofs)
- (gap-ofs! gb (+ gap-ofs len))))
-
-(define (gb-insert-char! gb char)
- (let ((gap-ofs (insert-prep gb 1)))
- (string-set! (s\: gb) gap-ofs char)
- (gap-ofs! gb (+ gap-ofs 1))))
-
-(define (gb-delete-char! gb count)
- (cond ((< count 0) ; backwards
- (gap-ofs! gb (max 0 (+ (gap-ofs\: gb) count))))
- ((> count 0) ; forwards
- (aft-ofs! gb (min (all-sz\: gb) (+ (aft-ofs\: gb) count))))
- ((= count 0) ; do nothing
- #t)))
-
-(define (gb-erase! gb)
- (gap-ofs! gb 0)
- (aft-ofs! gb (all-sz\: gb)))
-
-(define (point++n! gb n s gap-ofs aft-ofs) ; n>0; warning: reckless
- (substring-move! s aft-ofs (+ aft-ofs n) s gap-ofs)
- (gap-ofs! gb (+ gap-ofs n))
- (aft-ofs! gb (+ aft-ofs n)))
-
-(define (point+-n! gb n s gap-ofs aft-ofs) ; n<0; warning: reckless
- (substring-move! s (+ gap-ofs n) gap-ofs s (+ aft-ofs n))
- (gap-ofs! gb (+ gap-ofs n))
- (aft-ofs! gb (+ aft-ofs n)))
-
-(define (gb-goto-char gb new-point)
- (let ((pmax (gb-point-max gb)))
- (or (and (< new-point 1) (gb-goto-char gb 1))
- (and (> new-point pmax) (gb-goto-char gb pmax))
- (let ((delta (- new-point (gb-point gb))))
- (or (= delta 0)
- ((if (< delta 0)
- point+-n!
- point++n!)
- gb delta (s\: gb) (gap-ofs\: gb) (aft-ofs\: gb))))))
- new-point)
-
-(define (gb->string gb)
- (let ((s (s\: gb)))
- (string-append (substring s 0 (gap-ofs\: gb))
- (substring s (aft-ofs\: gb)))))
-
-(define (gb-filter! gb string-proc)
- (let ((new (string-proc (gb->string gb))))
- (gb-erase! gb)
- (gb-insert-string! gb new)))
-
-(define (gb->lines gb)
- (let ((str (gb->string gb)))
- (let loop ((start 0) (acc '()))
- (cond ((string-index str #\newline start)
- => (lambda (w)
- (loop (1+ w) (cons (substring str start w) acc))))
- (else (reverse (cons (substring str start) acc)))))))
-
-(define (gb-filter-lines! gb lines-proc)
- (let ((new-lines (lines-proc (gb->lines gb))))
- (gb-erase! gb)
- (gb-insert-string! gb (string-join new-lines #\newline))))
-
-(define (make-gap-buffer-port gb)
- (or (gb? gb)
- (error "not a gap-buffer:" gb))
- (make-soft-port
- (vector
- (lambda (c) (gb-insert-char! gb c))
- (lambda (s) (gb-insert-string! gb s))
- #f
- (lambda () (let ((gap-ofs (gap-ofs\: gb))
- (aft-ofs (aft-ofs\: gb)))
- (if (= aft-ofs (all-sz\: gb))
- #f
- (let* ((s (s\: gb))
- (c (string-ref s aft-ofs)))
- (string-set! s gap-ofs c)
- (gap-ofs! gb (1+ gap-ofs))
- (aft-ofs! gb (1+ aft-ofs))
- c))))
- #f)
- "rw"))
-
-;;; gap-buffer.scm ends here
-;;; Copyright (C) 1998, 2001, 2006, 2009, 2011 Free Software Foundation, Inc.
-;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Russ McManus (rewritten by Thien-Thi Nguyen)
-
-;;; Commentary:
-
-;;; This module implements some complex command line option parsing, in
-;;; the spirit of the GNU C library function `getopt_long'. Both long
-;;; and short options are supported.
-;;;
-;;; The theory is that people should be able to constrain the set of
-;;; options they want to process using a grammar, rather than some arbitrary
-;;; structure. The grammar makes the option descriptions easy to read.
-;;;
-;;; `getopt-long' is a procedure for parsing command-line arguments in a
-;;; manner consistent with other GNU programs. `option-ref' is a procedure
-;;; that facilitates processing of the `getopt-long' return value.
-
-;;; (getopt-long ARGS GRAMMAR)
-;;; Parse the arguments ARGS according to the argument list grammar GRAMMAR.
-;;;
-;;; ARGS should be a list of strings. Its first element should be the
-;;; name of the program; subsequent elements should be the arguments
-;;; that were passed to the program on the command line. The
-;;; `program-arguments' procedure returns a list of this form.
-;;;
-;;; GRAMMAR is a list of the form:
-;;; ((OPTION (PROPERTY VALUE) ...) ...)
-;;;
-;;; Each OPTION should be a symbol. `getopt-long' will accept a
-;;; command-line option named `--OPTION'.
-;;; Each option can have the following (PROPERTY VALUE) pairs:
-;;;
-;;; (single-char CHAR) --- Accept `-CHAR' as a single-character
-;;; equivalent to `--OPTION'. This is how to specify traditional
-;;; Unix-style flags.
-;;; (required? BOOL) --- If BOOL is true, the option is required.
-;;; getopt-long will raise an error if it is not found in ARGS.
-;;; (value BOOL) --- If BOOL is #t, the option accepts a value; if
-;;; it is #f, it does not; and if it is the symbol
-;;; `optional', the option may appear in ARGS with or
-;;; without a value.
-;;; (predicate FUNC) --- If the option accepts a value (i.e. you
-;;; specified `(value #t)' for this option), then getopt
-;;; will apply FUNC to the value, and throw an exception
-;;; if it returns #f. FUNC should be a procedure which
-;;; accepts a string and returns a boolean value; you may
-;;; need to use quasiquotes to get it into GRAMMAR.
-;;;
-;;; The (PROPERTY VALUE) pairs may occur in any order, but each
-;;; property may occur only once. By default, options do not have
-;;; single-character equivalents, are not required, and do not take
-;;; values.
-;;;
-;;; In ARGS, single-character options may be combined, in the usual
-;;; Unix fashion: ("-x" "-y") is equivalent to ("-xy"). If an option
-;;; accepts values, then it must be the last option in the
-;;; combination; the value is the next argument. So, for example, using
-;;; the following grammar:
-;;; ((apples (single-char #\a))
-;;; (blimps (single-char #\b) (value #t))
-;;; (catalexis (single-char #\c) (value #t)))
-;;; the following argument lists would be acceptable:
-;;; ("-a" "-b" "bang" "-c" "couth") ("bang" and "couth" are the values
-;;; for "blimps" and "catalexis")
-;;; ("-ab" "bang" "-c" "couth") (same)
-;;; ("-ac" "couth" "-b" "bang") (same)
-;;; ("-abc" "couth" "bang") (an error, since `-b' is not the
-;;; last option in its combination)
-;;;
-;;; If an option's value is optional, then `getopt-long' decides
-;;; whether it has a value by looking at what follows it in ARGS. If
-;;; the next element is does not appear to be an option itself, then
-;;; that element is the option's value.
-;;;
-;;; The value of a long option can appear as the next element in ARGS,
-;;; or it can follow the option name, separated by an `=' character.
-;;; Thus, using the same grammar as above, the following argument lists
-;;; are equivalent:
-;;; ("--apples" "Braeburn" "--blimps" "Goodyear")
-;;; ("--apples=Braeburn" "--blimps" "Goodyear")
-;;; ("--blimps" "Goodyear" "--apples=Braeburn")
-;;;
-;;; If the option "--" appears in ARGS, argument parsing stops there;
-;;; subsequent arguments are returned as ordinary arguments, even if
-;;; they resemble options. So, in the argument list:
-;;; ("--apples" "Granny Smith" "--" "--blimp" "Goodyear")
-;;; `getopt-long' will recognize the `apples' option as having the
-;;; value "Granny Smith", but it will not recognize the `blimp'
-;;; option; it will return the strings "--blimp" and "Goodyear" as
-;;; ordinary argument strings.
-;;;
-;;; The `getopt-long' function returns the parsed argument list as an
-;;; assocation list, mapping option names --- the symbols from GRAMMAR
-;;; --- onto their values, or #t if the option does not accept a value.
-;;; Unused options do not appear in the alist.
-;;;
-;;; All arguments that are not the value of any option are returned
-;;; as a list, associated with the empty list.
-;;;
-;;; `getopt-long' throws an exception if:
-;;; - it finds an unrecognized property in GRAMMAR
-;;; - the value of the `single-char' property is not a character
-;;; - it finds an unrecognized option in ARGS
-;;; - a required option is omitted
-;;; - an option that requires an argument doesn't get one
-;;; - an option that doesn't accept an argument does get one (this can
-;;; only happen using the long option `--opt=value' syntax)
-;;; - an option predicate fails
-;;;
-;;; So, for example:
-;;;
-;;; (define grammar
-;;; `((lockfile-dir (required? #t)
-;;; (value #t)
-;;; (single-char #\k)
-;;; (predicate ,file-is-directory?))
-;;; (verbose (required? #f)
-;;; (single-char #\v)
-;;; (value #f))
-;;; (x-includes (single-char #\x))
-;;; (rnet-server (single-char #\y)
-;;; (predicate ,string?))))
-;;;
-;;; (getopt-long '("my-prog" "-vk" "/tmp" "foo1" "--x-includes=/usr/include"
-;;; "--rnet-server=lamprod" "--" "-fred" "foo2" "foo3")
-;;; grammar)
-;;; => ((() "foo1" "-fred" "foo2" "foo3")
-;;; (rnet-server . "lamprod")
-;;; (x-includes . "/usr/include")
-;;; (lockfile-dir . "/tmp")
-;;; (verbose . #t))
-
-;;; (option-ref OPTIONS KEY DEFAULT)
-;;; Return value in alist OPTIONS using KEY, a symbol; or DEFAULT if not
-;;; found. The value is either a string or `#t'.
-;;;
-;;; For example, using the `getopt-long' return value from above:
-;;;
-;;; (option-ref (getopt-long ...) 'x-includes 42) => "/usr/include"
-;;; (option-ref (getopt-long ...) 'not-a-key! 31) => 31
-
-;;; Code:
-
-(define-module (ice-9 getopt-long)
- #\use-module ((ice-9 common-list) #\select (remove-if-not))
- #\use-module (srfi srfi-9)
- #\use-module (ice-9 match)
- #\use-module (ice-9 regex)
- #\use-module (ice-9 optargs)
- #\export (getopt-long option-ref))
-
-(define %program-name (make-fluid "guile"))
-(define (program-name)
- (fluid-ref %program-name))
-
-(define (fatal-error fmt . args)
- (format (current-error-port) "~a: " (program-name))
- (apply format (current-error-port) fmt args)
- (newline (current-error-port))
- (exit 1))
-
-(define-record-type option-spec
- (%make-option-spec name required? option-spec->single-char predicate value-policy)
- option-spec?
- (name
- option-spec->name set-option-spec-name!)
- (required?
- option-spec->required? set-option-spec-required?!)
- (option-spec->single-char
- option-spec->single-char set-option-spec-single-char!)
- (predicate
- option-spec->predicate set-option-spec-predicate!)
- (value-policy
- option-spec->value-policy set-option-spec-value-policy!))
-
-(define (make-option-spec name)
- (%make-option-spec name #f #f #f #f))
-
-(define (parse-option-spec desc)
- (let ((spec (make-option-spec (symbol->string (car desc)))))
- (for-each (match-lambda
- (('required? val)
- (set-option-spec-required?! spec val))
- (('value val)
- (set-option-spec-value-policy! spec val))
- (('single-char val)
- (or (char? val)
- (error "`single-char' value must be a char!"))
- (set-option-spec-single-char! spec val))
- (('predicate pred)
- (set-option-spec-predicate!
- spec (lambda (name val)
- (or (not val)
- (pred val)
- (fatal-error "option predicate failed: --~a"
- name)))))
- ((prop val)
- (error "invalid getopt-long option property:" prop)))
- (cdr desc))
- spec))
-
-(define (split-arg-list argument-list)
- ;; Scan ARGUMENT-LIST for "--" and return (BEFORE-LS . AFTER-LS).
- ;; Discard the "--". If no "--" is found, AFTER-LS is empty.
- (let loop ((yes '()) (no argument-list))
- (cond ((null? no) (cons (reverse yes) no))
- ((string=? "--" (car no)) (cons (reverse yes) (cdr no)))
- (else (loop (cons (car no) yes) (cdr no))))))
-
-(define short-opt-rx (make-regexp "^-([a-zA-Z]+)(.*)"))
-(define long-opt-no-value-rx (make-regexp "^--([^=]+)$"))
-(define long-opt-with-value-rx (make-regexp "^--([^=]+)=(.*)"))
-
-(define (looks-like-an-option string)
- (or (regexp-exec short-opt-rx string)
- (regexp-exec long-opt-with-value-rx string)
- (regexp-exec long-opt-no-value-rx string)))
-
-(define (process-options specs argument-ls stop-at-first-non-option)
- ;; Use SPECS to scan ARGUMENT-LS; return (FOUND . ETC).
- ;; FOUND is an unordered list of option specs for found options, while ETC
- ;; is an order-maintained list of elements in ARGUMENT-LS that are neither
- ;; options nor their values.
- (let ((idx (map (lambda (spec)
- (cons (option-spec->name spec) spec))
- specs))
- (sc-idx (map (lambda (spec)
- (cons (make-string 1 (option-spec->single-char spec))
- spec))
- (remove-if-not option-spec->single-char specs))))
- (let loop ((unclumped 0) (argument-ls argument-ls) (found '()) (etc '()))
- (define (eat! spec ls)
- (cond
- ((eq? 'optional (option-spec->value-policy spec))
- (if (or (null? ls)
- (looks-like-an-option (car ls)))
- (loop (- unclumped 1) ls (acons spec #t found) etc)
- (loop (- unclumped 2) (cdr ls) (acons spec (car ls) found) etc)))
- ((eq? #t (option-spec->value-policy spec))
- (if (or (null? ls)
- (looks-like-an-option (car ls)))
- (fatal-error "option must be specified with argument: --~a"
- (option-spec->name spec))
- (loop (- unclumped 2) (cdr ls) (acons spec (car ls) found) etc)))
- (else
- (loop (- unclumped 1) ls (acons spec #t found) etc))))
-
- (match argument-ls
- (()
- (cons found (reverse etc)))
- ((opt . rest)
- (cond
- ((regexp-exec short-opt-rx opt)
- => (lambda (match)
- (if (> unclumped 0)
- ;; Next option is known not to be clumped.
- (let* ((c (match:substring match 1))
- (spec (or (assoc-ref sc-idx c)
- (fatal-error "no such option: -~a" c))))
- (eat! spec rest))
- ;; Expand a clumped group of short options.
- (let* ((extra (match:substring match 2))
- (unclumped-opts
- (append (map (lambda (c)
- (string-append "-" (make-string 1 c)))
- (string->list
- (match:substring match 1)))
- (if (string=? "" extra) '() (list extra)))))
- (loop (length unclumped-opts)
- (append unclumped-opts rest)
- found
- etc)))))
- ((regexp-exec long-opt-no-value-rx opt)
- => (lambda (match)
- (let* ((opt (match:substring match 1))
- (spec (or (assoc-ref idx opt)
- (fatal-error "no such option: --~a" opt))))
- (eat! spec rest))))
- ((regexp-exec long-opt-with-value-rx opt)
- => (lambda (match)
- (let* ((opt (match:substring match 1))
- (spec (or (assoc-ref idx opt)
- (fatal-error "no such option: --~a" opt))))
- (if (option-spec->value-policy spec)
- (eat! spec (cons (match:substring match 2) rest))
- (fatal-error "option does not support argument: --~a"
- opt)))))
- ((and stop-at-first-non-option
- (<= unclumped 0))
- (cons found (append (reverse etc) argument-ls)))
- (else
- (loop (- unclumped 1) rest found (cons opt etc)))))))))
-
-(define* (getopt-long program-arguments option-desc-list
- #\key stop-at-first-non-option)
- "Process options, handling both long and short options, similar to
-the glibc function 'getopt_long'. PROGRAM-ARGUMENTS should be a value
-similar to what (program-arguments) returns. OPTION-DESC-LIST is a
-list of option descriptions. Each option description must satisfy the
-following grammar:
-
- <option-spec> :: (<name> . <attribute-ls>)
- <attribute-ls> :: (<attribute> . <attribute-ls>)
- | ()
- <attribute> :: <required-attribute>
- | <arg-required-attribute>
- | <single-char-attribute>
- | <predicate-attribute>
- | <value-attribute>
- <required-attribute> :: (required? <boolean>)
- <single-char-attribute> :: (single-char <char>)
- <value-attribute> :: (value #t)
- (value #f)
- (value optional)
- <predicate-attribute> :: (predicate <1-ary-function>)
-
- The procedure returns an alist of option names and values. Each
-option name is a symbol. The option value will be '#t' if no value
-was specified. There is a special item in the returned alist with a
-key of the empty list, (): the list of arguments that are not options
-or option values.
- By default, options are not required, and option values are not
-required. By default, single character equivalents are not supported;
-if you want to allow the user to use single character options, you need
-to add a `single-char' clause to the option description."
- (with-fluids ((%program-name (car program-arguments)))
- (let* ((specifications (map parse-option-spec option-desc-list))
- (pair (split-arg-list (cdr program-arguments)))
- (split-ls (car pair))
- (non-split-ls (cdr pair))
- (found/etc (process-options specifications split-ls
- stop-at-first-non-option))
- (found (car found/etc))
- (rest-ls (append (cdr found/etc) non-split-ls)))
- (for-each (lambda (spec)
- (let ((name (option-spec->name spec))
- (val (assq-ref found spec)))
- (and (option-spec->required? spec)
- (or val
- (fatal-error "option must be specified: --~a"
- name)))
- (let ((pred (option-spec->predicate spec)))
- (and pred (pred name val)))))
- specifications)
- (for-each (lambda (spec+val)
- (set-car! spec+val
- (string->symbol (option-spec->name (car spec+val)))))
- found)
- (cons (cons '() rest-ls) found))))
-
-(define (option-ref options key default)
- "Return value in alist OPTIONS using KEY, a symbol; or DEFAULT if not found.
-The value is either a string or `#t'."
- (or (assq-ref options key) default))
-
-;;; getopt-long.scm ends here
-;;;; hash-table.scm --- Additional hash table procedures
-;;;; Copyright (C) 2013 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-(define-module (ice-9 hash-table)
- #\export (alist->hash-table
- alist->hashq-table
- alist->hashv-table
- alist->hashx-table))
-
-(define-syntax-rule (define-alist-converter name hash-set-proc)
- (define (name alist)
- "Convert ALIST into a hash table."
- (let ((table (make-hash-table)))
- (for-each (lambda (pair)
- (hash-set-proc table (car pair) (cdr pair)))
- (reverse alist))
- table)))
-
-(define-alist-converter alist->hash-table hash-set!)
-(define-alist-converter alist->hashq-table hashq-set!)
-(define-alist-converter alist->hashv-table hashv-set!)
-
-(define (alist->hashx-table hash assoc alist)
- "Convert ALIST into a hash table with custom HASH and ASSOC
-procedures."
- (let ((table (make-hash-table)))
- (for-each (lambda (pair)
- (hashx-set! hash assoc table (car pair) (cdr pair)))
- (reverse alist))
- table))
-;;; installed-scm-file
-
-;;;; Copyright (C) 1995, 1996, 1998, 2001, 2003, 2006 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-(define-module (ice-9 hcons)
- \:export (hashq-cons-hash hashq-cons-assoc hashq-cons-get-handle
- hashq-cons-create-handle! hashq-cons-ref hashq-cons-set! hashq-cons
- hashq-conser make-gc-buffer))
-
-
-;;; {Eq? hash-consing}
-;;;
-;;; A hash conser maintains a private universe of pairs s.t. if
-;;; two cons calls pass eq? arguments, the pairs returned are eq?.
-;;;
-;;; A hash conser does not contribute life to the pairs it returns.
-;;;
-
-(define (hashq-cons-hash pair n)
- (modulo (logxor (hashq (car pair) 4194303)
- (hashq (cdr pair) 4194303))
- n))
-
-(define (hashq-cons-assoc key l)
- (and (not (null? l))
- (or (and (pair? l) ; If not a pair, use its cdr?
- (pair? (car l))
- (pair? (caar l))
- (eq? (car key) (caaar l))
- (eq? (cdr key) (cdaar l))
- (car l))
- (hashq-cons-assoc key (cdr l)))))
-
-(define (hashq-cons-get-handle table key)
- (hashx-get-handle hashq-cons-hash hashq-cons-assoc table key))
-
-(define (hashq-cons-create-handle! table key init)
- (hashx-create-handle! hashq-cons-hash hashq-cons-assoc table key init))
-
-(define (hashq-cons-ref table key)
- (hashx-ref hashq-cons-hash hashq-cons-assoc table key #f))
-
-(define (hashq-cons-set! table key val)
- (hashx-set! hashq-cons-hash hashq-cons-assoc table key val))
-
-(define (hashq-cons table a d)
- (car (hashq-cons-create-handle! table (cons a d) #f)))
-
-(define (hashq-conser hash-tab-or-size)
- (let ((table (if (vector? hash-tab-or-size)
- hash-tab-or-size
- (make-doubly-weak-hash-table hash-tab-or-size))))
- (lambda (a d) (hashq-cons table a d))))
-
-
-
-
-(define (make-gc-buffer n)
- (let ((ring (make-list n #f)))
- (append! ring ring)
- (lambda (next)
- (set-car! ring next)
- (set! ring (cdr ring))
- next)))
-;;;; Copyright (C) 2000, 2001, 2004, 2006, 2010 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;;; A simple value history support
-
-(define-module (ice-9 history)
- #\export (value-history-enabled? enable-value-history! disable-value-history!
- clear-value-history!))
-
-(define-module* '(value-history))
-
-(define *value-history-enabled?* #f)
-(define (value-history-enabled?)
- *value-history-enabled?*)
-
-(define (use-value-history x)
- (module-use! (current-module)
- (resolve-interface '(value-history))))
-
-(define save-value-history
- (let ((count 0)
- (history (resolve-module '(value-history))))
- (lambda (v)
- (if (not (unspecified? v))
- (let* ((c (1+ count))
- (s (string->symbol (simple-format #f "$~A" c))))
- (simple-format #t "~A = " s)
- (module-define! history s v)
- (module-export! history (list s))
- (set! count c))))))
-
-(define (enable-value-history!)
- (if (not (value-history-enabled?))
- (begin
- (add-hook! before-eval-hook use-value-history)
- (add-hook! before-print-hook save-value-history)
- (set! *value-history-enabled?* #t))))
-
-(define (disable-value-history!)
- (if (value-history-enabled?)
- (begin
- (remove-hook! before-eval-hook use-value-history)
- (remove-hook! before-print-hook save-value-history)
- (set! *value-history-enabled?* #f))))
-
-(define (clear-value-history!)
- (let ((history (resolve-module '(value-history))))
- (hash-clear! (module-obarray history))
- (hash-clear! (module-obarray (module-public-interface history)))))
-
-(enable-value-history!)
-;;;; i18n.scm --- internationalization support -*- coding: utf-8 -*-
-
-;;;; Copyright (C) 2006, 2007, 2009, 2010, 2012,
-;;;; 2017 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Ludovic Courtès <ludo@gnu.org>
-
-;;; Commentary:
-;;;
-;;; This module provides a number of routines that support
-;;; internationalization (e.g., locale-dependent text collation, character
-;;; mapping, etc.). It also defines `locale' objects, representing locale
-;;; settings, that may be passed around to most of these procedures.
-;;;
-
-;;; Code:
-
-(define-module (ice-9 i18n)
- \:use-module (ice-9 optargs)
- \:export (;; `locale' type
- make-locale locale?
- %global-locale
-
- ;; text collation
- string-locale<? string-locale>?
- string-locale-ci<? string-locale-ci>? string-locale-ci=?
-
- char-locale<? char-locale>?
- char-locale-ci<? char-locale-ci>? char-locale-ci=?
-
- ;; character mapping
- char-locale-downcase char-locale-upcase char-locale-titlecase
- string-locale-downcase string-locale-upcase string-locale-titlecase
-
- ;; reading numbers
- locale-string->integer locale-string->inexact
-
- ;; charset/encoding
- locale-encoding
-
- ;; days and months
- locale-day-short locale-day locale-month-short locale-month
-
- ;; date and time
- locale-am-string locale-pm-string
- locale-date+time-format locale-date-format locale-time-format
- locale-time+am/pm-format
- locale-era locale-era-year
- locale-era-date-format locale-era-date+time-format
- locale-era-time-format
-
- ;; monetary
- locale-currency-symbol
- locale-monetary-decimal-point locale-monetary-thousands-separator
- locale-monetary-grouping locale-monetary-fractional-digits
- locale-currency-symbol-precedes-positive?
- locale-currency-symbol-precedes-negative?
- locale-positive-separated-by-space?
- locale-negative-separated-by-space?
- locale-monetary-positive-sign locale-monetary-negative-sign
- locale-positive-sign-position locale-negative-sign-position
- monetary-amount->locale-string
-
- ;; number formatting
- locale-digit-grouping locale-decimal-point
- locale-thousands-separator
- number->locale-string
-
- ;; miscellaneous
- locale-yes-regexp locale-no-regexp))
-
-
-(eval-when (expand load eval)
- (load-extension (string-append "libguile-" (effective-version))
- "scm_init_i18n"))
-
-
-;;;
-;;; Charset/encoding.
-;;;
-
-(define (locale-encoding . locale)
- (apply nl-langinfo CODESET locale))
-
-
-;;;
-;;; Months and days.
-;;;
-
-;; Helper macro: Define a procedure named NAME that maps its argument to
-;; NL-ITEMS. Gnulib guarantees that these items are available.
-(define-macro (define-vector-langinfo-mapping name nl-items)
- (let* ((item-count (length nl-items))
- (defines `(define %nl-items (vector #f ,@nl-items)))
- (make-body (lambda (result)
- `(if (and (integer? item) (exact? item))
- (if (and (>= item 1) (<= item ,item-count))
- ,result
- (throw 'out-of-range "out of range" item))
- (throw 'wrong-type-arg "wrong argument type" item)))))
- `(define (,name item . locale)
- ,defines
- ,(make-body '(apply nl-langinfo (vector-ref %nl-items item) locale)))))
-
-
-(define-vector-langinfo-mapping locale-day-short
- (ABDAY_1 ABDAY_2 ABDAY_3 ABDAY_4 ABDAY_5 ABDAY_6 ABDAY_7))
-
-(define-vector-langinfo-mapping locale-day
- (DAY_1 DAY_2 DAY_3 DAY_4 DAY_5 DAY_6 DAY_7))
-
-(define-vector-langinfo-mapping locale-month-short
- (ABMON_1 ABMON_2 ABMON_3 ABMON_4 ABMON_5 ABMON_6
- ABMON_7 ABMON_8 ABMON_9 ABMON_10 ABMON_11 ABMON_12))
-
-(define-vector-langinfo-mapping locale-month
- (MON_1 MON_2 MON_3 MON_4 MON_5 MON_6 MON_7 MON_8 MON_9 MON_10 MON_11 MON_12))
-
-
-
-;;;
-;;; Date and time.
-;;;
-
-;; Define a procedure NAME that gets langinfo item ITEM. Gnulib's
-;; `nl_langinfo' does not guarantee that all these items are supported
-;; (for instance, `GROUPING' is lacking on Darwin and Gnulib provides no
-;; replacement), so use DEFAULT as the default value when ITEM is not
-;; available.
-(define-macro (define-simple-langinfo-mapping name item default)
- (let ((body (if (defined? item)
- `(apply nl-langinfo ,item locale)
- default)))
- `(define (,name . locale)
- ,body)))
-
-(define-simple-langinfo-mapping locale-am-string
- AM_STR "AM")
-(define-simple-langinfo-mapping locale-pm-string
- PM_STR "PM")
-(define-simple-langinfo-mapping locale-date+time-format
- D_T_FMT "%a %b %e %H:%M:%S %Y")
-(define-simple-langinfo-mapping locale-date-format
- D_FMT "%m/%d/%y")
-(define-simple-langinfo-mapping locale-time-format
- T_FMT "%H:%M:%S")
-(define-simple-langinfo-mapping locale-time+am/pm-format
- T_FMT_AMPM "%I:%M:%S %p")
-(define-simple-langinfo-mapping locale-era
- ERA "")
-(define-simple-langinfo-mapping locale-era-year
- ERA_YEAR "")
-(define-simple-langinfo-mapping locale-era-date+time-format
- ERA_D_T_FMT "")
-(define-simple-langinfo-mapping locale-era-date-format
- ERA_D_FMT "")
-(define-simple-langinfo-mapping locale-era-time-format
- ERA_T_FMT "")
-
-
-
-;;;
-;;; Monetary information.
-;;;
-
-;; Define a procedure NAME that gets item LOCAL-ITEM or INTL-ITEM,
-;; depending on whether the caller asked for the international version
-;; or not. Since Gnulib's `nl_langinfo' module doesn't guarantee that
-;; all these items are available, use DEFAULT/LOCAL and DEFAULT/INTL as
-;; default values when the system does not support them.
-(define-macro (define-monetary-langinfo-mapping name local-item intl-item
- default/local default/intl)
- (let ((body
- (let ((intl (if (defined? intl-item)
- `(apply nl-langinfo ,intl-item locale)
- default/intl))
- (local (if (defined? local-item)
- `(apply nl-langinfo ,local-item locale)
- default/local)))
- `(if intl? ,intl ,local))))
-
- `(define (,name intl? . locale)
- ,body)))
-
-;; FIXME: How can we use ALT_DIGITS?
-(define-monetary-langinfo-mapping locale-currency-symbol
- CRNCYSTR INT_CURR_SYMBOL
- "-" "")
-(define-monetary-langinfo-mapping locale-monetary-fractional-digits
- FRAC_DIGITS INT_FRAC_DIGITS
- 2 2)
-
-(define-simple-langinfo-mapping locale-monetary-positive-sign
- POSITIVE_SIGN "+")
-(define-simple-langinfo-mapping locale-monetary-negative-sign
- NEGATIVE_SIGN "-")
-(define-simple-langinfo-mapping locale-monetary-decimal-point
- MON_DECIMAL_POINT "")
-(define-simple-langinfo-mapping locale-monetary-thousands-separator
- MON_THOUSANDS_SEP "")
-(define-simple-langinfo-mapping locale-monetary-digit-grouping
- MON_GROUPING '())
-
-(define-monetary-langinfo-mapping locale-currency-symbol-precedes-positive?
- P_CS_PRECEDES INT_P_CS_PRECEDES
- #t #t)
-(define-monetary-langinfo-mapping locale-currency-symbol-precedes-negative?
- N_CS_PRECEDES INT_N_CS_PRECEDES
- #t #t)
-
-
-(define-monetary-langinfo-mapping locale-positive-separated-by-space?
- ;; Whether a space should be inserted between a positive amount and the
- ;; currency symbol.
- P_SEP_BY_SPACE INT_P_SEP_BY_SPACE
- #t #t)
-(define-monetary-langinfo-mapping locale-negative-separated-by-space?
- ;; Whether a space should be inserted between a negative amount and the
- ;; currency symbol.
- N_SEP_BY_SPACE INT_N_SEP_BY_SPACE
- #t #t)
-
-(define-monetary-langinfo-mapping locale-positive-sign-position
- ;; Position of the positive sign wrt. currency symbol and quantity in a
- ;; monetary amount.
- P_SIGN_POSN INT_P_SIGN_POSN
- 'unspecified 'unspecified)
-(define-monetary-langinfo-mapping locale-negative-sign-position
- ;; Position of the negative sign wrt. currency symbol and quantity in a
- ;; monetary amount.
- N_SIGN_POSN INT_N_SIGN_POSN
- 'unspecified 'unspecified)
-
-
-(define (integer->string number)
- "Return a string representing NUMBER, an integer, written in base 10."
- (define (digit->char digit)
- (integer->char (+ digit (char->integer #\0))))
-
- (if (zero? number)
- "0"
- (let loop ((number number)
- (digits '()))
- (if (zero? number)
- (list->string digits)
- (loop (quotient number 10)
- (cons (digit->char (modulo number 10))
- digits))))))
-
-(define (number-decimal-string number digit-count)
- "Return a string representing the decimal part of NUMBER. When
-DIGIT-COUNT is an integer, return exactly DIGIT-COUNT digits; when
-DIGIT-COUNT is #t, return as many decimals as necessary, up to an
-arbitrary limit."
- (define max-decimals
- 5)
-
- ;; XXX: This is brute-force and could be improved by following one of
- ;; the "Printing Floating-Point Numbers Quickly and Accurately"
- ;; papers.
- (if (integer? digit-count)
- (let ((number (* (expt 10 digit-count)
- (- number (floor number)))))
- (string-pad (integer->string (round (inexact->exact number)))
- digit-count
- #\0))
- (let loop ((decimals 0))
- (let ((number\' (* number (expt 10 decimals))))
- (if (or (= number\' (floor number\'))
- (>= decimals max-decimals))
- (let* ((fraction (- number\'
- (* (floor number)
- (expt 10 decimals))))
- (str (integer->string
- (round (inexact->exact fraction)))))
- (if (zero? fraction)
- ""
- str))
- (loop (+ decimals 1)))))))
-
-(define (%number-integer-part int grouping separator)
- ;; Process INT (a string denoting a number's integer part) and return a new
- ;; string with digit grouping and separators according to GROUPING (a list,
- ;; potentially circular) and SEPARATOR (a string).
-
- ;; Process INT from right to left.
- (let loop ((int int)
- (grouping grouping)
- (result '()))
- (cond ((string=? int "") (apply string-append result))
- ((null? grouping) (apply string-append int result))
- (else
- (let* ((len (string-length int))
- (cut (min (car grouping) len)))
- (loop (substring int 0 (- len cut))
- (cdr grouping)
- (let ((sub (substring int (- len cut) len)))
- (if (> len cut)
- (cons* separator sub result)
- (cons sub result)))))))))
-
-(define (add-monetary-sign+currency amount figure intl? locale)
- ;; Add a sign and currency symbol around FIGURE. FIGURE should be a
- ;; formatted unsigned amount (a string) representing AMOUNT.
- (let* ((positive? (> amount 0))
- (sign
- (cond ((> amount 0) (locale-monetary-positive-sign locale))
- ((< amount 0) (locale-monetary-negative-sign locale))
- (else "")))
- (currency (locale-currency-symbol intl? locale))
- (currency-precedes?
- (if positive?
- locale-currency-symbol-precedes-positive?
- locale-currency-symbol-precedes-negative?))
- (separated?
- (if positive?
- locale-positive-separated-by-space?
- locale-negative-separated-by-space?))
- (sign-position
- (if positive?
- locale-positive-sign-position
- locale-negative-sign-position))
- (currency-space
- (if (separated? intl? locale) " " ""))
- (append-currency
- (lambda (amt)
- (if (currency-precedes? intl? locale)
- (string-append currency currency-space amt)
- (string-append amt currency-space currency)))))
-
- (case (sign-position intl? locale)
- ((parenthesize)
- (string-append "(" (append-currency figure) ")"))
- ((sign-before)
- (string-append sign (append-currency figure)))
- ((sign-after unspecified)
- ;; following glibc's recommendation for `unspecified'.
- (if (currency-precedes? intl? locale)
- (string-append currency currency-space sign figure)
- (string-append figure currency-space currency sign)))
- ((sign-before-currency-symbol)
- (if (currency-precedes? intl? locale)
- (string-append sign currency currency-space figure)
- (string-append figure currency-space sign currency))) ;; unlikely
- ((sign-after-currency-symbol)
- (if (currency-precedes? intl? locale)
- (string-append currency sign currency-space figure)
- (string-append figure currency-space currency sign)))
- (else
- (error "unsupported sign position" (sign-position intl? locale))))))
-
-
-(define* (monetary-amount->locale-string amount intl?
- #\optional (locale %global-locale))
- "Convert @var{amount} (an inexact) into a string according to the cultural
-conventions of either @var{locale} (a locale object) or the current locale.
-If @var{intl?} is true, then the international monetary format for the given
-locale is used."
-
- (let* ((fraction-digits
- (or (locale-monetary-fractional-digits intl? locale) 2))
- (decimal-part
- (lambda (dec)
- (if (or (string=? dec "") (eq? 0 fraction-digits))
- ""
- (string-append (locale-monetary-decimal-point locale)
- (if (< fraction-digits (string-length dec))
- (substring dec 0 fraction-digits)
- dec)))))
-
- (int (integer->string (inexact->exact
- (floor (abs amount)))))
- (dec (decimal-part
- (number-decimal-string (abs amount)
- fraction-digits)))
- (grouping (locale-monetary-digit-grouping locale))
- (separator (locale-monetary-thousands-separator locale)))
-
- (add-monetary-sign+currency amount
- (string-append
- (%number-integer-part int grouping
- separator)
- dec)
- intl? locale)))
-
-
-
-;;;
-;;; Number formatting.
-;;;
-
-(define-simple-langinfo-mapping locale-digit-grouping
- GROUPING '())
-(define-simple-langinfo-mapping locale-decimal-point
- RADIXCHAR ".")
-(define-simple-langinfo-mapping locale-thousands-separator
- THOUSEP "")
-
-(define* (number->locale-string number
- #\optional (fraction-digits #t)
- (locale %global-locale))
- "Convert @var{number} (an inexact) into a string according to the cultural
-conventions of either @var{locale} (a locale object) or the current locale.
-By default, print as many fractional digits as necessary, up to an upper bound.
-Optionally, @var{fraction-digits} may be bound to an integer specifying the
-number of fractional digits to be displayed."
-
- (let* ((sign
- (cond ((> number 0) "")
- ((< number 0) "-")
- (else "")))
- (decimal-part
- (lambda (dec)
- (if (or (string=? dec "") (eq? 0 fraction-digits))
- ""
- (string-append (locale-decimal-point locale)
- (if (and (integer? fraction-digits)
- (< fraction-digits
- (string-length dec)))
- (substring dec 0 fraction-digits)
- dec))))))
-
- (let* ((int (integer->string (inexact->exact
- (floor (abs number)))))
- (dec (decimal-part
- (number-decimal-string (abs number)
- fraction-digits)))
- (grouping (locale-digit-grouping locale))
- (separator (locale-thousands-separator locale)))
-
- (string-append sign
- (%number-integer-part int grouping separator)
- dec))))
-
-
-;;;
-;;; Miscellaneous.
-;;;
-
-(define-simple-langinfo-mapping locale-yes-regexp
- YESEXPR "^[yY]")
-(define-simple-langinfo-mapping locale-no-regexp
- NOEXPR "^[nN]")
-
-;; `YESSTR' and `NOSTR' are considered deprecated so we don't provide them.
-
-;;; i18n.scm ends here
-;;; Encoding and decoding byte representations of strings
-
-;; Copyright (C) 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (ice-9 iconv)
- #\use-module (rnrs bytevectors)
- #\use-module (ice-9 binary-ports)
- #\use-module ((ice-9 rdelim) #\select (read-string))
- #\export (string->bytevector
- bytevector->string
- call-with-encoded-output-string))
-
-;; Like call-with-output-string, but actually closes the port.
-(define (call-with-output-string* proc)
- (let ((port (open-output-string)))
- (proc port)
- (let ((str (get-output-string port)))
- (close-port port)
- str)))
-
-(define (call-with-output-bytevector* proc)
- (call-with-values (lambda () (open-bytevector-output-port))
- (lambda (port get-bytevector)
- (proc port)
- (let ((bv (get-bytevector)))
- (close-port port)
- bv))))
-
-(define* (call-with-encoded-output-string encoding proc
- #\optional
- (conversion-strategy 'error))
- "Call PROC on a fresh port. Encode the resulting string as a
-bytevector according to ENCODING, and return the bytevector."
- (if (and (string-ci=? encoding "utf-8")
- (eq? conversion-strategy 'error))
- ;; I don't know why, but this appears to be faster; at least for
- ;; serving examples/debug-sxml.scm (1464 reqs/s versus 850
- ;; reqs/s).
- (string->utf8 (call-with-output-string* proc))
- (call-with-output-bytevector*
- (lambda (port)
- (set-port-encoding! port encoding)
- (if conversion-strategy
- (set-port-conversion-strategy! port conversion-strategy))
- (proc port)))))
-
-;; TODO: Provide C implementations that call scm_from_stringn and
-;; friends?
-
-(define* (string->bytevector str encoding
- #\optional (conversion-strategy 'error))
- "Encode STRING according to ENCODING, which should be a string naming
-a character encoding, like \"utf-8\"."
- (if (and (string-ci=? encoding "utf-8")
- (eq? conversion-strategy 'error))
- (string->utf8 str)
- (call-with-encoded-output-string
- encoding
- (lambda (port)
- (display str port))
- conversion-strategy)))
-
-(define* (bytevector->string bv encoding
- #\optional (conversion-strategy 'error))
- "Decode the string represented by BV. The bytes in the bytevector
-will be interpreted according to ENCODING, which should be a string
-naming a character encoding, like \"utf-8\"."
- (if (and (string-ci=? encoding "utf-8")
- (eq? conversion-strategy 'error))
- (utf8->string bv)
- (let ((p (open-bytevector-input-port bv)))
- (set-port-encoding! p encoding)
- (if conversion-strategy
- (set-port-conversion-strategy! p conversion-strategy))
- (let ((res (read-string p)))
- (close-port p)
- (if (eof-object? res)
- ""
- res)))))
-;;; installed-scm-file
-
-;;;; Copyright (C) 1996, 1998, 2001, 2003, 2006 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-
-(define-module (ice-9 lineio)
- \:use-module (ice-9 rdelim)
- \:export (unread-string read-string lineio-port?
- make-line-buffering-input-port))
-
-
-;;; {Line Buffering Input Ports}
-;;;
-;;; [This is a work-around to get past certain deficiencies in the capabilities
-;;; of ports. Eventually, ports should be fixed and this module nuked.]
-;;;
-;;; A line buffering input port supports:
-;;;
-;;; read-string which returns the next line of input
-;;; unread-string which pushes a line back onto the stream
-;;;
-;;; The implementation of unread-string is kind of limited; it doesn't
-;;; interact properly with unread-char, or any of the other port
-;;; reading functions. Only read-string will get you back the things that
-;;; unread-string accepts.
-;;;
-;;; Normally a "line" is all characters up to and including a newline.
-;;; If lines are put back using unread-string, they can be broken arbitrarily
-;;; -- that is, read-string returns strings passed to unread-string (or
-;;; shared substrings of them).
-;;;
-
-;; read-string port
-;; unread-string port str
-;; Read (or buffer) a line from PORT.
-;;
-;; Not all ports support these functions -- only those with
-;; 'unread-string and 'read-string properties, bound to hooks
-;; implementing these functions.
-;;
-(define (unread-string str line-buffering-input-port)
- ((object-property line-buffering-input-port 'unread-string) str))
-
-;;
-(define (read-string line-buffering-input-port)
- ((object-property line-buffering-input-port 'read-string)))
-
-
-(define (lineio-port? port)
- (not (not (object-property port 'read-string))))
-
-;; make-line-buffering-input-port port
-;; Return a wrapper for PORT. The wrapper handles read-string/unread-string.
-;;
-;; The port returned by this function reads newline terminated lines from PORT.
-;; It buffers these characters internally, and parsels them out via calls
-;; to read-char, read-string, and unread-string.
-;;
-
-(define (make-line-buffering-input-port underlying-port)
- (let* (;; buffers - a list of strings put back by unread-string or cached
- ;; using read-line.
- ;;
- (buffers '())
-
- ;; getc - return the next character from a buffer or from the underlying
- ;; port.
- ;;
- (getc (lambda ()
- (if (not buffers)
- (read-char underlying-port)
- (let ((c (string-ref (car buffers) 0)))
- (if (= 1 (string-length (car buffers)))
- (set! buffers (cdr buffers))
- (set-car! buffers (substring (car buffers) 1)))
- c))))
-
- (propogate-close (lambda () (close-port underlying-port)))
-
- (self (make-soft-port (vector #f #f #f getc propogate-close) "r"))
-
- (unread-string (lambda (str)
- (and (< 0 (string-length str))
- (set! buffers (cons str buffers)))))
-
- (read-string (lambda ()
- (cond
- ((not (null? buffers))
- (let ((answer (car buffers)))
- (set! buffers (cdr buffers))
- answer))
- (else
- (read-line underlying-port 'concat)))))) ;handle-newline->concat
-
- (set-object-property! self 'unread-string unread-string)
- (set-object-property! self 'read-string read-string)
- self))
-
-
-;;;; List functions not provided in R5RS or srfi-1
-
-;;; Copyright (C) 2003, 2006 Free Software Foundation, Inc.
-;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (ice-9 list)
- \:export (rassoc rassv rassq))
-
-(define (generic-rassoc key alist =)
- (let loop ((ls alist))
- (and (not (null? ls))
- (if (= key (cdar ls))
- (car ls)
- (loop (cdr ls))))))
-
-(define (rassoc key alist . =)
- (generic-rassoc key alist (if (null? =) equal? (car =))))
-
-(define (rassv key alist)
- (generic-rassoc key alist eqv?))
-
-(define (rassq key alist)
- (generic-rassoc key alist eq?))
-;;; -*- mode: scheme; coding: utf-8; -*-
-;;;
-;;; Copyright (C) 2012, 2013 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (ice-9 local-eval)
- #\use-module (ice-9 format)
- #\use-module (srfi srfi-9)
- #\use-module (srfi srfi-9 gnu)
- #\use-module (system base compile)
- #\use-module (system syntax)
- #\export (the-environment local-eval local-compile))
-
-(define-record-type lexical-environment-type
- (make-lexical-environment scope wrapper boxes patterns)
- lexical-environment?
- (scope lexenv-scope)
- (wrapper lexenv-wrapper)
- (boxes lexenv-boxes)
- (patterns lexenv-patterns))
-
-(set-record-type-printer!
- lexical-environment-type
- (lambda (e port)
- (format port "#<lexical-environment ~S (~S bindings)>"
- (syntax-module (lexenv-scope e))
- (+ (length (lexenv-boxes e)) (length (lexenv-patterns e))))))
-
-(define-syntax syntax-object-of
- (lambda (form)
- (syntax-case form ()
- ((_ x) #`(quote #,(datum->syntax #'x #'x))))))
-
-(define-syntax-rule (make-box v)
- (case-lambda
- (() v)
- ((x) (set! v x))))
-
-(define (make-transformer-from-box id trans)
- (set-procedure-property! trans 'identifier-syntax-box id)
- trans)
-
-(define-syntax-rule (identifier-syntax-from-box box)
- (make-transformer-from-box
- (syntax-object-of box)
- (identifier-syntax (id (box))
- ((set! id x) (box x)))))
-
-(define (unsupported-binding name)
- (make-variable-transformer
- (lambda (x)
- (syntax-violation
- 'local-eval
- "unsupported binding captured by (the-environment)"
- x))))
-
-(define (within-nested-ellipses id lvl)
- (let loop ((s id) (n lvl))
- (if (zero? n)
- s
- (loop #`(#,s (... ...)) (- n 1)))))
-
-;; Analyze the set of bound identifiers IDS. Return four values:
-;;
-;; capture: A list of forms that will be emitted in the expansion of
-;; `the-environment' to capture lexical variables.
-;;
-;; formals: Corresponding formal parameters for use in the lambda that
-;; re-introduces those variables. These are temporary identifiers, and
-;; as such if we have a nested `the-environment', there is no need to
-;; capture them. (See the notes on nested `the-environment' and
-;; proxies, below.)
-;;
-;; wrappers: A list of procedures of type SYNTAX -> SYNTAX, used to wrap
-;; the expression to be evaluated in forms that re-introduce the
-;; variable. The forms will be nested so that the variable shadowing
-;; semantics of the original form are maintained.
-;;
-;; patterns: A terrible hack. The issue is that for pattern variables,
-;; we can't emit lexically nested with-syntax forms, like:
-;;
-;; (with-syntax ((foo 1)) (the-environment))
-;; => (with-syntax ((foo 1))
-;; ... #'(with-syntax ((foo ...)) ... exp) ...)
-;;
-;; The reason is that the outer "foo" substitutes into the inner "foo",
-;; yielding something like:
-;;
-;; (with-syntax ((foo 1))
-;; ... (with-syntax ((1 ...)) ...)
-;;
-;; Which ain't what we want. So we hide the information needed to
-;; re-make the inner pattern binding form in the lexical environment
-;; object, and then introduce those identifiers via another with-syntax.
-;;
-;;
-;; There are four different kinds of lexical bindings: normal lexicals,
-;; macros, displaced lexicals, and pattern variables. See the
-;; documentation of syntax-local-binding for more info on these.
-;;
-;; We capture normal lexicals via `make-box', which creates a
-;; case-lambda that can reference or set a variable. These get
-;; re-introduced with an identifier-syntax.
-;;
-;; We can't capture macros currently. However we do recognize our own
-;; macros that are actually proxying lexicals, so that nested
-;; `the-environment' forms are possible. In that case we drill down to
-;; the identifier for the already-existing box, and just capture that
-;; box.
-;;
-;; And that's it: we skip displaced lexicals, and the pattern variables
-;; are discussed above.
-;;
-(define (analyze-identifiers ids)
- (define (mktmp)
- (datum->syntax #'here (gensym "t ")))
- (let lp ((ids ids) (capture '()) (formals '()) (wrappers '()) (patterns '()))
- (cond
- ((null? ids)
- (values capture formals wrappers patterns))
- (else
- (let ((id (car ids)) (ids (cdr ids)))
- (call-with-values (lambda () (syntax-local-binding id))
- (lambda (type val)
- (case type
- ((lexical)
- (if (or-map (lambda (x) (bound-identifier=? x id)) formals)
- (lp ids capture formals wrappers patterns)
- (let ((t (mktmp)))
- (lp ids
- (cons #`(make-box #,id) capture)
- (cons t formals)
- (cons (lambda (x)
- #`(let-syntax ((#,id (identifier-syntax-from-box #,t)))
- #,x))
- wrappers)
- patterns))))
- ((displaced-lexical)
- (lp ids capture formals wrappers patterns))
- ((macro)
- (let ((b (procedure-property val 'identifier-syntax-box)))
- (if b
- (lp ids (cons b capture) (cons b formals)
- (cons (lambda (x)
- #`(let-syntax ((#,id (identifier-syntax-from-box #,b)))
- #,x))
- wrappers)
- patterns)
- (lp ids capture formals
- (cons (lambda (x)
- #`(let-syntax ((#,id (unsupported-binding '#,id)))
- #,x))
- wrappers)
- patterns))))
- ((pattern-variable)
- (let ((t (datum->syntax id (gensym "p ")))
- (nested (within-nested-ellipses id (cdr val))))
- (lp ids capture formals
- (cons (lambda (x)
- #`(with-syntax ((#,t '#,nested))
- #,x))
- wrappers)
- ;; This dance is to hide these pattern variables
- ;; from the expander.
- (cons (list (datum->syntax #'here (syntax->datum id))
- (cdr val)
- t)
- patterns))))
- ((ellipsis)
- (lp ids capture formals
- (cons (lambda (x)
- #`(with-ellipsis #,val #,x))
- wrappers)
- patterns))
- (else
- (error "what" type val))))))))))
-
-(define-syntax the-environment
- (lambda (x)
- (syntax-case x ()
- ((the-environment)
- #'(the-environment the-environment))
- ((the-environment scope)
- (call-with-values (lambda ()
- (analyze-identifiers
- (syntax-locally-bound-identifiers #'scope)))
- (lambda (capture formals wrappers patterns)
- (define (wrap-expression x)
- (let lp ((x x) (wrappers wrappers))
- (if (null? wrappers)
- x
- (lp ((car wrappers) x) (cdr wrappers)))))
- (with-syntax (((f ...) formals)
- ((c ...) capture)
- (((pname plvl pformal) ...) patterns)
- (wrapped (wrap-expression #'(begin #f exp))))
- #'(make-lexical-environment
- #'scope
- (lambda (exp pformal ...)
- (with-syntax ((exp exp)
- (pformal pformal)
- ...)
- #'(lambda (f ...)
- wrapped)))
- (list c ...)
- (list (list 'pname plvl #'pformal) ...)))))))))
-
-(define (env-module e)
- (cond
- ((lexical-environment? e) (resolve-module (syntax-module (lexenv-scope e))))
- ((module? e) e)
- (else (error "invalid lexical environment" e))))
-
-(define (env-boxes e)
- (cond
- ((lexical-environment? e) (lexenv-boxes e))
- ((module? e) '())
- (else (error "invalid lexical environment" e))))
-
-(define (local-wrap x e)
- (cond
- ((lexical-environment? e)
- (apply (lexenv-wrapper e)
- (datum->syntax (lexenv-scope e) x)
- (map (lambda (l)
- (let ((name (car l))
- (lvl (cadr l))
- (scope (caddr l)))
- (within-nested-ellipses (datum->syntax scope name) lvl)))
- (lexenv-patterns e))))
- ((module? e) #`(lambda () #f #,x))
- (else (error "invalid lexical environment" e))))
-
-(define (local-eval x e)
- "Evaluate the expression @var{x} within the lexical environment @var{e}."
- (apply (eval (local-wrap x e) (env-module e))
- (env-boxes e)))
-
-(define* (local-compile x e #\key (opts '()))
- "Compile and evaluate the expression @var{x} within the lexical
-environment @var{e}."
- (apply (compile (local-wrap x e) #\env (env-module e)
- #\from 'scheme #\opts opts)
- (env-boxes e)))
-;;;; ls.scm --- functions for browsing modules
-;;;;
-;;;; Copyright (C) 1995, 1996, 1997, 1999, 2001, 2006, 2010 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-(define-module (ice-9 ls)
- \:use-module (ice-9 common-list)
- \:export (local-definitions-in definitions-in ls lls
- recursive-local-define))
-
-;;;;
-;;; local-definitions-in root name
-;;; Returns a list of names defined locally in the named
-;;; subdirectory of root.
-;;; definitions-in root name
-;;; Returns a list of all names defined in the named
-;;; subdirectory of root. The list includes alll locally
-;;; defined names as well as all names inherited from a
-;;; member of a use-list.
-;;;
-;;; A convenient interface for examining the nature of things:
-;;;
-;;; ls . various-names
-;;;
-;;; With no arguments, return a list of definitions in
-;;; `(current-module)'.
-;;;
-;;; With just one argument, interpret that argument as the
-;;; name of a subdirectory of the current module and
-;;; return a list of names defined there.
-;;;
-;;; With more than one argument, still compute
-;;; subdirectory lists, but return a list:
-;;; ((<subdir-name> . <names-defined-there>)
-;;; (<subdir-name> . <names-defined-there>)
-;;; ...)
-;;;
-;;; lls . various-names
-;;;
-;;; Analogous to `ls', but with local definitions only.
-
-(define (local-definitions-in root names)
- (let ((m (nested-ref-module root names)))
- (if m
- (module-map (lambda (k v) k) m)
- (nested-ref root names))))
-
-(define (definitions-in root names)
- (let ((m (nested-ref-module root names)))
- (if m
- (reduce union
- (cons (local-definitions-in m '())
- (map (lambda (m2) (definitions-in m2 '()))
- (module-uses m))))
- (nested-ref root names))))
-
-(define (ls . various-refs)
- (if (pair? various-refs)
- (if (cdr various-refs)
- (map (lambda (ref)
- (cons ref (definitions-in (current-module) ref)))
- various-refs)
- (definitions-in (current-module) (car various-refs)))
- (definitions-in (current-module) '())))
-
-(define (lls . various-refs)
- (if (pair? various-refs)
- (if (cdr various-refs)
- (map (lambda (ref)
- (cons ref (local-definitions-in (current-module) ref)))
- various-refs)
- (local-definitions-in (current-module) (car various-refs)))
- (local-definitions-in (current-module) '())))
-
-(define (recursive-local-define name value)
- (let ((parent (reverse! (cdr (reverse name)))))
- (module-define! (make-modules-in (current-module) parent)
- name value)))
-
-;;; ls.scm ends here
-;;; installed-scm-file
-
-;;;; Copyright (C) 1996, 2001, 2006, 2013 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-
-(define-module (ice-9 mapping)
- \:use-module (ice-9 poe)
- \:export (mapping-hooks-type make-mapping-hooks mapping-hooks?
- mapping-hooks-get-handle mapping-hooks-create-handle
- mapping-hooks-remove mapping-type make-mapping mapping?
- mapping-hooks mapping-data set-mapping-hooks! set-mapping-data!
- mapping-get-handle mapping-create-handle! mapping-remove!
- mapping-ref mapping-set! hash-table-mapping-hooks
- make-hash-table-mapping hash-table-mapping))
-
-(issue-deprecation-warning
- "(ice-9 mapping) is deprecated. Use srfi-69 or rnrs hash tables instead.")
-
-(define mapping-hooks-type (make-record-type 'mapping-hooks '(get-handle
- create-handle
- remove)))
-
-
-(define make-mapping-hooks (perfect-funcq 17 (record-constructor mapping-hooks-type)))
-(define mapping-hooks? (record-predicate mapping-hooks-type))
-(define mapping-hooks-get-handle (record-accessor mapping-hooks-type 'get-handle))
-(define mapping-hooks-create-handle (record-accessor mapping-hooks-type 'create-handle))
-(define mapping-hooks-remove (record-accessor mapping-hooks-type 'remove))
-
-(define mapping-type (make-record-type 'mapping '(hooks data)))
-(define make-mapping (record-constructor mapping-type))
-(define mapping? (record-predicate mapping-type))
-(define mapping-hooks (record-accessor mapping-type 'hooks))
-(define mapping-data (record-accessor mapping-type 'data))
-(define set-mapping-hooks! (record-modifier mapping-type 'hooks))
-(define set-mapping-data! (record-modifier mapping-type 'data))
-
-(define (mapping-get-handle map key)
- ((mapping-hooks-get-handle (mapping-hooks map)) map key))
-(define (mapping-create-handle! map key init)
- ((mapping-hooks-create-handle (mapping-hooks map)) map key init))
-(define (mapping-remove! map key)
- ((mapping-hooks-remove (mapping-hooks map)) map key))
-
-(define* (mapping-ref map key #\optional dflt)
- (cond
- ((mapping-get-handle map key) => cdr)
- (else dflt)))
-
-(define (mapping-set! map key val)
- (set-cdr! (mapping-create-handle! map key #f) val))
-
-
-
-(define hash-table-mapping-hooks
- (let ((wrap (lambda (proc) (lambda (1st . rest) (apply proc (mapping-data 1st) rest)))))
-
- (perfect-funcq 17
- (lambda (hash-proc assoc-proc)
- (let ((procs (list hash-proc assoc-proc)))
- (cond
- ((equal? procs `(,hashq ,assq))
- (make-mapping-hooks (wrap hashq-get-handle)
- (wrap hashq-create-handle!)
- (wrap hashq-remove!)))
- ((equal? procs `(,hashv ,assv))
- (make-mapping-hooks (wrap hashv-get-handle)
- (wrap hashv-create-handle!)
- (wrap hashv-remove!)))
- ((equal? procs `(,hash ,assoc))
- (make-mapping-hooks (wrap hash-get-handle)
- (wrap hash-create-handle!)
- (wrap hash-remove!)))
- (else
- (make-mapping-hooks (wrap
- (lambda (table key)
- (hashx-get-handle hash-proc assoc-proc table key)))
- (wrap
- (lambda (table key init)
- (hashx-create-handle! hash-proc assoc-proc table key init)))
- (wrap
- (lambda (table key)
- (hashx-remove! hash-proc assoc-proc table key)))))))))))
-
-(define (make-hash-table-mapping table hash-proc assoc-proc)
- (make-mapping (hash-table-mapping-hooks hash-proc assoc-proc) table))
-
-(define* (hash-table-mapping #\optional (size 71) #\key
- (hash-proc hash)
- (assoc-proc
- (or (assq-ref `((,hashq . ,assq)
- (,hashv . ,assv)
- (,hash . ,assoc))
- hash-proc)
- (error 'hash-table-mapping
- "Hash-procedure specified with no known assoc function."
- hash-proc)))
- (table-constructor
- (lambda (len) (make-vector len '()))))
- (make-hash-table-mapping (table-constructor size)
- hash-proc
- assoc-proc))
-;;; -*- mode: scheme; coding: utf-8; -*-
-;;;
-;;; Copyright (C) 2010, 2011, 2012 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (ice-9 match)
- #\export (match
- match-lambda
- match-lambda*
- match-let
- match-let*
- match-letrec))
-
-(define (error _ . args)
- ;; Error procedure for run-time "no matching pattern" errors.
- (apply throw 'match-error "match" args))
-
-;; Support for record matching.
-
-(define-syntax slot-ref
- (syntax-rules ()
- ((_ rtd rec n)
- (struct-ref rec n))))
-
-(define-syntax slot-set!
- (syntax-rules ()
- ((_ rtd rec n value)
- (struct-set! rec n value))))
-
-(define-syntax is-a?
- (syntax-rules ()
- ((_ rec rtd)
- (and (struct? rec)
- (eq? (struct-vtable rec) rtd)))))
-
-;; Compared to Andrew K. Wright's `match', this one lacks `match-define',
-;; `match:error-control', `match:set-error-control', `match:error',
-;; `match:set-error', and all structure-related procedures. Also,
-;; `match' doesn't support clauses of the form `(pat => exp)'.
-
-;; Unmodified public domain code by Alex Shinn retrieved from
-;; the Chibi-Scheme repository, commit 1206:acd808700e91.
-;;
-;; Note: Make sure to update `match.test.upstream' when updating this
-;; file.
-(include-from-path "ice-9/match.upstream.scm")
-;;;; match.scm -- portable hygienic pattern matcher -*- coding: utf-8 -*-
-;;
-;; This code is written by Alex Shinn and placed in the
-;; Public Domain. All warranties are disclaimed.
-
-;;> @example-import[(srfi 9)]
-
-;;> This is a full superset of the popular @hyperlink[
-;;> "http://www.cs.indiana.edu/scheme-repository/code.match.html"]{match}
-;;> package by Andrew Wright, written in fully portable @scheme{syntax-rules}
-;;> and thus preserving hygiene.
-
-;;> The most notable extensions are the ability to use @emph{non-linear}
-;;> patterns - patterns in which the same identifier occurs multiple
-;;> times, tail patterns after ellipsis, and the experimental tree patterns.
-
-;;> @subsubsection{Patterns}
-
-;;> Patterns are written to look like the printed representation of
-;;> the objects they match. The basic usage is
-
-;;> @scheme{(match expr (pat body ...) ...)}
-
-;;> where the result of @var{expr} is matched against each pattern in
-;;> turn, and the corresponding body is evaluated for the first to
-;;> succeed. Thus, a list of three elements matches a list of three
-;;> elements.
-
-;;> @example{(let ((ls (list 1 2 3))) (match ls ((1 2 3) #t)))}
-
-;;> If no patterns match an error is signalled.
-
-;;> Identifiers will match anything, and make the corresponding
-;;> binding available in the body.
-
-;;> @example{(match (list 1 2 3) ((a b c) b))}
-
-;;> If the same identifier occurs multiple times, the first instance
-;;> will match anything, but subsequent instances must match a value
-;;> which is @scheme{equal?} to the first.
-
-;;> @example{(match (list 1 2 1) ((a a b) 1) ((a b a) 2))}
-
-;;> The special identifier @scheme{_} matches anything, no matter how
-;;> many times it is used, and does not bind the result in the body.
-
-;;> @example{(match (list 1 2 1) ((_ _ b) 1) ((a b a) 2))}
-
-;;> To match a literal identifier (or list or any other literal), use
-;;> @scheme{quote}.
-
-;;> @example{(match 'a ('b 1) ('a 2))}
-
-;;> Analogous to its normal usage in scheme, @scheme{quasiquote} can
-;;> be used to quote a mostly literally matching object with selected
-;;> parts unquoted.
-
-;;> @example|{(match (list 1 2 3) (`(1 ,b ,c) (list b c)))}|
-
-;;> Often you want to match any number of a repeated pattern. Inside
-;;> a list pattern you can append @scheme{...} after an element to
-;;> match zero or more of that pattern (like a regexp Kleene star).
-
-;;> @example{(match (list 1 2) ((1 2 3 ...) #t))}
-;;> @example{(match (list 1 2 3) ((1 2 3 ...) #t))}
-;;> @example{(match (list 1 2 3 3 3) ((1 2 3 ...) #t))}
-
-;;> Pattern variables matched inside the repeated pattern are bound to
-;;> a list of each matching instance in the body.
-
-;;> @example{(match (list 1 2) ((a b c ...) c))}
-;;> @example{(match (list 1 2 3) ((a b c ...) c))}
-;;> @example{(match (list 1 2 3 4 5) ((a b c ...) c))}
-
-;;> More than one @scheme{...} may not be used in the same list, since
-;;> this would require exponential backtracking in the general case.
-;;> However, @scheme{...} need not be the final element in the list,
-;;> and may be succeeded by a fixed number of patterns.
-
-;;> @example{(match (list 1 2 3 4) ((a b c ... d e) c))}
-;;> @example{(match (list 1 2 3 4 5) ((a b c ... d e) c))}
-;;> @example{(match (list 1 2 3 4 5 6 7) ((a b c ... d e) c))}
-
-;;> @scheme{___} is provided as an alias for @scheme{...} when it is
-;;> inconvenient to use the ellipsis (as in a syntax-rules template).
-
-;;> The @scheme{..1} syntax is exactly like the @scheme{...} except
-;;> that it matches one or more repetitions (like a regexp "+").
-
-;;> @example{(match (list 1 2) ((a b c ..1) c))}
-;;> @example{(match (list 1 2 3) ((a b c ..1) c))}
-
-;;> The boolean operators @scheme{and}, @scheme{or} and @scheme{not}
-;;> can be used to group and negate patterns analogously to their
-;;> Scheme counterparts.
-
-;;> The @scheme{and} operator ensures that all subpatterns match.
-;;> This operator is often used with the idiom @scheme{(and x pat)} to
-;;> bind @var{x} to the entire value that matches @var{pat}
-;;> (c.f. "as-patterns" in ML or Haskell). Another common use is in
-;;> conjunction with @scheme{not} patterns to match a general case
-;;> with certain exceptions.
-
-;;> @example{(match 1 ((and) #t))}
-;;> @example{(match 1 ((and x) x))}
-;;> @example{(match 1 ((and x 1) x))}
-
-;;> The @scheme{or} operator ensures that at least one subpattern
-;;> matches. If the same identifier occurs in different subpatterns,
-;;> it is matched independently. All identifiers from all subpatterns
-;;> are bound if the @scheme{or} operator matches, but the binding is
-;;> only defined for identifiers from the subpattern which matched.
-
-;;> @example{(match 1 ((or) #t) (else #f))}
-;;> @example{(match 1 ((or x) x))}
-;;> @example{(match 1 ((or x 2) x))}
-
-;;> The @scheme{not} operator succeeds if the given pattern doesn't
-;;> match. None of the identifiers used are available in the body.
-
-;;> @example{(match 1 ((not 2) #t))}
-
-;;> The more general operator @scheme{?} can be used to provide a
-;;> predicate. The usage is @scheme{(? predicate pat ...)} where
-;;> @var{predicate} is a Scheme expression evaluating to a predicate
-;;> called on the value to match, and any optional patterns after the
-;;> predicate are then matched as in an @scheme{and} pattern.
-
-;;> @example{(match 1 ((? odd? x) x))}
-
-;;> The field operator @scheme{=} is used to extract an arbitrary
-;;> field and match against it. It is useful for more complex or
-;;> conditional destructuring that can't be more directly expressed in
-;;> the pattern syntax. The usage is @scheme{(= field pat)}, where
-;;> @var{field} can be any expression, and should result in a
-;;> procedure of one argument, which is applied to the value to match
-;;> to generate a new value to match against @var{pat}.
-
-;;> Thus the pattern @scheme{(and (= car x) (= cdr y))} is equivalent
-;;> to @scheme{(x . y)}, except it will result in an immediate error
-;;> if the value isn't a pair.
-
-;;> @example{(match '(1 . 2) ((= car x) x))}
-;;> @example{(match 4 ((= sqrt x) x))}
-
-;;> The record operator @scheme{$} is used as a concise way to match
-;;> records defined by SRFI-9 (or SRFI-99). The usage is
-;;> @scheme{($ rtd field ...)}, where @var{rtd} should be the record
-;;> type descriptor specified as the first argument to
-;;> @scheme{define-record-type}, and each @var{field} is a subpattern
-;;> matched against the fields of the record in order. Not all fields
-;;> must be present.
-
-;;> @example{
-;;> (let ()
-;;> (define-record-type employee
-;;> (make-employee name title)
-;;> employee?
-;;> (name get-name)
-;;> (title get-title))
-;;> (match (make-employee "Bob" "Doctor")
-;;> (($ employee n t) (list t n))))
-;;> }
-
-;;> The @scheme{set!} and @scheme{get!} operators are used to bind an
-;;> identifier to the setter and getter of a field, respectively. The
-;;> setter is a procedure of one argument, which mutates the field to
-;;> that argument. The getter is a procedure of no arguments which
-;;> returns the current value of the field.
-
-;;> @example{(let ((x (cons 1 2))) (match x ((1 . (set! s)) (s 3) x)))}
-;;> @example{(match '(1 . 2) ((1 . (get! g)) (g)))}
-
-;;> The new operator @scheme{***} can be used to search a tree for
-;;> subpatterns. A pattern of the form @scheme{(x *** y)} represents
-;;> the subpattern @var{y} located somewhere in a tree where the path
-;;> from the current object to @var{y} can be seen as a list of the
-;;> form @scheme{(x ...)}. @var{y} can immediately match the current
-;;> object in which case the path is the empty list. In a sense it's
-;;> a 2-dimensional version of the @scheme{...} pattern.
-
-;;> As a common case the pattern @scheme{(_ *** y)} can be used to
-;;> search for @var{y} anywhere in a tree, regardless of the path
-;;> used.
-
-;;> @example{(match '(a (a (a b))) ((x *** 'b) x))}
-;;> @example{(match '(a (b) (c (d e) (f g))) ((x *** 'g) x))}
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Notes
-
-;; The implementation is a simple generative pattern matcher - each
-;; pattern is expanded into the required tests, calling a failure
-;; continuation if the tests fail. This makes the logic easy to
-;; follow and extend, but produces sub-optimal code in cases where you
-;; have many similar clauses due to repeating the same tests.
-;; Nonetheless a smart compiler should be able to remove the redundant
-;; tests. For MATCH-LET and DESTRUCTURING-BIND type uses there is no
-;; performance hit.
-
-;; The original version was written on 2006/11/29 and described in the
-;; following Usenet post:
-;; http://groups.google.com/group/comp.lang.scheme/msg/0941234de7112ffd
-;; and is still available at
-;; http://synthcode.com/scheme/match-simple.scm
-;; It's just 80 lines for the core MATCH, and an extra 40 lines for
-;; MATCH-LET, MATCH-LAMBDA and other syntactic sugar.
-;;
-;; A variant of this file which uses COND-EXPAND in a few places for
-;; performance can be found at
-;; http://synthcode.com/scheme/match-cond-expand.scm
-;;
-;; 2012/05/23 - fixing combinatorial explosion of code in certain or patterns
-;; 2011/09/25 - fixing bug when directly matching an identifier repeated in
-;; the pattern (thanks to Stefan Israelsson Tampe)
-;; 2011/01/27 - fixing bug when matching tail patterns against improper lists
-;; 2010/09/26 - adding `..1' patterns (thanks to Ludovic Courtès)
-;; 2010/09/07 - fixing identifier extraction in some `...' and `***' patterns
-;; 2009/11/25 - adding `***' tree search patterns
-;; 2008/03/20 - fixing bug where (a ...) matched non-lists
-;; 2008/03/15 - removing redundant check in vector patterns
-;; 2008/03/06 - you can use `...' portably now (thanks to Taylor Campbell)
-;; 2007/09/04 - fixing quasiquote patterns
-;; 2007/07/21 - allowing ellipse patterns in non-final list positions
-;; 2007/04/10 - fixing potential hygiene issue in match-check-ellipse
-;; (thanks to Taylor Campbell)
-;; 2007/04/08 - clean up, commenting
-;; 2006/12/24 - bugfixes
-;; 2006/12/01 - non-linear patterns, shared variables in OR, get!/set!
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; force compile-time syntax errors with useful messages
-
-(define-syntax match-syntax-error
- (syntax-rules ()
- ((_) (match-syntax-error "invalid match-syntax-error usage"))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;;> @subsubsection{Syntax}
-
-;;> @subsubsubsection{@rawcode{(match expr (pattern . body) ...)@br{}
-;;> (match expr (pattern (=> failure) . body) ...)}}
-
-;;> The result of @var{expr} is matched against each @var{pattern} in
-;;> turn, according to the pattern rules described in the previous
-;;> section, until the the first @var{pattern} matches. When a match is
-;;> found, the corresponding @var{body}s are evaluated in order,
-;;> and the result of the last expression is returned as the result
-;;> of the entire @scheme{match}. If a @var{failure} is provided,
-;;> then it is bound to a procedure of no arguments which continues,
-;;> processing at the next @var{pattern}. If no @var{pattern} matches,
-;;> an error is signalled.
-
-;; The basic interface. MATCH just performs some basic syntax
-;; validation, binds the match expression to a temporary variable `v',
-;; and passes it on to MATCH-NEXT. It's a constant throughout the
-;; code below that the binding `v' is a direct variable reference, not
-;; an expression.
-
-(define-syntax match
- (syntax-rules ()
- ((match)
- (match-syntax-error "missing match expression"))
- ((match atom)
- (match-syntax-error "no match clauses"))
- ((match (app ...) (pat . body) ...)
- (let ((v (app ...)))
- (match-next v ((app ...) (set! (app ...))) (pat . body) ...)))
- ((match #(vec ...) (pat . body) ...)
- (let ((v #(vec ...)))
- (match-next v (v (set! v)) (pat . body) ...)))
- ((match atom (pat . body) ...)
- (let ((v atom))
- (match-next v (atom (set! atom)) (pat . body) ...)))
- ))
-
-;; MATCH-NEXT passes each clause to MATCH-ONE in turn with its failure
-;; thunk, which is expanded by recursing MATCH-NEXT on the remaining
-;; clauses. `g+s' is a list of two elements, the get! and set!
-;; expressions respectively.
-
-(define-syntax match-next
- (syntax-rules (=>)
- ;; no more clauses, the match failed
- ((match-next v g+s)
- ;; Here we wrap error within a double set of parentheses, so that
- ;; the call to 'error' won't be in tail position. This allows the
- ;; backtrace to show the source location of the failing match form.
- ((error 'match "no matching pattern" v)))
- ;; named failure continuation
- ((match-next v g+s (pat (=> failure) . body) . rest)
- (let ((failure (lambda () (match-next v g+s . rest))))
- ;; match-one analyzes the pattern for us
- (match-one v pat g+s (match-drop-ids (begin . body)) (failure) ())))
- ;; anonymous failure continuation, give it a dummy name
- ((match-next v g+s (pat . body) . rest)
- (match-next v g+s (pat (=> failure) . body) . rest))))
-
-;; MATCH-ONE first checks for ellipse patterns, otherwise passes on to
-;; MATCH-TWO.
-
-(define-syntax match-one
- (syntax-rules ()
- ;; If it's a list of two or more values, check to see if the
- ;; second one is an ellipse and handle accordingly, otherwise go
- ;; to MATCH-TWO.
- ((match-one v (p q . r) g+s sk fk i)
- (match-check-ellipse
- q
- (match-extract-vars p (match-gen-ellipses v p r g+s sk fk i) i ())
- (match-two v (p q . r) g+s sk fk i)))
- ;; Go directly to MATCH-TWO.
- ((match-one . x)
- (match-two . x))))
-
-;; This is the guts of the pattern matcher. We are passed a lot of
-;; information in the form:
-;;
-;; (match-two var pattern getter setter success-k fail-k (ids ...))
-;;
-;; usually abbreviated
-;;
-;; (match-two v p g+s sk fk i)
-;;
-;; where VAR is the symbol name of the current variable we are
-;; matching, PATTERN is the current pattern, getter and setter are the
-;; corresponding accessors (e.g. CAR and SET-CAR! of the pair holding
-;; VAR), SUCCESS-K is the success continuation, FAIL-K is the failure
-;; continuation (which is just a thunk call and is thus safe to expand
-;; multiple times) and IDS are the list of identifiers bound in the
-;; pattern so far.
-
-(define-syntax match-two
- (syntax-rules (_ ___ \.\.1 *** quote quasiquote ? $ = and or not set! get!)
- ((match-two v () g+s (sk ...) fk i)
- (if (null? v) (sk ... i) fk))
- ((match-two v (quote p) g+s (sk ...) fk i)
- (if (equal? v 'p) (sk ... i) fk))
- ((match-two v (quasiquote p) . x)
- (match-quasiquote v p . x))
- ((match-two v (and) g+s (sk ...) fk i) (sk ... i))
- ((match-two v (and p q ...) g+s sk fk i)
- (match-one v p g+s (match-one v (and q ...) g+s sk fk) fk i))
- ((match-two v (or) g+s sk fk i) fk)
- ((match-two v (or p) . x)
- (match-one v p . x))
- ((match-two v (or p ...) g+s sk fk i)
- (match-extract-vars (or p ...) (match-gen-or v (p ...) g+s sk fk i) i ()))
- ((match-two v (not p) g+s (sk ...) fk i)
- (match-one v p g+s (match-drop-ids fk) (sk ... i) i))
- ((match-two v (get! getter) (g s) (sk ...) fk i)
- (let ((getter (lambda () g))) (sk ... i)))
- ((match-two v (set! setter) (g (s ...)) (sk ...) fk i)
- (let ((setter (lambda (x) (s ... x)))) (sk ... i)))
- ((match-two v (? pred . p) g+s sk fk i)
- (if (pred v) (match-one v (and . p) g+s sk fk i) fk))
- ((match-two v (= proc p) . x)
- (let ((w (proc v))) (match-one w p . x)))
- ((match-two v (p ___ . r) g+s sk fk i)
- (match-extract-vars p (match-gen-ellipses v p r g+s sk fk i) i ()))
- ((match-two v (p) g+s sk fk i)
- (if (and (pair? v) (null? (cdr v)))
- (let ((w (car v)))
- (match-one w p ((car v) (set-car! v)) sk fk i))
- fk))
- ((match-two v (p *** q) g+s sk fk i)
- (match-extract-vars p (match-gen-search v p q g+s sk fk i) i ()))
- ((match-two v (p *** . q) g+s sk fk i)
- (match-syntax-error "invalid use of ***" (p *** . q)))
- ((match-two v (p \.\.1) g+s sk fk i)
- (if (pair? v)
- (match-one v (p ___) g+s sk fk i)
- fk))
- ((match-two v ($ rec p ...) g+s sk fk i)
- (if (is-a? v rec)
- (match-record-refs v rec 0 (p ...) g+s sk fk i)
- fk))
- ((match-two v (p . q) g+s sk fk i)
- (if (pair? v)
- (let ((w (car v)) (x (cdr v)))
- (match-one w p ((car v) (set-car! v))
- (match-one x q ((cdr v) (set-cdr! v)) sk fk)
- fk
- i))
- fk))
- ((match-two v #(p ...) g+s . x)
- (match-vector v 0 () (p ...) . x))
- ((match-two v _ g+s (sk ...) fk i) (sk ... i))
- ;; Not a pair or vector or special literal, test to see if it's a
- ;; new symbol, in which case we just bind it, or if it's an
- ;; already bound symbol or some other literal, in which case we
- ;; compare it with EQUAL?.
- ((match-two v x g+s (sk ...) fk (id ...))
- (let-syntax
- ((new-sym?
- (syntax-rules (id ...)
- ((new-sym? x sk2 fk2) sk2)
- ((new-sym? y sk2 fk2) fk2))))
- (new-sym? random-sym-to-match
- (let ((x v)) (sk ... (id ... x)))
- (if (equal? v x) (sk ... (id ...)) fk))))
- ))
-
-;; QUASIQUOTE patterns
-
-(define-syntax match-quasiquote
- (syntax-rules (unquote unquote-splicing quasiquote)
- ((_ v (unquote p) g+s sk fk i)
- (match-one v p g+s sk fk i))
- ((_ v ((unquote-splicing p) . rest) g+s sk fk i)
- (if (pair? v)
- (match-one v
- (p . tmp)
- (match-quasiquote tmp rest g+s sk fk)
- fk
- i)
- fk))
- ((_ v (quasiquote p) g+s sk fk i . depth)
- (match-quasiquote v p g+s sk fk i #f . depth))
- ((_ v (unquote p) g+s sk fk i x . depth)
- (match-quasiquote v p g+s sk fk i . depth))
- ((_ v (unquote-splicing p) g+s sk fk i x . depth)
- (match-quasiquote v p g+s sk fk i . depth))
- ((_ v (p . q) g+s sk fk i . depth)
- (if (pair? v)
- (let ((w (car v)) (x (cdr v)))
- (match-quasiquote
- w p g+s
- (match-quasiquote-step x q g+s sk fk depth)
- fk i . depth))
- fk))
- ((_ v #(elt ...) g+s sk fk i . depth)
- (if (vector? v)
- (let ((ls (vector->list v)))
- (match-quasiquote ls (elt ...) g+s sk fk i . depth))
- fk))
- ((_ v x g+s sk fk i . depth)
- (match-one v 'x g+s sk fk i))))
-
-(define-syntax match-quasiquote-step
- (syntax-rules ()
- ((match-quasiquote-step x q g+s sk fk depth i)
- (match-quasiquote x q g+s sk fk i . depth))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Utilities
-
-;; Takes two values and just expands into the first.
-(define-syntax match-drop-ids
- (syntax-rules ()
- ((_ expr ids ...) expr)))
-
-(define-syntax match-tuck-ids
- (syntax-rules ()
- ((_ (letish args (expr ...)) ids ...)
- (letish args (expr ... ids ...)))))
-
-(define-syntax match-drop-first-arg
- (syntax-rules ()
- ((_ arg expr) expr)))
-
-;; To expand an OR group we try each clause in succession, passing the
-;; first that succeeds to the success continuation. On failure for
-;; any clause, we just try the next clause, finally resorting to the
-;; failure continuation fk if all clauses fail. The only trick is
-;; that we want to unify the identifiers, so that the success
-;; continuation can refer to a variable from any of the OR clauses.
-
-(define-syntax match-gen-or
- (syntax-rules ()
- ((_ v p g+s (sk ...) fk (i ...) ((id id-ls) ...))
- (let ((sk2 (lambda (id ...) (sk ... (i ... id ...)))))
- (match-gen-or-step v p g+s (match-drop-ids (sk2 id ...)) fk (i ...))))))
-
-(define-syntax match-gen-or-step
- (syntax-rules ()
- ((_ v () g+s sk fk . x)
- ;; no OR clauses, call the failure continuation
- fk)
- ((_ v (p) . x)
- ;; last (or only) OR clause, just expand normally
- (match-one v p . x))
- ((_ v (p . q) g+s sk fk i)
- ;; match one and try the remaining on failure
- (let ((fk2 (lambda () (match-gen-or-step v q g+s sk fk i))))
- (match-one v p g+s sk (fk2) i)))
- ))
-
-;; We match a pattern (p ...) by matching the pattern p in a loop on
-;; each element of the variable, accumulating the bound ids into lists.
-
-;; Look at the body of the simple case - it's just a named let loop,
-;; matching each element in turn to the same pattern. The only trick
-;; is that we want to keep track of the lists of each extracted id, so
-;; when the loop recurses we cons the ids onto their respective list
-;; variables, and on success we bind the ids (what the user input and
-;; expects to see in the success body) to the reversed accumulated
-;; list IDs.
-
-(define-syntax match-gen-ellipses
- (syntax-rules ()
- ((_ v p () g+s (sk ...) fk i ((id id-ls) ...))
- (match-check-identifier p
- ;; simplest case equivalent to (p ...), just bind the list
- (let ((p v))
- (if (list? p)
- (sk ... i)
- fk))
- ;; simple case, match all elements of the list
- (let loop ((ls v) (id-ls '()) ...)
- (cond
- ((null? ls)
- (let ((id (reverse id-ls)) ...) (sk ... i)))
- ((pair? ls)
- (let ((w (car ls)))
- (match-one w p ((car ls) (set-car! ls))
- (match-drop-ids (loop (cdr ls) (cons id id-ls) ...))
- fk i)))
- (else
- fk)))))
- ((_ v p r g+s (sk ...) fk i ((id id-ls) ...))
- ;; general case, trailing patterns to match, keep track of the
- ;; remaining list length so we don't need any backtracking
- (match-verify-no-ellipses
- r
- (let* ((tail-len (length 'r))
- (ls v)
- (len (and (list? ls) (length ls))))
- (if (or (not len) (< len tail-len))
- fk
- (let loop ((ls ls) (n len) (id-ls '()) ...)
- (cond
- ((= n tail-len)
- (let ((id (reverse id-ls)) ...)
- (match-one ls r (#f #f) (sk ...) fk i)))
- ((pair? ls)
- (let ((w (car ls)))
- (match-one w p ((car ls) (set-car! ls))
- (match-drop-ids
- (loop (cdr ls) (- n 1) (cons id id-ls) ...))
- fk
- i)))
- (else
- fk)))))))))
-
-;; This is just a safety check. Although unlike syntax-rules we allow
-;; trailing patterns after an ellipses, we explicitly disable multiple
-;; ellipses at the same level. This is because in the general case
-;; such patterns are exponential in the number of ellipses, and we
-;; don't want to make it easy to construct very expensive operations
-;; with simple looking patterns. For example, it would be O(n^2) for
-;; patterns like (a ... b ...) because we must consider every trailing
-;; element for every possible break for the leading "a ...".
-
-(define-syntax match-verify-no-ellipses
- (syntax-rules ()
- ((_ (x . y) sk)
- (match-check-ellipse
- x
- (match-syntax-error
- "multiple ellipse patterns not allowed at same level")
- (match-verify-no-ellipses y sk)))
- ((_ () sk)
- sk)
- ((_ x sk)
- (match-syntax-error "dotted tail not allowed after ellipse" x))))
-
-;; To implement the tree search, we use two recursive procedures. TRY
-;; attempts to match Y once, and on success it calls the normal SK on
-;; the accumulated list ids as in MATCH-GEN-ELLIPSES. On failure, we
-;; call NEXT which first checks if the current value is a list
-;; beginning with X, then calls TRY on each remaining element of the
-;; list. Since TRY will recursively call NEXT again on failure, this
-;; effects a full depth-first search.
-;;
-;; The failure continuation throughout is a jump to the next step in
-;; the tree search, initialized with the original failure continuation
-;; FK.
-
-(define-syntax match-gen-search
- (syntax-rules ()
- ((match-gen-search v p q g+s sk fk i ((id id-ls) ...))
- (letrec ((try (lambda (w fail id-ls ...)
- (match-one w q g+s
- (match-tuck-ids
- (let ((id (reverse id-ls)) ...)
- sk))
- (next w fail id-ls ...) i)))
- (next (lambda (w fail id-ls ...)
- (if (not (pair? w))
- (fail)
- (let ((u (car w)))
- (match-one
- u p ((car w) (set-car! w))
- (match-drop-ids
- ;; accumulate the head variables from
- ;; the p pattern, and loop over the tail
- (let ((id-ls (cons id id-ls)) ...)
- (let lp ((ls (cdr w)))
- (if (pair? ls)
- (try (car ls)
- (lambda () (lp (cdr ls)))
- id-ls ...)
- (fail)))))
- (fail) i))))))
- ;; the initial id-ls binding here is a dummy to get the right
- ;; number of '()s
- (let ((id-ls '()) ...)
- (try v (lambda () fk) id-ls ...))))))
-
-;; Vector patterns are just more of the same, with the slight
-;; exception that we pass around the current vector index being
-;; matched.
-
-(define-syntax match-vector
- (syntax-rules (___)
- ((_ v n pats (p q) . x)
- (match-check-ellipse q
- (match-gen-vector-ellipses v n pats p . x)
- (match-vector-two v n pats (p q) . x)))
- ((_ v n pats (p ___) sk fk i)
- (match-gen-vector-ellipses v n pats p sk fk i))
- ((_ . x)
- (match-vector-two . x))))
-
-;; Check the exact vector length, then check each element in turn.
-
-(define-syntax match-vector-two
- (syntax-rules ()
- ((_ v n ((pat index) ...) () sk fk i)
- (if (vector? v)
- (let ((len (vector-length v)))
- (if (= len n)
- (match-vector-step v ((pat index) ...) sk fk i)
- fk))
- fk))
- ((_ v n (pats ...) (p . q) . x)
- (match-vector v (+ n 1) (pats ... (p n)) q . x))))
-
-(define-syntax match-vector-step
- (syntax-rules ()
- ((_ v () (sk ...) fk i) (sk ... i))
- ((_ v ((pat index) . rest) sk fk i)
- (let ((w (vector-ref v index)))
- (match-one w pat ((vector-ref v index) (vector-set! v index))
- (match-vector-step v rest sk fk)
- fk i)))))
-
-;; With a vector ellipse pattern we first check to see if the vector
-;; length is at least the required length.
-
-(define-syntax match-gen-vector-ellipses
- (syntax-rules ()
- ((_ v n ((pat index) ...) p sk fk i)
- (if (vector? v)
- (let ((len (vector-length v)))
- (if (>= len n)
- (match-vector-step v ((pat index) ...)
- (match-vector-tail v p n len sk fk)
- fk i)
- fk))
- fk))))
-
-(define-syntax match-vector-tail
- (syntax-rules ()
- ((_ v p n len sk fk i)
- (match-extract-vars p (match-vector-tail-two v p n len sk fk i) i ()))))
-
-(define-syntax match-vector-tail-two
- (syntax-rules ()
- ((_ v p n len (sk ...) fk i ((id id-ls) ...))
- (let loop ((j n) (id-ls '()) ...)
- (if (>= j len)
- (let ((id (reverse id-ls)) ...) (sk ... i))
- (let ((w (vector-ref v j)))
- (match-one w p ((vector-ref v j) (vetor-set! v j))
- (match-drop-ids (loop (+ j 1) (cons id id-ls) ...))
- fk i)))))))
-
-(define-syntax match-record-refs
- (syntax-rules ()
- ((_ v rec n (p . q) g+s sk fk i)
- (let ((w (slot-ref rec v n)))
- (match-one w p ((slot-ref rec v n) (slot-set! rec v n))
- (match-record-refs v rec (+ n 1) q g+s sk fk) fk i)))
- ((_ v rec n () g+s (sk ...) fk i)
- (sk ... i))))
-
-;; Extract all identifiers in a pattern. A little more complicated
-;; than just looking for symbols, we need to ignore special keywords
-;; and non-pattern forms (such as the predicate expression in ?
-;; patterns), and also ignore previously bound identifiers.
-;;
-;; Calls the continuation with all new vars as a list of the form
-;; ((orig-var tmp-name) ...), where tmp-name can be used to uniquely
-;; pair with the original variable (e.g. it's used in the ellipse
-;; generation for list variables).
-;;
-;; (match-extract-vars pattern continuation (ids ...) (new-vars ...))
-
-(define-syntax match-extract-vars
- (syntax-rules (_ ___ \.\.1 *** ? $ = quote quasiquote and or not get! set!)
- ((match-extract-vars (? pred . p) . x)
- (match-extract-vars p . x))
- ((match-extract-vars ($ rec . p) . x)
- (match-extract-vars p . x))
- ((match-extract-vars (= proc p) . x)
- (match-extract-vars p . x))
- ((match-extract-vars (quote x) (k ...) i v)
- (k ... v))
- ((match-extract-vars (quasiquote x) k i v)
- (match-extract-quasiquote-vars x k i v (#t)))
- ((match-extract-vars (and . p) . x)
- (match-extract-vars p . x))
- ((match-extract-vars (or . p) . x)
- (match-extract-vars p . x))
- ((match-extract-vars (not . p) . x)
- (match-extract-vars p . x))
- ;; A non-keyword pair, expand the CAR with a continuation to
- ;; expand the CDR.
- ((match-extract-vars (p q . r) k i v)
- (match-check-ellipse
- q
- (match-extract-vars (p . r) k i v)
- (match-extract-vars p (match-extract-vars-step (q . r) k i v) i ())))
- ((match-extract-vars (p . q) k i v)
- (match-extract-vars p (match-extract-vars-step q k i v) i ()))
- ((match-extract-vars #(p ...) . x)
- (match-extract-vars (p ...) . x))
- ((match-extract-vars _ (k ...) i v) (k ... v))
- ((match-extract-vars ___ (k ...) i v) (k ... v))
- ((match-extract-vars *** (k ...) i v) (k ... v))
- ((match-extract-vars \.\.1 (k ...) i v) (k ... v))
- ;; This is the main part, the only place where we might add a new
- ;; var if it's an unbound symbol.
- ((match-extract-vars p (k ...) (i ...) v)
- (let-syntax
- ((new-sym?
- (syntax-rules (i ...)
- ((new-sym? p sk fk) sk)
- ((new-sym? any sk fk) fk))))
- (new-sym? random-sym-to-match
- (k ... ((p p-ls) . v))
- (k ... v))))
- ))
-
-;; Stepper used in the above so it can expand the CAR and CDR
-;; separately.
-
-(define-syntax match-extract-vars-step
- (syntax-rules ()
- ((_ p k i v ((v2 v2-ls) ...))
- (match-extract-vars p k (v2 ... . i) ((v2 v2-ls) ... . v)))
- ))
-
-(define-syntax match-extract-quasiquote-vars
- (syntax-rules (quasiquote unquote unquote-splicing)
- ((match-extract-quasiquote-vars (quasiquote x) k i v d)
- (match-extract-quasiquote-vars x k i v (#t . d)))
- ((match-extract-quasiquote-vars (unquote-splicing x) k i v d)
- (match-extract-quasiquote-vars (unquote x) k i v d))
- ((match-extract-quasiquote-vars (unquote x) k i v (#t))
- (match-extract-vars x k i v))
- ((match-extract-quasiquote-vars (unquote x) k i v (#t . d))
- (match-extract-quasiquote-vars x k i v d))
- ((match-extract-quasiquote-vars (x . y) k i v (#t . d))
- (match-extract-quasiquote-vars
- x
- (match-extract-quasiquote-vars-step y k i v d) i ()))
- ((match-extract-quasiquote-vars #(x ...) k i v (#t . d))
- (match-extract-quasiquote-vars (x ...) k i v d))
- ((match-extract-quasiquote-vars x (k ...) i v (#t . d))
- (k ... v))
- ))
-
-(define-syntax match-extract-quasiquote-vars-step
- (syntax-rules ()
- ((_ x k i v d ((v2 v2-ls) ...))
- (match-extract-quasiquote-vars x k (v2 ... . i) ((v2 v2-ls) ... . v) d))
- ))
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Gimme some sugar baby.
-
-;;> Shortcut for @scheme{lambda} + @scheme{match}. Creates a
-;;> procedure of one argument, and matches that argument against each
-;;> clause.
-
-(define-syntax match-lambda
- (syntax-rules ()
- ((_ (pattern . body) ...) (lambda (expr) (match expr (pattern . body) ...)))))
-
-;;> Similar to @scheme{match-lambda}. Creates a procedure of any
-;;> number of arguments, and matches the argument list against each
-;;> clause.
-
-(define-syntax match-lambda*
- (syntax-rules ()
- ((_ (pattern . body) ...) (lambda expr (match expr (pattern . body) ...)))))
-
-;;> Matches each var to the corresponding expression, and evaluates
-;;> the body with all match variables in scope. Raises an error if
-;;> any of the expressions fail to match. Syntax analogous to named
-;;> let can also be used for recursive functions which match on their
-;;> arguments as in @scheme{match-lambda*}.
-
-(define-syntax match-let
- (syntax-rules ()
- ((_ ((var value) ...) . body)
- (match-let/helper let () () ((var value) ...) . body))
- ((_ loop ((var init) ...) . body)
- (match-named-let loop ((var init) ...) . body))))
-
-;;> Similar to @scheme{match-let}, but analogously to @scheme{letrec}
-;;> matches and binds the variables with all match variables in scope.
-
-(define-syntax match-letrec
- (syntax-rules ()
- ((_ ((var value) ...) . body)
- (match-let/helper letrec () () ((var value) ...) . body))))
-
-(define-syntax match-let/helper
- (syntax-rules ()
- ((_ let ((var expr) ...) () () . body)
- (let ((var expr) ...) . body))
- ((_ let ((var expr) ...) ((pat tmp) ...) () . body)
- (let ((var expr) ...)
- (match-let* ((pat tmp) ...)
- . body)))
- ((_ let (v ...) (p ...) (((a . b) expr) . rest) . body)
- (match-let/helper
- let (v ... (tmp expr)) (p ... ((a . b) tmp)) rest . body))
- ((_ let (v ...) (p ...) ((#(a ...) expr) . rest) . body)
- (match-let/helper
- let (v ... (tmp expr)) (p ... (#(a ...) tmp)) rest . body))
- ((_ let (v ...) (p ...) ((a expr) . rest) . body)
- (match-let/helper let (v ... (a expr)) (p ...) rest . body))))
-
-(define-syntax match-named-let
- (syntax-rules ()
- ((_ loop ((pat expr var) ...) () . body)
- (let loop ((var expr) ...)
- (match-let ((pat var) ...)
- . body)))
- ((_ loop (v ...) ((pat expr) . rest) . body)
- (match-named-let loop (v ... (pat expr tmp)) rest . body))))
-
-;;> @subsubsubsection{@rawcode{(match-let* ((var value) ...) body ...)}}
-
-;;> Similar to @scheme{match-let}, but analogously to @scheme{let*}
-;;> matches and binds the variables in sequence, with preceding match
-;;> variables in scope.
-
-(define-syntax match-let*
- (syntax-rules ()
- ((_ () . body)
- (begin . body))
- ((_ ((pat expr) . rest) . body)
- (match expr (pat (match-let* rest . body))))))
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Otherwise COND-EXPANDed bits.
-
-;; This *should* work, but doesn't :(
-;; (define-syntax match-check-ellipse
-;; (syntax-rules (...)
-;; ((_ ... sk fk) sk)
-;; ((_ x sk fk) fk)))
-
-;; This is a little more complicated, and introduces a new let-syntax,
-;; but should work portably in any R[56]RS Scheme. Taylor Campbell
-;; originally came up with the idea.
-(define-syntax match-check-ellipse
- (syntax-rules ()
- ;; these two aren't necessary but provide fast-case failures
- ((match-check-ellipse (a . b) success-k failure-k) failure-k)
- ((match-check-ellipse #(a ...) success-k failure-k) failure-k)
- ;; matching an atom
- ((match-check-ellipse id success-k failure-k)
- (let-syntax ((ellipse? (syntax-rules ()
- ;; iff `id' is `...' here then this will
- ;; match a list of any length
- ((ellipse? (foo id) sk fk) sk)
- ((ellipse? other sk fk) fk))))
- ;; this list of three elements will only many the (foo id) list
- ;; above if `id' is `...'
- (ellipse? (a b c) success-k failure-k)))))
-
-
-;; This is portable but can be more efficient with non-portable
-;; extensions. This trick was originally discovered by Oleg Kiselyov.
-
-(define-syntax match-check-identifier
- (syntax-rules ()
- ;; fast-case failures, lists and vectors are not identifiers
- ((_ (x . y) success-k failure-k) failure-k)
- ((_ #(x ...) success-k failure-k) failure-k)
- ;; x is an atom
- ((_ x success-k failure-k)
- (let-syntax
- ((sym?
- (syntax-rules ()
- ;; if the symbol `abracadabra' matches x, then x is a
- ;; symbol
- ((sym? x sk fk) sk)
- ;; otherwise x is a non-symbol datum
- ((sym? y sk fk) fk))))
- (sym? abracadabra success-k failure-k)))))
-;;; installed-scm-file
-
-;;;; Copyright (C) 1999, 2005, 2006, 2010 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-(eval-when (compile)
- (set-current-module (resolve-module '(guile))))
-
-(define (gethostbyaddr addr) (gethost addr))
-(define (gethostbyname name) (gethost name))
-
-(define (getnetbyaddr addr) (getnet addr))
-(define (getnetbyname name) (getnet name))
-
-(define (getprotobyname name) (getproto name))
-(define (getprotobynumber addr) (getproto addr))
-
-(define (getservbyname name proto) (getserv name proto))
-(define (getservbyport port proto) (getserv port proto))
-
-(define (sethostent . stayopen)
- (if (pair? stayopen)
- (sethost (car stayopen))
- (sethost #f)))
-(define (setnetent . stayopen)
- (if (pair? stayopen)
- (setnet (car stayopen))
- (setnet #f)))
-(define (setprotoent . stayopen)
- (if (pair? stayopen)
- (setproto (car stayopen))
- (setproto #f)))
-(define (setservent . stayopen)
- (if (pair? stayopen)
- (setserv (car stayopen))
- (setserv #f)))
-
-(define (gethostent) (gethost))
-(define (getnetent) (getnet))
-(define (getprotoent) (getproto))
-(define (getservent) (getserv))
-
-(define (endhostent) (sethost))
-(define (endnetent) (setnet))
-(define (endprotoent) (setproto))
-(define (endservent) (setserv))
-
-(define (hostent:name obj) (vector-ref obj 0))
-(define (hostent:aliases obj) (vector-ref obj 1))
-(define (hostent:addrtype obj) (vector-ref obj 2))
-(define (hostent:length obj) (vector-ref obj 3))
-(define (hostent:addr-list obj) (vector-ref obj 4))
-
-(define (netent:name obj) (vector-ref obj 0))
-(define (netent:aliases obj) (vector-ref obj 1))
-(define (netent:addrtype obj) (vector-ref obj 2))
-(define (netent:net obj) (vector-ref obj 3))
-
-(define (protoent:name obj) (vector-ref obj 0))
-(define (protoent:aliases obj) (vector-ref obj 1))
-(define (protoent:proto obj) (vector-ref obj 2))
-
-(define (servent:name obj) (vector-ref obj 0))
-(define (servent:aliases obj) (vector-ref obj 1))
-(define (servent:port obj) (vector-ref obj 2))
-(define (servent:proto obj) (vector-ref obj 3))
-
-(define (sockaddr:fam obj) (vector-ref obj 0))
-(define (sockaddr:path obj) (vector-ref obj 1))
-(define (sockaddr:addr obj) (vector-ref obj 1))
-(define (sockaddr:port obj) (vector-ref obj 2))
-(define (sockaddr:flowinfo obj) (vector-ref obj 3))
-(define (sockaddr:scopeid obj) (vector-ref obj 4))
-
-(define (addrinfo:flags obj) (vector-ref obj 0))
-(define (addrinfo:fam obj) (vector-ref obj 1))
-(define (addrinfo:socktype obj) (vector-ref obj 2))
-(define (addrinfo:protocol obj) (vector-ref obj 3))
-(define (addrinfo:addr obj) (vector-ref obj 4))
-(define (addrinfo:canonname obj) (vector-ref obj 5))
-;;;; Copyright (C) 2000, 2001, 2006 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;;; The null environment - only syntactic bindings
-
-(define-module (ice-9 null)
- \:re-export-syntax (define quote lambda if set!
-
- cond case and or
-
- let let* letrec
-
- begin do
-
- delay
-
- quasiquote
-
- define-syntax
- let-syntax letrec-syntax))
-;;;; Occam-like channels
-
-;;; Copyright (C) 2003, 2006 Free Software Foundation, Inc.
-;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (ice-9 occam-channel)
- #\use-module (oop goops)
- #\use-module (ice-9 threads)
- #\export-syntax (alt
- ;; macro use:
- oc:lock oc:unlock oc:consequence
- oc:immediate-dispatch oc:late-dispatch oc:first-channel
- oc:set-handshake-channel oc:unset-handshake-channel)
- #\export (make-channel
- ?
- !
- make-timer
- ;; macro use:
- handshake-channel mutex
- sender-waiting?
- immediate-receive late-receive
- )
- )
-
-(define no-data '(no-data))
-(define receiver-waiting '(receiver-waiting))
-
-(define-class <channel> ())
-
-(define-class <data-channel> (<channel>)
- (handshake-channel #\accessor handshake-channel)
- (data #\accessor data #\init-value no-data)
- (cv #\accessor cv #\init-form (make-condition-variable))
- (mutex #\accessor mutex #\init-form (make-mutex)))
-
-(define-method (initialize (ch <data-channel>) initargs)
- (next-method)
- (set! (handshake-channel ch) ch))
-
-(define-method (make-channel)
- (make <data-channel>))
-
-(define-method (sender-waiting? (ch <data-channel>))
- (not (eq? (data ch) no-data)))
-
-(define-method (receiver-waiting? (ch <data-channel>))
- (eq? (data ch) receiver-waiting))
-
-(define-method (immediate-receive (ch <data-channel>))
- (signal-condition-variable (cv ch))
- (let ((res (data ch)))
- (set! (data ch) no-data)
- res))
-
-(define-method (late-receive (ch <data-channel>))
- (let ((res (data ch)))
- (set! (data ch) no-data)
- res))
-
-(define-method (? (ch <data-channel>))
- (lock-mutex (mutex ch))
- (let ((res (cond ((receiver-waiting? ch)
- (unlock-mutex (mutex ch))
- (scm-error 'misc-error '?
- "another process is already receiving on ~A"
- (list ch) #f))
- ((sender-waiting? ch)
- (immediate-receive ch))
- (else
- (set! (data ch) receiver-waiting)
- (wait-condition-variable (cv ch) (mutex ch))
- (late-receive ch)))))
- (unlock-mutex (mutex ch))
- res))
-
-(define-method (! (ch <data-channel>))
- (! ch *unspecified*))
-
-(define-method (! (ch <data-channel>) (x <top>))
- (lock-mutex (mutex (handshake-channel ch)))
- (cond ((receiver-waiting? ch)
- (set! (data ch) x)
- (signal-condition-variable (cv (handshake-channel ch))))
- ((sender-waiting? ch)
- (unlock-mutex (mutex (handshake-channel ch)))
- (scm-error 'misc-error '! "another process is already sending on ~A"
- (list ch) #f))
- (else
- (set! (data ch) x)
- (wait-condition-variable (cv ch) (mutex ch))))
- (unlock-mutex (mutex (handshake-channel ch))))
-
-;;; Add protocols?
-
-(define-class <port-channel> (<channel>)
- (port #\accessor port #\init-keyword #\port))
-
-(define-method (make-channel (port <port>))
- (make <port-channel> #\port port))
-
-(define-method (? (ch <port-channel>))
- (read (port ch)))
-
-(define-method (! (ch <port-channel>))
- (write (port ch)))
-
-(define-class <timer-channel> (<channel>))
-
-(define the-timer (make <timer-channel>))
-
-(define timer-cv (make-condition-variable))
-(define timer-mutex (make-mutex))
-
-(define (make-timer)
- the-timer)
-
-(define (timeofday->us t)
- (+ (* 1000000 (car t)) (cdr t)))
-
-(define (us->timeofday n)
- (cons (quotient n 1000000)
- (remainder n 1000000)))
-
-(define-method (? (ch <timer-channel>))
- (timeofday->us (gettimeofday)))
-
-(define-method (? (ch <timer-channel>) (t <integer>))
- (lock-mutex timer-mutex)
- (wait-condition-variable timer-cv timer-mutex (us->timeofday t))
- (unlock-mutex timer-mutex))
-
-;;; (alt CLAUSE ...)
-;;;
-;;; CLAUSE ::= ((? CH) FORM ...)
-;;; | (EXP (? CH) FORM ...)
-;;; | (EXP FORM ...)
-;;;
-;;; where FORM ... can be => (lambda (x) ...)
-;;;
-;;; *fixme* Currently only handles <data-channel>:s
-;;;
-
-(define-syntax oc:lock
- (syntax-rules (?)
- ((_ ((? ch) form ...)) (lock-mutex (mutex ch)))
- ((_ (exp (? ch) form ...)) (lock-mutex (mutex ch)))
- ((_ (exp form ...)) #f)))
-
-(define-syntax oc:unlock
- (syntax-rules (?)
- ((_ ((? ch) form ...)) (unlock-mutex (mutex ch)))
- ((_ (exp (? ch) form ...)) (unlock-mutex (mutex ch)))
- ((_ (exp form ...)) #f)))
-
-(define-syntax oc:consequence
- (syntax-rules (=>)
- ((_ data) data)
- ((_ data => (lambda (x) e1 e2 ...))
- (let ((x data)) e1 e2 ...))
- ((_ data e1 e2 ...)
- (begin data e1 e2 ...))))
-
-(define-syntax oc:immediate-dispatch
- (syntax-rules (?)
- ((_ ((? ch) e1 ...))
- ((sender-waiting? ch)
- (oc:consequence (immediate-receive ch) e1 ...)))
- ((_ (exp (? ch) e1 ...))
- ((and exp (sender-waiting? ch))
- (oc:consequence (immediate-receive ch) e1 ...)))
- ((_ (exp e1 ...))
- (exp e1 ...))))
-
-(define-syntax oc:late-dispatch
- (syntax-rules (?)
- ((_ ((? ch) e1 ...))
- ((sender-waiting? ch)
- (oc:consequence (late-receive ch) e1 ...)))
- ((_ (exp (? ch) e1 ...))
- ((and exp (sender-waiting? ch))
- (oc:consequence (late-receive ch) e1 ...)))
- ((_ (exp e1 ...))
- (#f))))
-
-(define-syntax oc:first-channel
- (syntax-rules (?)
- ((_ ((? ch) e1 ...) c2 ...)
- ch)
- ((_ (exp (? ch) e1 ...) c2 ...)
- ch)
- ((_ c1 c2 ...)
- (first-channel c2 ...))))
-
-(define-syntax oc:set-handshake-channel
- (syntax-rules (?)
- ((_ ((? ch) e1 ...) handshake)
- (set! (handshake-channel ch) handshake))
- ((_ (exp (? ch) e1 ...) handshake)
- (and exp (set! (handshake-channel ch) handshake)))
- ((_ (exp e1 ...) handshake)
- #f)))
-
-(define-syntax oc:unset-handshake-channel
- (syntax-rules (?)
- ((_ ((? ch) e1 ...))
- (set! (handshake-channel ch) ch))
- ((_ (exp (? ch) e1 ...))
- (and exp (set! (handshake-channel ch) ch)))
- ((_ (exp e1 ...))
- #f)))
-
-(define-syntax alt
- (lambda (x)
- (define (else-clause? x)
- (syntax-case x (else)
- ((_) #f)
- ((_ (else e1 e2 ...)) #t)
- ((_ c1 c2 ...) (else-clause? (syntax (_ c2 ...))))))
-
- (syntax-case x (else)
- ((_ c1 c2 ...)
- (else-clause? x)
- (syntax (begin
- (oc:lock c1)
- (oc:lock c2) ...
- (let ((res (cond (oc:immediate-dispatch c1)
- (oc:immediate-dispatch c2) ...)))
- (oc:unlock c1)
- (oc:unlock c2) ...
- res))))
- ((_ c1 c2 ...)
- (syntax (begin
- (oc:lock c1)
- (oc:lock c2) ...
- (let ((res (cond (oc:immediate-dispatch c1)
- (oc:immediate-dispatch c2) ...
- (else (let ((ch (oc:first-channel c1 c2 ...)))
- (oc:set-handshake-channel c1 ch)
- (oc:set-handshake-channel c2 ch) ...
- (wait-condition-variable (cv ch)
- (mutex ch))
- (oc:unset-handshake-channel c1)
- (oc:unset-handshake-channel c2) ...
- (cond (oc:late-dispatch c1)
- (oc:late-dispatch c2) ...))))))
- (oc:unlock c1)
- (oc:unlock c2) ...
- res)))))))
-;;;; optargs.scm -- support for optional arguments
-;;;;
-;;;; Copyright (C) 1997, 1998, 1999, 2001, 2002, 2004, 2006, 2009, 2010, 2011 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-;;;; Contributed by Maciej Stachowiak <mstachow@alum.mit.edu>
-
-
-
-;;; Commentary:
-
-;;; {Optional Arguments}
-;;;
-;;; The C interface for creating Guile procedures has a very handy
-;;; "optional argument" feature. This module attempts to provide
-;;; similar functionality for procedures defined in Scheme with
-;;; a convenient and attractive syntax.
-;;;
-;;; exported macros are:
-;;; let-optional
-;;; let-optional*
-;;; let-keywords
-;;; let-keywords*
-;;; lambda*
-;;; define*
-;;; define*-public
-;;; defmacro*
-;;; defmacro*-public
-;;;
-;;;
-;;; Summary of the lambda* extended parameter list syntax (brackets
-;;; are used to indicate grouping only):
-;;;
-;;; ext-param-list ::= [identifier]* [#\optional [ext-var-decl]+]?
-;;; [#\key [ext-var-decl]+ [#\allow-other-keys]?]?
-;;; [[#\rest identifier]|[. identifier]]?
-;;;
-;;; ext-var-decl ::= identifier | ( identifier expression )
-;;;
-;;; The characters `*', `+' and `?' are not to be taken literally; they
-;;; mean respectively, zero or more occurences, one or more occurences,
-;;; and one or zero occurences.
-;;;
-
-;;; Code:
-
-(define-module (ice-9 optargs)
- #\use-module (system base pmatch)
- #\re-export (lambda* define*)
- #\export (let-optional
- let-optional*
- let-keywords
- let-keywords*
- define*-public
- defmacro*
- defmacro*-public))
-
-;; let-optional rest-arg (binding ...) . body
-;; let-optional* rest-arg (binding ...) . body
-;; macros used to bind optional arguments
-;;
-;; These two macros give you an optional argument interface that is
-;; very "Schemey" and introduces no fancy syntax. They are compatible
-;; with the scsh macros of the same name, but are slightly
-;; extended. Each of binding may be of one of the forms <var> or
-;; (<var> <default-value>). rest-arg should be the rest-argument of
-;; the procedures these are used from. The items in rest-arg are
-;; sequentially bound to the variable namess are given. When rest-arg
-;; runs out, the remaining vars are bound either to the default values
-;; or to `#f' if no default value was specified. rest-arg remains
-;; bound to whatever may have been left of rest-arg.
-;;
-
-(define (vars&inits bindings)
- (let lp ((bindings bindings) (vars '()) (inits '()))
- (syntax-case bindings ()
- (()
- (values (reverse vars) (reverse inits)))
- (((v init) . rest) (identifier? #'v)
- (lp #'rest (cons #'v vars) (cons #'init inits)))
- ((v . rest) (identifier? #'v)
- (lp #'rest (cons #'v vars) (cons #'#f inits))))))
-
-(define-syntax let-optional
- (lambda (x)
- (syntax-case x ()
- ((_ rest-arg (binding ...) b0 b1 ...) (identifier? #'rest-arg)
- (call-with-values (lambda () (vars&inits #'(binding ...)))
- (lambda (vars inits)
- (with-syntax ((n (length vars))
- (n+1 (1+ (length vars)))
- (vars (append vars (list #'rest-arg)))
- ((t ...) (generate-temporaries vars))
- ((i ...) inits))
- #'(let ((t (lambda vars i))
- ...)
- (apply (lambda vars b0 b1 ...)
- (or (parse-lambda-case '(0 n n n+1 #f '())
- (list t ...)
- rest-arg)
- (error "sth" rest-arg)))))))))))
-
-(define-syntax let-optional*
- (lambda (x)
- (syntax-case x ()
- ((_ rest-arg (binding ...) b0 b1 ...) (identifier? #'rest-arg)
- (call-with-values (lambda () (vars&inits #'(binding ...)))
- (lambda (vars inits)
- (with-syntax ((n (length vars))
- (n+1 (1+ (length vars)))
- (vars (append vars (list #'rest-arg)))
- ((i ...) inits))
- #'(apply (lambda vars b0 b1 ...)
- (or (parse-lambda-case '(0 n n n+1 #f '())
- (list (lambda vars i) ...)
- rest-arg)
- (error "sth" rest-arg))))))))))
-
-
-;; let-keywords rest-arg allow-other-keys? (binding ...) . body
-;; let-keywords* rest-arg allow-other-keys? (binding ...) . body
-;; macros used to bind keyword arguments
-;;
-;; These macros pick out keyword arguments from rest-arg, but do not
-;; modify it. This is consistent at least with Common Lisp, which
-;; duplicates keyword args in the rest arg. More explanation of what
-;; keyword arguments in a lambda list look like can be found below in
-;; the documentation for lambda*. Bindings can have the same form as
-;; for let-optional. If allow-other-keys? is false, an error will be
-;; thrown if anything that looks like a keyword argument but does not
-;; match a known keyword parameter will result in an error.
-;;
-
-
-(define-syntax let-keywords
- (lambda (x)
- (syntax-case x ()
- ((_ rest-arg aok (binding ...) b0 b1 ...) (identifier? #'rest-arg)
- (call-with-values (lambda () (vars&inits #'(binding ...)))
- (lambda (vars inits)
- (with-syntax ((n (length vars))
- (vars vars)
- (ivars (generate-temporaries vars))
- ((kw ...) (map symbol->keyword
- (map syntax->datum vars)))
- ((idx ...) (iota (length vars)))
- ((t ...) (generate-temporaries vars))
- ((i ...) inits))
- #'(let ((t (lambda ivars i))
- ...)
- (apply (lambda vars b0 b1 ...)
- (or (parse-lambda-case '(0 0 #f n aok ((kw . idx) ...))
- (list t ...)
- rest-arg)
- (error "sth" rest-arg))))))))
- ((_ rest-arg aok (binding ...) b0 b1 ...)
- #'(let ((r rest-arg))
- (let-keywords r aok (binding ...) b0 b1 ...))))))
-
-(define-syntax let-keywords*
- (lambda (x)
- (syntax-case x ()
- ((_ rest-arg aok (binding ...) b0 b1 ...) (identifier? #'rest-arg)
- (call-with-values (lambda () (vars&inits #'(binding ...)))
- (lambda (vars inits)
- (with-syntax ((n (length vars))
- (vars vars)
- ((kw ...) (map symbol->keyword
- (map syntax->datum vars)))
- ((idx ...) (iota (length vars)))
- ((i ...) inits))
- #'(apply (lambda vars b0 b1 ...)
- (or (parse-lambda-case '(0 0 #f n aok ((kw . idx) ...))
- (list (lambda vars i) ...)
- rest-arg)
- (error "sth" rest-arg)))))))
- ((_ rest-arg aok (binding ...) b0 b1 ...)
- #'(let ((r rest-arg))
- (let-keywords* r aok (binding ...) b0 b1 ...))))))
-
-;; lambda* args . body
-;; lambda extended for optional and keyword arguments
-;;
-;; lambda* creates a procedure that takes optional arguments. These
-;; are specified by putting them inside brackets at the end of the
-;; paramater list, but before any dotted rest argument. For example,
-;; (lambda* (a b #\optional c d . e) '())
-;; creates a procedure with fixed arguments a and b, optional arguments c
-;; and d, and rest argument e. If the optional arguments are omitted
-;; in a call, the variables for them are bound to `#f'.
-;;
-;; lambda* can also take keyword arguments. For example, a procedure
-;; defined like this:
-;; (lambda* (#\key xyzzy larch) '())
-;; can be called with any of the argument lists (#\xyzzy 11)
-;; (#\larch 13) (#\larch 42 #\xyzzy 19) (). Whichever arguments
-;; are given as keywords are bound to values.
-;;
-;; Optional and keyword arguments can also be given default values
-;; which they take on when they are not present in a call, by giving a
-;; two-item list in place of an optional argument, for example in:
-;; (lambda* (foo #\optional (bar 42) #\key (baz 73)) (list foo bar baz))
-;; foo is a fixed argument, bar is an optional argument with default
-;; value 42, and baz is a keyword argument with default value 73.
-;; Default value expressions are not evaluated unless they are needed
-;; and until the procedure is called.
-;;
-;; lambda* now supports two more special parameter list keywords.
-;;
-;; lambda*-defined procedures now throw an error by default if a
-;; keyword other than one of those specified is found in the actual
-;; passed arguments. However, specifying #\allow-other-keys
-;; immediately after the keyword argument declarations restores the
-;; previous behavior of ignoring unknown keywords. lambda* also now
-;; guarantees that if the same keyword is passed more than once, the
-;; last one passed is the one that takes effect. For example,
-;; ((lambda* (#\key (heads 0) (tails 0)) (display (list heads tails)))
-;; #\heads 37 #\tails 42 #\heads 99)
-;; would result in (99 47) being displayed.
-;;
-;; #\rest is also now provided as a synonym for the dotted syntax rest
-;; argument. The argument lists (a . b) and (a #\rest b) are equivalent in
-;; all respects to lambda*. This is provided for more similarity to DSSSL,
-;; MIT-Scheme and Kawa among others, as well as for refugees from other
-;; Lisp dialects.
-
-
-;; define* args . body
-;; define*-public args . body
-;; define and define-public extended for optional and keyword arguments
-;;
-;; define* and define*-public support optional arguments with
-;; a similar syntax to lambda*. Some examples:
-;; (define* (x y #\optional a (z 3) #\key w . u) (display (list y z u)))
-;; defines a procedure x with a fixed argument y, an optional agument
-;; a, another optional argument z with default value 3, a keyword argument w,
-;; and a rest argument u.
-;;
-;; Of course, define*[-public] also supports #\rest and #\allow-other-keys
-;; in the same way as lambda*.
-
-(define-syntax define*-public
- (lambda (x)
- (syntax-case x ()
- ((_ (id . args) b0 b1 ...)
- #'(define-public id (lambda* args b0 b1 ...)))
- ((_ id val) (identifier? #'id)
- #'(define-public id val)))))
-
-
-;; defmacro* name args . body
-;; defmacro*-public args . body
-;; defmacro and defmacro-public extended for optional and keyword arguments
-;;
-;; These are just like defmacro and defmacro-public except that they
-;; take lambda*-style extended paramter lists, where #\optional,
-;; #\key, #\allow-other-keys and #\rest are allowed with the usual
-;; semantics. Here is an example of a macro with an optional argument:
-;; (defmacro* transmogrify (a #\optional b)
-
-(define-syntax defmacro*
- (lambda (x)
- (syntax-case x ()
- ((_ id args doc b0 b1 ...) (string? (syntax->datum #'doc))
- #'(define-macro id doc (lambda* args b0 b1 ...)))
- ((_ id args b0 b1 ...)
- #'(define-macro id #f (lambda* args b0 b1 ...))))))
-(define-syntax-rule (defmacro*-public id args b0 b1 ...)
- (begin
- (defmacro* id args b0 b1 ...)
- (export-syntax id)))
-
-;;; Support for optional & keyword args with the interpreter.
-(define *uninitialized* (list 'uninitialized))
-(define (parse-lambda-case spec inits args)
- (pmatch spec
- ((,nreq ,nopt ,rest-idx ,nargs ,allow-other-keys? ,kw-indices)
- (define (req args prev tail n)
- (cond
- ((zero? n)
- (if prev (set-cdr! prev '()))
- (let ((slots-tail (make-list (- nargs nreq) *uninitialized*)))
- (opt (if prev (append! args slots-tail) slots-tail)
- slots-tail tail nopt inits)))
- ((null? tail)
- #f) ;; fail
- (else
- (req args tail (cdr tail) (1- n)))))
- (define (opt slots slots-tail args-tail n inits)
- (cond
- ((zero? n)
- (rest-or-key slots slots-tail args-tail inits rest-idx))
- ((null? args-tail)
- (set-car! slots-tail (apply (car inits) slots))
- (opt slots (cdr slots-tail) '() (1- n) (cdr inits)))
- (else
- (set-car! slots-tail (car args-tail))
- (opt slots (cdr slots-tail) (cdr args-tail) (1- n) (cdr inits)))))
- (define (rest-or-key slots slots-tail args-tail inits rest-idx)
- (cond
- (rest-idx
- ;; it has to be this way, vars are allocated in this order
- (set-car! slots-tail args-tail)
- (if (pair? kw-indices)
- (permissive-keys slots (cdr slots-tail) args-tail inits)
- (rest-or-key slots (cdr slots-tail) '() inits #f)))
- ((pair? kw-indices)
- ;; fail early here, because once we're in keyword land we throw
- ;; errors instead of failing
- (and (or (null? args-tail) rest-idx (keyword? (car args-tail)))
- (key slots slots-tail args-tail inits)))
- ((pair? args-tail)
- #f) ;; fail
- (else
- slots)))
- (define (permissive-keys slots slots-tail args-tail inits)
- (cond
- ((null? args-tail)
- (if (null? inits)
- slots
- (begin
- (if (eq? (car slots-tail) *uninitialized*)
- (set-car! slots-tail (apply (car inits) slots)))
- (permissive-keys slots (cdr slots-tail) '() (cdr inits)))))
- ((not (keyword? (car args-tail)))
- (permissive-keys slots slots-tail (cdr args-tail) inits))
- ((and (keyword? (car args-tail))
- (pair? (cdr args-tail))
- (assq-ref kw-indices (car args-tail)))
- => (lambda (i)
- (list-set! slots i (cadr args-tail))
- (permissive-keys slots slots-tail (cddr args-tail) inits)))
- ((and (keyword? (car args-tail))
- (pair? (cdr args-tail))
- allow-other-keys?)
- (permissive-keys slots slots-tail (cddr args-tail) inits))
- (else (scm-error 'keyword-argument-error #f "Unrecognized keyword"
- '() args-tail))))
- (define (key slots slots-tail args-tail inits)
- (cond
- ((null? args-tail)
- (if (null? inits)
- slots
- (begin
- (if (eq? (car slots-tail) *uninitialized*)
- (set-car! slots-tail (apply (car inits) slots)))
- (key slots (cdr slots-tail) '() (cdr inits)))))
- ((not (keyword? (car args-tail)))
- (if rest-idx
- ;; no error checking, everything goes to the rest..
- (key slots slots-tail '() inits)
- (scm-error 'keyword-argument-error #f "Invalid keyword"
- '() args-tail)))
- ((and (keyword? (car args-tail))
- (pair? (cdr args-tail))
- (assq-ref kw-indices (car args-tail)))
- => (lambda (i)
- (list-set! slots i (cadr args-tail))
- (key slots slots-tail (cddr args-tail) inits)))
- ((and (keyword? (car args-tail))
- (pair? (cdr args-tail))
- allow-other-keys?)
- (key slots slots-tail (cddr args-tail) inits))
- (else (scm-error 'keyword-argument-error #f "Unrecognized keyword"
- '() args-tail))))
- (let ((args (list-copy args)))
- (req args #f args nreq)))
- (else (error "unexpected spec" spec))))
-;;; installed-scm-file
-
-;;;; Copyright (C) 1996, 2001, 2006, 2011 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-(define-module (ice-9 poe)
- \:use-module (ice-9 hcons)
- \:export (pure-funcq perfect-funcq))
-
-
-
-
-;;; {Pure Functions}
-;;;
-;;; A pure function (of some sort) is characterized by two equality
-;;; relations: one on argument lists and one on return values.
-;;; A pure function is one that when applied to equal arguments lists
-;;; yields equal results.
-;;;
-;;; If the equality relationship on return values can be eq?, it may make
-;;; sense to cache values returned by the function. Choosing the right
-;;; equality relation on arguments is tricky.
-;;;
-
-
-;;; {pure-funcq}
-;;;
-;;; The simplest case of pure functions are those in which results
-;;; are only certainly eq? if all of the arguments are. These functions
-;;; are called "pure-funcq", for obvious reasons.
-;;;
-
-
-(define funcq-memo (make-weak-key-hash-table 523)) ; !!! randomly selected values
-(define funcq-buffer (make-gc-buffer 256))
-
-(define (funcq-hash arg-list n)
- (let ((it (let loop ((x 0)
- (arg-list arg-list))
- (if (null? arg-list)
- (modulo x n)
- (loop (logior x (hashq (car arg-list) 4194303))
- (cdr arg-list))))))
- it))
-
-;; return true if lists X and Y are the same length and each element is `eq?'
-(define (eq?-list x y)
- (if (null? x)
- (null? y)
- (and (not (null? y))
- (eq? (car x) (car y))
- (eq?-list (cdr x) (cdr y)))))
-
-(define (funcq-assoc arg-list alist)
- (if (null? alist)
- #f
- (if (eq?-list arg-list (caar alist))
- (car alist)
- (funcq-assoc arg-list (cdr alist)))))
-
-
-(define not-found (list 'not-found))
-
-
-(define (pure-funcq base-func)
- (lambda args
- (let* ((key (cons base-func args))
- (cached (hashx-ref funcq-hash funcq-assoc funcq-memo key not-found)))
- (if (not (eq? cached not-found))
- (begin
- (funcq-buffer key)
- cached)
-
- (let ((val (apply base-func args)))
- (funcq-buffer key)
- (hashx-set! funcq-hash funcq-assoc funcq-memo key val)
- val)))))
-
-
-
-;;; {Perfect funq}
-;;;
-;;; A pure funq may sometimes forget its past but a perfect
-;;; funcq never does.
-;;;
-
-(define (perfect-funcq size base-func)
- (define funcq-memo (make-hash-table size))
-
- (lambda args
- (let* ((key (cons base-func args))
- (cached (hashx-ref funcq-hash funcq-assoc funcq-memo key not-found)))
- (if (not (eq? cached not-found))
- (begin
- (funcq-buffer key)
- cached)
-
- (let ((val (apply base-func args)))
- (funcq-buffer key)
- (hashx-set! funcq-hash funcq-assoc funcq-memo key val)
- val)))))
-;; poll
-
-;;;; Copyright (C) 2010, 2011 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-(define-module (ice-9 poll)
- #\use-module (srfi srfi-9)
- #\use-module (srfi srfi-9 gnu)
- #\use-module (rnrs bytevectors)
- #\export (make-empty-poll-set
- poll-set?
- poll-set-nfds
- poll-set-find-port
- poll-set-port
- poll-set-events
- set-poll-set-events!
- poll-set-revents
- set-poll-set-revents!
- poll-set-add!
- poll-set-remove!
- poll))
-
-(eval-when (expand load eval)
- (load-extension (string-append "libguile-" (effective-version))
- "scm_init_poll"))
-
-(if (not (= %sizeof-struct-pollfd 8))
- (error "Unexpected struct pollfd size" %sizeof-struct-pollfd))
-
-(if (defined? 'POLLIN)
- (export POLLIN))
-
-(if (defined? 'POLLPRI)
- (export POLLPRI))
-
-(if (defined? 'POLLOUT)
- (export POLLOUT))
-
-(if (defined? 'POLLRDHUP)
- (export POLLRDHUP))
-
-(if (defined? 'POLLERR)
- (export POLLERR))
-
-(if (defined? 'POLLHUP)
- (export POLLHUP))
-
-(if (defined? 'POLLNVAL)
- (export POLLNVAL))
-
-
-(define-record-type <poll-set>
- (make-poll-set pollfds nfds ports)
- poll-set?
- (pollfds pset-pollfds set-pset-pollfds!)
- (nfds poll-set-nfds set-pset-nfds!)
- (ports pset-ports set-pset-ports!)
- )
-
-(define-syntax-rule (pollfd-offset n)
- (* n 8))
-
-(define* (make-empty-poll-set #\optional (pre-allocated 4))
- (make-poll-set (make-bytevector (pollfd-offset pre-allocated) 0)
- 0
- (make-vector pre-allocated #f)))
-
-(define (pset-size set)
- (vector-length (pset-ports set)))
-
-(define (ensure-pset-size! set size)
- (let ((prev (pset-size set)))
- (if (< prev size)
- (let lp ((new prev))
- (if (< new size)
- (lp (* new 2))
- (let ((old-pollfds (pset-pollfds set))
- (nfds (poll-set-nfds set))
- (old-ports (pset-ports set))
- (new-pollfds (make-bytevector (pollfd-offset new) 0))
- (new-ports (make-vector new #f)))
- (bytevector-copy! old-pollfds 0 new-pollfds 0
- (pollfd-offset nfds))
- (vector-move-left! old-ports 0 nfds new-ports 0)
- (set-pset-pollfds! set new-pollfds)
- (set-pset-ports! set new-ports)))))))
-
-(define (poll-set-find-port set port)
- (let lp ((i 0))
- (if (< i (poll-set-nfds set))
- (if (equal? (vector-ref (pset-ports set) i) port)
- i
- (lp (1+ i)))
- #f)))
-
-(define (poll-set-port set idx)
- (if (< idx (poll-set-nfds set))
- (vector-ref (pset-ports set) idx)
- (error "poll set index out of bounds" set idx)))
-
-(define (poll-set-events set idx)
- (if (< idx (poll-set-nfds set))
- (bytevector-u16-native-ref (pset-pollfds set) (+ (pollfd-offset idx) 4))
- (error "poll set index out of bounds" set idx)))
-
-(define (set-poll-set-events! set idx events)
- (if (< idx (poll-set-nfds set))
- (bytevector-u16-native-set! (pset-pollfds set) (+ (pollfd-offset idx) 4)
- events)
- (error "poll set index out of bounds" set idx)))
-
-(define (poll-set-revents set idx)
- (if (< idx (poll-set-nfds set))
- (bytevector-u16-native-ref (pset-pollfds set) (+ (pollfd-offset idx) 6))
- (error "poll set index out of bounds" set idx)))
-
-(define (set-poll-set-revents! set idx revents)
- (if (< idx (poll-set-nfds set))
- (bytevector-u16-native-set! (pset-pollfds set) (+ (pollfd-offset idx) 6)
- revents)
- (error "poll set index out of bounds" set idx)))
-
-(define (poll-set-add! set fd-or-port events)
- (let* ((idx (poll-set-nfds set))
- (off (pollfd-offset idx))
- (fd (if (integer? fd-or-port)
- fd-or-port
- (port->fdes fd-or-port))))
-
- (if (port? fd-or-port)
- ;; As we store the port in the fdset, there is no need to
- ;; increment the revealed count to prevent the fd from being
- ;; closed by a gc'd port.
- (release-port-handle fd-or-port))
-
- (ensure-pset-size! set (1+ idx))
- (bytevector-s32-native-set! (pset-pollfds set) off fd)
- (bytevector-u16-native-set! (pset-pollfds set) (+ off 4) events)
- (bytevector-u16-native-set! (pset-pollfds set) (+ off 6) 0) ; revents
- (vector-set! (pset-ports set) idx fd-or-port)
- (set-pset-nfds! set (1+ idx))))
-
-(define (poll-set-remove! set idx)
- (if (not (< idx (poll-set-nfds set)))
- (error "poll set index out of bounds" set idx))
- (let ((nfds (poll-set-nfds set))
- (off (pollfd-offset idx))
- (port (vector-ref (pset-ports set) idx)))
- (vector-move-left! (pset-ports set) (1+ idx) nfds
- (pset-ports set) idx)
- (vector-set! (pset-ports set) (1- nfds) #f)
- (bytevector-copy! (pset-pollfds set) (pollfd-offset (1+ idx))
- (pset-pollfds set) off
- (- (pollfd-offset nfds) (pollfd-offset (1+ idx))))
- ;; zero the struct pollfd all at once
- (bytevector-u64-native-set! (pset-pollfds set) (pollfd-offset (1- nfds)) 0)
- (set-pset-nfds! set (1- nfds))
- port))
-
-(define* (poll poll-set #\optional (timeout -1))
- (primitive-poll (pset-pollfds poll-set)
- (poll-set-nfds poll-set)
- (pset-ports poll-set)
- timeout))
-;; popen emulation, for non-stdio based ports.
-
-;;;; Copyright (C) 1998, 1999, 2000, 2001, 2003, 2006, 2010, 2011, 2012,
-;;;; 2013 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-(define-module (ice-9 popen)
- \:use-module (ice-9 threads)
- \:use-module (srfi srfi-9)
- \:export (port/pid-table open-pipe* open-pipe close-pipe open-input-pipe
- open-output-pipe open-input-output-pipe))
-
-(eval-when (expand load eval)
- (load-extension (string-append "libguile-" (effective-version))
- "scm_init_popen"))
-
-(define-record-type <pipe-info>
- (make-pipe-info pid)
- pipe-info?
- (pid pipe-info-pid set-pipe-info-pid!))
-
-(define (make-rw-port read-port write-port)
- (make-soft-port
- (vector
- (lambda (c) (write-char c write-port))
- (lambda (s) (display s write-port))
- (lambda () (force-output write-port))
- (lambda () (read-char read-port))
- (lambda () (close-port read-port) (close-port write-port)))
- "r+"))
-
-;; a guardian to ensure the cleanup is done correctly when
-;; an open pipe is gc'd or a close-port is used.
-(define pipe-guardian (make-guardian))
-
-;; a weak hash-table to store the process ids.
-;; XXX use of this table is deprecated. It is no longer used here, and
-;; is populated for backward compatibility only (since it is exported).
-(define port/pid-table (make-weak-key-hash-table 31))
-(define port/pid-table-mutex (make-mutex))
-
-(define (open-pipe* mode command . args)
- "Executes the program @var{command} with optional arguments
-@var{args} (all strings) in a subprocess.
-A port to the process (based on pipes) is created and returned.
-@var{mode} specifies whether an input, an output or an input-output
-port to the process is created: it should be the value of
-@code{OPEN_READ}, @code{OPEN_WRITE} or @code{OPEN_BOTH}."
- (call-with-values (lambda ()
- (apply open-process mode command args))
- (lambda (read-port write-port pid)
- (let ((port (or (and read-port write-port
- (make-rw-port read-port write-port))
- read-port
- write-port
- (%make-void-port mode)))
- (pipe-info (make-pipe-info pid)))
-
- ;; Guard the pipe-info instead of the port, so that we can still
- ;; call 'waitpid' even if 'close-port' is called (which clears
- ;; the port entry).
- (pipe-guardian pipe-info)
- (%set-port-property! port 'popen-pipe-info pipe-info)
-
- ;; XXX populate port/pid-table for backward compatibility.
- (with-mutex port/pid-table-mutex
- (hashq-set! port/pid-table port pid))
-
- port))))
-
-(define (open-pipe command mode)
- "Executes the shell command @var{command} (a string) in a subprocess.
-A port to the process (based on pipes) is created and returned.
-@var{mode} specifies whether an input, an output or an input-output
-port to the process is created: it should be the value of
-@code{OPEN_READ}, @code{OPEN_WRITE} or @code{OPEN_BOTH}."
- (open-pipe* mode "/bin/sh" "-c" command))
-
-(define (fetch-pipe-info port)
- (%port-property port 'popen-pipe-info))
-
-(define (close-process port pid)
- (close-port port)
- (cdr (waitpid pid)))
-
-(define (close-pipe p)
- "Closes the pipe created by @code{open-pipe}, then waits for the process
-to terminate and returns its status value, @xref{Processes, waitpid}, for
-information on how to interpret this value."
- (let ((pipe-info (fetch-pipe-info p)))
- (unless pipe-info
- (error "close-pipe: port not created by (ice-9 popen)"))
- (let ((pid (pipe-info-pid pipe-info)))
- (unless pid
- (error "close-pipe: pid has already been cleared"))
- ;; clear the pid to avoid repeated calls to 'waitpid'.
- (set-pipe-info-pid! pipe-info #f)
- (close-process p pid))))
-
-(define (reap-pipes)
- (let loop ()
- (let ((pipe-info (pipe-guardian)))
- (when pipe-info
- (let ((pid (pipe-info-pid pipe-info)))
- ;; maybe 'close-pipe' was already called.
- (when pid
- ;; clean up without reporting errors. also avoids blocking
- ;; the process: if the child isn't ready to be collected,
- ;; puts it back into the guardian's live list so it can be
- ;; tried again the next time the cleanup runs.
- (catch 'system-error
- (lambda ()
- (let ((pid/status (waitpid pid WNOHANG)))
- (if (zero? (car pid/status))
- (pipe-guardian pipe-info) ; not ready for collection
- (set-pipe-info-pid! pipe-info #f))))
- (lambda args #f))))
- (loop)))))
-
-(add-hook! after-gc-hook reap-pipes)
-
-(define (open-input-pipe command)
- "Equivalent to @code{open-pipe} with mode @code{OPEN_READ}"
- (open-pipe command OPEN_READ))
-
-(define (open-output-pipe command)
- "Equivalent to @code{open-pipe} with mode @code{OPEN_WRITE}"
- (open-pipe command OPEN_WRITE))
-
-(define (open-input-output-pipe command)
- "Equivalent to @code{open-pipe} with mode @code{OPEN_BOTH}"
- (open-pipe command OPEN_BOTH))
-
-;;; installed-scm-file
-
-;;;; Copyright (C) 1999, 2006 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-(eval-when (compile)
- (set-current-module (resolve-module '(guile))))
-
-(define (stat:dev f) (vector-ref f 0))
-(define (stat:ino f) (vector-ref f 1))
-(define (stat:mode f) (vector-ref f 2))
-(define (stat:nlink f) (vector-ref f 3))
-(define (stat:uid f) (vector-ref f 4))
-(define (stat:gid f) (vector-ref f 5))
-(define (stat:rdev f) (vector-ref f 6))
-(define (stat:size f) (vector-ref f 7))
-(define (stat:atime f) (vector-ref f 8))
-(define (stat:mtime f) (vector-ref f 9))
-(define (stat:ctime f) (vector-ref f 10))
-(define (stat:blksize f) (vector-ref f 11))
-(define (stat:blocks f) (vector-ref f 12))
-(define (stat:atimensec f) (vector-ref f 15))
-(define (stat:mtimensec f) (vector-ref f 16))
-(define (stat:ctimensec f) (vector-ref f 17))
-
-;; derived from stat mode.
-(define (stat:type f) (vector-ref f 13))
-(define (stat:perms f) (vector-ref f 14))
-
-(define (passwd:name obj) (vector-ref obj 0))
-(define (passwd:passwd obj) (vector-ref obj 1))
-(define (passwd:uid obj) (vector-ref obj 2))
-(define (passwd:gid obj) (vector-ref obj 3))
-(define (passwd:gecos obj) (vector-ref obj 4))
-(define (passwd:dir obj) (vector-ref obj 5))
-(define (passwd:shell obj) (vector-ref obj 6))
-
-(define (group:name obj) (vector-ref obj 0))
-(define (group:passwd obj) (vector-ref obj 1))
-(define (group:gid obj) (vector-ref obj 2))
-(define (group:mem obj) (vector-ref obj 3))
-
-(define (utsname:sysname obj) (vector-ref obj 0))
-(define (utsname:nodename obj) (vector-ref obj 1))
-(define (utsname:release obj) (vector-ref obj 2))
-(define (utsname:version obj) (vector-ref obj 3))
-(define (utsname:machine obj) (vector-ref obj 4))
-
-(define (getpwent) (getpw))
-(define (setpwent) (setpw #t))
-(define (endpwent) (setpw))
-
-(define (getpwnam name) (getpw name))
-(define (getpwuid uid) (getpw uid))
-
-(define (getgrent) (getgr))
-(define (setgrent) (setgr #t))
-(define (endgrent) (setgr))
-
-(define (getgrnam name) (getgr name))
-(define (getgrgid id) (getgr id))
-;;;; -*- coding: utf-8; mode: scheme -*-
-;;;;
-;;;; Copyright (C) 2001, 2004, 2006, 2009, 2010,
-;;;; 2012, 2014 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-(define-module (ice-9 pretty-print)
- #\use-module (ice-9 match)
- #\use-module (srfi srfi-1)
- #\use-module (rnrs bytevectors)
- #\export (pretty-print
- truncated-print))
-
-
-;; From SLIB.
-
-;;"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 genwrite:newline-str (make-string 1 #\newline))
-
-(define (generic-write
- obj display? width max-expr-width per-line-prefix 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)))
- (case head
- ((quote) "'")
- ((quasiquote) "`")
- ((unquote) ",")
- ((unquote-splicing) ",@"))))
-
- (define (out str col)
- (and col (output str) (+ col (string-length str))))
-
- (define (wr obj col)
- (let loop ((obj obj)
- (col col))
- (match obj
- (((or 'quote 'quasiquote 'unquote 'unquote-splicing) body)
- (wr body (out (read-macro-prefix obj) col)))
- ((head . (rest ...))
- ;; A proper list: do our own list printing so as to catch read
- ;; macros that appear in the middle of the list.
- (let ((col (loop head (out "(" col))))
- (out ")"
- (fold (lambda (i col)
- (loop i (out " " col)))
- col rest))))
- (_
- (out (object->string obj (if display? display write)) 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 genwrite:newline-str col)
- (out per-line-prefix 0)
- (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 max-expr-width ""
- (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 (pp-SYNTAX-CASE expr col extra)
- (pp-general expr col extra #t pp-expr-list #f pp-expr))
-
- ; define formatting style (change these to suit your style)
-
- (define indent-general 2)
-
- (define max-call-head-width 5)
-
- (define (style head)
- (case head
- ((lambda lambda* let* letrec define define* define-public
- define-syntax let-syntax letrec-syntax with-syntax)
- 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)
- ((syntax-rules) pp-LAMBDA)
- ((syntax-case) pp-SYNTAX-CASE)
- (else #f)))
-
- (pr obj col 0 pp-expr))
-
- (out per-line-prefix 0)
- (if width
- (out genwrite:newline-str (pp obj 0))
- (wr obj 0))
- ;; Return `unspecified'
- (if #f #f))
-
-; (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))
-
-(define* (pretty-print obj #\optional port*
- #\key
- (port (or port* (current-output-port)))
- (width 79)
- (max-expr-width 50)
- (display? #f)
- (per-line-prefix ""))
- "Pretty-print OBJ on PORT, which is a keyword argument defaulting to
-the current output port. Formatting can be controlled by a number of
-keyword arguments: Each line in the output is preceded by the string
-PER-LINE-PREFIX, which is empty by default. The output lines will be
-at most WIDTH characters wide; the default is 79. If DISPLAY? is
-true, display rather than write representation will be used.
-
-Instead of with a keyword argument, you can also specify the output
-port directly after OBJ, like (pretty-print OBJ PORT)."
- (generic-write obj display?
- (- width (string-length per-line-prefix))
- max-expr-width
- per-line-prefix
- (lambda (s) (display s port) #t)))
-
-
-;; `truncated-print' was written in 2009 by Andy Wingo, and is not from
-;; genwrite.scm.
-(define* (truncated-print x #\optional port*
- #\key
- (port (or port* (current-output-port)))
- (width 79)
- (display? #f)
- (breadth-first? #f))
- "Print @var{x}, truncating the output, if necessary, to make it fit
-into @var{width} characters. By default, @var{x} will be printed using
-@code{write}, though that behavior can be overriden via the
-@var{display?} keyword argument.
-
-The default behaviour is to print depth-first, meaning that the entire
-remaining width will be available to each sub-expression of @var{x} --
-e.g., if @var{x} is a vector, each member of @var{x}. One can attempt to
-\"ration\" the available width, trying to allocate it equally to each
-sub-expression, via the @var{breadth-first?} keyword argument."
-
- ;; Make sure string ports are created with the right encoding.
- (with-fluids ((%default-port-encoding (port-encoding port)))
-
- (define ellipsis
- ;; Choose between `HORIZONTAL ELLIPSIS' (U+2026) and three dots, depending
- ;; on the encoding of PORT.
- (let ((e "…"))
- (catch 'encoding-error
- (lambda ()
- (with-fluids ((%default-port-conversion-strategy 'error))
- (with-output-to-string
- (lambda ()
- (display e)))))
- (lambda (key . args)
- "..."))))
-
- (let ((ellipsis-width (string-length ellipsis)))
-
- (define (print-sequence x width len ref next)
- (let lp ((x x)
- (width width)
- (i 0))
- (if (> i 0)
- (display #\space))
- (cond
- ((= i len)) ; catches 0-length case
- ((and (= i (1- len)) (or (zero? i) (> width 1)))
- (print (ref x i) (if (zero? i) width (1- width))))
- ((<= width (+ 1 ellipsis-width))
- (display ellipsis))
- (else
- (let ((str
- (with-fluids ((%default-port-encoding (port-encoding port)))
- (with-output-to-string
- (lambda ()
- (print (ref x i)
- (if breadth-first?
- (max 1
- (1- (floor (/ width (- len i)))))
- (- width (+ 1 ellipsis-width)))))))))
- (display str)
- (lp (next x) (- width 1 (string-length str)) (1+ i)))))))
-
- (define (print-tree x width)
- ;; width is >= the width of # . #, which is 5
- (let lp ((x x)
- (width width))
- (cond
- ((or (not (pair? x)) (<= width 4))
- (display ". ")
- (print x (- width 2)))
- (else
- ;; width >= 5
- (let ((str (with-output-to-string
- (lambda ()
- (print (car x)
- (if breadth-first?
- (floor (/ (- width 3) 2))
- (- width 4)))))))
- (display str)
- (display " ")
- (lp (cdr x) (- width 1 (string-length str))))))))
-
- (define (truncate-string str width)
- ;; width is < (string-length str)
- (let lp ((fixes '(("#<" . ">")
- ("#(" . ")")
- ("(" . ")")
- ("\"" . "\""))))
- (cond
- ((null? fixes)
- "#")
- ((and (string-prefix? (caar fixes) str)
- (string-suffix? (cdar fixes) str)
- (>= (string-length str)
- width
- (+ (string-length (caar fixes))
- (string-length (cdar fixes))
- ellipsis-width)))
- (format #f "~a~a~a~a"
- (caar fixes)
- (substring str (string-length (caar fixes))
- (- width (string-length (cdar fixes))
- ellipsis-width))
- ellipsis
- (cdar fixes)))
- (else
- (lp (cdr fixes))))))
-
- (define (print x width)
- (cond
- ((<= width 0)
- (error "expected a positive width" width))
- ((list? x)
- (cond
- ((>= width (+ 2 ellipsis-width))
- (display "(")
- (print-sequence x (- width 2) (length x)
- (lambda (x i) (car x)) cdr)
- (display ")"))
- (else
- (display "#"))))
- ((vector? x)
- (cond
- ((>= width (+ 3 ellipsis-width))
- (display "#(")
- (print-sequence x (- width 3) (vector-length x)
- vector-ref identity)
- (display ")"))
- (else
- (display "#"))))
- ((bytevector? x)
- (cond
- ((>= width 9)
- (format #t "#~a(" (array-type x))
- (print-sequence x (- width 6) (array-length x)
- array-ref identity)
- (display ")"))
- (else
- (display "#"))))
- ((pair? x)
- (cond
- ((>= width (+ 4 ellipsis-width))
- (display "(")
- (print-tree x (- width 2))
- (display ")"))
- (else
- (display "#"))))
- (else
- (let* ((str (with-output-to-string
- (lambda () (if display? (display x) (write x)))))
- (len (string-length str)))
- (display (if (<= (string-length str) width)
- str
- (truncate-string str width)))))))
-
- (with-output-to-port port
- (lambda ()
- (print x width))))))
-(eval-when (compile) (set-current-module (resolve-module (quote (guile)))))
-(if #f #f)
-
-(letrec*
- ((make-void
- (lambda (src) (make-struct (vector-ref %expanded-vtables 0) 0 src)))
- (make-const
- (lambda (src exp)
- (make-struct (vector-ref %expanded-vtables 1) 0 src exp)))
- (make-primitive-ref
- (lambda (src name)
- (make-struct (vector-ref %expanded-vtables 2) 0 src name)))
- (make-lexical-ref
- (lambda (src name gensym)
- (make-struct (vector-ref %expanded-vtables 3) 0 src name gensym)))
- (make-lexical-set
- (lambda (src name gensym exp)
- (make-struct (vector-ref %expanded-vtables 4) 0 src name gensym exp)))
- (make-module-ref
- (lambda (src mod name public?)
- (make-struct (vector-ref %expanded-vtables 5) 0 src mod name public?)))
- (make-module-set
- (lambda (src mod name public? exp)
- (make-struct
- (vector-ref %expanded-vtables 6)
- 0
- src
- mod
- name
- public?
- exp)))
- (make-toplevel-ref
- (lambda (src name)
- (make-struct (vector-ref %expanded-vtables 7) 0 src name)))
- (make-toplevel-set
- (lambda (src name exp)
- (make-struct (vector-ref %expanded-vtables 8) 0 src name exp)))
- (make-toplevel-define
- (lambda (src name exp)
- (make-struct (vector-ref %expanded-vtables 9) 0 src name exp)))
- (make-conditional
- (lambda (src test consequent alternate)
- (make-struct
- (vector-ref %expanded-vtables 10)
- 0
- src
- test
- consequent
- alternate)))
- (make-application
- (lambda (src proc args)
- (make-struct (vector-ref %expanded-vtables 11) 0 src proc args)))
- (make-sequence
- (lambda (src exps)
- (make-struct (vector-ref %expanded-vtables 12) 0 src exps)))
- (make-lambda
- (lambda (src meta body)
- (make-struct (vector-ref %expanded-vtables 13) 0 src meta body)))
- (make-lambda-case
- (lambda (src req opt rest kw inits gensyms body alternate)
- (make-struct
- (vector-ref %expanded-vtables 14)
- 0
- src
- req
- opt
- rest
- kw
- inits
- gensyms
- body
- alternate)))
- (make-let
- (lambda (src names gensyms vals body)
- (make-struct
- (vector-ref %expanded-vtables 15)
- 0
- src
- names
- gensyms
- vals
- body)))
- (make-letrec
- (lambda (src in-order? names gensyms vals body)
- (make-struct
- (vector-ref %expanded-vtables 16)
- 0
- src
- in-order?
- names
- gensyms
- vals
- body)))
- (make-dynlet
- (lambda (src fluids vals body)
- (make-struct
- (vector-ref %expanded-vtables 17)
- 0
- src
- fluids
- vals
- body)))
- (lambda?
- (lambda (x)
- (and (struct? x)
- (eq? (struct-vtable x) (vector-ref %expanded-vtables 13)))))
- (lambda-meta (lambda (x) (struct-ref x 1)))
- (set-lambda-meta! (lambda (x v) (struct-set! x 1 v)))
- (top-level-eval-hook (lambda (x mod) (primitive-eval x)))
- (local-eval-hook (lambda (x mod) (primitive-eval x)))
- (session-id
- (let ((v (module-variable (current-module) 'syntax-session-id)))
- (lambda () ((variable-ref v)))))
- (put-global-definition-hook
- (lambda (symbol type val)
- (module-define!
- (current-module)
- symbol
- (make-syntax-transformer symbol type val))))
- (get-global-definition-hook
- (lambda (symbol module)
- (if (and (not module) (current-module))
- (warn "module system is booted, we should have a module" symbol))
- (let ((v (module-variable
- (if module (resolve-module (cdr module)) (current-module))
- symbol)))
- (and v
- (variable-bound? v)
- (let ((val (variable-ref v)))
- (and (macro? val)
- (macro-type val)
- (cons (macro-type val) (macro-binding val))))))))
- (decorate-source
- (lambda (e s)
- (if (and s (supports-source-properties? e))
- (set-source-properties! e s))
- e))
- (maybe-name-value!
- (lambda (name val)
- (if (lambda? val)
- (let ((meta (lambda-meta val)))
- (if (not (assq 'name meta))
- (set-lambda-meta! val (acons 'name name meta)))))))
- (build-void (lambda (source) (make-void source)))
- (build-application
- (lambda (source fun-exp arg-exps)
- (make-application source fun-exp arg-exps)))
- (build-conditional
- (lambda (source test-exp then-exp else-exp)
- (make-conditional source test-exp then-exp else-exp)))
- (build-dynlet
- (lambda (source fluids vals body)
- (make-dynlet source fluids vals body)))
- (build-lexical-reference
- (lambda (type source name var) (make-lexical-ref source name var)))
- (build-lexical-assignment
- (lambda (source name var exp)
- (maybe-name-value! name exp)
- (make-lexical-set source name var exp)))
- (analyze-variable
- (lambda (mod var modref-cont bare-cont)
- (if (not mod)
- (bare-cont var)
- (let ((kind (car mod)) (mod (cdr mod)))
- (let ((key kind))
- (cond ((memv key '(public)) (modref-cont mod var #t))
- ((memv key '(private))
- (if (not (equal? mod (module-name (current-module))))
- (modref-cont mod var #f)
- (bare-cont var)))
- ((memv key '(bare)) (bare-cont var))
- ((memv key '(hygiene))
- (if (and (not (equal? mod (module-name (current-module))))
- (module-variable (resolve-module mod) var))
- (modref-cont mod var #f)
- (bare-cont var)))
- (else (syntax-violation #f "bad module kind" var mod))))))))
- (build-global-reference
- (lambda (source var mod)
- (analyze-variable
- mod
- var
- (lambda (mod var public?) (make-module-ref source mod var public?))
- (lambda (var) (make-toplevel-ref source var)))))
- (build-global-assignment
- (lambda (source var exp mod)
- (maybe-name-value! var exp)
- (analyze-variable
- mod
- var
- (lambda (mod var public?)
- (make-module-set source mod var public? exp))
- (lambda (var) (make-toplevel-set source var exp)))))
- (build-global-definition
- (lambda (source var exp)
- (maybe-name-value! var exp)
- (make-toplevel-define source var exp)))
- (build-simple-lambda
- (lambda (src req rest vars meta exp)
- (make-lambda
- src
- meta
- (make-lambda-case src req #f rest #f '() vars exp #f))))
- (build-case-lambda
- (lambda (src meta body) (make-lambda src meta body)))
- (build-lambda-case
- (lambda (src req opt rest kw inits vars body else-case)
- (make-lambda-case src req opt rest kw inits vars body else-case)))
- (build-primref
- (lambda (src name)
- (if (equal? (module-name (current-module)) '(guile))
- (make-toplevel-ref src name)
- (make-module-ref src '(guile) name #f))))
- (build-data (lambda (src exp) (make-const src exp)))
- (build-sequence
- (lambda (src exps)
- (if (null? (cdr exps)) (car exps) (make-sequence src exps))))
- (build-let
- (lambda (src ids vars val-exps body-exp)
- (for-each maybe-name-value! ids val-exps)
- (if (null? vars) body-exp (make-let src ids vars val-exps body-exp))))
- (build-named-let
- (lambda (src ids vars val-exps body-exp)
- (let ((f (car vars)) (f-name (car ids)) (vars (cdr vars)) (ids (cdr ids)))
- (let ((proc (build-simple-lambda src ids #f vars '() body-exp)))
- (maybe-name-value! f-name proc)
- (for-each maybe-name-value! ids val-exps)
- (make-letrec
- src
- #f
- (list f-name)
- (list f)
- (list proc)
- (build-application
- src
- (build-lexical-reference 'fun src f-name f)
- val-exps))))))
- (build-letrec
- (lambda (src in-order? ids vars val-exps body-exp)
- (if (null? vars)
- body-exp
- (begin
- (for-each maybe-name-value! ids val-exps)
- (make-letrec src in-order? ids vars val-exps body-exp)))))
- (make-syntax-object
- (lambda (expression wrap module)
- (vector 'syntax-object expression wrap module)))
- (syntax-object?
- (lambda (x)
- (and (vector? x)
- (= (vector-length x) 4)
- (eq? (vector-ref x 0) 'syntax-object))))
- (syntax-object-expression (lambda (x) (vector-ref x 1)))
- (syntax-object-wrap (lambda (x) (vector-ref x 2)))
- (syntax-object-module (lambda (x) (vector-ref x 3)))
- (set-syntax-object-expression!
- (lambda (x update) (vector-set! x 1 update)))
- (set-syntax-object-wrap!
- (lambda (x update) (vector-set! x 2 update)))
- (set-syntax-object-module!
- (lambda (x update) (vector-set! x 3 update)))
- (source-annotation
- (lambda (x)
- (let ((props (source-properties
- (if (syntax-object? x) (syntax-object-expression x) x))))
- (and (pair? props) props))))
- (extend-env
- (lambda (labels bindings r)
- (if (null? labels)
- r
- (extend-env
- (cdr labels)
- (cdr bindings)
- (cons (cons (car labels) (car bindings)) r)))))
- (extend-var-env
- (lambda (labels vars r)
- (if (null? labels)
- r
- (extend-var-env
- (cdr labels)
- (cdr vars)
- (cons (cons (car labels) (cons 'lexical (car vars))) r)))))
- (macros-only-env
- (lambda (r)
- (if (null? r)
- '()
- (let ((a (car r)))
- (if (memq (cadr a) '(macro ellipsis))
- (cons a (macros-only-env (cdr r)))
- (macros-only-env (cdr r)))))))
- (lookup
- (lambda (x r mod)
- (let ((t (assq x r)))
- (cond (t (cdr t))
- ((symbol? x) (or (get-global-definition-hook x mod) '(global)))
- (else '(displaced-lexical))))))
- (global-extend
- (lambda (type sym val) (put-global-definition-hook sym type val)))
- (nonsymbol-id?
- (lambda (x)
- (and (syntax-object? x) (symbol? (syntax-object-expression x)))))
- (id? (lambda (x)
- (if (symbol? x)
- #t
- (and (syntax-object? x) (symbol? (syntax-object-expression x))))))
- (id-sym-name&marks
- (lambda (x w)
- (if (syntax-object? x)
- (values
- (syntax-object-expression x)
- (join-marks (car w) (car (syntax-object-wrap x))))
- (values x (car w)))))
- (gen-label (lambda () (symbol->string (module-gensym "l"))))
- (gen-labels
- (lambda (ls)
- (if (null? ls) '() (cons (gen-label) (gen-labels (cdr ls))))))
- (make-ribcage
- (lambda (symnames marks labels)
- (vector 'ribcage symnames marks labels)))
- (ribcage?
- (lambda (x)
- (and (vector? x)
- (= (vector-length x) 4)
- (eq? (vector-ref x 0) 'ribcage))))
- (ribcage-symnames (lambda (x) (vector-ref x 1)))
- (ribcage-marks (lambda (x) (vector-ref x 2)))
- (ribcage-labels (lambda (x) (vector-ref x 3)))
- (set-ribcage-symnames! (lambda (x update) (vector-set! x 1 update)))
- (set-ribcage-marks! (lambda (x update) (vector-set! x 2 update)))
- (set-ribcage-labels! (lambda (x update) (vector-set! x 3 update)))
- (anti-mark
- (lambda (w) (cons (cons #f (car w)) (cons 'shift (cdr w)))))
- (extend-ribcage!
- (lambda (ribcage id label)
- (set-ribcage-symnames!
- ribcage
- (cons (syntax-object-expression id) (ribcage-symnames ribcage)))
- (set-ribcage-marks!
- ribcage
- (cons (car (syntax-object-wrap id)) (ribcage-marks ribcage)))
- (set-ribcage-labels! ribcage (cons label (ribcage-labels ribcage)))))
- (make-binding-wrap
- (lambda (ids labels w)
- (if (null? ids)
- w
- (cons (car w)
- (cons (let* ((labelvec (list->vector labels)) (n (vector-length labelvec)))
- (let ((symnamevec (make-vector n)) (marksvec (make-vector n)))
- (let f ((ids ids) (i 0))
- (if (not (null? ids))
- (call-with-values
- (lambda () (id-sym-name&marks (car ids) w))
- (lambda (symname marks)
- (vector-set! symnamevec i symname)
- (vector-set! marksvec i marks)
- (f (cdr ids) (+ i 1))))))
- (make-ribcage symnamevec marksvec labelvec)))
- (cdr w))))))
- (smart-append (lambda (m1 m2) (if (null? m2) m1 (append m1 m2))))
- (join-wraps
- (lambda (w1 w2)
- (let ((m1 (car w1)) (s1 (cdr w1)))
- (if (null? m1)
- (if (null? s1) w2 (cons (car w2) (smart-append s1 (cdr w2))))
- (cons (smart-append m1 (car w2)) (smart-append s1 (cdr w2)))))))
- (join-marks (lambda (m1 m2) (smart-append m1 m2)))
- (same-marks?
- (lambda (x y)
- (or (eq? x y)
- (and (not (null? x))
- (not (null? y))
- (eq? (car x) (car y))
- (same-marks? (cdr x) (cdr y))))))
- (id-var-name
- (lambda (id w)
- (letrec*
- ((search
- (lambda (sym subst marks)
- (if (null? subst)
- (values #f marks)
- (let ((fst (car subst)))
- (if (eq? fst 'shift)
- (search sym (cdr subst) (cdr marks))
- (let ((symnames (ribcage-symnames fst)))
- (if (vector? symnames)
- (search-vector-rib sym subst marks symnames fst)
- (search-list-rib sym subst marks symnames fst))))))))
- (search-list-rib
- (lambda (sym subst marks symnames ribcage)
- (let f ((symnames symnames) (i 0))
- (cond ((null? symnames) (search sym (cdr subst) marks))
- ((and (eq? (car symnames) sym)
- (same-marks? marks (list-ref (ribcage-marks ribcage) i)))
- (values (list-ref (ribcage-labels ribcage) i) marks))
- (else (f (cdr symnames) (+ i 1)))))))
- (search-vector-rib
- (lambda (sym subst marks symnames ribcage)
- (let ((n (vector-length symnames)))
- (let f ((i 0))
- (cond ((= i n) (search sym (cdr subst) marks))
- ((and (eq? (vector-ref symnames i) sym)
- (same-marks? marks (vector-ref (ribcage-marks ribcage) i)))
- (values (vector-ref (ribcage-labels ribcage) i) marks))
- (else (f (+ i 1)))))))))
- (cond ((symbol? id) (or (search id (cdr w) (car w)) id))
- ((syntax-object? id)
- (let ((id (syntax-object-expression id)) (w1 (syntax-object-wrap id)))
- (let ((marks (join-marks (car w) (car w1))))
- (call-with-values
- (lambda () (search id (cdr w) marks))
- (lambda (new-id marks) (or new-id (search id (cdr w1) marks) id))))))
- (else (syntax-violation 'id-var-name "invalid id" id))))))
- (locally-bound-identifiers
- (lambda (w mod)
- (letrec*
- ((scan (lambda (subst results)
- (if (null? subst)
- results
- (let ((fst (car subst)))
- (if (eq? fst 'shift)
- (scan (cdr subst) results)
- (let ((symnames (ribcage-symnames fst)) (marks (ribcage-marks fst)))
- (if (vector? symnames)
- (scan-vector-rib subst symnames marks results)
- (scan-list-rib subst symnames marks results))))))))
- (scan-list-rib
- (lambda (subst symnames marks results)
- (let f ((symnames symnames) (marks marks) (results results))
- (if (null? symnames)
- (scan (cdr subst) results)
- (f (cdr symnames)
- (cdr marks)
- (cons (wrap (car symnames) (anti-mark (cons (car marks) subst)) mod)
- results))))))
- (scan-vector-rib
- (lambda (subst symnames marks results)
- (let ((n (vector-length symnames)))
- (let f ((i 0) (results results))
- (if (= i n)
- (scan (cdr subst) results)
- (f (+ i 1)
- (cons (wrap (vector-ref symnames i)
- (anti-mark (cons (vector-ref marks i) subst))
- mod)
- results))))))))
- (scan (cdr w) '()))))
- (resolve-identifier
- (lambda (id w r mod)
- (letrec*
- ((resolve-global
- (lambda (var mod)
- (let ((b (or (get-global-definition-hook var mod) '(global))))
- (if (eq? (car b) 'global)
- (values 'global var mod)
- (values (car b) (cdr b) mod)))))
- (resolve-lexical
- (lambda (label mod)
- (let ((b (or (assq-ref r label) '(displaced-lexical))))
- (values (car b) (cdr b) mod)))))
- (let ((n (id-var-name id w)))
- (cond ((symbol? n)
- (resolve-global
- n
- (if (syntax-object? id) (syntax-object-module id) mod)))
- ((string? n)
- (resolve-lexical
- n
- (if (syntax-object? id) (syntax-object-module id) mod)))
- (else (error "unexpected id-var-name" id w n)))))))
- (transformer-environment
- (make-fluid
- (lambda (k)
- (error "called outside the dynamic extent of a syntax transformer"))))
- (with-transformer-environment
- (lambda (k) ((fluid-ref transformer-environment) k)))
- (free-id=?
- (lambda (i j)
- (and (eq? (let ((x i)) (if (syntax-object? x) (syntax-object-expression x) x))
- (let ((x j)) (if (syntax-object? x) (syntax-object-expression x) x)))
- (eq? (id-var-name i '(())) (id-var-name j '(()))))))
- (bound-id=?
- (lambda (i j)
- (if (and (syntax-object? i) (syntax-object? j))
- (and (eq? (syntax-object-expression i) (syntax-object-expression j))
- (same-marks?
- (car (syntax-object-wrap i))
- (car (syntax-object-wrap j))))
- (eq? i j))))
- (valid-bound-ids?
- (lambda (ids)
- (and (let all-ids? ((ids ids))
- (or (null? ids) (and (id? (car ids)) (all-ids? (cdr ids)))))
- (distinct-bound-ids? ids))))
- (distinct-bound-ids?
- (lambda (ids)
- (let distinct? ((ids ids))
- (or (null? ids)
- (and (not (bound-id-member? (car ids) (cdr ids)))
- (distinct? (cdr ids)))))))
- (bound-id-member?
- (lambda (x list)
- (and (not (null? list))
- (or (bound-id=? x (car list)) (bound-id-member? x (cdr list))))))
- (wrap (lambda (x w defmod)
- (cond ((and (null? (car w)) (null? (cdr w))) x)
- ((syntax-object? x)
- (make-syntax-object
- (syntax-object-expression x)
- (join-wraps w (syntax-object-wrap x))
- (syntax-object-module x)))
- ((null? x) x)
- (else (make-syntax-object x w defmod)))))
- (source-wrap
- (lambda (x w s defmod) (wrap (decorate-source x s) w defmod)))
- (expand-sequence
- (lambda (body r w s mod)
- (build-sequence
- s
- (let dobody ((body body) (r r) (w w) (mod mod))
- (if (null? body)
- '()
- (let ((first (expand (car body) r w mod)))
- (cons first (dobody (cdr body) r w mod))))))))
- (expand-top-sequence
- (lambda (body r w s m esew mod)
- (letrec*
- ((scan (lambda (body r w s m esew mod exps)
- (if (null? body)
- exps
- (call-with-values
- (lambda ()
- (call-with-values
- (lambda ()
- (let ((e (car body)))
- (syntax-type e r w (or (source-annotation e) s) #f mod #f)))
- (lambda (type value form e w s mod)
- (let ((key type))
- (cond ((memv key '(begin-form))
- (let* ((tmp e) (tmp-1 ($sc-dispatch tmp '(_))))
- (if tmp-1
- (apply (lambda () exps) tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(_ any . each-any))))
- (if tmp-1
- (apply (lambda (e1 e2) (scan (cons e1 e2) r w s m esew mod exps))
- tmp-1)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp))))))
- ((memv key '(local-syntax-form))
- (expand-local-syntax
- value
- e
- r
- w
- s
- mod
- (lambda (body r w s mod) (scan body r w s m esew mod exps))))
- ((memv key '(eval-when-form))
- (let* ((tmp-1 e) (tmp ($sc-dispatch tmp-1 '(_ each-any any . each-any))))
- (if tmp
- (apply (lambda (x e1 e2)
- (let ((when-list (parse-when-list e x)) (body (cons e1 e2)))
- (cond ((eq? m 'e)
- (if (memq 'eval when-list)
- (scan body
- r
- w
- s
- (if (memq 'expand when-list) 'c&e 'e)
- '(eval)
- mod
- exps)
- (begin
- (if (memq 'expand when-list)
- (top-level-eval-hook
- (expand-top-sequence body r w s 'e '(eval) mod)
- mod))
- (values exps))))
- ((memq 'load when-list)
- (cond ((or (memq 'compile when-list)
- (memq 'expand when-list)
- (and (eq? m 'c&e) (memq 'eval when-list)))
- (scan body r w s 'c&e '(compile load) mod exps))
- ((memq m '(c c&e))
- (scan body r w s 'c '(load) mod exps))
- (else (values exps))))
- ((or (memq 'compile when-list)
- (memq 'expand when-list)
- (and (eq? m 'c&e) (memq 'eval when-list)))
- (top-level-eval-hook
- (expand-top-sequence body r w s 'e '(eval) mod)
- mod)
- (values exps))
- (else (values exps)))))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1))))
- ((memv key '(define-syntax-form define-syntax-parameter-form))
- (let ((n (id-var-name value w)) (r (macros-only-env r)))
- (let ((key m))
- (cond ((memv key '(c))
- (cond ((memq 'compile esew)
- (let ((e (expand-install-global n (expand e r w mod))))
- (top-level-eval-hook e mod)
- (if (memq 'load esew) (values (cons e exps)) (values exps))))
- ((memq 'load esew)
- (values
- (cons (expand-install-global n (expand e r w mod)) exps)))
- (else (values exps))))
- ((memv key '(c&e))
- (let ((e (expand-install-global n (expand e r w mod))))
- (top-level-eval-hook e mod)
- (values (cons e exps))))
- (else
- (if (memq 'eval esew)
- (top-level-eval-hook
- (expand-install-global n (expand e r w mod))
- mod))
- (values exps))))))
- ((memv key '(define-form))
- (let* ((n (id-var-name value w)) (type (car (lookup n r mod))) (key type))
- (cond ((memv key '(global core macro module-ref))
- (if (and (memq m '(c c&e))
- (not (module-local-variable (current-module) n))
- (current-module))
- (let ((old (module-variable (current-module) n)))
- (if (and (variable? old)
- (variable-bound? old)
- (not (macro? (variable-ref old))))
- (module-define! (current-module) n (variable-ref old))
- (module-add! (current-module) n (make-undefined-variable)))))
- (values
- (cons (if (eq? m 'c&e)
- (let ((x (build-global-definition s n (expand e r w mod))))
- (top-level-eval-hook x mod)
- x)
- (lambda () (build-global-definition s n (expand e r w mod))))
- exps)))
- ((memv key '(displaced-lexical))
- (syntax-violation
- #f
- "identifier out of context"
- (source-wrap form w s mod)
- (wrap value w mod)))
- (else
- (syntax-violation
- #f
- "cannot define keyword at top level"
- (source-wrap form w s mod)
- (wrap value w mod))))))
- (else
- (values
- (cons (if (eq? m 'c&e)
- (let ((x (expand-expr type value form e r w s mod)))
- (top-level-eval-hook x mod)
- x)
- (lambda () (expand-expr type value form e r w s mod)))
- exps))))))))
- (lambda (exps) (scan (cdr body) r w s m esew mod exps)))))))
- (call-with-values
- (lambda () (scan body r w s m esew mod '()))
- (lambda (exps)
- (if (null? exps)
- (build-void s)
- (build-sequence
- s
- (let lp ((in exps) (out '()))
- (if (null? in)
- out
- (let ((e (car in)))
- (lp (cdr in) (cons (if (procedure? e) (e) e) out))))))))))))
- (expand-install-global
- (lambda (name e)
- (build-global-definition
- #f
- name
- (build-application
- #f
- (build-primref #f 'make-syntax-transformer)
- (list (build-data #f name) (build-data #f 'macro) e)))))
- (parse-when-list
- (lambda (e when-list)
- (let ((result (strip when-list '(()))))
- (let lp ((l result))
- (cond ((null? l) result)
- ((memq (car l) '(compile load eval expand)) (lp (cdr l)))
- (else (syntax-violation 'eval-when "invalid situation" e (car l))))))))
- (syntax-type
- (lambda (e r w s rib mod for-car?)
- (cond ((symbol? e)
- (let* ((n (id-var-name e w))
- (b (lookup n r mod))
- (type (car b))
- (key type))
- (cond ((memv key '(lexical)) (values type (cdr b) e e w s mod))
- ((memv key '(global)) (values type n e e w s mod))
- ((memv key '(macro))
- (if for-car?
- (values type (cdr b) e e w s mod)
- (syntax-type
- (expand-macro (cdr b) e r w s rib mod)
- r
- '(())
- s
- rib
- mod
- #f)))
- (else (values type (cdr b) e e w s mod)))))
- ((pair? e)
- (let ((first (car e)))
- (call-with-values
- (lambda () (syntax-type first r w s rib mod #t))
- (lambda (ftype fval fform fe fw fs fmod)
- (let ((key ftype))
- (cond ((memv key '(lexical)) (values 'lexical-call fval e e w s mod))
- ((memv key '(global))
- (values 'global-call (make-syntax-object fval w fmod) e e w s mod))
- ((memv key '(macro))
- (syntax-type
- (expand-macro fval e r w s rib mod)
- r
- '(())
- s
- rib
- mod
- for-car?))
- ((memv key '(module-ref))
- (call-with-values
- (lambda () (fval e r w))
- (lambda (e r w s mod) (syntax-type e r w s rib mod for-car?))))
- ((memv key '(core)) (values 'core-form fval e e w s mod))
- ((memv key '(local-syntax))
- (values 'local-syntax-form fval e e w s mod))
- ((memv key '(begin)) (values 'begin-form #f e e w s mod))
- ((memv key '(eval-when)) (values 'eval-when-form #f e e w s mod))
- ((memv key '(define))
- (let* ((tmp e) (tmp-1 ($sc-dispatch tmp '(_ any any))))
- (if (and tmp-1 (apply (lambda (name val) (id? name)) tmp-1))
- (apply (lambda (name val) (values 'define-form name e val w s mod))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(_ (any . any) any . each-any))))
- (if (and tmp-1
- (apply (lambda (name args e1 e2)
- (and (id? name) (valid-bound-ids? (lambda-var-list args))))
- tmp-1))
- (apply (lambda (name args e1 e2)
- (values
- 'define-form
- (wrap name w mod)
- (wrap e w mod)
- (decorate-source
- (cons '#(syntax-object lambda ((top)) (hygiene guile))
- (wrap (cons args (cons e1 e2)) w mod))
- s)
- '(())
- s
- mod))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(_ any))))
- (if (and tmp-1 (apply (lambda (name) (id? name)) tmp-1))
- (apply (lambda (name)
- (values
- 'define-form
- (wrap name w mod)
- (wrap e w mod)
- '(#(syntax-object if ((top)) (hygiene guile)) #f #f)
- '(())
- s
- mod))
- tmp-1)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp))))))))
- ((memv key '(define-syntax))
- (let* ((tmp-1 e) (tmp ($sc-dispatch tmp-1 '(_ any any))))
- (if (and tmp (apply (lambda (name val) (id? name)) tmp))
- (apply (lambda (name val) (values 'define-syntax-form name e val w s mod))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1))))
- ((memv key '(define-syntax-parameter))
- (let* ((tmp-1 e) (tmp ($sc-dispatch tmp-1 '(_ any any))))
- (if (and tmp (apply (lambda (name val) (id? name)) tmp))
- (apply (lambda (name val)
- (values 'define-syntax-parameter-form name e val w s mod))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1))))
- (else (values 'call #f e e w s mod))))))))
- ((syntax-object? e)
- (syntax-type
- (syntax-object-expression e)
- r
- (join-wraps w (syntax-object-wrap e))
- (or (source-annotation e) s)
- rib
- (or (syntax-object-module e) mod)
- for-car?))
- ((self-evaluating? e) (values 'constant #f e e w s mod))
- (else (values 'other #f e e w s mod)))))
- (expand
- (lambda (e r w mod)
- (call-with-values
- (lambda () (syntax-type e r w (source-annotation e) #f mod #f))
- (lambda (type value form e w s mod)
- (expand-expr type value form e r w s mod)))))
- (expand-expr
- (lambda (type value form e r w s mod)
- (let ((key type))
- (cond ((memv key '(lexical)) (build-lexical-reference 'value s e value))
- ((memv key '(core core-form)) (value e r w s mod))
- ((memv key '(module-ref))
- (call-with-values
- (lambda () (value e r w))
- (lambda (e r w s mod) (expand e r w mod))))
- ((memv key '(lexical-call))
- (expand-application
- (let ((id (car e)))
- (build-lexical-reference
- 'fun
- (source-annotation id)
- (if (syntax-object? id) (syntax->datum id) id)
- value))
- e
- r
- w
- s
- mod))
- ((memv key '(global-call))
- (expand-application
- (build-global-reference
- (source-annotation (car e))
- (if (syntax-object? value) (syntax-object-expression value) value)
- (if (syntax-object? value) (syntax-object-module value) mod))
- e
- r
- w
- s
- mod))
- ((memv key '(constant))
- (build-data s (strip (source-wrap e w s mod) '(()))))
- ((memv key '(global)) (build-global-reference s value mod))
- ((memv key '(call))
- (expand-application (expand (car e) r w mod) e r w s mod))
- ((memv key '(begin-form))
- (let* ((tmp e) (tmp-1 ($sc-dispatch tmp '(_ any . each-any))))
- (if tmp-1
- (apply (lambda (e1 e2) (expand-sequence (cons e1 e2) r w s mod))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(_))))
- (if tmp-1
- (apply (lambda ()
- (if (include-deprecated-features)
- (begin
- (issue-deprecation-warning
- "Sequences of zero expressions are deprecated. Use *unspecified*.")
- (expand-void))
- (syntax-violation
- #f
- "sequence of zero expressions"
- (source-wrap e w s mod))))
- tmp-1)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp))))))
- ((memv key '(local-syntax-form))
- (expand-local-syntax value e r w s mod expand-sequence))
- ((memv key '(eval-when-form))
- (let* ((tmp-1 e) (tmp ($sc-dispatch tmp-1 '(_ each-any any . each-any))))
- (if tmp
- (apply (lambda (x e1 e2)
- (let ((when-list (parse-when-list e x)))
- (if (memq 'eval when-list)
- (expand-sequence (cons e1 e2) r w s mod)
- (expand-void))))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1))))
- ((memv key
- '(define-form define-syntax-form define-syntax-parameter-form))
- (syntax-violation
- #f
- "definition in expression context, where definitions are not allowed,"
- (source-wrap form w s mod)))
- ((memv key '(syntax))
- (syntax-violation
- #f
- "reference to pattern variable outside syntax form"
- (source-wrap e w s mod)))
- ((memv key '(displaced-lexical))
- (syntax-violation
- #f
- "reference to identifier outside its scope"
- (source-wrap e w s mod)))
- (else
- (syntax-violation #f "unexpected syntax" (source-wrap e w s mod)))))))
- (expand-application
- (lambda (x e r w s mod)
- (let* ((tmp-1 e) (tmp ($sc-dispatch tmp-1 '(any . each-any))))
- (if tmp
- (apply (lambda (e0 e1)
- (build-application s x (map (lambda (e) (expand e r w mod)) e1)))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))
- (expand-macro
- (lambda (p e r w s rib mod)
- (letrec*
- ((rebuild-macro-output
- (lambda (x m)
- (cond ((pair? x)
- (decorate-source
- (cons (rebuild-macro-output (car x) m)
- (rebuild-macro-output (cdr x) m))
- s))
- ((syntax-object? x)
- (let ((w (syntax-object-wrap x)))
- (let ((ms (car w)) (ss (cdr w)))
- (if (and (pair? ms) (eq? (car ms) #f))
- (make-syntax-object
- (syntax-object-expression x)
- (cons (cdr ms) (if rib (cons rib (cdr ss)) (cdr ss)))
- (syntax-object-module x))
- (make-syntax-object
- (decorate-source (syntax-object-expression x) s)
- (cons (cons m ms)
- (if rib (cons rib (cons 'shift ss)) (cons 'shift ss)))
- (syntax-object-module x))))))
- ((vector? x)
- (let* ((n (vector-length x)) (v (decorate-source (make-vector n) s)))
- (let loop ((i 0))
- (if (= i n)
- (begin (if #f #f) v)
- (begin
- (vector-set! v i (rebuild-macro-output (vector-ref x i) m))
- (loop (+ i 1)))))))
- ((symbol? x)
- (syntax-violation
- #f
- "encountered raw symbol in macro output"
- (source-wrap e w (cdr w) mod)
- x))
- (else (decorate-source x s))))))
- (with-fluids
- ((transformer-environment (lambda (k) (k e r w s rib mod))))
- (rebuild-macro-output
- (p (source-wrap e (anti-mark w) s mod))
- (module-gensym "m"))))))
- (expand-body
- (lambda (body outer-form r w mod)
- (let* ((r (cons '("placeholder" placeholder) r))
- (ribcage (make-ribcage '() '() '()))
- (w (cons (car w) (cons ribcage (cdr w)))))
- (let parse ((body (map (lambda (x) (cons r (wrap x w mod))) body))
- (ids '())
- (labels '())
- (var-ids '())
- (vars '())
- (vals '())
- (bindings '()))
- (if (null? body)
- (syntax-violation #f "no expressions in body" outer-form)
- (let ((e (cdar body)) (er (caar body)))
- (call-with-values
- (lambda ()
- (syntax-type e er '(()) (source-annotation e) ribcage mod #f))
- (lambda (type value form e w s mod)
- (let ((key type))
- (cond ((memv key '(define-form))
- (let ((id (wrap value w mod)) (label (gen-label)))
- (let ((var (gen-var id)))
- (extend-ribcage! ribcage id label)
- (parse (cdr body)
- (cons id ids)
- (cons label labels)
- (cons id var-ids)
- (cons var vars)
- (cons (cons er (wrap e w mod)) vals)
- (cons (cons 'lexical var) bindings)))))
- ((memv key '(define-syntax-form define-syntax-parameter-form))
- (let ((id (wrap value w mod))
- (label (gen-label))
- (trans-r (macros-only-env er)))
- (extend-ribcage! ribcage id label)
- (set-cdr!
- r
- (extend-env
- (list label)
- (list (cons 'macro (eval-local-transformer (expand e trans-r w mod) mod)))
- (cdr r)))
- (parse (cdr body) (cons id ids) labels var-ids vars vals bindings)))
- ((memv key '(begin-form))
- (let* ((tmp-1 e) (tmp ($sc-dispatch tmp-1 '(_ . each-any))))
- (if tmp
- (apply (lambda (e1)
- (parse (let f ((forms e1))
- (if (null? forms)
- (cdr body)
- (cons (cons er (wrap (car forms) w mod)) (f (cdr forms)))))
- ids
- labels
- var-ids
- vars
- vals
- bindings))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1))))
- ((memv key '(local-syntax-form))
- (expand-local-syntax
- value
- e
- er
- w
- s
- mod
- (lambda (forms er w s mod)
- (parse (let f ((forms forms))
- (if (null? forms)
- (cdr body)
- (cons (cons er (wrap (car forms) w mod)) (f (cdr forms)))))
- ids
- labels
- var-ids
- vars
- vals
- bindings))))
- ((null? ids)
- (build-sequence
- #f
- (map (lambda (x) (expand (cdr x) (car x) '(()) mod))
- (cons (cons er (source-wrap e w s mod)) (cdr body)))))
- (else
- (if (not (valid-bound-ids? ids))
- (syntax-violation
- #f
- "invalid or duplicate identifier in definition"
- outer-form))
- (set-cdr! r (extend-env labels bindings (cdr r)))
- (build-letrec
- #f
- #t
- (reverse (map syntax->datum var-ids))
- (reverse vars)
- (map (lambda (x) (expand (cdr x) (car x) '(()) mod)) (reverse vals))
- (build-sequence
- #f
- (map (lambda (x) (expand (cdr x) (car x) '(()) mod))
- (cons (cons er (source-wrap e w s mod)) (cdr body))))))))))))))))
- (expand-local-syntax
- (lambda (rec? e r w s mod k)
- (let* ((tmp e)
- (tmp ($sc-dispatch tmp '(_ #(each (any any)) any . each-any))))
- (if tmp
- (apply (lambda (id val e1 e2)
- (let ((ids id))
- (if (not (valid-bound-ids? ids))
- (syntax-violation #f "duplicate bound keyword" e)
- (let* ((labels (gen-labels ids)) (new-w (make-binding-wrap ids labels w)))
- (k (cons e1 e2)
- (extend-env
- labels
- (let ((w (if rec? new-w w)) (trans-r (macros-only-env r)))
- (map (lambda (x)
- (cons 'macro (eval-local-transformer (expand x trans-r w mod) mod)))
- val))
- r)
- new-w
- s
- mod)))))
- tmp)
- (syntax-violation
- #f
- "bad local syntax definition"
- (source-wrap e w s mod))))))
- (eval-local-transformer
- (lambda (expanded mod)
- (let ((p (local-eval-hook expanded mod)))
- (if (procedure? p)
- p
- (syntax-violation #f "nonprocedure transformer" p)))))
- (expand-void (lambda () (build-void #f)))
- (ellipsis?
- (lambda (e r mod)
- (and (nonsymbol-id? e)
- (let* ((id (make-syntax-object
- '#{ $sc-ellipsis }
- (syntax-object-wrap e)
- (syntax-object-module e)))
- (n (id-var-name id '(())))
- (b (lookup n r mod)))
- (if (eq? (car b) 'ellipsis)
- (bound-id=? e (cdr b))
- (free-id=? e '#(syntax-object ... ((top)) (hygiene guile))))))))
- (lambda-formals
- (lambda (orig-args)
- (letrec*
- ((req (lambda (args rreq)
- (let* ((tmp args) (tmp-1 ($sc-dispatch tmp '())))
- (if tmp-1
- (apply (lambda () (check (reverse rreq) #f)) tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any . any))))
- (if (and tmp-1 (apply (lambda (a b) (id? a)) tmp-1))
- (apply (lambda (a b) (req b (cons a rreq))) tmp-1)
- (let ((tmp-1 (list tmp)))
- (if (and tmp-1 (apply (lambda (r) (id? r)) tmp-1))
- (apply (lambda (r) (check (reverse rreq) r)) tmp-1)
- (let ((else tmp))
- (syntax-violation 'lambda "invalid argument list" orig-args args))))))))))
- (check (lambda (req rest)
- (if (distinct-bound-ids? (if rest (cons rest req) req))
- (values req #f rest #f)
- (syntax-violation
- 'lambda
- "duplicate identifier in argument list"
- orig-args)))))
- (req orig-args '()))))
- (expand-simple-lambda
- (lambda (e r w s mod req rest meta body)
- (let* ((ids (if rest (append req (list rest)) req))
- (vars (map gen-var ids))
- (labels (gen-labels ids)))
- (build-simple-lambda
- s
- (map syntax->datum req)
- (and rest (syntax->datum rest))
- vars
- meta
- (expand-body
- body
- (source-wrap e w s mod)
- (extend-var-env labels vars r)
- (make-binding-wrap ids labels w)
- mod)))))
- (lambda*-formals
- (lambda (orig-args)
- (letrec*
- ((req (lambda (args rreq)
- (let* ((tmp args) (tmp-1 ($sc-dispatch tmp '())))
- (if tmp-1
- (apply (lambda () (check (reverse rreq) '() #f '())) tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any . any))))
- (if (and tmp-1 (apply (lambda (a b) (id? a)) tmp-1))
- (apply (lambda (a b) (req b (cons a rreq))) tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any . any))))
- (if (and tmp-1
- (apply (lambda (a b) (eq? (syntax->datum a) #\optional)) tmp-1))
- (apply (lambda (a b) (opt b (reverse rreq) '())) tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any . any))))
- (if (and tmp-1
- (apply (lambda (a b) (eq? (syntax->datum a) #\key)) tmp-1))
- (apply (lambda (a b) (key b (reverse rreq) '() '())) tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any any))))
- (if (and tmp-1
- (apply (lambda (a b) (eq? (syntax->datum a) #\rest)) tmp-1))
- (apply (lambda (a b) (rest b (reverse rreq) '() '())) tmp-1)
- (let ((tmp-1 (list tmp)))
- (if (and tmp-1 (apply (lambda (r) (id? r)) tmp-1))
- (apply (lambda (r) (rest r (reverse rreq) '() '())) tmp-1)
- (let ((else tmp))
- (syntax-violation
- 'lambda*
- "invalid argument list"
- orig-args
- args))))))))))))))))
- (opt (lambda (args req ropt)
- (let* ((tmp args) (tmp-1 ($sc-dispatch tmp '())))
- (if tmp-1
- (apply (lambda () (check req (reverse ropt) #f '())) tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any . any))))
- (if (and tmp-1 (apply (lambda (a b) (id? a)) tmp-1))
- (apply (lambda (a b) (opt b req (cons (cons a '(#f)) ropt))) tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '((any any) . any))))
- (if (and tmp-1 (apply (lambda (a init b) (id? a)) tmp-1))
- (apply (lambda (a init b) (opt b req (cons (list a init) ropt)))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any . any))))
- (if (and tmp-1
- (apply (lambda (a b) (eq? (syntax->datum a) #\key)) tmp-1))
- (apply (lambda (a b) (key b req (reverse ropt) '())) tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any any))))
- (if (and tmp-1
- (apply (lambda (a b) (eq? (syntax->datum a) #\rest)) tmp-1))
- (apply (lambda (a b) (rest b req (reverse ropt) '())) tmp-1)
- (let ((tmp-1 (list tmp)))
- (if (and tmp-1 (apply (lambda (r) (id? r)) tmp-1))
- (apply (lambda (r) (rest r req (reverse ropt) '())) tmp-1)
- (let ((else tmp))
- (syntax-violation
- 'lambda*
- "invalid optional argument list"
- orig-args
- args))))))))))))))))
- (key (lambda (args req opt rkey)
- (let* ((tmp args) (tmp-1 ($sc-dispatch tmp '())))
- (if tmp-1
- (apply (lambda () (check req opt #f (cons #f (reverse rkey)))) tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any . any))))
- (if (and tmp-1 (apply (lambda (a b) (id? a)) tmp-1))
- (apply (lambda (a b)
- (let* ((tmp (symbol->keyword (syntax->datum a))) (k tmp))
- (key b req opt (cons (cons k (cons a '(#f))) rkey))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '((any any) . any))))
- (if (and tmp-1 (apply (lambda (a init b) (id? a)) tmp-1))
- (apply (lambda (a init b)
- (let* ((tmp (symbol->keyword (syntax->datum a))) (k tmp))
- (key b req opt (cons (list k a init) rkey))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '((any any any) . any))))
- (if (and tmp-1
- (apply (lambda (a init k b) (and (id? a) (keyword? (syntax->datum k))))
- tmp-1))
- (apply (lambda (a init k b) (key b req opt (cons (list k a init) rkey)))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any))))
- (if (and tmp-1
- (apply (lambda (aok) (eq? (syntax->datum aok) #\allow-other-keys))
- tmp-1))
- (apply (lambda (aok) (check req opt #f (cons #t (reverse rkey))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any any any))))
- (if (and tmp-1
- (apply (lambda (aok a b)
- (and (eq? (syntax->datum aok) #\allow-other-keys)
- (eq? (syntax->datum a) #\rest)))
- tmp-1))
- (apply (lambda (aok a b) (rest b req opt (cons #t (reverse rkey))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any . any))))
- (if (and tmp-1
- (apply (lambda (aok r)
- (and (eq? (syntax->datum aok) #\allow-other-keys) (id? r)))
- tmp-1))
- (apply (lambda (aok r) (rest r req opt (cons #t (reverse rkey))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any any))))
- (if (and tmp-1
- (apply (lambda (a b) (eq? (syntax->datum a) #\rest)) tmp-1))
- (apply (lambda (a b) (rest b req opt (cons #f (reverse rkey))))
- tmp-1)
- (let ((tmp-1 (list tmp)))
- (if (and tmp-1 (apply (lambda (r) (id? r)) tmp-1))
- (apply (lambda (r) (rest r req opt (cons #f (reverse rkey))))
- tmp-1)
- (let ((else tmp))
- (syntax-violation
- 'lambda*
- "invalid keyword argument list"
- orig-args
- args))))))))))))))))))))))
- (rest (lambda (args req opt kw)
- (let* ((tmp-1 args) (tmp (list tmp-1)))
- (if (and tmp (apply (lambda (r) (id? r)) tmp))
- (apply (lambda (r) (check req opt r kw)) tmp)
- (let ((else tmp-1))
- (syntax-violation 'lambda* "invalid rest argument" orig-args args))))))
- (check (lambda (req opt rest kw)
- (if (distinct-bound-ids?
- (append
- req
- (map car opt)
- (if rest (list rest) '())
- (if (pair? kw) (map cadr (cdr kw)) '())))
- (values req opt rest kw)
- (syntax-violation
- 'lambda*
- "duplicate identifier in argument list"
- orig-args)))))
- (req orig-args '()))))
- (expand-lambda-case
- (lambda (e r w s mod get-formals clauses)
- (letrec*
- ((parse-req
- (lambda (req opt rest kw body)
- (let ((vars (map gen-var req)) (labels (gen-labels req)))
- (let ((r* (extend-var-env labels vars r))
- (w* (make-binding-wrap req labels w)))
- (parse-opt
- (map syntax->datum req)
- opt
- rest
- kw
- body
- (reverse vars)
- r*
- w*
- '()
- '())))))
- (parse-opt
- (lambda (req opt rest kw body vars r* w* out inits)
- (cond ((pair? opt)
- (let* ((tmp-1 (car opt)) (tmp ($sc-dispatch tmp-1 '(any any))))
- (if tmp
- (apply (lambda (id i)
- (let* ((v (gen-var id))
- (l (gen-labels (list v)))
- (r** (extend-var-env l (list v) r*))
- (w** (make-binding-wrap (list id) l w*)))
- (parse-opt
- req
- (cdr opt)
- rest
- kw
- body
- (cons v vars)
- r**
- w**
- (cons (syntax->datum id) out)
- (cons (expand i r* w* mod) inits))))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1))))
- (rest
- (let* ((v (gen-var rest))
- (l (gen-labels (list v)))
- (r* (extend-var-env l (list v) r*))
- (w* (make-binding-wrap (list rest) l w*)))
- (parse-kw
- req
- (and (pair? out) (reverse out))
- (syntax->datum rest)
- (if (pair? kw) (cdr kw) kw)
- body
- (cons v vars)
- r*
- w*
- (and (pair? kw) (car kw))
- '()
- inits)))
- (else
- (parse-kw
- req
- (and (pair? out) (reverse out))
- #f
- (if (pair? kw) (cdr kw) kw)
- body
- vars
- r*
- w*
- (and (pair? kw) (car kw))
- '()
- inits)))))
- (parse-kw
- (lambda (req opt rest kw body vars r* w* aok out inits)
- (if (pair? kw)
- (let* ((tmp-1 (car kw)) (tmp ($sc-dispatch tmp-1 '(any any any))))
- (if tmp
- (apply (lambda (k id i)
- (let* ((v (gen-var id))
- (l (gen-labels (list v)))
- (r** (extend-var-env l (list v) r*))
- (w** (make-binding-wrap (list id) l w*)))
- (parse-kw
- req
- opt
- rest
- (cdr kw)
- body
- (cons v vars)
- r**
- w**
- aok
- (cons (list (syntax->datum k) (syntax->datum id) v) out)
- (cons (expand i r* w* mod) inits))))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))
- (parse-body
- req
- opt
- rest
- (and (or aok (pair? out)) (cons aok (reverse out)))
- body
- (reverse vars)
- r*
- w*
- (reverse inits)
- '()))))
- (parse-body
- (lambda (req opt rest kw body vars r* w* inits meta)
- (let* ((tmp body) (tmp-1 ($sc-dispatch tmp '(any any . each-any))))
- (if (and tmp-1
- (apply (lambda (docstring e1 e2) (string? (syntax->datum docstring)))
- tmp-1))
- (apply (lambda (docstring e1 e2)
- (parse-body
- req
- opt
- rest
- kw
- (cons e1 e2)
- vars
- r*
- w*
- inits
- (append meta (list (cons 'documentation (syntax->datum docstring))))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(#(vector #(each (any . any))) any . each-any))))
- (if tmp-1
- (apply (lambda (k v e1 e2)
- (parse-body
- req
- opt
- rest
- kw
- (cons e1 e2)
- vars
- r*
- w*
- inits
- (append meta (syntax->datum (map cons k v)))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any . each-any))))
- (if tmp-1
- (apply (lambda (e1 e2)
- (values
- meta
- req
- opt
- rest
- kw
- inits
- vars
- (expand-body (cons e1 e2) (source-wrap e w s mod) r* w* mod)))
- tmp-1)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp))))))))))
- (let* ((tmp clauses) (tmp-1 ($sc-dispatch tmp '())))
- (if tmp-1
- (apply (lambda () (values '() #f)) tmp-1)
- (let ((tmp-1 ($sc-dispatch
- tmp
- '((any any . each-any) . #(each (any any . each-any))))))
- (if tmp-1
- (apply (lambda (args e1 e2 args* e1* e2*)
- (call-with-values
- (lambda () (get-formals args))
- (lambda (req opt rest kw)
- (call-with-values
- (lambda () (parse-req req opt rest kw (cons e1 e2)))
- (lambda (meta req opt rest kw inits vars body)
- (call-with-values
- (lambda ()
- (expand-lambda-case
- e
- r
- w
- s
- mod
- get-formals
- (map (lambda (tmp-bde397a-a85 tmp-bde397a-a84 tmp-bde397a-a83)
- (cons tmp-bde397a-a83 (cons tmp-bde397a-a84 tmp-bde397a-a85)))
- e2*
- e1*
- args*)))
- (lambda (meta* else*)
- (values
- (append meta meta*)
- (build-lambda-case s req opt rest kw inits vars body else*)))))))))
- tmp-1)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp))))))))
- (strip (lambda (x w)
- (if (memq 'top (car w))
- x
- (let f ((x x))
- (cond ((syntax-object? x)
- (strip (syntax-object-expression x) (syntax-object-wrap x)))
- ((pair? x)
- (let ((a (f (car x))) (d (f (cdr x))))
- (if (and (eq? a (car x)) (eq? d (cdr x))) x (cons a d))))
- ((vector? x)
- (let* ((old (vector->list x)) (new (map f old)))
- (let lp ((l1 old) (l2 new))
- (cond ((null? l1) x)
- ((eq? (car l1) (car l2)) (lp (cdr l1) (cdr l2)))
- (else (list->vector new))))))
- (else x))))))
- (gen-var
- (lambda (id)
- (let ((id (if (syntax-object? id) (syntax-object-expression id) id)))
- (module-gensym (symbol->string id)))))
- (lambda-var-list
- (lambda (vars)
- (let lvl ((vars vars) (ls '()) (w '(())))
- (cond ((pair? vars) (lvl (cdr vars) (cons (wrap (car vars) w #f) ls) w))
- ((id? vars) (cons (wrap vars w #f) ls))
- ((null? vars) ls)
- ((syntax-object? vars)
- (lvl (syntax-object-expression vars)
- ls
- (join-wraps w (syntax-object-wrap vars))))
- (else (cons vars ls)))))))
- (global-extend 'local-syntax 'letrec-syntax #t)
- (global-extend 'local-syntax 'let-syntax #f)
- (global-extend
- 'core
- 'syntax-parameterize
- (lambda (e r w s mod)
- (let* ((tmp e)
- (tmp ($sc-dispatch tmp '(_ #(each (any any)) any . each-any))))
- (if (and tmp (apply (lambda (var val e1 e2) (valid-bound-ids? var)) tmp))
- (apply (lambda (var val e1 e2)
- (let ((names (map (lambda (x) (id-var-name x w)) var)))
- (for-each
- (lambda (id n)
- (let ((key (car (lookup n r mod))))
- (if (memv key '(displaced-lexical))
- (syntax-violation
- 'syntax-parameterize
- "identifier out of context"
- e
- (source-wrap id w s mod)))))
- var
- names)
- (expand-body
- (cons e1 e2)
- (source-wrap e w s mod)
- (extend-env
- names
- (let ((trans-r (macros-only-env r)))
- (map (lambda (x)
- (cons 'macro (eval-local-transformer (expand x trans-r w mod) mod)))
- val))
- r)
- w
- mod)))
- tmp)
- (syntax-violation
- 'syntax-parameterize
- "bad syntax"
- (source-wrap e w s mod))))))
- (global-extend
- 'core
- 'quote
- (lambda (e r w s mod)
- (let* ((tmp e) (tmp ($sc-dispatch tmp '(_ any))))
- (if tmp
- (apply (lambda (e) (build-data s (strip e w))) tmp)
- (syntax-violation 'quote "bad syntax" (source-wrap e w s mod))))))
- (global-extend
- 'core
- 'syntax
- (letrec*
- ((gen-syntax
- (lambda (src e r maps ellipsis? mod)
- (if (id? e)
- (let* ((label (id-var-name e '(()))) (b (lookup label r mod)))
- (cond ((eq? (car b) 'syntax)
- (call-with-values
- (lambda ()
- (let ((var.lev (cdr b)))
- (gen-ref src (car var.lev) (cdr var.lev) maps)))
- (lambda (var maps) (values (list 'ref var) maps))))
- ((ellipsis? e r mod)
- (syntax-violation 'syntax "misplaced ellipsis" src))
- (else (values (list 'quote e) maps))))
- (let* ((tmp e) (tmp-1 ($sc-dispatch tmp '(any any))))
- (if (and tmp-1 (apply (lambda (dots e) (ellipsis? dots r mod)) tmp-1))
- (apply (lambda (dots e) (gen-syntax src e r maps (lambda (e r mod) #f) mod))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any any . any))))
- (if (and tmp-1 (apply (lambda (x dots y) (ellipsis? dots r mod)) tmp-1))
- (apply (lambda (x dots y)
- (let f ((y y)
- (k (lambda (maps)
- (call-with-values
- (lambda () (gen-syntax src x r (cons '() maps) ellipsis? mod))
- (lambda (x maps)
- (if (null? (car maps))
- (syntax-violation 'syntax "extra ellipsis" src)
- (values (gen-map x (car maps)) (cdr maps))))))))
- (let* ((tmp y) (tmp ($sc-dispatch tmp '(any . any))))
- (if (and tmp (apply (lambda (dots y) (ellipsis? dots r mod)) tmp))
- (apply (lambda (dots y)
- (f y
- (lambda (maps)
- (call-with-values
- (lambda () (k (cons '() maps)))
- (lambda (x maps)
- (if (null? (car maps))
- (syntax-violation 'syntax "extra ellipsis" src)
- (values (gen-mappend x (car maps)) (cdr maps))))))))
- tmp)
- (call-with-values
- (lambda () (gen-syntax src y r maps ellipsis? mod))
- (lambda (y maps)
- (call-with-values
- (lambda () (k maps))
- (lambda (x maps) (values (gen-append x y) maps)))))))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any . any))))
- (if tmp-1
- (apply (lambda (x y)
- (call-with-values
- (lambda () (gen-syntax src x r maps ellipsis? mod))
- (lambda (x maps)
- (call-with-values
- (lambda () (gen-syntax src y r maps ellipsis? mod))
- (lambda (y maps) (values (gen-cons x y) maps))))))
- tmp-1)
- (let ((tmp ($sc-dispatch tmp '#(vector (any . each-any)))))
- (if tmp
- (apply (lambda (e1 e2)
- (call-with-values
- (lambda () (gen-syntax src (cons e1 e2) r maps ellipsis? mod))
- (lambda (e maps) (values (gen-vector e) maps))))
- tmp)
- (values (list 'quote e) maps))))))))))))
- (gen-ref
- (lambda (src var level maps)
- (cond ((= level 0) (values var maps))
- ((null? maps) (syntax-violation 'syntax "missing ellipsis" src))
- (else
- (call-with-values
- (lambda () (gen-ref src var (- level 1) (cdr maps)))
- (lambda (outer-var outer-maps)
- (let ((b (assq outer-var (car maps))))
- (if b
- (values (cdr b) maps)
- (let ((inner-var (gen-var 'tmp)))
- (values
- inner-var
- (cons (cons (cons outer-var inner-var) (car maps)) outer-maps)))))))))))
- (gen-mappend
- (lambda (e map-env)
- (list 'apply '(primitive append) (gen-map e map-env))))
- (gen-map
- (lambda (e map-env)
- (let ((formals (map cdr map-env))
- (actuals (map (lambda (x) (list 'ref (car x))) map-env)))
- (cond ((eq? (car e) 'ref) (car actuals))
- ((and-map
- (lambda (x) (and (eq? (car x) 'ref) (memq (cadr x) formals)))
- (cdr e))
- (cons 'map
- (cons (list 'primitive (car e))
- (map (let ((r (map cons formals actuals)))
- (lambda (x) (cdr (assq (cadr x) r))))
- (cdr e)))))
- (else (cons 'map (cons (list 'lambda formals e) actuals)))))))
- (gen-cons
- (lambda (x y)
- (let ((key (car y)))
- (cond ((memv key '(quote))
- (cond ((eq? (car x) 'quote) (list 'quote (cons (cadr x) (cadr y))))
- ((eq? (cadr y) '()) (list 'list x))
- (else (list 'cons x y))))
- ((memv key '(list)) (cons 'list (cons x (cdr y))))
- (else (list 'cons x y))))))
- (gen-append (lambda (x y) (if (equal? y ''()) x (list 'append x y))))
- (gen-vector
- (lambda (x)
- (cond ((eq? (car x) 'list) (cons 'vector (cdr x)))
- ((eq? (car x) 'quote) (list 'quote (list->vector (cadr x))))
- (else (list 'list->vector x)))))
- (regen (lambda (x)
- (let ((key (car x)))
- (cond ((memv key '(ref))
- (build-lexical-reference 'value #f (cadr x) (cadr x)))
- ((memv key '(primitive)) (build-primref #f (cadr x)))
- ((memv key '(quote)) (build-data #f (cadr x)))
- ((memv key '(lambda))
- (if (list? (cadr x))
- (build-simple-lambda #f (cadr x) #f (cadr x) '() (regen (caddr x)))
- (error "how did we get here" x)))
- (else
- (build-application #f (build-primref #f (car x)) (map regen (cdr x)))))))))
- (lambda (e r w s mod)
- (let* ((e (source-wrap e w s mod))
- (tmp e)
- (tmp ($sc-dispatch tmp '(_ any))))
- (if tmp
- (apply (lambda (x)
- (call-with-values
- (lambda () (gen-syntax e x r '() ellipsis? mod))
- (lambda (e maps) (regen e))))
- tmp)
- (syntax-violation 'syntax "bad `syntax' form" e))))))
- (global-extend
- 'core
- 'lambda
- (lambda (e r w s mod)
- (let* ((tmp e) (tmp ($sc-dispatch tmp '(_ any any . each-any))))
- (if tmp
- (apply (lambda (args e1 e2)
- (call-with-values
- (lambda () (lambda-formals args))
- (lambda (req opt rest kw)
- (let lp ((body (cons e1 e2)) (meta '()))
- (let* ((tmp-1 body) (tmp ($sc-dispatch tmp-1 '(any any . each-any))))
- (if (and tmp
- (apply (lambda (docstring e1 e2) (string? (syntax->datum docstring)))
- tmp))
- (apply (lambda (docstring e1 e2)
- (lp (cons e1 e2)
- (append meta (list (cons 'documentation (syntax->datum docstring))))))
- tmp)
- (let ((tmp ($sc-dispatch tmp-1 '(#(vector #(each (any . any))) any . each-any))))
- (if tmp
- (apply (lambda (k v e1 e2)
- (lp (cons e1 e2) (append meta (syntax->datum (map cons k v)))))
- tmp)
- (expand-simple-lambda e r w s mod req rest meta body)))))))))
- tmp)
- (syntax-violation 'lambda "bad lambda" e)))))
- (global-extend
- 'core
- 'lambda*
- (lambda (e r w s mod)
- (let* ((tmp e) (tmp ($sc-dispatch tmp '(_ any any . each-any))))
- (if tmp
- (apply (lambda (args e1 e2)
- (call-with-values
- (lambda ()
- (expand-lambda-case
- e
- r
- w
- s
- mod
- lambda*-formals
- (list (cons args (cons e1 e2)))))
- (lambda (meta lcase) (build-case-lambda s meta lcase))))
- tmp)
- (syntax-violation 'lambda "bad lambda*" e)))))
- (global-extend
- 'core
- 'case-lambda
- (lambda (e r w s mod)
- (letrec*
- ((build-it
- (lambda (meta clauses)
- (call-with-values
- (lambda () (expand-lambda-case e r w s mod lambda-formals clauses))
- (lambda (meta* lcase)
- (build-case-lambda s (append meta meta*) lcase))))))
- (let* ((tmp-1 e)
- (tmp ($sc-dispatch tmp-1 '(_ . #(each (any any . each-any))))))
- (if tmp
- (apply (lambda (args e1 e2)
- (build-it
- '()
- (map (lambda (tmp-bde397a-c50 tmp-bde397a-c4f tmp-bde397a-c4e)
- (cons tmp-bde397a-c4e (cons tmp-bde397a-c4f tmp-bde397a-c50)))
- e2
- e1
- args)))
- tmp)
- (let ((tmp ($sc-dispatch tmp-1 '(_ any . #(each (any any . each-any))))))
- (if (and tmp
- (apply (lambda (docstring args e1 e2) (string? (syntax->datum docstring)))
- tmp))
- (apply (lambda (docstring args e1 e2)
- (build-it
- (list (cons 'documentation (syntax->datum docstring)))
- (map (lambda (tmp-bde397a-c66 tmp-bde397a-c65 tmp-bde397a-c64)
- (cons tmp-bde397a-c64 (cons tmp-bde397a-c65 tmp-bde397a-c66)))
- e2
- e1
- args)))
- tmp)
- (syntax-violation 'case-lambda "bad case-lambda" e))))))))
- (global-extend
- 'core
- 'case-lambda*
- (lambda (e r w s mod)
- (letrec*
- ((build-it
- (lambda (meta clauses)
- (call-with-values
- (lambda () (expand-lambda-case e r w s mod lambda*-formals clauses))
- (lambda (meta* lcase)
- (build-case-lambda s (append meta meta*) lcase))))))
- (let* ((tmp-1 e)
- (tmp ($sc-dispatch tmp-1 '(_ . #(each (any any . each-any))))))
- (if tmp
- (apply (lambda (args e1 e2)
- (build-it
- '()
- (map (lambda (tmp-bde397a-c86 tmp-bde397a-c85 tmp-bde397a-c84)
- (cons tmp-bde397a-c84 (cons tmp-bde397a-c85 tmp-bde397a-c86)))
- e2
- e1
- args)))
- tmp)
- (let ((tmp ($sc-dispatch tmp-1 '(_ any . #(each (any any . each-any))))))
- (if (and tmp
- (apply (lambda (docstring args e1 e2) (string? (syntax->datum docstring)))
- tmp))
- (apply (lambda (docstring args e1 e2)
- (build-it
- (list (cons 'documentation (syntax->datum docstring)))
- (map (lambda (tmp-bde397a-c9c tmp-bde397a-c9b tmp-bde397a-c9a)
- (cons tmp-bde397a-c9a (cons tmp-bde397a-c9b tmp-bde397a-c9c)))
- e2
- e1
- args)))
- tmp)
- (syntax-violation 'case-lambda "bad case-lambda*" e))))))))
- (global-extend
- 'core
- 'with-ellipsis
- (lambda (e r w s mod)
- (let* ((tmp e) (tmp ($sc-dispatch tmp '(_ any any . each-any))))
- (if (and tmp (apply (lambda (dots e1 e2) (id? dots)) tmp))
- (apply (lambda (dots e1 e2)
- (let ((id (if (symbol? dots)
- '#{ $sc-ellipsis }
- (make-syntax-object
- '#{ $sc-ellipsis }
- (syntax-object-wrap dots)
- (syntax-object-module dots)))))
- (let ((ids (list id))
- (labels (list (gen-label)))
- (bindings (list (cons 'ellipsis (source-wrap dots w s mod)))))
- (let ((nw (make-binding-wrap ids labels w))
- (nr (extend-env labels bindings r)))
- (expand-body (cons e1 e2) (source-wrap e nw s mod) nr nw mod)))))
- tmp)
- (syntax-violation
- 'with-ellipsis
- "bad syntax"
- (source-wrap e w s mod))))))
- (global-extend
- 'core
- 'let
- (letrec*
- ((expand-let
- (lambda (e r w s mod constructor ids vals exps)
- (if (not (valid-bound-ids? ids))
- (syntax-violation 'let "duplicate bound variable" e)
- (let ((labels (gen-labels ids)) (new-vars (map gen-var ids)))
- (let ((nw (make-binding-wrap ids labels w))
- (nr (extend-var-env labels new-vars r)))
- (constructor
- s
- (map syntax->datum ids)
- new-vars
- (map (lambda (x) (expand x r w mod)) vals)
- (expand-body exps (source-wrap e nw s mod) nr nw mod))))))))
- (lambda (e r w s mod)
- (let* ((tmp-1 e)
- (tmp ($sc-dispatch tmp-1 '(_ #(each (any any)) any . each-any))))
- (if (and tmp (apply (lambda (id val e1 e2) (and-map id? id)) tmp))
- (apply (lambda (id val e1 e2)
- (expand-let e r w s mod build-let id val (cons e1 e2)))
- tmp)
- (let ((tmp ($sc-dispatch tmp-1 '(_ any #(each (any any)) any . each-any))))
- (if (and tmp
- (apply (lambda (f id val e1 e2) (and (id? f) (and-map id? id))) tmp))
- (apply (lambda (f id val e1 e2)
- (expand-let e r w s mod build-named-let (cons f id) val (cons e1 e2)))
- tmp)
- (syntax-violation 'let "bad let" (source-wrap e w s mod)))))))))
- (global-extend
- 'core
- 'letrec
- (lambda (e r w s mod)
- (let* ((tmp e)
- (tmp ($sc-dispatch tmp '(_ #(each (any any)) any . each-any))))
- (if (and tmp (apply (lambda (id val e1 e2) (and-map id? id)) tmp))
- (apply (lambda (id val e1 e2)
- (let ((ids id))
- (if (not (valid-bound-ids? ids))
- (syntax-violation 'letrec "duplicate bound variable" e)
- (let ((labels (gen-labels ids)) (new-vars (map gen-var ids)))
- (let ((w (make-binding-wrap ids labels w))
- (r (extend-var-env labels new-vars r)))
- (build-letrec
- s
- #f
- (map syntax->datum ids)
- new-vars
- (map (lambda (x) (expand x r w mod)) val)
- (expand-body (cons e1 e2) (source-wrap e w s mod) r w mod)))))))
- tmp)
- (syntax-violation 'letrec "bad letrec" (source-wrap e w s mod))))))
- (global-extend
- 'core
- 'letrec*
- (lambda (e r w s mod)
- (let* ((tmp e)
- (tmp ($sc-dispatch tmp '(_ #(each (any any)) any . each-any))))
- (if (and tmp (apply (lambda (id val e1 e2) (and-map id? id)) tmp))
- (apply (lambda (id val e1 e2)
- (let ((ids id))
- (if (not (valid-bound-ids? ids))
- (syntax-violation 'letrec* "duplicate bound variable" e)
- (let ((labels (gen-labels ids)) (new-vars (map gen-var ids)))
- (let ((w (make-binding-wrap ids labels w))
- (r (extend-var-env labels new-vars r)))
- (build-letrec
- s
- #t
- (map syntax->datum ids)
- new-vars
- (map (lambda (x) (expand x r w mod)) val)
- (expand-body (cons e1 e2) (source-wrap e w s mod) r w mod)))))))
- tmp)
- (syntax-violation 'letrec* "bad letrec*" (source-wrap e w s mod))))))
- (global-extend
- 'core
- 'set!
- (lambda (e r w s mod)
- (let* ((tmp-1 e) (tmp ($sc-dispatch tmp-1 '(_ any any))))
- (if (and tmp (apply (lambda (id val) (id? id)) tmp))
- (apply (lambda (id val)
- (let ((n (id-var-name id w))
- (id-mod (if (syntax-object? id) (syntax-object-module id) mod)))
- (let* ((b (lookup n r id-mod)) (key (car b)))
- (cond ((memv key '(lexical))
- (build-lexical-assignment
- s
- (syntax->datum id)
- (cdr b)
- (expand val r w mod)))
- ((memv key '(global))
- (build-global-assignment s n (expand val r w mod) id-mod))
- ((memv key '(macro))
- (let ((p (cdr b)))
- (if (procedure-property p 'variable-transformer)
- (expand (expand-macro p e r w s #f mod) r '(()) mod)
- (syntax-violation
- 'set!
- "not a variable transformer"
- (wrap e w mod)
- (wrap id w id-mod)))))
- ((memv key '(displaced-lexical))
- (syntax-violation 'set! "identifier out of context" (wrap id w mod)))
- (else (syntax-violation 'set! "bad set!" (source-wrap e w s mod)))))))
- tmp)
- (let ((tmp ($sc-dispatch tmp-1 '(_ (any . each-any) any))))
- (if tmp
- (apply (lambda (head tail val)
- (call-with-values
- (lambda () (syntax-type head r '(()) #f #f mod #t))
- (lambda (type value formform ee ww ss modmod)
- (let ((key type))
- (if (memv key '(module-ref))
- (let ((val (expand val r w mod)))
- (call-with-values
- (lambda () (value (cons head tail) r w))
- (lambda (e r w s* mod)
- (let* ((tmp-1 e) (tmp (list tmp-1)))
- (if (and tmp (apply (lambda (e) (id? e)) tmp))
- (apply (lambda (e) (build-global-assignment s (syntax->datum e) val mod))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1))))))
- (build-application
- s
- (expand
- (list '#(syntax-object setter ((top)) (hygiene guile)) head)
- r
- w
- mod)
- (map (lambda (e) (expand e r w mod)) (append tail (list val)))))))))
- tmp)
- (syntax-violation 'set! "bad set!" (source-wrap e w s mod))))))))
- (global-extend
- 'module-ref
- '@
- (lambda (e r w)
- (let* ((tmp-1 e) (tmp ($sc-dispatch tmp-1 '(_ each-any any))))
- (if (and tmp
- (apply (lambda (mod id) (and (and-map id? mod) (id? id))) tmp))
- (apply (lambda (mod id)
- (values
- (syntax->datum id)
- r
- '((top))
- #f
- (syntax->datum
- (cons '#(syntax-object public ((top)) (hygiene guile)) mod))))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))
- (global-extend
- 'module-ref
- '@@
- (lambda (e r w)
- (letrec*
- ((remodulate
- (lambda (x mod)
- (cond ((pair? x) (cons (remodulate (car x) mod) (remodulate (cdr x) mod)))
- ((syntax-object? x)
- (make-syntax-object
- (remodulate (syntax-object-expression x) mod)
- (syntax-object-wrap x)
- mod))
- ((vector? x)
- (let* ((n (vector-length x)) (v (make-vector n)))
- (let loop ((i 0))
- (if (= i n)
- (begin (if #f #f) v)
- (begin
- (vector-set! v i (remodulate (vector-ref x i) mod))
- (loop (+ i 1)))))))
- (else x)))))
- (let* ((tmp-1 e) (tmp ($sc-dispatch tmp-1 '(_ each-any any))))
- (if (and tmp
- (apply (lambda (mod id) (and (and-map id? mod) (id? id))) tmp))
- (apply (lambda (mod id)
- (values
- (syntax->datum id)
- r
- '((top))
- #f
- (syntax->datum
- (cons '#(syntax-object private ((top)) (hygiene guile)) mod))))
- tmp)
- (let ((tmp ($sc-dispatch
- tmp-1
- '(_ #(free-id #(syntax-object @@ ((top)) (hygiene guile)))
- each-any
- any))))
- (if (and tmp (apply (lambda (mod exp) (and-map id? mod)) tmp))
- (apply (lambda (mod exp)
- (let ((mod (syntax->datum
- (cons '#(syntax-object private ((top)) (hygiene guile)) mod))))
- (values (remodulate exp mod) r w (source-annotation exp) mod)))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1))))))))
- (global-extend
- 'core
- 'if
- (lambda (e r w s mod)
- (let* ((tmp e) (tmp-1 ($sc-dispatch tmp '(_ any any))))
- (if tmp-1
- (apply (lambda (test then)
- (build-conditional
- s
- (expand test r w mod)
- (expand then r w mod)
- (build-void #f)))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(_ any any any))))
- (if tmp-1
- (apply (lambda (test then else)
- (build-conditional
- s
- (expand test r w mod)
- (expand then r w mod)
- (expand else r w mod)))
- tmp-1)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp)))))))
- (global-extend
- 'core
- 'with-fluids
- (lambda (e r w s mod)
- (let* ((tmp-1 e)
- (tmp ($sc-dispatch tmp-1 '(_ #(each (any any)) any . each-any))))
- (if tmp
- (apply (lambda (fluid val b b*)
- (build-dynlet
- s
- (map (lambda (x) (expand x r w mod)) fluid)
- (map (lambda (x) (expand x r w mod)) val)
- (expand-body (cons b b*) (source-wrap e w s mod) r w mod)))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))
- (global-extend 'begin 'begin '())
- (global-extend 'define 'define '())
- (global-extend 'define-syntax 'define-syntax '())
- (global-extend 'define-syntax-parameter 'define-syntax-parameter '())
- (global-extend 'eval-when 'eval-when '())
- (global-extend
- 'core
- 'syntax-case
- (letrec*
- ((convert-pattern
- (lambda (pattern keys ellipsis?)
- (letrec*
- ((cvt* (lambda (p* n ids)
- (let* ((tmp p*) (tmp ($sc-dispatch tmp '(any . any))))
- (if tmp
- (apply (lambda (x y)
- (call-with-values
- (lambda () (cvt* y n ids))
- (lambda (y ids)
- (call-with-values
- (lambda () (cvt x n ids))
- (lambda (x ids) (values (cons x y) ids))))))
- tmp)
- (cvt p* n ids)))))
- (v-reverse
- (lambda (x)
- (let loop ((r '()) (x x))
- (if (not (pair? x)) (values r x) (loop (cons (car x) r) (cdr x))))))
- (cvt (lambda (p n ids)
- (if (id? p)
- (cond ((bound-id-member? p keys) (values (vector 'free-id p) ids))
- ((free-id=? p '#(syntax-object _ ((top)) (hygiene guile)))
- (values '_ ids))
- (else (values 'any (cons (cons p n) ids))))
- (let* ((tmp p) (tmp-1 ($sc-dispatch tmp '(any any))))
- (if (and tmp-1 (apply (lambda (x dots) (ellipsis? dots)) tmp-1))
- (apply (lambda (x dots)
- (call-with-values
- (lambda () (cvt x (+ n 1) ids))
- (lambda (p ids)
- (values (if (eq? p 'any) 'each-any (vector 'each p)) ids))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any any . any))))
- (if (and tmp-1 (apply (lambda (x dots ys) (ellipsis? dots)) tmp-1))
- (apply (lambda (x dots ys)
- (call-with-values
- (lambda () (cvt* ys n ids))
- (lambda (ys ids)
- (call-with-values
- (lambda () (cvt x (+ n 1) ids))
- (lambda (x ids)
- (call-with-values
- (lambda () (v-reverse ys))
- (lambda (ys e) (values (vector 'each+ x ys e) ids))))))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any . any))))
- (if tmp-1
- (apply (lambda (x y)
- (call-with-values
- (lambda () (cvt y n ids))
- (lambda (y ids)
- (call-with-values
- (lambda () (cvt x n ids))
- (lambda (x ids) (values (cons x y) ids))))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '())))
- (if tmp-1
- (apply (lambda () (values '() ids)) tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '#(vector each-any))))
- (if tmp-1
- (apply (lambda (x)
- (call-with-values
- (lambda () (cvt x n ids))
- (lambda (p ids) (values (vector 'vector p) ids))))
- tmp-1)
- (let ((x tmp)) (values (vector 'atom (strip p '(()))) ids))))))))))))))))
- (cvt pattern 0 '()))))
- (build-dispatch-call
- (lambda (pvars exp y r mod)
- (let ((ids (map car pvars)) (levels (map cdr pvars)))
- (let ((labels (gen-labels ids)) (new-vars (map gen-var ids)))
- (build-application
- #f
- (build-primref #f 'apply)
- (list (build-simple-lambda
- #f
- (map syntax->datum ids)
- #f
- new-vars
- '()
- (expand
- exp
- (extend-env
- labels
- (map (lambda (var level) (cons 'syntax (cons var level)))
- new-vars
- (map cdr pvars))
- r)
- (make-binding-wrap ids labels '(()))
- mod))
- y))))))
- (gen-clause
- (lambda (x keys clauses r pat fender exp mod)
- (call-with-values
- (lambda ()
- (convert-pattern pat keys (lambda (e) (ellipsis? e r mod))))
- (lambda (p pvars)
- (cond ((not (and-map (lambda (x) (not (ellipsis? (car x) r mod))) pvars))
- (syntax-violation 'syntax-case "misplaced ellipsis" pat))
- ((not (distinct-bound-ids? (map car pvars)))
- (syntax-violation 'syntax-case "duplicate pattern variable" pat))
- (else
- (let ((y (gen-var 'tmp)))
- (build-application
- #f
- (build-simple-lambda
- #f
- (list 'tmp)
- #f
- (list y)
- '()
- (let ((y (build-lexical-reference 'value #f 'tmp y)))
- (build-conditional
- #f
- (let* ((tmp fender) (tmp ($sc-dispatch tmp '#(atom #t))))
- (if tmp
- (apply (lambda () y) tmp)
- (build-conditional
- #f
- y
- (build-dispatch-call pvars fender y r mod)
- (build-data #f #f))))
- (build-dispatch-call pvars exp y r mod)
- (gen-syntax-case x keys clauses r mod))))
- (list (if (eq? p 'any)
- (build-application #f (build-primref #f 'list) (list x))
- (build-application
- #f
- (build-primref #f '$sc-dispatch)
- (list x (build-data #f p)))))))))))))
- (gen-syntax-case
- (lambda (x keys clauses r mod)
- (if (null? clauses)
- (build-application
- #f
- (build-primref #f 'syntax-violation)
- (list (build-data #f #f)
- (build-data #f "source expression failed to match any pattern")
- x))
- (let* ((tmp-1 (car clauses)) (tmp ($sc-dispatch tmp-1 '(any any))))
- (if tmp
- (apply (lambda (pat exp)
- (if (and (id? pat)
- (and-map
- (lambda (x) (not (free-id=? pat x)))
- (cons '#(syntax-object ... ((top)) (hygiene guile)) keys)))
- (if (free-id=? pat '#(syntax-object _ ((top)) (hygiene guile)))
- (expand exp r '(()) mod)
- (let ((labels (list (gen-label))) (var (gen-var pat)))
- (build-application
- #f
- (build-simple-lambda
- #f
- (list (syntax->datum pat))
- #f
- (list var)
- '()
- (expand
- exp
- (extend-env labels (list (cons 'syntax (cons var 0))) r)
- (make-binding-wrap (list pat) labels '(()))
- mod))
- (list x))))
- (gen-clause x keys (cdr clauses) r pat #t exp mod)))
- tmp)
- (let ((tmp ($sc-dispatch tmp-1 '(any any any))))
- (if tmp
- (apply (lambda (pat fender exp)
- (gen-clause x keys (cdr clauses) r pat fender exp mod))
- tmp)
- (syntax-violation 'syntax-case "invalid clause" (car clauses))))))))))
- (lambda (e r w s mod)
- (let* ((e (source-wrap e w s mod))
- (tmp-1 e)
- (tmp ($sc-dispatch tmp-1 '(_ any each-any . each-any))))
- (if tmp
- (apply (lambda (val key m)
- (if (and-map (lambda (x) (and (id? x) (not (ellipsis? x r mod)))) key)
- (let ((x (gen-var 'tmp)))
- (build-application
- s
- (build-simple-lambda
- #f
- (list 'tmp)
- #f
- (list x)
- '()
- (gen-syntax-case
- (build-lexical-reference 'value #f 'tmp x)
- key
- m
- r
- mod))
- (list (expand val r '(()) mod))))
- (syntax-violation 'syntax-case "invalid literals list" e)))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1))))))
- (set! macroexpand
- (lambda* (x #\optional (m 'e) (esew '(eval)))
- (expand-top-sequence
- (list x)
- '()
- '((top))
- #f
- m
- esew
- (cons 'hygiene (module-name (current-module))))))
- (set! identifier? (lambda (x) (nonsymbol-id? x)))
- (set! datum->syntax
- (lambda (id datum)
- (make-syntax-object
- datum
- (syntax-object-wrap id)
- (syntax-object-module id))))
- (set! syntax->datum (lambda (x) (strip x '(()))))
- (set! syntax-source (lambda (x) (source-annotation x)))
- (set! generate-temporaries
- (lambda (ls)
- (let ((x ls))
- (if (not (list? x))
- (syntax-violation 'generate-temporaries "invalid argument" x)))
- (let ((mod (cons 'hygiene (module-name (current-module)))))
- (map (lambda (x) (wrap (module-gensym "t") '((top)) mod)) ls))))
- (set! free-identifier=?
- (lambda (x y)
- (let ((x x))
- (if (not (nonsymbol-id? x))
- (syntax-violation 'free-identifier=? "invalid argument" x)))
- (let ((x y))
- (if (not (nonsymbol-id? x))
- (syntax-violation 'free-identifier=? "invalid argument" x)))
- (free-id=? x y)))
- (set! bound-identifier=?
- (lambda (x y)
- (let ((x x))
- (if (not (nonsymbol-id? x))
- (syntax-violation 'bound-identifier=? "invalid argument" x)))
- (let ((x y))
- (if (not (nonsymbol-id? x))
- (syntax-violation 'bound-identifier=? "invalid argument" x)))
- (bound-id=? x y)))
- (set! syntax-violation
- (lambda* (who message form #\optional (subform #f))
- (let ((x who))
- (if (not (let ((x x)) (or (not x) (string? x) (symbol? x))))
- (syntax-violation 'syntax-violation "invalid argument" x)))
- (let ((x message))
- (if (not (string? x))
- (syntax-violation 'syntax-violation "invalid argument" x)))
- (throw 'syntax-error
- who
- message
- (or (source-annotation subform) (source-annotation form))
- (strip form '(()))
- (and subform (strip subform '(()))))))
- (letrec*
- ((syntax-module
- (lambda (id)
- (let ((x id))
- (if (not (nonsymbol-id? x))
- (syntax-violation 'syntax-module "invalid argument" x)))
- (cdr (syntax-object-module id))))
- (syntax-local-binding
- (lambda (id)
- (let ((x id))
- (if (not (nonsymbol-id? x))
- (syntax-violation 'syntax-local-binding "invalid argument" x)))
- (with-transformer-environment
- (lambda (e r w s rib mod)
- (letrec*
- ((strip-anti-mark
- (lambda (w)
- (let ((ms (car w)) (s (cdr w)))
- (if (and (pair? ms) (eq? (car ms) #f))
- (cons (cdr ms) (if rib (cons rib (cdr s)) (cdr s)))
- (cons ms (if rib (cons rib s) s)))))))
- (call-with-values
- (lambda ()
- (resolve-identifier
- (syntax-object-expression id)
- (strip-anti-mark (syntax-object-wrap id))
- r
- (syntax-object-module id)))
- (lambda (type value mod)
- (let ((key type))
- (cond ((memv key '(lexical)) (values 'lexical value))
- ((memv key '(macro)) (values 'macro value))
- ((memv key '(syntax)) (values 'pattern-variable value))
- ((memv key '(displaced-lexical)) (values 'displaced-lexical #f))
- ((memv key '(global)) (values 'global (cons value (cdr mod))))
- ((memv key '(ellipsis))
- (values
- 'ellipsis
- (make-syntax-object
- (syntax-object-expression value)
- (anti-mark (syntax-object-wrap value))
- (syntax-object-module value))))
- (else (values 'other #f)))))))))))
- (syntax-locally-bound-identifiers
- (lambda (id)
- (let ((x id))
- (if (not (nonsymbol-id? x))
- (syntax-violation
- 'syntax-locally-bound-identifiers
- "invalid argument"
- x)))
- (locally-bound-identifiers
- (syntax-object-wrap id)
- (syntax-object-module id)))))
- (define! 'syntax-module syntax-module)
- (define! 'syntax-local-binding syntax-local-binding)
- (define!
- 'syntax-locally-bound-identifiers
- syntax-locally-bound-identifiers))
- (letrec*
- ((match-each
- (lambda (e p w mod)
- (cond ((pair? e)
- (let ((first (match (car e) p w '() mod)))
- (and first
- (let ((rest (match-each (cdr e) p w mod)))
- (and rest (cons first rest))))))
- ((null? e) '())
- ((syntax-object? e)
- (match-each
- (syntax-object-expression e)
- p
- (join-wraps w (syntax-object-wrap e))
- (syntax-object-module e)))
- (else #f))))
- (match-each+
- (lambda (e x-pat y-pat z-pat w r mod)
- (let f ((e e) (w w))
- (cond ((pair? e)
- (call-with-values
- (lambda () (f (cdr e) w))
- (lambda (xr* y-pat r)
- (if r
- (if (null? y-pat)
- (let ((xr (match (car e) x-pat w '() mod)))
- (if xr (values (cons xr xr*) y-pat r) (values #f #f #f)))
- (values '() (cdr y-pat) (match (car e) (car y-pat) w r mod)))
- (values #f #f #f)))))
- ((syntax-object? e)
- (f (syntax-object-expression e)
- (join-wraps w (syntax-object-wrap e))))
- (else (values '() y-pat (match e z-pat w r mod)))))))
- (match-each-any
- (lambda (e w mod)
- (cond ((pair? e)
- (let ((l (match-each-any (cdr e) w mod)))
- (and l (cons (wrap (car e) w mod) l))))
- ((null? e) '())
- ((syntax-object? e)
- (match-each-any
- (syntax-object-expression e)
- (join-wraps w (syntax-object-wrap e))
- mod))
- (else #f))))
- (match-empty
- (lambda (p r)
- (cond ((null? p) r)
- ((eq? p '_) r)
- ((eq? p 'any) (cons '() r))
- ((pair? p) (match-empty (car p) (match-empty (cdr p) r)))
- ((eq? p 'each-any) (cons '() r))
- (else
- (let ((key (vector-ref p 0)))
- (cond ((memv key '(each)) (match-empty (vector-ref p 1) r))
- ((memv key '(each+))
- (match-empty
- (vector-ref p 1)
- (match-empty
- (reverse (vector-ref p 2))
- (match-empty (vector-ref p 3) r))))
- ((memv key '(free-id atom)) r)
- ((memv key '(vector)) (match-empty (vector-ref p 1) r))))))))
- (combine
- (lambda (r* r)
- (if (null? (car r*)) r (cons (map car r*) (combine (map cdr r*) r)))))
- (match*
- (lambda (e p w r mod)
- (cond ((null? p) (and (null? e) r))
- ((pair? p)
- (and (pair? e)
- (match (car e) (car p) w (match (cdr e) (cdr p) w r mod) mod)))
- ((eq? p 'each-any)
- (let ((l (match-each-any e w mod))) (and l (cons l r))))
- (else
- (let ((key (vector-ref p 0)))
- (cond ((memv key '(each))
- (if (null? e)
- (match-empty (vector-ref p 1) r)
- (let ((l (match-each e (vector-ref p 1) w mod)))
- (and l
- (let collect ((l l))
- (if (null? (car l)) r (cons (map car l) (collect (map cdr l)))))))))
- ((memv key '(each+))
- (call-with-values
- (lambda ()
- (match-each+
- e
- (vector-ref p 1)
- (vector-ref p 2)
- (vector-ref p 3)
- w
- r
- mod))
- (lambda (xr* y-pat r)
- (and r
- (null? y-pat)
- (if (null? xr*) (match-empty (vector-ref p 1) r) (combine xr* r))))))
- ((memv key '(free-id))
- (and (id? e) (free-id=? (wrap e w mod) (vector-ref p 1)) r))
- ((memv key '(atom)) (and (equal? (vector-ref p 1) (strip e w)) r))
- ((memv key '(vector))
- (and (vector? e) (match (vector->list e) (vector-ref p 1) w r mod)))))))))
- (match (lambda (e p w r mod)
- (cond ((not r) #f)
- ((eq? p '_) r)
- ((eq? p 'any) (cons (wrap e w mod) r))
- ((syntax-object? e)
- (match*
- (syntax-object-expression e)
- p
- (join-wraps w (syntax-object-wrap e))
- r
- (syntax-object-module e)))
- (else (match* e p w r mod))))))
- (set! $sc-dispatch
- (lambda (e p)
- (cond ((eq? p 'any) (list e))
- ((eq? p '_) '())
- ((syntax-object? e)
- (match*
- (syntax-object-expression e)
- p
- (syntax-object-wrap e)
- '()
- (syntax-object-module e)))
- (else (match* e p '(()) '() #f)))))))
-
-(define with-syntax
- (make-syntax-transformer
- 'with-syntax
- 'macro
- (lambda (x)
- (let ((tmp x))
- (let ((tmp-1 ($sc-dispatch tmp '(_ () any . each-any))))
- (if tmp-1
- (apply (lambda (e1 e2)
- (cons '#(syntax-object let ((top)) (hygiene guile))
- (cons '() (cons e1 e2))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(_ ((any any)) any . each-any))))
- (if tmp-1
- (apply (lambda (out in e1 e2)
- (list '#(syntax-object syntax-case ((top)) (hygiene guile))
- in
- '()
- (list out
- (cons '#(syntax-object let ((top)) (hygiene guile))
- (cons '() (cons e1 e2))))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(_ #(each (any any)) any . each-any))))
- (if tmp-1
- (apply (lambda (out in e1 e2)
- (list '#(syntax-object syntax-case ((top)) (hygiene guile))
- (cons '#(syntax-object list ((top)) (hygiene guile)) in)
- '()
- (list out
- (cons '#(syntax-object let ((top)) (hygiene guile))
- (cons '() (cons e1 e2))))))
- tmp-1)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp)))))))))))
-
-(define syntax-error
- (make-syntax-transformer
- 'syntax-error
- 'macro
- (lambda (x)
- (let ((tmp-1 x))
- (let ((tmp ($sc-dispatch tmp-1 '(_ (any . any) any . each-any))))
- (if (if tmp
- (apply (lambda (keyword operands message arg)
- (string? (syntax->datum message)))
- tmp)
- #f)
- (apply (lambda (keyword operands message arg)
- (syntax-violation
- (syntax->datum keyword)
- (string-join
- (cons (syntax->datum message)
- (map (lambda (x) (object->string (syntax->datum x))) arg)))
- (if (syntax->datum keyword) (cons keyword operands) #f)))
- tmp)
- (let ((tmp ($sc-dispatch tmp-1 '(_ any . each-any))))
- (if (if tmp
- (apply (lambda (message arg) (string? (syntax->datum message))) tmp)
- #f)
- (apply (lambda (message arg)
- (cons '#(syntax-object syntax-error ((top)) (hygiene guile))
- (cons '(#f) (cons message arg))))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))))))
-
-(define syntax-rules
- (make-syntax-transformer
- 'syntax-rules
- 'macro
- (lambda (xx)
- (letrec*
- ((expand-clause
- (lambda (clause)
- (let ((tmp-1 clause))
- (let ((tmp ($sc-dispatch
- tmp-1
- '((any . any)
- (#(free-id #(syntax-object syntax-error ((top)) (hygiene guile)))
- any
- .
- each-any)))))
- (if (if tmp
- (apply (lambda (keyword pattern message arg)
- (string? (syntax->datum message)))
- tmp)
- #f)
- (apply (lambda (keyword pattern message arg)
- (list (cons '#(syntax-object dummy ((top)) (hygiene guile)) pattern)
- (list '#(syntax-object syntax ((top)) (hygiene guile))
- (cons '#(syntax-object syntax-error ((top)) (hygiene guile))
- (cons (cons '#(syntax-object dummy ((top)) (hygiene guile)) pattern)
- (cons message arg))))))
- tmp)
- (let ((tmp ($sc-dispatch tmp-1 '((any . any) any))))
- (if tmp
- (apply (lambda (keyword pattern template)
- (list (cons '#(syntax-object dummy ((top)) (hygiene guile)) pattern)
- (list '#(syntax-object syntax ((top)) (hygiene guile)) template)))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1))))))))
- (expand-syntax-rules
- (lambda (dots keys docstrings clauses)
- (let ((tmp-1 (list keys docstrings clauses (map expand-clause clauses))))
- (let ((tmp ($sc-dispatch
- tmp-1
- '(each-any each-any #(each ((any . any) any)) each-any))))
- (if tmp
- (apply (lambda (k docstring keyword pattern template clause)
- (let ((tmp (cons '#(syntax-object lambda ((top)) (hygiene guile))
- (cons '(#(syntax-object x ((top)) (hygiene guile)))
- (append
- docstring
- (list (vector
- '(#(syntax-object macro-type ((top)) (hygiene guile))
- .
- #(syntax-object syntax-rules ((top)) (hygiene guile)))
- (cons '#(syntax-object patterns ((top)) (hygiene guile))
- pattern))
- (cons '#(syntax-object syntax-case ((top)) (hygiene guile))
- (cons '#(syntax-object x ((top)) (hygiene guile))
- (cons k clause)))))))))
- (let ((form tmp))
- (if dots
- (let ((tmp dots))
- (let ((dots tmp))
- (list '#(syntax-object with-ellipsis ((top)) (hygiene guile))
- dots
- form)))
- form))))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))))
- (let ((tmp xx))
- (let ((tmp-1 ($sc-dispatch tmp '(_ each-any . #(each ((any . any) any))))))
- (if tmp-1
- (apply (lambda (k keyword pattern template)
- (expand-syntax-rules
- #f
- k
- '()
- (map (lambda (tmp-bde397a-10fd tmp-bde397a-10fc tmp-bde397a-10fb)
- (list (cons tmp-bde397a-10fb tmp-bde397a-10fc) tmp-bde397a-10fd))
- template
- pattern
- keyword)))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(_ each-any any . #(each ((any . any) any))))))
- (if (if tmp-1
- (apply (lambda (k docstring keyword pattern template)
- (string? (syntax->datum docstring)))
- tmp-1)
- #f)
- (apply (lambda (k docstring keyword pattern template)
- (expand-syntax-rules
- #f
- k
- (list docstring)
- (map (lambda (tmp-bde397a-2 tmp-bde397a-1 tmp-bde397a)
- (list (cons tmp-bde397a tmp-bde397a-1) tmp-bde397a-2))
- template
- pattern
- keyword)))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(_ any each-any . #(each ((any . any) any))))))
- (if (if tmp-1
- (apply (lambda (dots k keyword pattern template) (identifier? dots))
- tmp-1)
- #f)
- (apply (lambda (dots k keyword pattern template)
- (expand-syntax-rules
- dots
- k
- '()
- (map (lambda (tmp-bde397a-112f tmp-bde397a-112e tmp-bde397a-112d)
- (list (cons tmp-bde397a-112d tmp-bde397a-112e) tmp-bde397a-112f))
- template
- pattern
- keyword)))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(_ any each-any any . #(each ((any . any) any))))))
- (if (if tmp-1
- (apply (lambda (dots k docstring keyword pattern template)
- (if (identifier? dots) (string? (syntax->datum docstring)) #f))
- tmp-1)
- #f)
- (apply (lambda (dots k docstring keyword pattern template)
- (expand-syntax-rules
- dots
- k
- (list docstring)
- (map (lambda (tmp-bde397a-114e tmp-bde397a-114d tmp-bde397a-114c)
- (list (cons tmp-bde397a-114c tmp-bde397a-114d) tmp-bde397a-114e))
- template
- pattern
- keyword)))
- tmp-1)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp))))))))))))))
-
-(define define-syntax-rule
- (make-syntax-transformer
- 'define-syntax-rule
- 'macro
- (lambda (x)
- (let ((tmp-1 x))
- (let ((tmp ($sc-dispatch tmp-1 '(_ (any . any) any))))
- (if tmp
- (apply (lambda (name pattern template)
- (list '#(syntax-object define-syntax ((top)) (hygiene guile))
- name
- (list '#(syntax-object syntax-rules ((top)) (hygiene guile))
- '()
- (list (cons '#(syntax-object _ ((top)) (hygiene guile)) pattern)
- template))))
- tmp)
- (let ((tmp ($sc-dispatch tmp-1 '(_ (any . any) any any))))
- (if (if tmp
- (apply (lambda (name pattern docstring template)
- (string? (syntax->datum docstring)))
- tmp)
- #f)
- (apply (lambda (name pattern docstring template)
- (list '#(syntax-object define-syntax ((top)) (hygiene guile))
- name
- (list '#(syntax-object syntax-rules ((top)) (hygiene guile))
- '()
- docstring
- (list (cons '#(syntax-object _ ((top)) (hygiene guile)) pattern)
- template))))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))))))
-
-(define let*
- (make-syntax-transformer
- 'let*
- 'macro
- (lambda (x)
- (let ((tmp-1 x))
- (let ((tmp ($sc-dispatch tmp-1 '(any #(each (any any)) any . each-any))))
- (if (if tmp
- (apply (lambda (let* x v e1 e2) (and-map identifier? x)) tmp)
- #f)
- (apply (lambda (let* x v e1 e2)
- (let f ((bindings (map list x v)))
- (if (null? bindings)
- (cons '#(syntax-object let ((top)) (hygiene guile))
- (cons '() (cons e1 e2)))
- (let ((tmp-1 (list (f (cdr bindings)) (car bindings))))
- (let ((tmp ($sc-dispatch tmp-1 '(any any))))
- (if tmp
- (apply (lambda (body binding)
- (list '#(syntax-object let ((top)) (hygiene guile))
- (list binding)
- body))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))))
-
-(define quasiquote
- (make-syntax-transformer
- 'quasiquote
- 'macro
- (letrec*
- ((quasi (lambda (p lev)
- (let ((tmp p))
- (let ((tmp-1 ($sc-dispatch
- tmp
- '(#(free-id #(syntax-object unquote ((top)) (hygiene guile))) any))))
- (if tmp-1
- (apply (lambda (p)
- (if (= lev 0)
- (list "value" p)
- (quasicons
- '("quote" #(syntax-object unquote ((top)) (hygiene guile)))
- (quasi (list p) (- lev 1)))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch
- tmp
- '(#(free-id #(syntax-object quasiquote ((top)) (hygiene guile))) any))))
- (if tmp-1
- (apply (lambda (p)
- (quasicons
- '("quote" #(syntax-object quasiquote ((top)) (hygiene guile)))
- (quasi (list p) (+ lev 1))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(any . any))))
- (if tmp-1
- (apply (lambda (p q)
- (let ((tmp-1 p))
- (let ((tmp ($sc-dispatch
- tmp-1
- '(#(free-id #(syntax-object unquote ((top)) (hygiene guile)))
- .
- each-any))))
- (if tmp
- (apply (lambda (p)
- (if (= lev 0)
- (quasilist*
- (map (lambda (tmp-bde397a-11b3)
- (list "value" tmp-bde397a-11b3))
- p)
- (quasi q lev))
- (quasicons
- (quasicons
- '("quote" #(syntax-object unquote ((top)) (hygiene guile)))
- (quasi p (- lev 1)))
- (quasi q lev))))
- tmp)
- (let ((tmp ($sc-dispatch
- tmp-1
- '(#(free-id
- #(syntax-object unquote-splicing ((top)) (hygiene guile)))
- .
- each-any))))
- (if tmp
- (apply (lambda (p)
- (if (= lev 0)
- (quasiappend
- (map (lambda (tmp-bde397a-11b8)
- (list "value" tmp-bde397a-11b8))
- p)
- (quasi q lev))
- (quasicons
- (quasicons
- '("quote"
- #(syntax-object
- unquote-splicing
- ((top))
- (hygiene guile)))
- (quasi p (- lev 1)))
- (quasi q lev))))
- tmp)
- (quasicons (quasi p lev) (quasi q lev))))))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '#(vector each-any))))
- (if tmp-1
- (apply (lambda (x) (quasivector (vquasi x lev))) tmp-1)
- (let ((p tmp)) (list "quote" p)))))))))))))
- (vquasi
- (lambda (p lev)
- (let ((tmp p))
- (let ((tmp-1 ($sc-dispatch tmp '(any . any))))
- (if tmp-1
- (apply (lambda (p q)
- (let ((tmp-1 p))
- (let ((tmp ($sc-dispatch
- tmp-1
- '(#(free-id #(syntax-object unquote ((top)) (hygiene guile)))
- .
- each-any))))
- (if tmp
- (apply (lambda (p)
- (if (= lev 0)
- (quasilist*
- (map (lambda (tmp-bde397a-11ce) (list "value" tmp-bde397a-11ce)) p)
- (vquasi q lev))
- (quasicons
- (quasicons
- '("quote" #(syntax-object unquote ((top)) (hygiene guile)))
- (quasi p (- lev 1)))
- (vquasi q lev))))
- tmp)
- (let ((tmp ($sc-dispatch
- tmp-1
- '(#(free-id #(syntax-object unquote-splicing ((top)) (hygiene guile)))
- .
- each-any))))
- (if tmp
- (apply (lambda (p)
- (if (= lev 0)
- (quasiappend
- (map (lambda (tmp-bde397a-11d3) (list "value" tmp-bde397a-11d3)) p)
- (vquasi q lev))
- (quasicons
- (quasicons
- '("quote" #(syntax-object unquote-splicing ((top)) (hygiene guile)))
- (quasi p (- lev 1)))
- (vquasi q lev))))
- tmp)
- (quasicons (quasi p lev) (vquasi q lev))))))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '())))
- (if tmp-1
- (apply (lambda () '("quote" ())) tmp-1)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp))))))))
- (quasicons
- (lambda (x y)
- (let ((tmp-1 (list x y)))
- (let ((tmp ($sc-dispatch tmp-1 '(any any))))
- (if tmp
- (apply (lambda (x y)
- (let ((tmp y))
- (let ((tmp-1 ($sc-dispatch tmp '(#(atom "quote") any))))
- (if tmp-1
- (apply (lambda (dy)
- (let ((tmp x))
- (let ((tmp ($sc-dispatch tmp '(#(atom "quote") any))))
- (if tmp
- (apply (lambda (dx) (list "quote" (cons dx dy))) tmp)
- (if (null? dy) (list "list" x) (list "list*" x y))))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(#(atom "list") . any))))
- (if tmp-1
- (apply (lambda (stuff) (cons "list" (cons x stuff))) tmp-1)
- (let ((tmp ($sc-dispatch tmp '(#(atom "list*") . any))))
- (if tmp
- (apply (lambda (stuff) (cons "list*" (cons x stuff))) tmp)
- (list "list*" x y)))))))))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1))))))
- (quasiappend
- (lambda (x y)
- (let ((tmp y))
- (let ((tmp ($sc-dispatch tmp '(#(atom "quote") ()))))
- (if tmp
- (apply (lambda ()
- (if (null? x)
- '("quote" ())
- (if (null? (cdr x))
- (car x)
- (let ((tmp-1 x))
- (let ((tmp ($sc-dispatch tmp-1 'each-any)))
- (if tmp
- (apply (lambda (p) (cons "append" p)) tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))))
- tmp)
- (if (null? x)
- y
- (let ((tmp-1 (list x y)))
- (let ((tmp ($sc-dispatch tmp-1 '(each-any any))))
- (if tmp
- (apply (lambda (p y) (cons "append" (append p (list y)))) tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1))))))))))
- (quasilist*
- (lambda (x y)
- (let f ((x x)) (if (null? x) y (quasicons (car x) (f (cdr x)))))))
- (quasivector
- (lambda (x)
- (let ((tmp x))
- (let ((tmp ($sc-dispatch tmp '(#(atom "quote") each-any))))
- (if tmp
- (apply (lambda (x) (list "quote" (list->vector x))) tmp)
- (let f ((y x)
- (k (lambda (ls)
- (let ((tmp-1 ls))
- (let ((tmp ($sc-dispatch tmp-1 'each-any)))
- (if tmp
- (apply (lambda (t-bde397a-121c) (cons "vector" t-bde397a-121c)) tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))))
- (let ((tmp y))
- (let ((tmp-1 ($sc-dispatch tmp '(#(atom "quote") each-any))))
- (if tmp-1
- (apply (lambda (y)
- (k (map (lambda (tmp-bde397a) (list "quote" tmp-bde397a)) y)))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(#(atom "list") . each-any))))
- (if tmp-1
- (apply (lambda (y) (k y)) tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(#(atom "list*") . #(each+ any (any) ())))))
- (if tmp-1
- (apply (lambda (y z) (f z (lambda (ls) (k (append y ls))))) tmp-1)
- (let ((else tmp))
- (let ((tmp x))
- (let ((t-bde397a tmp)) (list "list->vector" t-bde397a)))))))))))))))))
- (emit (lambda (x)
- (let ((tmp x))
- (let ((tmp-1 ($sc-dispatch tmp '(#(atom "quote") any))))
- (if tmp-1
- (apply (lambda (x) (list '#(syntax-object quote ((top)) (hygiene guile)) x))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(#(atom "list") . each-any))))
- (if tmp-1
- (apply (lambda (x)
- (let ((tmp-1 (map emit x)))
- (let ((tmp ($sc-dispatch tmp-1 'each-any)))
- (if tmp
- (apply (lambda (t-bde397a)
- (cons '#(syntax-object list ((top)) (hygiene guile)) t-bde397a))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(#(atom "list*") . #(each+ any (any) ())))))
- (if tmp-1
- (apply (lambda (x y)
- (let f ((x* x))
- (if (null? x*)
- (emit y)
- (let ((tmp-1 (list (emit (car x*)) (f (cdr x*)))))
- (let ((tmp ($sc-dispatch tmp-1 '(any any))))
- (if tmp
- (apply (lambda (t-bde397a-125a t-bde397a)
- (list '#(syntax-object cons ((top)) (hygiene guile))
- t-bde397a-125a
- t-bde397a))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(#(atom "append") . each-any))))
- (if tmp-1
- (apply (lambda (x)
- (let ((tmp-1 (map emit x)))
- (let ((tmp ($sc-dispatch tmp-1 'each-any)))
- (if tmp
- (apply (lambda (t-bde397a)
- (cons '#(syntax-object append ((top)) (hygiene guile))
- t-bde397a))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(#(atom "vector") . each-any))))
- (if tmp-1
- (apply (lambda (x)
- (let ((tmp-1 (map emit x)))
- (let ((tmp ($sc-dispatch tmp-1 'each-any)))
- (if tmp
- (apply (lambda (t-bde397a)
- (cons '#(syntax-object vector ((top)) (hygiene guile))
- t-bde397a))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(#(atom "list->vector") any))))
- (if tmp-1
- (apply (lambda (x)
- (let ((tmp (emit x)))
- (let ((t-bde397a-127e tmp))
- (list '#(syntax-object list->vector ((top)) (hygiene guile))
- t-bde397a-127e))))
- tmp-1)
- (let ((tmp-1 ($sc-dispatch tmp '(#(atom "value") any))))
- (if tmp-1
- (apply (lambda (x) x) tmp-1)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp)))))))))))))))))))
- (lambda (x)
- (let ((tmp-1 x))
- (let ((tmp ($sc-dispatch tmp-1 '(_ any))))
- (if tmp
- (apply (lambda (e) (emit (quasi e 0))) tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1))))))))
-
-(define include
- (make-syntax-transformer
- 'include
- 'macro
- (lambda (x)
- (letrec*
- ((read-file
- (lambda (fn dir k)
- (let ((p (open-input-file
- (if (absolute-file-name? fn)
- fn
- (if dir
- (in-vicinity dir fn)
- (syntax-violation
- 'include
- "relative file name only allowed when the include form is in a file"
- x))))))
- (let ((enc (file-encoding p)))
- (set-port-encoding! p (let ((t enc)) (if t t "UTF-8")))
- (let f ((x (read p)) (result '()))
- (if (eof-object? x)
- (begin (close-input-port p) (reverse result))
- (f (read p) (cons (datum->syntax k x) result)))))))))
- (let ((src (syntax-source x)))
- (let ((file (if src (assq-ref src 'filename) #f)))
- (let ((dir (if (string? file) (dirname file) #f)))
- (let ((tmp-1 x))
- (let ((tmp ($sc-dispatch tmp-1 '(any any))))
- (if tmp
- (apply (lambda (k filename)
- (let ((fn (syntax->datum filename)))
- (let ((tmp-1 (read-file fn dir filename)))
- (let ((tmp ($sc-dispatch tmp-1 'each-any)))
- (if tmp
- (apply (lambda (exp)
- (cons '#(syntax-object begin ((top)) (hygiene guile)) exp))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1))))))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))))))))
-
-(define include-from-path
- (make-syntax-transformer
- 'include-from-path
- 'macro
- (lambda (x)
- (let ((tmp-1 x))
- (let ((tmp ($sc-dispatch tmp-1 '(any any))))
- (if tmp
- (apply (lambda (k filename)
- (let ((fn (syntax->datum filename)))
- (let ((tmp (datum->syntax
- filename
- (let ((t (%search-load-path fn)))
- (if t
- t
- (syntax-violation
- 'include-from-path
- "file not found in path"
- x
- filename))))))
- (let ((fn tmp))
- (list '#(syntax-object include ((top)) (hygiene guile)) fn)))))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))))
-
-(define unquote
- (make-syntax-transformer
- 'unquote
- 'macro
- (lambda (x)
- (syntax-violation
- 'unquote
- "expression not valid outside of quasiquote"
- x))))
-
-(define unquote-splicing
- (make-syntax-transformer
- 'unquote-splicing
- 'macro
- (lambda (x)
- (syntax-violation
- 'unquote-splicing
- "expression not valid outside of quasiquote"
- x))))
-
-(define make-variable-transformer
- (lambda (proc)
- (if (procedure? proc)
- (let ((trans (lambda (x) (proc x))))
- (set-procedure-property! trans 'variable-transformer #t)
- trans)
- (error "variable transformer not a procedure" proc))))
-
-(define identifier-syntax
- (make-syntax-transformer
- 'identifier-syntax
- 'macro
- (lambda (xx)
- (let ((tmp-1 xx))
- (let ((tmp ($sc-dispatch tmp-1 '(_ any))))
- (if tmp
- (apply (lambda (e)
- (list '#(syntax-object lambda ((top)) (hygiene guile))
- '(#(syntax-object x ((top)) (hygiene guile)))
- '#((#(syntax-object macro-type ((top)) (hygiene guile))
- .
- #(syntax-object identifier-syntax ((top)) (hygiene guile))))
- (list '#(syntax-object syntax-case ((top)) (hygiene guile))
- '#(syntax-object x ((top)) (hygiene guile))
- '()
- (list '#(syntax-object id ((top)) (hygiene guile))
- '(#(syntax-object identifier? ((top)) (hygiene guile))
- (#(syntax-object syntax ((top)) (hygiene guile))
- #(syntax-object id ((top)) (hygiene guile))))
- (list '#(syntax-object syntax ((top)) (hygiene guile)) e))
- (list '(#(syntax-object _ ((top)) (hygiene guile))
- #(syntax-object x ((top)) (hygiene guile))
- #(syntax-object ... ((top)) (hygiene guile)))
- (list '#(syntax-object syntax ((top)) (hygiene guile))
- (cons e
- '(#(syntax-object x ((top)) (hygiene guile))
- #(syntax-object ... ((top)) (hygiene guile)))))))))
- tmp)
- (let ((tmp ($sc-dispatch
- tmp-1
- '(_ (any any)
- ((#(free-id #(syntax-object set! ((top)) (hygiene guile))) any any)
- any)))))
- (if (if tmp
- (apply (lambda (id exp1 var val exp2)
- (if (identifier? id) (identifier? var) #f))
- tmp)
- #f)
- (apply (lambda (id exp1 var val exp2)
- (list '#(syntax-object make-variable-transformer ((top)) (hygiene guile))
- (list '#(syntax-object lambda ((top)) (hygiene guile))
- '(#(syntax-object x ((top)) (hygiene guile)))
- '#((#(syntax-object macro-type ((top)) (hygiene guile))
- .
- #(syntax-object variable-transformer ((top)) (hygiene guile))))
- (list '#(syntax-object syntax-case ((top)) (hygiene guile))
- '#(syntax-object x ((top)) (hygiene guile))
- '(#(syntax-object set! ((top)) (hygiene guile)))
- (list (list '#(syntax-object set! ((top)) (hygiene guile)) var val)
- (list '#(syntax-object syntax ((top)) (hygiene guile)) exp2))
- (list (cons id
- '(#(syntax-object x ((top)) (hygiene guile))
- #(syntax-object ... ((top)) (hygiene guile))))
- (list '#(syntax-object syntax ((top)) (hygiene guile))
- (cons exp1
- '(#(syntax-object x ((top)) (hygiene guile))
- #(syntax-object ... ((top)) (hygiene guile))))))
- (list id
- (list '#(syntax-object identifier? ((top)) (hygiene guile))
- (list '#(syntax-object syntax ((top)) (hygiene guile)) id))
- (list '#(syntax-object syntax ((top)) (hygiene guile)) exp1))))))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))))))
-
-(define define*
- (make-syntax-transformer
- 'define*
- 'macro
- (lambda (x)
- (let ((tmp-1 x))
- (let ((tmp ($sc-dispatch tmp-1 '(_ (any . any) any . each-any))))
- (if tmp
- (apply (lambda (id args b0 b1)
- (list '#(syntax-object define ((top)) (hygiene guile))
- id
- (cons '#(syntax-object lambda* ((top)) (hygiene guile))
- (cons args (cons b0 b1)))))
- tmp)
- (let ((tmp ($sc-dispatch tmp-1 '(_ any any))))
- (if (if tmp (apply (lambda (id val) (identifier? id)) tmp) #f)
- (apply (lambda (id val)
- (list '#(syntax-object define ((top)) (hygiene guile)) id val))
- tmp)
- (syntax-violation
- #f
- "source expression failed to match any pattern"
- tmp-1)))))))))
-
-;;;; -*-scheme-*-
-;;;;
-;;;; Copyright (C) 2001, 2003, 2006, 2009, 2010, 2011,
-;;;; 2012, 2013, 2016 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-;;; Portable implementation of syntax-case
-;;; Originally extracted from Chez Scheme Version 5.9f
-;;; Authors: R. Kent Dybvig, Oscar Waddell, Bob Hieb, Carl Bruggeman
-
-;;; Copyright (c) 1992-1997 Cadence Research Systems
-;;; 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.
-
-;;; Modified by Mikael Djurfeldt <djurfeldt@nada.kth.se> according
-;;; to the ChangeLog distributed in the same directory as this file:
-;;; 1997-08-19, 1997-09-03, 1997-09-10, 2000-08-13, 2000-08-24,
-;;; 2000-09-12, 2001-03-08
-
-;;; Modified by Andy Wingo <wingo@pobox.com> according to the Git
-;;; revision control logs corresponding to this file: 2009, 2010.
-
-;;; Modified by Mark H Weaver <mhw@netris.org> according to the Git
-;;; revision control logs corresponding to this file: 2012, 2013.
-
-
-;;; This code is based on "Syntax Abstraction in Scheme"
-;;; by R. Kent Dybvig, Robert Hieb, and Carl Bruggeman.
-;;; Lisp and Symbolic Computation 5:4, 295-326, 1992.
-;;; <http://www.cs.indiana.edu/~dyb/pubs/LaSC-5-4-pp295-326.pdf>
-
-
-;;; This file defines the syntax-case expander, macroexpand, and a set
-;;; of associated syntactic forms and procedures. Of these, the
-;;; following are documented in The Scheme Programming Language,
-;;; Fourth Edition (R. Kent Dybvig, MIT Press, 2009), and in the
-;;; R6RS:
-;;;
-;;; bound-identifier=?
-;;; datum->syntax
-;;; define-syntax
-;;; syntax-parameterize
-;;; free-identifier=?
-;;; generate-temporaries
-;;; identifier?
-;;; identifier-syntax
-;;; let-syntax
-;;; letrec-syntax
-;;; syntax
-;;; syntax-case
-;;; syntax->datum
-;;; syntax-rules
-;;; with-syntax
-;;;
-;;; Additionally, the expander provides definitions for a number of core
-;;; Scheme syntactic bindings, such as `let', `lambda', and the like.
-
-;;; The remaining exports are listed below:
-;;;
-;;; (macroexpand datum)
-;;; if datum represents a valid expression, macroexpand returns an
-;;; expanded version of datum in a core language that includes no
-;;; syntactic abstractions. The core language includes begin,
-;;; define, if, lambda, letrec, quote, and set!.
-;;; (eval-when situations expr ...)
-;;; conditionally evaluates expr ... at compile-time or run-time
-;;; depending upon situations (see the Chez Scheme System Manual,
-;;; Revision 3, for a complete description)
-;;; (syntax-violation who message form [subform])
-;;; used to report errors found during expansion
-;;; ($sc-dispatch e p)
-;;; used by expanded code to handle syntax-case matching
-
-;;; This file is shipped along with an expanded version of itself,
-;;; psyntax-pp.scm, which is loaded when psyntax.scm has not yet been
-;;; compiled. In this way, psyntax bootstraps off of an expanded
-;;; version of itself.
-
-;;; This implementation of the expander sometimes uses syntactic
-;;; abstractions when procedural abstractions would suffice. For
-;;; example, we define top-wrap and top-marked? as
-;;;
-;;; (define-syntax top-wrap (identifier-syntax '((top))))
-;;; (define-syntax top-marked?
-;;; (syntax-rules ()
-;;; ((_ w) (memq 'top (wrap-marks w)))))
-;;;
-;;; rather than
-;;;
-;;; (define top-wrap '((top)))
-;;; (define top-marked?
-;;; (lambda (w) (memq 'top (wrap-marks w))))
-;;;
-;;; On the other hand, we don't do this consistently; we define
-;;; make-wrap, wrap-marks, and wrap-subst simply as
-;;;
-;;; (define make-wrap cons)
-;;; (define wrap-marks car)
-;;; (define wrap-subst cdr)
-;;;
-;;; In Chez Scheme, the syntactic and procedural forms of these
-;;; abstractions are equivalent, since the optimizer consistently
-;;; integrates constants and small procedures. This will be true of
-;;; Guile as well, once we implement a proper inliner.
-
-
-;;; Implementation notes:
-
-;;; Objects with no standard print syntax, including objects containing
-;;; cycles and syntax object, are allowed in quoted data as long as they
-;;; are contained within a syntax form or produced by datum->syntax.
-;;; Such objects are never copied.
-
-;;; All identifiers that don't have macro definitions and are not bound
-;;; lexically are assumed to be global variables.
-
-;;; Top-level definitions of macro-introduced identifiers are allowed.
-;;; This may not be appropriate for implementations in which the
-;;; model is that bindings are created by definitions, as opposed to
-;;; one in which initial values are assigned by definitions.
-
-;;; Identifiers and syntax objects are implemented as vectors for
-;;; portability. As a result, it is possible to "forge" syntax objects.
-
-;;; The implementation of generate-temporaries assumes that it is
-;;; possible to generate globally unique symbols (gensyms).
-
-;;; The source location associated with incoming expressions is tracked
-;;; via the source-properties mechanism, a weak map from expression to
-;;; source information. At times the source is separated from the
-;;; expression; see the note below about "efficiency and confusion".
-
-
-;;; Bootstrapping:
-
-;;; When changing syntax-object representations, it is necessary to support
-;;; both old and new syntax-object representations in id-var-name. It
-;;; should be sufficient to recognize old representations and treat
-;;; them as not lexically bound.
-
-
-
-(eval-when (compile)
- (set-current-module (resolve-module '(guile))))
-
-(let ()
- (define-syntax define-expansion-constructors
- (lambda (x)
- (syntax-case x ()
- ((_)
- (let lp ((n 0) (out '()))
- (if (< n (vector-length %expanded-vtables))
- (lp (1+ n)
- (let* ((vtable (vector-ref %expanded-vtables n))
- (stem (struct-ref vtable (+ vtable-offset-user 0)))
- (fields (struct-ref vtable (+ vtable-offset-user 2)))
- (sfields (map (lambda (f) (datum->syntax x f)) fields))
- (ctor (datum->syntax x (symbol-append 'make- stem))))
- (cons #`(define (#,ctor #,@sfields)
- (make-struct (vector-ref %expanded-vtables #,n) 0
- #,@sfields))
- out)))
- #`(begin #,@(reverse out))))))))
-
- (define-syntax define-expansion-accessors
- (lambda (x)
- (syntax-case x ()
- ((_ stem field ...)
- (let lp ((n 0))
- (let ((vtable (vector-ref %expanded-vtables n))
- (stem (syntax->datum #'stem)))
- (if (eq? (struct-ref vtable (+ vtable-offset-user 0)) stem)
- #`(begin
- (define (#,(datum->syntax x (symbol-append stem '?)) x)
- (and (struct? x)
- (eq? (struct-vtable x)
- (vector-ref %expanded-vtables #,n))))
- #,@(map
- (lambda (f)
- (let ((get (datum->syntax x (symbol-append stem '- f)))
- (set (datum->syntax x (symbol-append 'set- stem '- f '!)))
- (idx (list-index (struct-ref vtable
- (+ vtable-offset-user 2))
- f)))
- #`(begin
- (define (#,get x)
- (struct-ref x #,idx))
- (define (#,set x v)
- (struct-set! x #,idx v)))))
- (syntax->datum #'(field ...))))
- (lp (1+ n)))))))))
-
- (define-syntax define-structure
- (lambda (x)
- (define construct-name
- (lambda (template-identifier . args)
- (datum->syntax
- template-identifier
- (string->symbol
- (apply string-append
- (map (lambda (x)
- (if (string? x)
- x
- (symbol->string (syntax->datum x))))
- args))))))
- (syntax-case x ()
- ((_ (name id1 ...))
- (and-map identifier? #'(name id1 ...))
- (with-syntax
- ((constructor (construct-name #'name "make-" #'name))
- (predicate (construct-name #'name #'name "?"))
- ((access ...)
- (map (lambda (x) (construct-name x #'name "-" x))
- #'(id1 ...)))
- ((assign ...)
- (map (lambda (x)
- (construct-name x "set-" #'name "-" x "!"))
- #'(id1 ...)))
- (structure-length
- (+ (length #'(id1 ...)) 1))
- ((index ...)
- (let f ((i 1) (ids #'(id1 ...)))
- (if (null? ids)
- '()
- (cons i (f (+ i 1) (cdr ids)))))))
- #'(begin
- (define constructor
- (lambda (id1 ...)
- (vector 'name id1 ... )))
- (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 assign
- (lambda (x update)
- (vector-set! x index update)))
- ...))))))
-
- (let ()
- (define-expansion-constructors)
- (define-expansion-accessors lambda meta)
-
- ;; hooks to nonportable run-time helpers
- (begin
- (define-syntax fx+ (identifier-syntax +))
- (define-syntax fx- (identifier-syntax -))
- (define-syntax fx= (identifier-syntax =))
- (define-syntax fx< (identifier-syntax <))
-
- (define top-level-eval-hook
- (lambda (x mod)
- (primitive-eval x)))
-
- (define local-eval-hook
- (lambda (x mod)
- (primitive-eval x)))
-
- ;; Capture syntax-session-id before we shove it off into a module.
- (define session-id
- (let ((v (module-variable (current-module) 'syntax-session-id)))
- (lambda ()
- ((variable-ref v)))))
-
- (define put-global-definition-hook
- (lambda (symbol type val)
- (module-define! (current-module)
- symbol
- (make-syntax-transformer symbol type val))))
-
- (define get-global-definition-hook
- (lambda (symbol module)
- (if (and (not module) (current-module))
- (warn "module system is booted, we should have a module" symbol))
- (let ((v (module-variable (if module
- (resolve-module (cdr module))
- (current-module))
- symbol)))
- (and v (variable-bound? v)
- (let ((val (variable-ref v)))
- (and (macro? val) (macro-type val)
- (cons (macro-type val)
- (macro-binding val)))))))))
-
-
- (define (decorate-source e s)
- (if (and s (supports-source-properties? e))
- (set-source-properties! e s))
- e)
-
- (define (maybe-name-value! name val)
- (if (lambda? val)
- (let ((meta (lambda-meta val)))
- (if (not (assq 'name meta))
- (set-lambda-meta! val (acons 'name name meta))))))
-
- ;; output constructors
- (define build-void
- (lambda (source)
- (make-void source)))
-
- (define build-application
- (lambda (source fun-exp arg-exps)
- (make-application source fun-exp arg-exps)))
-
- (define build-conditional
- (lambda (source test-exp then-exp else-exp)
- (make-conditional source test-exp then-exp else-exp)))
-
- (define build-dynlet
- (lambda (source fluids vals body)
- (make-dynlet source fluids vals body)))
-
- (define build-lexical-reference
- (lambda (type source name var)
- (make-lexical-ref source name var)))
-
- (define build-lexical-assignment
- (lambda (source name var exp)
- (maybe-name-value! name exp)
- (make-lexical-set source name var exp)))
-
- (define (analyze-variable mod var modref-cont bare-cont)
- (if (not mod)
- (bare-cont var)
- (let ((kind (car mod))
- (mod (cdr mod)))
- (case kind
- ((public) (modref-cont mod var #t))
- ((private) (if (not (equal? mod (module-name (current-module))))
- (modref-cont mod var #f)
- (bare-cont var)))
- ((bare) (bare-cont var))
- ((hygiene) (if (and (not (equal? mod (module-name (current-module))))
- (module-variable (resolve-module mod) var))
- (modref-cont mod var #f)
- (bare-cont var)))
- (else (syntax-violation #f "bad module kind" var mod))))))
-
- (define build-global-reference
- (lambda (source var mod)
- (analyze-variable
- mod var
- (lambda (mod var public?)
- (make-module-ref source mod var public?))
- (lambda (var)
- (make-toplevel-ref source var)))))
-
- (define build-global-assignment
- (lambda (source var exp mod)
- (maybe-name-value! var exp)
- (analyze-variable
- mod var
- (lambda (mod var public?)
- (make-module-set source mod var public? exp))
- (lambda (var)
- (make-toplevel-set source var exp)))))
-
- (define build-global-definition
- (lambda (source var exp)
- (maybe-name-value! var exp)
- (make-toplevel-define source var exp)))
-
- (define build-simple-lambda
- (lambda (src req rest vars meta exp)
- (make-lambda src
- meta
- ;; hah, a case in which kwargs would be nice.
- (make-lambda-case
- ;; src req opt rest kw inits vars body else
- src req #f rest #f '() vars exp #f))))
-
- (define build-case-lambda
- (lambda (src meta body)
- (make-lambda src meta body)))
-
- (define build-lambda-case
- ;; req := (name ...)
- ;; opt := (name ...) | #f
- ;; rest := name | #f
- ;; kw := (allow-other-keys? (keyword name var) ...) | #f
- ;; inits: (init ...)
- ;; vars: (sym ...)
- ;; vars map to named arguments in the following order:
- ;; required, optional (positional), rest, keyword.
- ;; the body of a lambda: anything, already expanded
- ;; else: lambda-case | #f
- (lambda (src req opt rest kw inits vars body else-case)
- (make-lambda-case src req opt rest kw inits vars body else-case)))
-
- (define build-primref
- (lambda (src name)
- (if (equal? (module-name (current-module)) '(guile))
- (make-toplevel-ref src name)
- (make-module-ref src '(guile) name #f))))
-
- (define (build-data src exp)
- (make-const src exp))
-
- (define build-sequence
- (lambda (src exps)
- (if (null? (cdr exps))
- (car exps)
- (make-sequence src exps))))
-
- (define build-let
- (lambda (src ids vars val-exps body-exp)
- (for-each maybe-name-value! ids val-exps)
- (if (null? vars)
- body-exp
- (make-let src ids vars val-exps body-exp))))
-
- (define build-named-let
- (lambda (src ids vars val-exps body-exp)
- (let ((f (car vars))
- (f-name (car ids))
- (vars (cdr vars))
- (ids (cdr ids)))
- (let ((proc (build-simple-lambda src ids #f vars '() body-exp)))
- (maybe-name-value! f-name proc)
- (for-each maybe-name-value! ids val-exps)
- (make-letrec
- src #f
- (list f-name) (list f) (list proc)
- (build-application src (build-lexical-reference 'fun src f-name f)
- val-exps))))))
-
- (define build-letrec
- (lambda (src in-order? ids vars val-exps body-exp)
- (if (null? vars)
- body-exp
- (begin
- (for-each maybe-name-value! ids val-exps)
- (make-letrec src in-order? ids vars val-exps body-exp)))))
-
-
- (define-syntax-rule (build-lexical-var src id)
- ;; Use a per-module counter instead of the global counter of
- ;; 'gensym' so that the generated identifier is reproducible.
- (module-gensym (symbol->string id)))
-
- (define-structure (syntax-object expression wrap module))
-
- (define-syntax no-source (identifier-syntax #f))
-
- (define source-annotation
- (lambda (x)
- (let ((props (source-properties
- (if (syntax-object? x)
- (syntax-object-expression x)
- x))))
- (and (pair? props) props))))
-
- (define-syntax-rule (arg-check pred? e who)
- (let ((x e))
- (if (not (pred? x)) (syntax-violation who "invalid argument" x))))
-
- ;; compile-time environments
-
- ;; wrap and environment comprise two level mapping.
- ;; wrap : id --> label
- ;; env : label --> <element>
-
- ;; environments are represented in two parts: a lexical part and a global
- ;; part. The lexical part is a simple list of associations from labels
- ;; to bindings. The global part is implemented by
- ;; {put,get}-global-definition-hook and associates symbols with
- ;; bindings.
-
- ;; global (assumed global variable) and displaced-lexical (see below)
- ;; do not show up in any environment; instead, they are fabricated by
- ;; lookup when it finds no other bindings.
-
- ;; <environment> ::= ((<label> . <binding>)*)
-
- ;; identifier bindings include a type and a value
-
- ;; <binding> ::= (macro . <procedure>) macros
- ;; (core . <procedure>) core forms
- ;; (module-ref . <procedure>) @ or @@
- ;; (begin) begin
- ;; (define) define
- ;; (define-syntax) define-syntax
- ;; (define-syntax-parameter) define-syntax-parameter
- ;; (local-syntax . rec?) let-syntax/letrec-syntax
- ;; (eval-when) eval-when
- ;; (syntax . (<var> . <level>)) pattern variables
- ;; (global) assumed global variable
- ;; (lexical . <var>) lexical variables
- ;; (ellipsis . <identifier>) custom ellipsis
- ;; (displaced-lexical) displaced lexicals
- ;; <level> ::= <nonnegative integer>
- ;; <var> ::= variable returned by build-lexical-var
-
- ;; a macro is a user-defined syntactic-form. a core is a
- ;; system-defined syntactic form. begin, define, define-syntax,
- ;; define-syntax-parameter, and eval-when are treated specially
- ;; since they are sensitive to whether the form is at top-level and
- ;; (except for eval-when) can denote valid internal definitions.
-
- ;; a pattern variable is a variable introduced by syntax-case and can
- ;; be referenced only within a syntax form.
-
- ;; any identifier for which no top-level syntax definition or local
- ;; binding of any kind has been seen is assumed to be a global
- ;; variable.
-
- ;; a lexical variable is a lambda- or letrec-bound variable.
-
- ;; an ellipsis binding is introduced by the 'with-ellipsis' special
- ;; form.
-
- ;; a displaced-lexical identifier is a lexical identifier removed from
- ;; it's scope by the return of a syntax object containing the identifier.
- ;; a displaced lexical can also appear when a letrec-syntax-bound
- ;; keyword is referenced on the rhs of one of the letrec-syntax clauses.
- ;; a displaced lexical should never occur with properly written macros.
-
- (define-syntax make-binding
- (syntax-rules (quote)
- ((_ type value) (cons type value))
- ((_ 'type) '(type))
- ((_ type) (cons type '()))))
- (define-syntax-rule (binding-type x)
- (car x))
- (define-syntax-rule (binding-value x)
- (cdr x))
-
- (define-syntax null-env (identifier-syntax '()))
-
- (define extend-env
- (lambda (labels bindings r)
- (if (null? labels)
- r
- (extend-env (cdr labels) (cdr bindings)
- (cons (cons (car labels) (car bindings)) r)))))
-
- (define extend-var-env
- ;; variant of extend-env that forms "lexical" binding
- (lambda (labels vars r)
- (if (null? labels)
- r
- (extend-var-env (cdr labels) (cdr vars)
- (cons (cons (car labels) (make-binding 'lexical (car vars))) r)))))
-
- ;; we use a "macros only" environment in expansion of local macro
- ;; definitions so that their definitions can use local macros without
- ;; attempting to use other lexical identifiers.
- (define macros-only-env
- (lambda (r)
- (if (null? r)
- '()
- (let ((a (car r)))
- (if (memq (cadr a) '(macro ellipsis))
- (cons a (macros-only-env (cdr r)))
- (macros-only-env (cdr r)))))))
-
- (define lookup
- ;; x may be a label or a symbol
- ;; although symbols are usually global, we check the environment first
- ;; anyway because a temporary binding may have been established by
- ;; fluid-let-syntax
- (lambda (x r mod)
- (cond
- ((assq x r) => cdr)
- ((symbol? x)
- (or (get-global-definition-hook x mod) (make-binding 'global)))
- (else (make-binding 'displaced-lexical)))))
-
- (define global-extend
- (lambda (type sym val)
- (put-global-definition-hook sym type val)))
-
-
- ;; Conceptually, identifiers are always syntax objects. Internally,
- ;; however, the wrap is sometimes maintained separately (a source of
- ;; efficiency and confusion), so that symbols are also considered
- ;; identifiers by id?. Externally, they are always wrapped.
-
- (define nonsymbol-id?
- (lambda (x)
- (and (syntax-object? x)
- (symbol? (syntax-object-expression x)))))
-
- (define id?
- (lambda (x)
- (cond
- ((symbol? x) #t)
- ((syntax-object? x) (symbol? (syntax-object-expression x)))
- (else #f))))
-
- (define-syntax-rule (id-sym-name e)
- (let ((x e))
- (if (syntax-object? x)
- (syntax-object-expression x)
- x)))
-
- (define id-sym-name&marks
- (lambda (x w)
- (if (syntax-object? x)
- (values
- (syntax-object-expression x)
- (join-marks (wrap-marks w) (wrap-marks (syntax-object-wrap x))))
- (values x (wrap-marks w)))))
-
- ;; syntax object wraps
-
- ;; <wrap> ::= ((<mark> ...) . (<subst> ...))
- ;; <subst> ::= shift | <subs>
- ;; <subs> ::= #(ribcage #(<sym> ...) #(<mark> ...) #(<label> ...))
- ;; | #(ribcage (<sym> ...) (<mark> ...) (<label> ...))
-
- (define-syntax make-wrap (identifier-syntax cons))
- (define-syntax wrap-marks (identifier-syntax car))
- (define-syntax wrap-subst (identifier-syntax cdr))
-
- ;; labels must be comparable with "eq?", have read-write invariance,
- ;; and distinct from symbols.
- (define (gen-label)
- (symbol->string (module-gensym "l")))
-
- (define gen-labels
- (lambda (ls)
- (if (null? ls)
- '()
- (cons (gen-label) (gen-labels (cdr ls))))))
-
- (define-structure (ribcage symnames marks labels))
-
- (define-syntax empty-wrap (identifier-syntax '(())))
-
- (define-syntax top-wrap (identifier-syntax '((top))))
-
- (define-syntax-rule (top-marked? w)
- (memq 'top (wrap-marks w)))
-
- ;; Marks must be comparable with "eq?" and distinct from pairs and
- ;; the symbol top. We do not use integers so that marks will remain
- ;; unique even across file compiles.
-
- (define-syntax the-anti-mark (identifier-syntax #f))
-
- (define anti-mark
- (lambda (w)
- (make-wrap (cons the-anti-mark (wrap-marks w))
- (cons 'shift (wrap-subst w)))))
-
- (define-syntax-rule (new-mark)
- (module-gensym "m"))
-
- ;; make-empty-ribcage and extend-ribcage maintain list-based ribcages for
- ;; internal definitions, in which the ribcages are built incrementally
- (define-syntax-rule (make-empty-ribcage)
- (make-ribcage '() '() '()))
-
- (define extend-ribcage!
- ;; must receive ids with complete wraps
- (lambda (ribcage id label)
- (set-ribcage-symnames! ribcage
- (cons (syntax-object-expression id)
- (ribcage-symnames ribcage)))
- (set-ribcage-marks! ribcage
- (cons (wrap-marks (syntax-object-wrap id))
- (ribcage-marks ribcage)))
- (set-ribcage-labels! ribcage
- (cons label (ribcage-labels ribcage)))))
-
- ;; make-binding-wrap creates vector-based ribcages
- (define make-binding-wrap
- (lambda (ids labels w)
- (if (null? ids)
- w
- (make-wrap
- (wrap-marks w)
- (cons
- (let ((labelvec (list->vector labels)))
- (let ((n (vector-length labelvec)))
- (let ((symnamevec (make-vector n)) (marksvec (make-vector n)))
- (let f ((ids ids) (i 0))
- (if (not (null? ids))
- (call-with-values
- (lambda () (id-sym-name&marks (car ids) w))
- (lambda (symname marks)
- (vector-set! symnamevec i symname)
- (vector-set! marksvec i marks)
- (f (cdr ids) (fx+ i 1))))))
- (make-ribcage symnamevec marksvec labelvec))))
- (wrap-subst w))))))
-
- (define smart-append
- (lambda (m1 m2)
- (if (null? m2)
- m1
- (append m1 m2))))
-
- (define join-wraps
- (lambda (w1 w2)
- (let ((m1 (wrap-marks w1)) (s1 (wrap-subst w1)))
- (if (null? m1)
- (if (null? s1)
- w2
- (make-wrap
- (wrap-marks w2)
- (smart-append s1 (wrap-subst w2))))
- (make-wrap
- (smart-append m1 (wrap-marks w2))
- (smart-append s1 (wrap-subst w2)))))))
-
- (define join-marks
- (lambda (m1 m2)
- (smart-append m1 m2)))
-
- (define same-marks?
- (lambda (x y)
- (or (eq? x y)
- (and (not (null? x))
- (not (null? y))
- (eq? (car x) (car y))
- (same-marks? (cdr x) (cdr y))))))
-
- (define id-var-name
- (lambda (id w)
- (define-syntax-rule (first e)
- ;; Rely on Guile's multiple-values truncation.
- e)
- (define search
- (lambda (sym subst marks)
- (if (null? subst)
- (values #f marks)
- (let ((fst (car subst)))
- (if (eq? fst 'shift)
- (search sym (cdr subst) (cdr marks))
- (let ((symnames (ribcage-symnames fst)))
- (if (vector? symnames)
- (search-vector-rib sym subst marks symnames fst)
- (search-list-rib sym subst marks symnames fst))))))))
- (define search-list-rib
- (lambda (sym subst marks symnames ribcage)
- (let f ((symnames symnames) (i 0))
- (cond
- ((null? symnames) (search sym (cdr subst) marks))
- ((and (eq? (car symnames) sym)
- (same-marks? marks (list-ref (ribcage-marks ribcage) i)))
- (values (list-ref (ribcage-labels ribcage) i) marks))
- (else (f (cdr symnames) (fx+ i 1)))))))
- (define search-vector-rib
- (lambda (sym subst marks symnames ribcage)
- (let ((n (vector-length symnames)))
- (let f ((i 0))
- (cond
- ((fx= i n) (search sym (cdr subst) marks))
- ((and (eq? (vector-ref symnames i) sym)
- (same-marks? marks (vector-ref (ribcage-marks ribcage) i)))
- (values (vector-ref (ribcage-labels ribcage) i) marks))
- (else (f (fx+ i 1))))))))
- (cond
- ((symbol? id)
- (or (first (search id (wrap-subst w) (wrap-marks w))) id))
- ((syntax-object? id)
- (let ((id (syntax-object-expression id))
- (w1 (syntax-object-wrap id)))
- (let ((marks (join-marks (wrap-marks w) (wrap-marks w1))))
- (call-with-values (lambda () (search id (wrap-subst w) marks))
- (lambda (new-id marks)
- (or new-id
- (first (search id (wrap-subst w1) marks))
- id))))))
- (else (syntax-violation 'id-var-name "invalid id" id)))))
-
- ;; A helper procedure for syntax-locally-bound-identifiers, which
- ;; itself is a helper for transformer procedures.
- ;; `locally-bound-identifiers' returns a list of all bindings
- ;; visible to a syntax object with the given wrap. They are in
- ;; order from outer to inner.
- ;;
- ;; The purpose of this procedure is to give a transformer procedure
- ;; references on bound identifiers, that the transformer can then
- ;; introduce some of them in its output. As such, the identifiers
- ;; are anti-marked, so that rebuild-macro-output doesn't apply new
- ;; marks to them.
- ;;
- (define locally-bound-identifiers
- (lambda (w mod)
- (define scan
- (lambda (subst results)
- (if (null? subst)
- results
- (let ((fst (car subst)))
- (if (eq? fst 'shift)
- (scan (cdr subst) results)
- (let ((symnames (ribcage-symnames fst))
- (marks (ribcage-marks fst)))
- (if (vector? symnames)
- (scan-vector-rib subst symnames marks results)
- (scan-list-rib subst symnames marks results))))))))
- (define scan-list-rib
- (lambda (subst symnames marks results)
- (let f ((symnames symnames) (marks marks) (results results))
- (if (null? symnames)
- (scan (cdr subst) results)
- (f (cdr symnames) (cdr marks)
- (cons (wrap (car symnames)
- (anti-mark (make-wrap (car marks) subst))
- mod)
- results))))))
- (define scan-vector-rib
- (lambda (subst symnames marks results)
- (let ((n (vector-length symnames)))
- (let f ((i 0) (results results))
- (if (fx= i n)
- (scan (cdr subst) results)
- (f (fx+ i 1)
- (cons (wrap (vector-ref symnames i)
- (anti-mark (make-wrap (vector-ref marks i) subst))
- mod)
- results)))))))
- (scan (wrap-subst w) '())))
-
- ;; Returns three values: binding type, binding value, the module (for
- ;; resolving toplevel vars).
- (define (resolve-identifier id w r mod)
- (define (resolve-global var mod)
- (let ((b (or (get-global-definition-hook var mod)
- (make-binding 'global))))
- (if (eq? (binding-type b) 'global)
- (values 'global var mod)
- (values (binding-type b) (binding-value b) mod))))
- (define (resolve-lexical label mod)
- (let ((b (or (assq-ref r label)
- (make-binding 'displaced-lexical))))
- (values (binding-type b) (binding-value b) mod)))
- (let ((n (id-var-name id w)))
- (cond
- ((symbol? n)
- (resolve-global n (if (syntax-object? id)
- (syntax-object-module id)
- mod)))
- ((string? n)
- (resolve-lexical n (if (syntax-object? id)
- (syntax-object-module id)
- mod)))
- (else
- (error "unexpected id-var-name" id w n)))))
-
- (define transformer-environment
- (make-fluid
- (lambda (k)
- (error "called outside the dynamic extent of a syntax transformer"))))
-
- (define (with-transformer-environment k)
- ((fluid-ref transformer-environment) k))
-
- ;; free-id=? must be passed fully wrapped ids since (free-id=? x y)
- ;; may be true even if (free-id=? (wrap x w) (wrap y w)) is not.
-
- (define free-id=?
- (lambda (i j)
- (and (eq? (id-sym-name i) (id-sym-name j)) ; accelerator
- (eq? (id-var-name i empty-wrap) (id-var-name j empty-wrap)))))
-
- ;; bound-id=? may be passed unwrapped (or partially wrapped) ids as
- ;; long as the missing portion of the wrap is common to both of the ids
- ;; since (bound-id=? x y) iff (bound-id=? (wrap x w) (wrap y w))
-
- (define bound-id=?
- (lambda (i j)
- (if (and (syntax-object? i) (syntax-object? j))
- (and (eq? (syntax-object-expression i)
- (syntax-object-expression j))
- (same-marks? (wrap-marks (syntax-object-wrap i))
- (wrap-marks (syntax-object-wrap j))))
- (eq? i j))))
-
- ;; "valid-bound-ids?" returns #t if it receives a list of distinct ids.
- ;; valid-bound-ids? may be passed unwrapped (or partially wrapped) ids
- ;; as long as the missing portion of the wrap is common to all of the
- ;; ids.
-
- (define valid-bound-ids?
- (lambda (ids)
- (and (let all-ids? ((ids ids))
- (or (null? ids)
- (and (id? (car ids))
- (all-ids? (cdr ids)))))
- (distinct-bound-ids? ids))))
-
- ;; distinct-bound-ids? expects a list of ids and returns #t if there are
- ;; no duplicates. It is quadratic on the length of the id list; long
- ;; lists could be sorted to make it more efficient. distinct-bound-ids?
- ;; may be passed unwrapped (or partially wrapped) ids as long as the
- ;; missing portion of the wrap is common to all of the ids.
-
- (define distinct-bound-ids?
- (lambda (ids)
- (let distinct? ((ids ids))
- (or (null? ids)
- (and (not (bound-id-member? (car ids) (cdr ids)))
- (distinct? (cdr ids)))))))
-
- (define bound-id-member?
- (lambda (x list)
- (and (not (null? list))
- (or (bound-id=? x (car list))
- (bound-id-member? x (cdr list))))))
-
- ;; wrapping expressions and identifiers
-
- (define wrap
- (lambda (x w defmod)
- (cond
- ((and (null? (wrap-marks w)) (null? (wrap-subst w))) x)
- ((syntax-object? x)
- (make-syntax-object
- (syntax-object-expression x)
- (join-wraps w (syntax-object-wrap x))
- (syntax-object-module x)))
- ((null? x) x)
- (else (make-syntax-object x w defmod)))))
-
- (define source-wrap
- (lambda (x w s defmod)
- (wrap (decorate-source x s) w defmod)))
-
- ;; expanding
-
- (define expand-sequence
- (lambda (body r w s mod)
- (build-sequence s
- (let dobody ((body body) (r r) (w w) (mod mod))
- (if (null? body)
- '()
- (let ((first (expand (car body) r w mod)))
- (cons first (dobody (cdr body) r w mod))))))))
-
- ;; At top-level, we allow mixed definitions and expressions. Like
- ;; expand-body we expand in two passes.
- ;;
- ;; First, from left to right, we expand just enough to know what
- ;; expressions are definitions, syntax definitions, and splicing
- ;; statements (`begin'). If we anything needs evaluating at
- ;; expansion-time, it is expanded directly.
- ;;
- ;; Otherwise we collect expressions to expand, in thunks, and then
- ;; expand them all at the end. This allows all syntax expanders
- ;; visible in a toplevel sequence to be visible during the
- ;; expansions of all normal definitions and expressions in the
- ;; sequence.
- ;;
- (define expand-top-sequence
- (lambda (body r w s m esew mod)
- (define (scan body r w s m esew mod exps)
- (cond
- ((null? body)
- ;; in reversed order
- exps)
- (else
- (call-with-values
- (lambda ()
- (call-with-values
- (lambda ()
- (let ((e (car body)))
- (syntax-type e r w (or (source-annotation e) s) #f mod #f)))
- (lambda (type value form e w s mod)
- (case type
- ((begin-form)
- (syntax-case e ()
- ((_) exps)
- ((_ e1 e2 ...)
- (scan #'(e1 e2 ...) r w s m esew mod exps))))
- ((local-syntax-form)
- (expand-local-syntax value e r w s mod
- (lambda (body r w s mod)
- (scan body r w s m esew mod exps))))
- ((eval-when-form)
- (syntax-case e ()
- ((_ (x ...) e1 e2 ...)
- (let ((when-list (parse-when-list e #'(x ...)))
- (body #'(e1 e2 ...)))
- (cond
- ((eq? m 'e)
- (if (memq 'eval when-list)
- (scan body r w s
- (if (memq 'expand when-list) 'c&e 'e)
- '(eval)
- mod exps)
- (begin
- (if (memq 'expand when-list)
- (top-level-eval-hook
- (expand-top-sequence body r w s 'e '(eval) mod)
- mod))
- (values exps))))
- ((memq 'load when-list)
- (if (or (memq 'compile when-list)
- (memq 'expand when-list)
- (and (eq? m 'c&e) (memq 'eval when-list)))
- (scan body r w s 'c&e '(compile load) mod exps)
- (if (memq m '(c c&e))
- (scan body r w s 'c '(load) mod exps)
- (values exps))))
- ((or (memq 'compile when-list)
- (memq 'expand when-list)
- (and (eq? m 'c&e) (memq 'eval when-list)))
- (top-level-eval-hook
- (expand-top-sequence body r w s 'e '(eval) mod)
- mod)
- (values exps))
- (else
- (values exps)))))))
- ((define-syntax-form define-syntax-parameter-form)
- (let ((n (id-var-name value w)) (r (macros-only-env r)))
- (case m
- ((c)
- (if (memq 'compile esew)
- (let ((e (expand-install-global n (expand e r w mod))))
- (top-level-eval-hook e mod)
- (if (memq 'load esew)
- (values (cons e exps))
- (values exps)))
- (if (memq 'load esew)
- (values (cons (expand-install-global n (expand e r w mod))
- exps))
- (values exps))))
- ((c&e)
- (let ((e (expand-install-global n (expand e r w mod))))
- (top-level-eval-hook e mod)
- (values (cons e exps))))
- (else
- (if (memq 'eval esew)
- (top-level-eval-hook
- (expand-install-global n (expand e r w mod))
- mod))
- (values exps)))))
- ((define-form)
- (let* ((n (id-var-name value w))
- ;; Lookup the name in the module of the define form.
- (type (binding-type (lookup n r mod))))
- (case type
- ((global core macro module-ref)
- ;; affect compile-time environment (once we have booted)
- (if (and (memq m '(c c&e))
- (not (module-local-variable (current-module) n))
- (current-module))
- (let ((old (module-variable (current-module) n)))
- ;; use value of the same-named imported variable, if
- ;; any
- (if (and (variable? old)
- (variable-bound? old)
- (not (macro? (variable-ref old))))
- (module-define! (current-module) n (variable-ref old))
- (module-add! (current-module) n (make-undefined-variable)))))
- (values
- (cons
- (if (eq? m 'c&e)
- (let ((x (build-global-definition s n (expand e r w mod))))
- (top-level-eval-hook x mod)
- x)
- (lambda ()
- (build-global-definition s n (expand e r w mod))))
- exps)))
- ((displaced-lexical)
- (syntax-violation #f "identifier out of context"
- (source-wrap form w s mod)
- (wrap value w mod)))
- (else
- (syntax-violation #f "cannot define keyword at top level"
- (source-wrap form w s mod)
- (wrap value w mod))))))
- (else
- (values (cons
- (if (eq? m 'c&e)
- (let ((x (expand-expr type value form e r w s mod)))
- (top-level-eval-hook x mod)
- x)
- (lambda ()
- (expand-expr type value form e r w s mod)))
- exps)))))))
- (lambda (exps)
- (scan (cdr body) r w s m esew mod exps))))))
-
- (call-with-values (lambda ()
- (scan body r w s m esew mod '()))
- (lambda (exps)
- (if (null? exps)
- (build-void s)
- (build-sequence
- s
- (let lp ((in exps) (out '()))
- (if (null? in) out
- (let ((e (car in)))
- (lp (cdr in)
- (cons (if (procedure? e) (e) e) out)))))))))))
-
- (define expand-install-global
- (lambda (name e)
- (build-global-definition
- no-source
- name
- (build-application
- no-source
- (build-primref no-source 'make-syntax-transformer)
- (list (build-data no-source name)
- (build-data no-source 'macro)
- e)))))
-
- (define parse-when-list
- (lambda (e when-list)
- ;; when-list is syntax'd version of list of situations
- (let ((result (strip when-list empty-wrap)))
- (let lp ((l result))
- (if (null? l)
- result
- (if (memq (car l) '(compile load eval expand))
- (lp (cdr l))
- (syntax-violation 'eval-when "invalid situation" e
- (car l))))))))
-
- ;; syntax-type returns seven values: type, value, form, e, w, s, and
- ;; mod. The first two are described in the table below.
- ;;
- ;; type value explanation
- ;; -------------------------------------------------------------------
- ;; core procedure core singleton
- ;; core-form procedure core form
- ;; module-ref procedure @ or @@ singleton
- ;; lexical name lexical variable reference
- ;; global name global variable reference
- ;; begin none begin keyword
- ;; define none define keyword
- ;; define-syntax none define-syntax keyword
- ;; define-syntax-parameter none define-syntax-parameter keyword
- ;; local-syntax rec? letrec-syntax/let-syntax keyword
- ;; eval-when none eval-when keyword
- ;; syntax level pattern variable
- ;; displaced-lexical none displaced lexical identifier
- ;; lexical-call name call to lexical variable
- ;; global-call name call to global variable
- ;; call none any other call
- ;; begin-form none begin expression
- ;; define-form id variable definition
- ;; define-syntax-form id syntax definition
- ;; define-syntax-parameter-form id syntax parameter definition
- ;; local-syntax-form rec? syntax definition
- ;; eval-when-form none eval-when form
- ;; constant none self-evaluating datum
- ;; other none anything else
- ;;
- ;; form is the entire form. For definition forms (define-form,
- ;; define-syntax-form, and define-syntax-parameter-form), e is the
- ;; rhs expression. For all others, e is the entire form. w is the
- ;; wrap for both form and e. s is the source for the entire form.
- ;; mod is the module for both form and e.
- ;;
- ;; syntax-type expands macros and unwraps as necessary to get to one
- ;; of the forms above. It also parses definition forms, although
- ;; perhaps this should be done by the consumer.
-
- (define syntax-type
- (lambda (e r w s rib mod for-car?)
- (cond
- ((symbol? e)
- (let* ((n (id-var-name e w))
- (b (lookup n r mod))
- (type (binding-type b)))
- (case type
- ((lexical) (values type (binding-value b) e e w s mod))
- ((global) (values type n e e w s mod))
- ((macro)
- (if for-car?
- (values type (binding-value b) e e w s mod)
- (syntax-type (expand-macro (binding-value b) e r w s rib mod)
- r empty-wrap s rib mod #f)))
- (else (values type (binding-value b) e e w s mod)))))
- ((pair? e)
- (let ((first (car e)))
- (call-with-values
- (lambda () (syntax-type first r w s rib mod #t))
- (lambda (ftype fval fform fe fw fs fmod)
- (case ftype
- ((lexical)
- (values 'lexical-call fval e e w s mod))
- ((global)
- ;; If we got here via an (@@ ...) expansion, we need to
- ;; make sure the fmod information is propagated back
- ;; correctly -- hence this consing.
- (values 'global-call (make-syntax-object fval w fmod)
- e e w s mod))
- ((macro)
- (syntax-type (expand-macro fval e r w s rib mod)
- r empty-wrap s rib mod for-car?))
- ((module-ref)
- (call-with-values (lambda () (fval e r w))
- (lambda (e r w s mod)
- (syntax-type e r w s rib mod for-car?))))
- ((core)
- (values 'core-form fval e e w s mod))
- ((local-syntax)
- (values 'local-syntax-form fval e e w s mod))
- ((begin)
- (values 'begin-form #f e e w s mod))
- ((eval-when)
- (values 'eval-when-form #f e e w s mod))
- ((define)
- (syntax-case e ()
- ((_ name val)
- (id? #'name)
- (values 'define-form #'name e #'val w s mod))
- ((_ (name . args) e1 e2 ...)
- (and (id? #'name)
- (valid-bound-ids? (lambda-var-list #'args)))
- ;; need lambda here...
- (values 'define-form (wrap #'name w mod)
- (wrap e w mod)
- (decorate-source
- (cons #'lambda (wrap #'(args e1 e2 ...) w mod))
- s)
- empty-wrap s mod))
- ((_ name)
- (id? #'name)
- (values 'define-form (wrap #'name w mod)
- (wrap e w mod)
- #'(if #f #f)
- empty-wrap s mod))))
- ((define-syntax)
- (syntax-case e ()
- ((_ name val)
- (id? #'name)
- (values 'define-syntax-form #'name e #'val w s mod))))
- ((define-syntax-parameter)
- (syntax-case e ()
- ((_ name val)
- (id? #'name)
- (values 'define-syntax-parameter-form #'name e #'val w s mod))))
- (else
- (values 'call #f e e w s mod)))))))
- ((syntax-object? e)
- (syntax-type (syntax-object-expression e)
- r
- (join-wraps w (syntax-object-wrap e))
- (or (source-annotation e) s) rib
- (or (syntax-object-module e) mod) for-car?))
- ((self-evaluating? e) (values 'constant #f e e w s mod))
- (else (values 'other #f e e w s mod)))))
-
- (define expand
- (lambda (e r w mod)
- (call-with-values
- (lambda () (syntax-type e r w (source-annotation e) #f mod #f))
- (lambda (type value form e w s mod)
- (expand-expr type value form e r w s mod)))))
-
- (define expand-expr
- (lambda (type value form e r w s mod)
- (case type
- ((lexical)
- (build-lexical-reference 'value s e value))
- ((core core-form)
- ;; apply transformer
- (value e r w s mod))
- ((module-ref)
- (call-with-values (lambda () (value e r w))
- (lambda (e r w s mod)
- (expand e r w mod))))
- ((lexical-call)
- (expand-application
- (let ((id (car e)))
- (build-lexical-reference 'fun (source-annotation id)
- (if (syntax-object? id)
- (syntax->datum id)
- id)
- value))
- e r w s mod))
- ((global-call)
- (expand-application
- (build-global-reference (source-annotation (car e))
- (if (syntax-object? value)
- (syntax-object-expression value)
- value)
- (if (syntax-object? value)
- (syntax-object-module value)
- mod))
- e r w s mod))
- ((constant) (build-data s (strip (source-wrap e w s mod) empty-wrap)))
- ((global) (build-global-reference s value mod))
- ((call) (expand-application (expand (car e) r w mod) e r w s mod))
- ((begin-form)
- (syntax-case e ()
- ((_ e1 e2 ...) (expand-sequence #'(e1 e2 ...) r w s mod))
- ((_)
- (if (include-deprecated-features)
- (begin
- (issue-deprecation-warning
- "Sequences of zero expressions are deprecated. Use *unspecified*.")
- (expand-void))
- (syntax-violation #f "sequence of zero expressions"
- (source-wrap e w s mod))))))
- ((local-syntax-form)
- (expand-local-syntax value e r w s mod expand-sequence))
- ((eval-when-form)
- (syntax-case e ()
- ((_ (x ...) e1 e2 ...)
- (let ((when-list (parse-when-list e #'(x ...))))
- (if (memq 'eval when-list)
- (expand-sequence #'(e1 e2 ...) r w s mod)
- (expand-void))))))
- ((define-form define-syntax-form define-syntax-parameter-form)
- (syntax-violation #f "definition in expression context, where definitions are not allowed,"
- (source-wrap form w s mod)))
- ((syntax)
- (syntax-violation #f "reference to pattern variable outside syntax form"
- (source-wrap e w s mod)))
- ((displaced-lexical)
- (syntax-violation #f "reference to identifier outside its scope"
- (source-wrap e w s mod)))
- (else (syntax-violation #f "unexpected syntax"
- (source-wrap e w s mod))))))
-
- (define expand-application
- (lambda (x e r w s mod)
- (syntax-case e ()
- ((e0 e1 ...)
- (build-application s x
- (map (lambda (e) (expand e r w mod)) #'(e1 ...)))))))
-
- ;; (What follows is my interpretation of what's going on here -- Andy)
- ;;
- ;; A macro takes an expression, a tree, the leaves of which are identifiers
- ;; and datums. Identifiers are symbols along with a wrap and a module. For
- ;; efficiency, subtrees that share wraps and modules may be grouped as one
- ;; syntax object.
- ;;
- ;; Going into the expansion, the expression is given an anti-mark, which
- ;; logically propagates to all leaves. Then, in the new expression returned
- ;; from the transfomer, if we see an expression with an anti-mark, we know it
- ;; pertains to the original expression; conversely, expressions without the
- ;; anti-mark are known to be introduced by the transformer.
- ;;
- ;; OK, good until now. We know this algorithm does lexical scoping
- ;; appropriately because it's widely known in the literature, and psyntax is
- ;; widely used. But what about modules? Here we're on our own. What we do is
- ;; to mark the module of expressions produced by a macro as pertaining to the
- ;; module that was current when the macro was defined -- that is, free
- ;; identifiers introduced by a macro are scoped in the macro's module, not in
- ;; the expansion's module. Seems to work well.
- ;;
- ;; The only wrinkle is when we want a macro to expand to code in another
- ;; module, as is the case for the r6rs `library' form -- the body expressions
- ;; should be scoped relative the new module, the one defined by the macro.
- ;; For that, use `(@@ mod-name body)'.
- ;;
- ;; Part of the macro output will be from the site of the macro use and part
- ;; from the macro definition. We allow source information from the macro use
- ;; to pass through, but we annotate the parts coming from the macro with the
- ;; source location information corresponding to the macro use. It would be
- ;; really nice if we could also annotate introduced expressions with the
- ;; locations corresponding to the macro definition, but that is not yet
- ;; possible.
- (define expand-macro
- (lambda (p e r w s rib mod)
- (define rebuild-macro-output
- (lambda (x m)
- (cond ((pair? x)
- (decorate-source
- (cons (rebuild-macro-output (car x) m)
- (rebuild-macro-output (cdr x) m))
- s))
- ((syntax-object? x)
- (let ((w (syntax-object-wrap x)))
- (let ((ms (wrap-marks w)) (ss (wrap-subst w)))
- (if (and (pair? ms) (eq? (car ms) the-anti-mark))
- ;; output is from original text
- (make-syntax-object
- (syntax-object-expression x)
- (make-wrap (cdr ms) (if rib (cons rib (cdr ss)) (cdr ss)))
- (syntax-object-module x))
- ;; output introduced by macro
- (make-syntax-object
- (decorate-source (syntax-object-expression x) s)
- (make-wrap (cons m ms)
- (if rib
- (cons rib (cons 'shift ss))
- (cons 'shift ss)))
- (syntax-object-module x))))))
-
- ((vector? x)
- (let* ((n (vector-length x))
- (v (decorate-source (make-vector n) s)))
- (do ((i 0 (fx+ i 1)))
- ((fx= i n) v)
- (vector-set! v i
- (rebuild-macro-output (vector-ref x i) m)))))
- ((symbol? x)
- (syntax-violation #f "encountered raw symbol in macro output"
- (source-wrap e w (wrap-subst w) mod) x))
- (else (decorate-source x s)))))
- (with-fluids ((transformer-environment
- (lambda (k) (k e r w s rib mod))))
- (rebuild-macro-output (p (source-wrap e (anti-mark w) s mod))
- (new-mark)))))
-
- (define expand-body
- ;; In processing the forms of the body, we create a new, empty wrap.
- ;; This wrap is augmented (destructively) each time we discover that
- ;; the next form is a definition. This is done:
- ;;
- ;; (1) to allow the first nondefinition form to be a call to
- ;; one of the defined ids even if the id previously denoted a
- ;; definition keyword or keyword for a macro expanding into a
- ;; definition;
- ;; (2) to prevent subsequent definition forms (but unfortunately
- ;; not earlier ones) and the first nondefinition form from
- ;; confusing one of the bound identifiers for an auxiliary
- ;; keyword; and
- ;; (3) so that we do not need to restart the expansion of the
- ;; first nondefinition form, which is problematic anyway
- ;; since it might be the first element of a begin that we
- ;; have just spliced into the body (meaning if we restarted,
- ;; we'd really need to restart with the begin or the macro
- ;; call that expanded into the begin, and we'd have to give
- ;; up allowing (begin <defn>+ <expr>+), which is itself
- ;; problematic since we don't know if a begin contains only
- ;; definitions until we've expanded it).
- ;;
- ;; Before processing the body, we also create a new environment
- ;; containing a placeholder for the bindings we will add later and
- ;; associate this environment with each form. In processing a
- ;; let-syntax or letrec-syntax, the associated environment may be
- ;; augmented with local keyword bindings, so the environment may
- ;; be different for different forms in the body. Once we have
- ;; gathered up all of the definitions, we evaluate the transformer
- ;; expressions and splice into r at the placeholder the new variable
- ;; and keyword bindings. This allows let-syntax or letrec-syntax
- ;; forms local to a portion or all of the body to shadow the
- ;; definition bindings.
- ;;
- ;; Subforms of a begin, let-syntax, or letrec-syntax are spliced
- ;; into the body.
- ;;
- ;; outer-form is fully wrapped w/source
- (lambda (body outer-form r w mod)
- (let* ((r (cons '("placeholder" . (placeholder)) r))
- (ribcage (make-empty-ribcage))
- (w (make-wrap (wrap-marks w) (cons ribcage (wrap-subst w)))))
- (let parse ((body (map (lambda (x) (cons r (wrap x w mod))) body))
- (ids '()) (labels '())
- (var-ids '()) (vars '()) (vals '()) (bindings '()))
- (if (null? body)
- (syntax-violation #f "no expressions in body" outer-form)
- (let ((e (cdar body)) (er (caar body)))
- (call-with-values
- (lambda () (syntax-type e er empty-wrap (source-annotation e) ribcage mod #f))
- (lambda (type value form e w s mod)
- (case type
- ((define-form)
- (let ((id (wrap value w mod)) (label (gen-label)))
- (let ((var (gen-var id)))
- (extend-ribcage! ribcage id label)
- (parse (cdr body)
- (cons id ids) (cons label labels)
- (cons id var-ids)
- (cons var vars) (cons (cons er (wrap e w mod)) vals)
- (cons (make-binding 'lexical var) bindings)))))
- ((define-syntax-form define-syntax-parameter-form)
- (let ((id (wrap value w mod))
- (label (gen-label))
- (trans-r (macros-only-env er)))
- (extend-ribcage! ribcage id label)
- ;; As required by R6RS, evaluate the right-hand-sides of internal
- ;; syntax definition forms and add their transformers to the
- ;; compile-time environment immediately, so that the newly-defined
- ;; keywords may be used in definition context within the same
- ;; lexical contour.
- (set-cdr! r (extend-env (list label)
- (list (make-binding 'macro
- (eval-local-transformer
- (expand e trans-r w mod)
- mod)))
- (cdr r)))
- (parse (cdr body) (cons id ids) labels var-ids vars vals bindings)))
- ((begin-form)
- (syntax-case e ()
- ((_ e1 ...)
- (parse (let f ((forms #'(e1 ...)))
- (if (null? forms)
- (cdr body)
- (cons (cons er (wrap (car forms) w mod))
- (f (cdr forms)))))
- ids labels var-ids vars vals bindings))))
- ((local-syntax-form)
- (expand-local-syntax value e er w s mod
- (lambda (forms er w s mod)
- (parse (let f ((forms forms))
- (if (null? forms)
- (cdr body)
- (cons (cons er (wrap (car forms) w mod))
- (f (cdr forms)))))
- ids labels var-ids vars vals bindings))))
- (else ; found a non-definition
- (if (null? ids)
- (build-sequence no-source
- (map (lambda (x)
- (expand (cdr x) (car x) empty-wrap mod))
- (cons (cons er (source-wrap e w s mod))
- (cdr body))))
- (begin
- (if (not (valid-bound-ids? ids))
- (syntax-violation
- #f "invalid or duplicate identifier in definition"
- outer-form))
- (set-cdr! r (extend-env labels bindings (cdr r)))
- (build-letrec no-source #t
- (reverse (map syntax->datum var-ids))
- (reverse vars)
- (map (lambda (x)
- (expand (cdr x) (car x) empty-wrap mod))
- (reverse vals))
- (build-sequence no-source
- (map (lambda (x)
- (expand (cdr x) (car x) empty-wrap mod))
- (cons (cons er (source-wrap e w s mod))
- (cdr body)))))))))))))))))
-
- (define expand-local-syntax
- (lambda (rec? e r w s mod k)
- (syntax-case e ()
- ((_ ((id val) ...) e1 e2 ...)
- (let ((ids #'(id ...)))
- (if (not (valid-bound-ids? ids))
- (syntax-violation #f "duplicate bound keyword" e)
- (let ((labels (gen-labels ids)))
- (let ((new-w (make-binding-wrap ids labels w)))
- (k #'(e1 e2 ...)
- (extend-env
- labels
- (let ((w (if rec? new-w w))
- (trans-r (macros-only-env r)))
- (map (lambda (x)
- (make-binding 'macro
- (eval-local-transformer
- (expand x trans-r w mod)
- mod)))
- #'(val ...)))
- r)
- new-w
- s
- mod))))))
- (_ (syntax-violation #f "bad local syntax definition"
- (source-wrap e w s mod))))))
-
- (define eval-local-transformer
- (lambda (expanded mod)
- (let ((p (local-eval-hook expanded mod)))
- (if (procedure? p)
- p
- (syntax-violation #f "nonprocedure transformer" p)))))
-
- (define expand-void
- (lambda ()
- (build-void no-source)))
-
- (define ellipsis?
- (lambda (e r mod)
- (and (nonsymbol-id? e)
- ;; If there is a binding for the special identifier
- ;; #{ $sc-ellipsis }# in the lexical environment of E,
- ;; and if the associated binding type is 'ellipsis',
- ;; then the binding's value specifies the custom ellipsis
- ;; identifier within that lexical environment, and the
- ;; comparison is done using 'bound-id=?'.
- (let* ((id (make-syntax-object '#{ $sc-ellipsis }
- (syntax-object-wrap e)
- (syntax-object-module e)))
- (n (id-var-name id empty-wrap))
- (b (lookup n r mod)))
- (if (eq? (binding-type b) 'ellipsis)
- (bound-id=? e (binding-value b))
- (free-id=? e #'(... ...)))))))
-
- (define lambda-formals
- (lambda (orig-args)
- (define (req args rreq)
- (syntax-case args ()
- (()
- (check (reverse rreq) #f))
- ((a . b) (id? #'a)
- (req #'b (cons #'a rreq)))
- (r (id? #'r)
- (check (reverse rreq) #'r))
- (else
- (syntax-violation 'lambda "invalid argument list" orig-args args))))
- (define (check req rest)
- (cond
- ((distinct-bound-ids? (if rest (cons rest req) req))
- (values req #f rest #f))
- (else
- (syntax-violation 'lambda "duplicate identifier in argument list"
- orig-args))))
- (req orig-args '())))
-
- (define expand-simple-lambda
- (lambda (e r w s mod req rest meta body)
- (let* ((ids (if rest (append req (list rest)) req))
- (vars (map gen-var ids))
- (labels (gen-labels ids)))
- (build-simple-lambda
- s
- (map syntax->datum req) (and rest (syntax->datum rest)) vars
- meta
- (expand-body body (source-wrap e w s mod)
- (extend-var-env labels vars r)
- (make-binding-wrap ids labels w)
- mod)))))
-
- (define lambda*-formals
- (lambda (orig-args)
- (define (req args rreq)
- (syntax-case args ()
- (()
- (check (reverse rreq) '() #f '()))
- ((a . b) (id? #'a)
- (req #'b (cons #'a rreq)))
- ((a . b) (eq? (syntax->datum #'a) #\optional)
- (opt #'b (reverse rreq) '()))
- ((a . b) (eq? (syntax->datum #'a) #\key)
- (key #'b (reverse rreq) '() '()))
- ((a b) (eq? (syntax->datum #'a) #\rest)
- (rest #'b (reverse rreq) '() '()))
- (r (id? #'r)
- (rest #'r (reverse rreq) '() '()))
- (else
- (syntax-violation 'lambda* "invalid argument list" orig-args args))))
- (define (opt args req ropt)
- (syntax-case args ()
- (()
- (check req (reverse ropt) #f '()))
- ((a . b) (id? #'a)
- (opt #'b req (cons #'(a #f) ropt)))
- (((a init) . b) (id? #'a)
- (opt #'b req (cons #'(a init) ropt)))
- ((a . b) (eq? (syntax->datum #'a) #\key)
- (key #'b req (reverse ropt) '()))
- ((a b) (eq? (syntax->datum #'a) #\rest)
- (rest #'b req (reverse ropt) '()))
- (r (id? #'r)
- (rest #'r req (reverse ropt) '()))
- (else
- (syntax-violation 'lambda* "invalid optional argument list"
- orig-args args))))
- (define (key args req opt rkey)
- (syntax-case args ()
- (()
- (check req opt #f (cons #f (reverse rkey))))
- ((a . b) (id? #'a)
- (with-syntax ((k (symbol->keyword (syntax->datum #'a))))
- (key #'b req opt (cons #'(k a #f) rkey))))
- (((a init) . b) (id? #'a)
- (with-syntax ((k (symbol->keyword (syntax->datum #'a))))
- (key #'b req opt (cons #'(k a init) rkey))))
- (((a init k) . b) (and (id? #'a)
- (keyword? (syntax->datum #'k)))
- (key #'b req opt (cons #'(k a init) rkey)))
- ((aok) (eq? (syntax->datum #'aok) #\allow-other-keys)
- (check req opt #f (cons #t (reverse rkey))))
- ((aok a b) (and (eq? (syntax->datum #'aok) #\allow-other-keys)
- (eq? (syntax->datum #'a) #\rest))
- (rest #'b req opt (cons #t (reverse rkey))))
- ((aok . r) (and (eq? (syntax->datum #'aok) #\allow-other-keys)
- (id? #'r))
- (rest #'r req opt (cons #t (reverse rkey))))
- ((a b) (eq? (syntax->datum #'a) #\rest)
- (rest #'b req opt (cons #f (reverse rkey))))
- (r (id? #'r)
- (rest #'r req opt (cons #f (reverse rkey))))
- (else
- (syntax-violation 'lambda* "invalid keyword argument list"
- orig-args args))))
- (define (rest args req opt kw)
- (syntax-case args ()
- (r (id? #'r)
- (check req opt #'r kw))
- (else
- (syntax-violation 'lambda* "invalid rest argument"
- orig-args args))))
- (define (check req opt rest kw)
- (cond
- ((distinct-bound-ids?
- (append req (map car opt) (if rest (list rest) '())
- (if (pair? kw) (map cadr (cdr kw)) '())))
- (values req opt rest kw))
- (else
- (syntax-violation 'lambda* "duplicate identifier in argument list"
- orig-args))))
- (req orig-args '())))
-
- (define expand-lambda-case
- (lambda (e r w s mod get-formals clauses)
- (define (parse-req req opt rest kw body)
- (let ((vars (map gen-var req))
- (labels (gen-labels req)))
- (let ((r* (extend-var-env labels vars r))
- (w* (make-binding-wrap req labels w)))
- (parse-opt (map syntax->datum req)
- opt rest kw body (reverse vars) r* w* '() '()))))
- (define (parse-opt req opt rest kw body vars r* w* out inits)
- (cond
- ((pair? opt)
- (syntax-case (car opt) ()
- ((id i)
- (let* ((v (gen-var #'id))
- (l (gen-labels (list v)))
- (r** (extend-var-env l (list v) r*))
- (w** (make-binding-wrap (list #'id) l w*)))
- (parse-opt req (cdr opt) rest kw body (cons v vars)
- r** w** (cons (syntax->datum #'id) out)
- (cons (expand #'i r* w* mod) inits))))))
- (rest
- (let* ((v (gen-var rest))
- (l (gen-labels (list v)))
- (r* (extend-var-env l (list v) r*))
- (w* (make-binding-wrap (list rest) l w*)))
- (parse-kw req (if (pair? out) (reverse out) #f)
- (syntax->datum rest)
- (if (pair? kw) (cdr kw) kw)
- body (cons v vars) r* w*
- (if (pair? kw) (car kw) #f)
- '() inits)))
- (else
- (parse-kw req (if (pair? out) (reverse out) #f) #f
- (if (pair? kw) (cdr kw) kw)
- body vars r* w*
- (if (pair? kw) (car kw) #f)
- '() inits))))
- (define (parse-kw req opt rest kw body vars r* w* aok out inits)
- (cond
- ((pair? kw)
- (syntax-case (car kw) ()
- ((k id i)
- (let* ((v (gen-var #'id))
- (l (gen-labels (list v)))
- (r** (extend-var-env l (list v) r*))
- (w** (make-binding-wrap (list #'id) l w*)))
- (parse-kw req opt rest (cdr kw) body (cons v vars)
- r** w** aok
- (cons (list (syntax->datum #'k)
- (syntax->datum #'id)
- v)
- out)
- (cons (expand #'i r* w* mod) inits))))))
- (else
- (parse-body req opt rest
- (if (or aok (pair? out)) (cons aok (reverse out)) #f)
- body (reverse vars) r* w* (reverse inits) '()))))
- (define (parse-body req opt rest kw body vars r* w* inits meta)
- (syntax-case body ()
- ((docstring e1 e2 ...) (string? (syntax->datum #'docstring))
- (parse-body req opt rest kw #'(e1 e2 ...) vars r* w* inits
- (append meta
- `((documentation
- . ,(syntax->datum #'docstring))))))
- ((#((k . v) ...) e1 e2 ...)
- (parse-body req opt rest kw #'(e1 e2 ...) vars r* w* inits
- (append meta (syntax->datum #'((k . v) ...)))))
- ((e1 e2 ...)
- (values meta req opt rest kw inits vars
- (expand-body #'(e1 e2 ...) (source-wrap e w s mod)
- r* w* mod)))))
-
- (syntax-case clauses ()
- (() (values '() #f))
- (((args e1 e2 ...) (args* e1* e2* ...) ...)
- (call-with-values (lambda () (get-formals #'args))
- (lambda (req opt rest kw)
- (call-with-values (lambda ()
- (parse-req req opt rest kw #'(e1 e2 ...)))
- (lambda (meta req opt rest kw inits vars body)
- (call-with-values
- (lambda ()
- (expand-lambda-case e r w s mod get-formals
- #'((args* e1* e2* ...) ...)))
- (lambda (meta* else*)
- (values
- (append meta meta*)
- (build-lambda-case s req opt rest kw inits vars
- body else*))))))))))))
-
- ;; data
-
- ;; strips syntax-objects down to top-wrap
- ;;
- ;; since only the head of a list is annotated by the reader, not each pair
- ;; in the spine, we also check for pairs whose cars are annotated in case
- ;; we've been passed the cdr of an annotated list
-
- (define strip
- (lambda (x w)
- (if (top-marked? w)
- x
- (let f ((x x))
- (cond
- ((syntax-object? x)
- (strip (syntax-object-expression x) (syntax-object-wrap x)))
- ((pair? x)
- (let ((a (f (car x))) (d (f (cdr x))))
- (if (and (eq? a (car x)) (eq? d (cdr x)))
- x
- (cons a d))))
- ((vector? x)
- (let ((old (vector->list x)))
- (let ((new (map f old)))
- ;; inlined and-map with two args
- (let lp ((l1 old) (l2 new))
- (if (null? l1)
- x
- (if (eq? (car l1) (car l2))
- (lp (cdr l1) (cdr l2))
- (list->vector new)))))))
- (else x))))))
-
- ;; lexical variables
-
- (define gen-var
- (lambda (id)
- (let ((id (if (syntax-object? id) (syntax-object-expression id) id)))
- (build-lexical-var no-source id))))
-
- ;; appears to return a reversed list
- (define lambda-var-list
- (lambda (vars)
- (let lvl ((vars vars) (ls '()) (w empty-wrap))
- (cond
- ((pair? vars) (lvl (cdr vars) (cons (wrap (car vars) w #f) ls) w))
- ((id? vars) (cons (wrap vars w #f) ls))
- ((null? vars) ls)
- ((syntax-object? vars)
- (lvl (syntax-object-expression vars)
- ls
- (join-wraps w (syntax-object-wrap vars))))
- ;; include anything else to be caught by subsequent error
- ;; checking
- (else (cons vars ls))))))
-
- ;; core transformers
-
- (global-extend 'local-syntax 'letrec-syntax #t)
- (global-extend 'local-syntax 'let-syntax #f)
-
- (global-extend 'core 'syntax-parameterize
- (lambda (e r w s mod)
- (syntax-case e ()
- ((_ ((var val) ...) e1 e2 ...)
- (valid-bound-ids? #'(var ...))
- (let ((names (map (lambda (x) (id-var-name x w)) #'(var ...))))
- (for-each
- (lambda (id n)
- (case (binding-type (lookup n r mod))
- ((displaced-lexical)
- (syntax-violation 'syntax-parameterize
- "identifier out of context"
- e
- (source-wrap id w s mod)))))
- #'(var ...)
- names)
- (expand-body
- #'(e1 e2 ...)
- (source-wrap e w s mod)
- (extend-env
- names
- (let ((trans-r (macros-only-env r)))
- (map (lambda (x)
- (make-binding 'macro
- (eval-local-transformer (expand x trans-r w mod)
- mod)))
- #'(val ...)))
- r)
- w
- mod)))
- (_ (syntax-violation 'syntax-parameterize "bad syntax"
- (source-wrap e w s mod))))))
-
- (global-extend 'core 'quote
- (lambda (e r w s mod)
- (syntax-case e ()
- ((_ e) (build-data s (strip #'e w)))
- (_ (syntax-violation 'quote "bad syntax"
- (source-wrap e w s mod))))))
-
- (global-extend 'core 'syntax
- (let ()
- (define gen-syntax
- (lambda (src e r maps ellipsis? mod)
- (if (id? e)
- (let ((label (id-var-name e empty-wrap)))
- ;; Mod does not matter, we are looking to see if
- ;; the id is lexical syntax.
- (let ((b (lookup label r mod)))
- (if (eq? (binding-type b) 'syntax)
- (call-with-values
- (lambda ()
- (let ((var.lev (binding-value b)))
- (gen-ref src (car var.lev) (cdr var.lev) maps)))
- (lambda (var maps) (values `(ref ,var) maps)))
- (if (ellipsis? e r mod)
- (syntax-violation 'syntax "misplaced ellipsis" src)
- (values `(quote ,e) maps)))))
- (syntax-case e ()
- ((dots e)
- (ellipsis? #'dots r mod)
- (gen-syntax src #'e r maps (lambda (e r mod) #f) mod))
- ((x dots . y)
- ;; this could be about a dozen lines of code, except that we
- ;; choose to handle #'(x ... ...) forms
- (ellipsis? #'dots r mod)
- (let f ((y #'y)
- (k (lambda (maps)
- (call-with-values
- (lambda ()
- (gen-syntax src #'x r
- (cons '() maps) ellipsis? mod))
- (lambda (x maps)
- (if (null? (car maps))
- (syntax-violation 'syntax "extra ellipsis"
- src)
- (values (gen-map x (car maps))
- (cdr maps))))))))
- (syntax-case y ()
- ((dots . y)
- (ellipsis? #'dots r mod)
- (f #'y
- (lambda (maps)
- (call-with-values
- (lambda () (k (cons '() maps)))
- (lambda (x maps)
- (if (null? (car maps))
- (syntax-violation 'syntax "extra ellipsis" src)
- (values (gen-mappend x (car maps))
- (cdr maps))))))))
- (_ (call-with-values
- (lambda () (gen-syntax src y r maps ellipsis? mod))
- (lambda (y maps)
- (call-with-values
- (lambda () (k maps))
- (lambda (x maps)
- (values (gen-append x y) maps)))))))))
- ((x . y)
- (call-with-values
- (lambda () (gen-syntax src #'x r maps ellipsis? mod))
- (lambda (x maps)
- (call-with-values
- (lambda () (gen-syntax src #'y r maps ellipsis? mod))
- (lambda (y maps) (values (gen-cons x y) maps))))))
- (#(e1 e2 ...)
- (call-with-values
- (lambda ()
- (gen-syntax src #'(e1 e2 ...) r maps ellipsis? mod))
- (lambda (e maps) (values (gen-vector e) maps))))
- (_ (values `(quote ,e) maps))))))
-
- (define gen-ref
- (lambda (src var level maps)
- (if (fx= level 0)
- (values var maps)
- (if (null? maps)
- (syntax-violation 'syntax "missing ellipsis" src)
- (call-with-values
- (lambda () (gen-ref src var (fx- level 1) (cdr maps)))
- (lambda (outer-var outer-maps)
- (let ((b (assq outer-var (car maps))))
- (if b
- (values (cdr b) maps)
- (let ((inner-var (gen-var 'tmp)))
- (values inner-var
- (cons (cons (cons outer-var inner-var)
- (car maps))
- outer-maps)))))))))))
-
- (define gen-mappend
- (lambda (e map-env)
- `(apply (primitive append) ,(gen-map e map-env))))
-
- (define gen-map
- (lambda (e map-env)
- (let ((formals (map cdr map-env))
- (actuals (map (lambda (x) `(ref ,(car x))) map-env)))
- (cond
- ((eq? (car e) 'ref)
- ;; identity map equivalence:
- ;; (map (lambda (x) x) y) == y
- (car actuals))
- ((and-map
- (lambda (x) (and (eq? (car x) 'ref) (memq (cadr x) formals)))
- (cdr e))
- ;; eta map equivalence:
- ;; (map (lambda (x ...) (f x ...)) y ...) == (map f y ...)
- `(map (primitive ,(car e))
- ,@(map (let ((r (map cons formals actuals)))
- (lambda (x) (cdr (assq (cadr x) r))))
- (cdr e))))
- (else `(map (lambda ,formals ,e) ,@actuals))))))
-
- (define gen-cons
- (lambda (x y)
- (case (car y)
- ((quote)
- (if (eq? (car x) 'quote)
- `(quote (,(cadr x) . ,(cadr y)))
- (if (eq? (cadr y) '())
- `(list ,x)
- `(cons ,x ,y))))
- ((list) `(list ,x ,@(cdr y)))
- (else `(cons ,x ,y)))))
-
- (define gen-append
- (lambda (x y)
- (if (equal? y '(quote ()))
- x
- `(append ,x ,y))))
-
- (define gen-vector
- (lambda (x)
- (cond
- ((eq? (car x) 'list) `(vector ,@(cdr x)))
- ((eq? (car x) 'quote) `(quote #(,@(cadr x))))
- (else `(list->vector ,x)))))
-
-
- (define regen
- (lambda (x)
- (case (car x)
- ((ref) (build-lexical-reference 'value no-source (cadr x) (cadr x)))
- ((primitive) (build-primref no-source (cadr x)))
- ((quote) (build-data no-source (cadr x)))
- ((lambda)
- (if (list? (cadr x))
- (build-simple-lambda no-source (cadr x) #f (cadr x) '() (regen (caddr x)))
- (error "how did we get here" x)))
- (else (build-application no-source
- (build-primref no-source (car x))
- (map regen (cdr x)))))))
-
- (lambda (e r w s mod)
- (let ((e (source-wrap e w s mod)))
- (syntax-case e ()
- ((_ x)
- (call-with-values
- (lambda () (gen-syntax e #'x r '() ellipsis? mod))
- (lambda (e maps) (regen e))))
- (_ (syntax-violation 'syntax "bad `syntax' form" e)))))))
-
- (global-extend 'core 'lambda
- (lambda (e r w s mod)
- (syntax-case e ()
- ((_ args e1 e2 ...)
- (call-with-values (lambda () (lambda-formals #'args))
- (lambda (req opt rest kw)
- (let lp ((body #'(e1 e2 ...)) (meta '()))
- (syntax-case body ()
- ((docstring e1 e2 ...) (string? (syntax->datum #'docstring))
- (lp #'(e1 e2 ...)
- (append meta
- `((documentation
- . ,(syntax->datum #'docstring))))))
- ((#((k . v) ...) e1 e2 ...)
- (lp #'(e1 e2 ...)
- (append meta (syntax->datum #'((k . v) ...)))))
- (_ (expand-simple-lambda e r w s mod req rest meta body)))))))
- (_ (syntax-violation 'lambda "bad lambda" e)))))
-
- (global-extend 'core 'lambda*
- (lambda (e r w s mod)
- (syntax-case e ()
- ((_ args e1 e2 ...)
- (call-with-values
- (lambda ()
- (expand-lambda-case e r w s mod
- lambda*-formals #'((args e1 e2 ...))))
- (lambda (meta lcase)
- (build-case-lambda s meta lcase))))
- (_ (syntax-violation 'lambda "bad lambda*" e)))))
-
- (global-extend 'core 'case-lambda
- (lambda (e r w s mod)
- (define (build-it meta clauses)
- (call-with-values
- (lambda ()
- (expand-lambda-case e r w s mod
- lambda-formals
- clauses))
- (lambda (meta* lcase)
- (build-case-lambda s (append meta meta*) lcase))))
- (syntax-case e ()
- ((_ (args e1 e2 ...) ...)
- (build-it '() #'((args e1 e2 ...) ...)))
- ((_ docstring (args e1 e2 ...) ...)
- (string? (syntax->datum #'docstring))
- (build-it `((documentation
- . ,(syntax->datum #'docstring)))
- #'((args e1 e2 ...) ...)))
- (_ (syntax-violation 'case-lambda "bad case-lambda" e)))))
-
- (global-extend 'core 'case-lambda*
- (lambda (e r w s mod)
- (define (build-it meta clauses)
- (call-with-values
- (lambda ()
- (expand-lambda-case e r w s mod
- lambda*-formals
- clauses))
- (lambda (meta* lcase)
- (build-case-lambda s (append meta meta*) lcase))))
- (syntax-case e ()
- ((_ (args e1 e2 ...) ...)
- (build-it '() #'((args e1 e2 ...) ...)))
- ((_ docstring (args e1 e2 ...) ...)
- (string? (syntax->datum #'docstring))
- (build-it `((documentation
- . ,(syntax->datum #'docstring)))
- #'((args e1 e2 ...) ...)))
- (_ (syntax-violation 'case-lambda "bad case-lambda*" e)))))
-
- (global-extend 'core 'with-ellipsis
- (lambda (e r w s mod)
- (syntax-case e ()
- ((_ dots e1 e2 ...)
- (id? #'dots)
- (let ((id (if (symbol? #'dots)
- '#{ $sc-ellipsis }
- (make-syntax-object '#{ $sc-ellipsis }
- (syntax-object-wrap #'dots)
- (syntax-object-module #'dots)))))
- (let ((ids (list id))
- (labels (list (gen-label)))
- (bindings (list (make-binding 'ellipsis (source-wrap #'dots w s mod)))))
- (let ((nw (make-binding-wrap ids labels w))
- (nr (extend-env labels bindings r)))
- (expand-body #'(e1 e2 ...) (source-wrap e nw s mod) nr nw mod)))))
- (_ (syntax-violation 'with-ellipsis "bad syntax"
- (source-wrap e w s mod))))))
-
- (global-extend 'core 'let
- (let ()
- (define (expand-let e r w s mod constructor ids vals exps)
- (if (not (valid-bound-ids? ids))
- (syntax-violation 'let "duplicate bound variable" e)
- (let ((labels (gen-labels ids))
- (new-vars (map gen-var ids)))
- (let ((nw (make-binding-wrap ids labels w))
- (nr (extend-var-env labels new-vars r)))
- (constructor s
- (map syntax->datum ids)
- new-vars
- (map (lambda (x) (expand x r w mod)) vals)
- (expand-body exps (source-wrap e nw s mod)
- nr nw mod))))))
- (lambda (e r w s mod)
- (syntax-case e ()
- ((_ ((id val) ...) e1 e2 ...)
- (and-map id? #'(id ...))
- (expand-let e r w s mod
- build-let
- #'(id ...)
- #'(val ...)
- #'(e1 e2 ...)))
- ((_ f ((id val) ...) e1 e2 ...)
- (and (id? #'f) (and-map id? #'(id ...)))
- (expand-let e r w s mod
- build-named-let
- #'(f id ...)
- #'(val ...)
- #'(e1 e2 ...)))
- (_ (syntax-violation 'let "bad let" (source-wrap e w s mod)))))))
-
-
- (global-extend 'core 'letrec
- (lambda (e r w s mod)
- (syntax-case e ()
- ((_ ((id val) ...) e1 e2 ...)
- (and-map id? #'(id ...))
- (let ((ids #'(id ...)))
- (if (not (valid-bound-ids? ids))
- (syntax-violation 'letrec "duplicate bound variable" e)
- (let ((labels (gen-labels ids))
- (new-vars (map gen-var ids)))
- (let ((w (make-binding-wrap ids labels w))
- (r (extend-var-env labels new-vars r)))
- (build-letrec s #f
- (map syntax->datum ids)
- new-vars
- (map (lambda (x) (expand x r w mod)) #'(val ...))
- (expand-body #'(e1 e2 ...)
- (source-wrap e w s mod) r w mod)))))))
- (_ (syntax-violation 'letrec "bad letrec" (source-wrap e w s mod))))))
-
-
- (global-extend 'core 'letrec*
- (lambda (e r w s mod)
- (syntax-case e ()
- ((_ ((id val) ...) e1 e2 ...)
- (and-map id? #'(id ...))
- (let ((ids #'(id ...)))
- (if (not (valid-bound-ids? ids))
- (syntax-violation 'letrec* "duplicate bound variable" e)
- (let ((labels (gen-labels ids))
- (new-vars (map gen-var ids)))
- (let ((w (make-binding-wrap ids labels w))
- (r (extend-var-env labels new-vars r)))
- (build-letrec s #t
- (map syntax->datum ids)
- new-vars
- (map (lambda (x) (expand x r w mod)) #'(val ...))
- (expand-body #'(e1 e2 ...)
- (source-wrap e w s mod) r w mod)))))))
- (_ (syntax-violation 'letrec* "bad letrec*" (source-wrap e w s mod))))))
-
-
- (global-extend 'core 'set!
- (lambda (e r w s mod)
- (syntax-case e ()
- ((_ id val)
- (id? #'id)
- (let ((n (id-var-name #'id w))
- ;; Lookup id in its module
- (id-mod (if (syntax-object? #'id)
- (syntax-object-module #'id)
- mod)))
- (let ((b (lookup n r id-mod)))
- (case (binding-type b)
- ((lexical)
- (build-lexical-assignment s
- (syntax->datum #'id)
- (binding-value b)
- (expand #'val r w mod)))
- ((global)
- (build-global-assignment s n (expand #'val r w mod) id-mod))
- ((macro)
- (let ((p (binding-value b)))
- (if (procedure-property p 'variable-transformer)
- ;; As syntax-type does, call expand-macro with
- ;; the mod of the expression. Hmm.
- (expand (expand-macro p e r w s #f mod) r empty-wrap mod)
- (syntax-violation 'set! "not a variable transformer"
- (wrap e w mod)
- (wrap #'id w id-mod)))))
- ((displaced-lexical)
- (syntax-violation 'set! "identifier out of context"
- (wrap #'id w mod)))
- (else (syntax-violation 'set! "bad set!"
- (source-wrap e w s mod)))))))
- ((_ (head tail ...) val)
- (call-with-values
- (lambda () (syntax-type #'head r empty-wrap no-source #f mod #t))
- (lambda (type value formform ee ww ss modmod)
- (case type
- ((module-ref)
- (let ((val (expand #'val r w mod)))
- (call-with-values (lambda () (value #'(head tail ...) r w))
- (lambda (e r w s* mod)
- (syntax-case e ()
- (e (id? #'e)
- (build-global-assignment s (syntax->datum #'e)
- val mod)))))))
- (else
- (build-application s
- (expand #'(setter head) r w mod)
- (map (lambda (e) (expand e r w mod))
- #'(tail ... val))))))))
- (_ (syntax-violation 'set! "bad set!" (source-wrap e w s mod))))))
-
- (global-extend 'module-ref '@
- (lambda (e r w)
- (syntax-case e ()
- ((_ (mod ...) id)
- (and (and-map id? #'(mod ...)) (id? #'id))
- ;; Strip the wrap from the identifier and return top-wrap
- ;; so that the identifier will not be captured by lexicals.
- (values (syntax->datum #'id) r top-wrap #f
- (syntax->datum
- #'(public mod ...)))))))
-
- (global-extend 'module-ref '@@
- (lambda (e r w)
- (define remodulate
- (lambda (x mod)
- (cond ((pair? x)
- (cons (remodulate (car x) mod)
- (remodulate (cdr x) mod)))
- ((syntax-object? x)
- (make-syntax-object
- (remodulate (syntax-object-expression x) mod)
- (syntax-object-wrap x)
- ;; hither the remodulation
- mod))
- ((vector? x)
- (let* ((n (vector-length x)) (v (make-vector n)))
- (do ((i 0 (fx+ i 1)))
- ((fx= i n) v)
- (vector-set! v i (remodulate (vector-ref x i) mod)))))
- (else x))))
- (syntax-case e (@@)
- ((_ (mod ...) id)
- (and (and-map id? #'(mod ...)) (id? #'id))
- ;; Strip the wrap from the identifier and return top-wrap
- ;; so that the identifier will not be captured by lexicals.
- (values (syntax->datum #'id) r top-wrap #f
- (syntax->datum
- #'(private mod ...))))
- ((_ @@ (mod ...) exp)
- (and-map id? #'(mod ...))
- ;; This is a special syntax used to support R6RS library forms.
- ;; Unlike the syntax above, the last item is not restricted to
- ;; be a single identifier, and the syntax objects are kept
- ;; intact, with only their module changed.
- (let ((mod (syntax->datum #'(private mod ...))))
- (values (remodulate #'exp mod)
- r w (source-annotation #'exp)
- mod))))))
-
- (global-extend 'core 'if
- (lambda (e r w s mod)
- (syntax-case e ()
- ((_ test then)
- (build-conditional
- s
- (expand #'test r w mod)
- (expand #'then r w mod)
- (build-void no-source)))
- ((_ test then else)
- (build-conditional
- s
- (expand #'test r w mod)
- (expand #'then r w mod)
- (expand #'else r w mod))))))
-
- (global-extend 'core 'with-fluids
- (lambda (e r w s mod)
- (syntax-case e ()
- ((_ ((fluid val) ...) b b* ...)
- (build-dynlet
- s
- (map (lambda (x) (expand x r w mod)) #'(fluid ...))
- (map (lambda (x) (expand x r w mod)) #'(val ...))
- (expand-body #'(b b* ...)
- (source-wrap e w s mod) r w mod))))))
-
- (global-extend 'begin 'begin '())
-
- (global-extend 'define 'define '())
-
- (global-extend 'define-syntax 'define-syntax '())
- (global-extend 'define-syntax-parameter 'define-syntax-parameter '())
-
- (global-extend 'eval-when 'eval-when '())
-
- (global-extend 'core 'syntax-case
- (let ()
- (define convert-pattern
- ;; accepts pattern & keys
- ;; returns $sc-dispatch pattern & ids
- (lambda (pattern keys ellipsis?)
- (define cvt*
- (lambda (p* n ids)
- (syntax-case p* ()
- ((x . y)
- (call-with-values
- (lambda () (cvt* #'y n ids))
- (lambda (y ids)
- (call-with-values
- (lambda () (cvt #'x n ids))
- (lambda (x ids)
- (values (cons x y) ids))))))
- (_ (cvt p* n ids)))))
-
- (define (v-reverse x)
- (let loop ((r '()) (x x))
- (if (not (pair? x))
- (values r x)
- (loop (cons (car x) r) (cdr x)))))
-
- (define cvt
- (lambda (p n ids)
- (if (id? p)
- (cond
- ((bound-id-member? p keys)
- (values (vector 'free-id p) ids))
- ((free-id=? p #'_)
- (values '_ ids))
- (else
- (values 'any (cons (cons p n) ids))))
- (syntax-case p ()
- ((x dots)
- (ellipsis? (syntax dots))
- (call-with-values
- (lambda () (cvt (syntax x) (fx+ n 1) ids))
- (lambda (p ids)
- (values (if (eq? p 'any) 'each-any (vector 'each p))
- ids))))
- ((x dots . ys)
- (ellipsis? (syntax dots))
- (call-with-values
- (lambda () (cvt* (syntax ys) n ids))
- (lambda (ys ids)
- (call-with-values
- (lambda () (cvt (syntax x) (+ n 1) ids))
- (lambda (x ids)
- (call-with-values
- (lambda () (v-reverse ys))
- (lambda (ys e)
- (values `#(each+ ,x ,ys ,e)
- ids))))))))
- ((x . y)
- (call-with-values
- (lambda () (cvt (syntax y) n ids))
- (lambda (y ids)
- (call-with-values
- (lambda () (cvt (syntax x) n ids))
- (lambda (x ids)
- (values (cons x y) ids))))))
- (() (values '() ids))
- (#(x ...)
- (call-with-values
- (lambda () (cvt (syntax (x ...)) n ids))
- (lambda (p ids) (values (vector 'vector p) ids))))
- (x (values (vector 'atom (strip p empty-wrap)) ids))))))
- (cvt pattern 0 '())))
-
- (define build-dispatch-call
- (lambda (pvars exp y r mod)
- (let ((ids (map car pvars)) (levels (map cdr pvars)))
- (let ((labels (gen-labels ids)) (new-vars (map gen-var ids)))
- (build-application no-source
- (build-primref no-source 'apply)
- (list (build-simple-lambda no-source (map syntax->datum ids) #f new-vars '()
- (expand exp
- (extend-env
- labels
- (map (lambda (var level)
- (make-binding 'syntax `(,var . ,level)))
- new-vars
- (map cdr pvars))
- r)
- (make-binding-wrap ids labels empty-wrap)
- mod))
- y))))))
-
- (define gen-clause
- (lambda (x keys clauses r pat fender exp mod)
- (call-with-values
- (lambda () (convert-pattern pat keys (lambda (e) (ellipsis? e r mod))))
- (lambda (p pvars)
- (cond
- ((not (and-map (lambda (x) (not (ellipsis? (car x) r mod))) pvars))
- (syntax-violation 'syntax-case "misplaced ellipsis" pat))
- ((not (distinct-bound-ids? (map car pvars)))
- (syntax-violation 'syntax-case "duplicate pattern variable" pat))
- (else
- (let ((y (gen-var 'tmp)))
- ;; fat finger binding and references to temp variable y
- (build-application no-source
- (build-simple-lambda no-source (list 'tmp) #f (list y) '()
- (let ((y (build-lexical-reference 'value no-source
- 'tmp y)))
- (build-conditional no-source
- (syntax-case fender ()
- (#t y)
- (_ (build-conditional no-source
- y
- (build-dispatch-call pvars fender y r mod)
- (build-data no-source #f))))
- (build-dispatch-call pvars exp y r mod)
- (gen-syntax-case x keys clauses r mod))))
- (list (if (eq? p 'any)
- (build-application no-source
- (build-primref no-source 'list)
- (list x))
- (build-application no-source
- (build-primref no-source '$sc-dispatch)
- (list x (build-data no-source p)))))))))))))
-
- (define gen-syntax-case
- (lambda (x keys clauses r mod)
- (if (null? clauses)
- (build-application no-source
- (build-primref no-source 'syntax-violation)
- (list (build-data no-source #f)
- (build-data no-source
- "source expression failed to match any pattern")
- x))
- (syntax-case (car clauses) ()
- ((pat exp)
- (if (and (id? #'pat)
- (and-map (lambda (x) (not (free-id=? #'pat x)))
- (cons #'(... ...) keys)))
- (if (free-id=? #'pat #'_)
- (expand #'exp r empty-wrap mod)
- (let ((labels (list (gen-label)))
- (var (gen-var #'pat)))
- (build-application no-source
- (build-simple-lambda
- no-source (list (syntax->datum #'pat)) #f (list var)
- '()
- (expand #'exp
- (extend-env labels
- (list (make-binding 'syntax `(,var . 0)))
- r)
- (make-binding-wrap #'(pat)
- labels empty-wrap)
- mod))
- (list x))))
- (gen-clause x keys (cdr clauses) r
- #'pat #t #'exp mod)))
- ((pat fender exp)
- (gen-clause x keys (cdr clauses) r
- #'pat #'fender #'exp mod))
- (_ (syntax-violation 'syntax-case "invalid clause"
- (car clauses)))))))
-
- (lambda (e r w s mod)
- (let ((e (source-wrap e w s mod)))
- (syntax-case e ()
- ((_ val (key ...) m ...)
- (if (and-map (lambda (x) (and (id? x) (not (ellipsis? x r mod))))
- #'(key ...))
- (let ((x (gen-var 'tmp)))
- ;; fat finger binding and references to temp variable x
- (build-application s
- (build-simple-lambda no-source (list 'tmp) #f (list x) '()
- (gen-syntax-case (build-lexical-reference 'value no-source
- 'tmp x)
- #'(key ...) #'(m ...)
- r
- mod))
- (list (expand #'val r empty-wrap mod))))
- (syntax-violation 'syntax-case "invalid literals list" e))))))))
-
- ;; The portable macroexpand seeds expand-top's mode m with 'e (for
- ;; evaluating) and esew (which stands for "eval syntax expanders
- ;; when") with '(eval). In Chez Scheme, m is set to 'c instead of e
- ;; if we are compiling a file, and esew is set to
- ;; (eval-syntactic-expanders-when), which defaults to the list
- ;; '(compile load eval). This means that, by default, top-level
- ;; syntactic definitions are evaluated immediately after they are
- ;; expanded, and the expanded definitions are also residualized into
- ;; the object file if we are compiling a file.
- (set! macroexpand
- (lambda* (x #\optional (m 'e) (esew '(eval)))
- (expand-top-sequence (list x) null-env top-wrap #f m esew
- (cons 'hygiene (module-name (current-module))))))
-
- (set! identifier?
- (lambda (x)
- (nonsymbol-id? x)))
-
- (set! datum->syntax
- (lambda (id datum)
- (make-syntax-object datum (syntax-object-wrap id)
- (syntax-object-module id))))
-
- (set! syntax->datum
- ;; accepts any object, since syntax objects may consist partially
- ;; or entirely of unwrapped, nonsymbolic data
- (lambda (x)
- (strip x empty-wrap)))
-
- (set! syntax-source
- (lambda (x) (source-annotation x)))
-
- (set! generate-temporaries
- (lambda (ls)
- (arg-check list? ls 'generate-temporaries)
- (let ((mod (cons 'hygiene (module-name (current-module)))))
- (map (lambda (x)
- (wrap (module-gensym "t") top-wrap mod))
- ls))))
-
- (set! free-identifier=?
- (lambda (x y)
- (arg-check nonsymbol-id? x 'free-identifier=?)
- (arg-check nonsymbol-id? y 'free-identifier=?)
- (free-id=? x y)))
-
- (set! bound-identifier=?
- (lambda (x y)
- (arg-check nonsymbol-id? x 'bound-identifier=?)
- (arg-check nonsymbol-id? y 'bound-identifier=?)
- (bound-id=? x y)))
-
- (set! syntax-violation
- (lambda* (who message form #\optional subform)
- (arg-check (lambda (x) (or (not x) (string? x) (symbol? x)))
- who 'syntax-violation)
- (arg-check string? message 'syntax-violation)
- (throw 'syntax-error who message
- (or (source-annotation subform)
- (source-annotation form))
- (strip form empty-wrap)
- (and subform (strip subform empty-wrap)))))
-
- (let ()
- (define (syntax-module id)
- (arg-check nonsymbol-id? id 'syntax-module)
- (cdr (syntax-object-module id)))
-
- (define (syntax-local-binding id)
- (arg-check nonsymbol-id? id 'syntax-local-binding)
- (with-transformer-environment
- (lambda (e r w s rib mod)
- (define (strip-anti-mark w)
- (let ((ms (wrap-marks w)) (s (wrap-subst w)))
- (if (and (pair? ms) (eq? (car ms) the-anti-mark))
- ;; output is from original text
- (make-wrap (cdr ms) (if rib (cons rib (cdr s)) (cdr s)))
- ;; output introduced by macro
- (make-wrap ms (if rib (cons rib s) s)))))
- (call-with-values (lambda ()
- (resolve-identifier
- (syntax-object-expression id)
- (strip-anti-mark (syntax-object-wrap id))
- r
- (syntax-object-module id)))
- (lambda (type value mod)
- (case type
- ((lexical) (values 'lexical value))
- ((macro) (values 'macro value))
- ((syntax) (values 'pattern-variable value))
- ((displaced-lexical) (values 'displaced-lexical #f))
- ((global) (values 'global (cons value (cdr mod))))
- ((ellipsis)
- (values 'ellipsis
- (make-syntax-object (syntax-object-expression value)
- (anti-mark (syntax-object-wrap value))
- (syntax-object-module value))))
- (else (values 'other #f))))))))
-
- (define (syntax-locally-bound-identifiers id)
- (arg-check nonsymbol-id? id 'syntax-locally-bound-identifiers)
- (locally-bound-identifiers (syntax-object-wrap id)
- (syntax-object-module id)))
-
- ;; Using define! instead of set! to avoid warnings at
- ;; compile-time, after the variables are stolen away into (system
- ;; syntax). See the end of boot-9.scm.
- ;;
- (define! 'syntax-module syntax-module)
- (define! 'syntax-local-binding syntax-local-binding)
- (define! 'syntax-locally-bound-identifiers syntax-locally-bound-identifiers))
-
- ;; $sc-dispatch expects an expression and a pattern. If the expression
- ;; matches the pattern a list of the matching expressions for each
- ;; "any" is returned. Otherwise, #f is returned. (This use of #f will
- ;; not work on r4rs implementations that violate the ieee requirement
- ;; that #f and () be distinct.)
-
- ;; The expression is matched with the pattern as follows:
-
- ;; pattern: matches:
- ;; () empty list
- ;; any anything
- ;; (<pattern>1 . <pattern>2) (<pattern>1 . <pattern>2)
- ;; each-any (any*)
- ;; #(free-id <key>) <key> with free-identifier=?
- ;; #(each <pattern>) (<pattern>*)
- ;; #(each+ p1 (p2_1 ... p2_n) p3) (p1* (p2_n ... p2_1) . p3)
- ;; #(vector <pattern>) (list->vector <pattern>)
- ;; #(atom <object>) <object> with "equal?"
-
- ;; Vector cops out to pair under assumption that vectors are rare. If
- ;; not, should convert to:
- ;; #(vector <pattern>*) #(<pattern>*)
-
- (let ()
-
- (define match-each
- (lambda (e p w mod)
- (cond
- ((pair? e)
- (let ((first (match (car e) p w '() mod)))
- (and first
- (let ((rest (match-each (cdr e) p w mod)))
- (and rest (cons first rest))))))
- ((null? e) '())
- ((syntax-object? e)
- (match-each (syntax-object-expression e)
- p
- (join-wraps w (syntax-object-wrap e))
- (syntax-object-module e)))
- (else #f))))
-
- (define match-each+
- (lambda (e x-pat y-pat z-pat w r mod)
- (let f ((e e) (w w))
- (cond
- ((pair? e)
- (call-with-values (lambda () (f (cdr e) w))
- (lambda (xr* y-pat r)
- (if r
- (if (null? y-pat)
- (let ((xr (match (car e) x-pat w '() mod)))
- (if xr
- (values (cons xr xr*) y-pat r)
- (values #f #f #f)))
- (values
- '()
- (cdr y-pat)
- (match (car e) (car y-pat) w r mod)))
- (values #f #f #f)))))
- ((syntax-object? e)
- (f (syntax-object-expression e)
- (join-wraps w (syntax-object-wrap e))))
- (else
- (values '() y-pat (match e z-pat w r mod)))))))
-
- (define match-each-any
- (lambda (e w mod)
- (cond
- ((pair? e)
- (let ((l (match-each-any (cdr e) w mod)))
- (and l (cons (wrap (car e) w mod) l))))
- ((null? e) '())
- ((syntax-object? e)
- (match-each-any (syntax-object-expression e)
- (join-wraps w (syntax-object-wrap e))
- mod))
- (else #f))))
-
- (define match-empty
- (lambda (p r)
- (cond
- ((null? p) r)
- ((eq? p '_) r)
- ((eq? p 'any) (cons '() r))
- ((pair? p) (match-empty (car p) (match-empty (cdr p) r)))
- ((eq? p 'each-any) (cons '() r))
- (else
- (case (vector-ref p 0)
- ((each) (match-empty (vector-ref p 1) r))
- ((each+) (match-empty (vector-ref p 1)
- (match-empty
- (reverse (vector-ref p 2))
- (match-empty (vector-ref p 3) r))))
- ((free-id atom) r)
- ((vector) (match-empty (vector-ref p 1) r)))))))
-
- (define combine
- (lambda (r* r)
- (if (null? (car r*))
- r
- (cons (map car r*) (combine (map cdr r*) r)))))
-
- (define match*
- (lambda (e p w r mod)
- (cond
- ((null? p) (and (null? e) r))
- ((pair? p)
- (and (pair? e) (match (car e) (car p) w
- (match (cdr e) (cdr p) w r mod)
- mod)))
- ((eq? p 'each-any)
- (let ((l (match-each-any e w mod))) (and l (cons l r))))
- (else
- (case (vector-ref p 0)
- ((each)
- (if (null? e)
- (match-empty (vector-ref p 1) r)
- (let ((l (match-each e (vector-ref p 1) w mod)))
- (and l
- (let collect ((l l))
- (if (null? (car l))
- r
- (cons (map car l) (collect (map cdr l)))))))))
- ((each+)
- (call-with-values
- (lambda ()
- (match-each+ e (vector-ref p 1) (vector-ref p 2) (vector-ref p 3) w r mod))
- (lambda (xr* y-pat r)
- (and r
- (null? y-pat)
- (if (null? xr*)
- (match-empty (vector-ref p 1) r)
- (combine xr* r))))))
- ((free-id) (and (id? e) (free-id=? (wrap e w mod) (vector-ref p 1)) r))
- ((atom) (and (equal? (vector-ref p 1) (strip e w)) r))
- ((vector)
- (and (vector? e)
- (match (vector->list e) (vector-ref p 1) w r mod))))))))
-
- (define match
- (lambda (e p w r mod)
- (cond
- ((not r) #f)
- ((eq? p '_) r)
- ((eq? p 'any) (cons (wrap e w mod) r))
- ((syntax-object? e)
- (match*
- (syntax-object-expression e)
- p
- (join-wraps w (syntax-object-wrap e))
- r
- (syntax-object-module e)))
- (else (match* e p w r mod)))))
-
- (set! $sc-dispatch
- (lambda (e p)
- (cond
- ((eq? p 'any) (list e))
- ((eq? p '_) '())
- ((syntax-object? e)
- (match* (syntax-object-expression e)
- p (syntax-object-wrap e) '() (syntax-object-module e)))
- (else (match* e p empty-wrap '() #f))))))))
-
-
-(define-syntax with-syntax
- (lambda (x)
- (syntax-case x ()
- ((_ () e1 e2 ...)
- #'(let () e1 e2 ...))
- ((_ ((out in)) e1 e2 ...)
- #'(syntax-case in ()
- (out (let () e1 e2 ...))))
- ((_ ((out in) ...) e1 e2 ...)
- #'(syntax-case (list in ...) ()
- ((out ...) (let () e1 e2 ...)))))))
-
-(define-syntax syntax-error
- (lambda (x)
- (syntax-case x ()
- ;; Extended internal syntax which provides the original form
- ;; as the first operand, for improved error reporting.
- ((_ (keyword . operands) message arg ...)
- (string? (syntax->datum #'message))
- (syntax-violation (syntax->datum #'keyword)
- (string-join (cons (syntax->datum #'message)
- (map (lambda (x)
- (object->string
- (syntax->datum x)))
- #'(arg ...))))
- (and (syntax->datum #'keyword)
- #'(keyword . operands))))
- ;; Standard R7RS syntax
- ((_ message arg ...)
- (string? (syntax->datum #'message))
- #'(syntax-error (#f) message arg ...)))))
-
-(define-syntax syntax-rules
- (lambda (xx)
- (define (expand-clause clause)
- ;; Convert a 'syntax-rules' clause into a 'syntax-case' clause.
- (syntax-case clause (syntax-error)
- ;; If the template is a 'syntax-error' form, use the extended
- ;; internal syntax, which adds the original form as the first
- ;; operand for improved error reporting.
- (((keyword . pattern) (syntax-error message arg ...))
- (string? (syntax->datum #'message))
- #'((dummy . pattern) #'(syntax-error (dummy . pattern) message arg ...)))
- ;; Normal case
- (((keyword . pattern) template)
- #'((dummy . pattern) #'template))))
- (define (expand-syntax-rules dots keys docstrings clauses)
- (with-syntax
- (((k ...) keys)
- ((docstring ...) docstrings)
- ((((keyword . pattern) template) ...) clauses)
- ((clause ...) (map expand-clause clauses)))
- (with-syntax
- ((form #'(lambda (x)
- docstring ... ; optional docstring
- #((macro-type . syntax-rules)
- (patterns pattern ...)) ; embed patterns as procedure metadata
- (syntax-case x (k ...)
- clause ...))))
- (if dots
- (with-syntax ((dots dots))
- #'(with-ellipsis dots form))
- #'form))))
- (syntax-case xx ()
- ((_ (k ...) ((keyword . pattern) template) ...)
- (expand-syntax-rules #f #'(k ...) #'() #'(((keyword . pattern) template) ...)))
- ((_ (k ...) docstring ((keyword . pattern) template) ...)
- (string? (syntax->datum #'docstring))
- (expand-syntax-rules #f #'(k ...) #'(docstring) #'(((keyword . pattern) template) ...)))
- ((_ dots (k ...) ((keyword . pattern) template) ...)
- (identifier? #'dots)
- (expand-syntax-rules #'dots #'(k ...) #'() #'(((keyword . pattern) template) ...)))
- ((_ dots (k ...) docstring ((keyword . pattern) template) ...)
- (and (identifier? #'dots) (string? (syntax->datum #'docstring)))
- (expand-syntax-rules #'dots #'(k ...) #'(docstring) #'(((keyword . pattern) template) ...))))))
-
-(define-syntax define-syntax-rule
- (lambda (x)
- (syntax-case x ()
- ((_ (name . pattern) template)
- #'(define-syntax name
- (syntax-rules ()
- ((_ . pattern) template))))
- ((_ (name . pattern) docstring template)
- (string? (syntax->datum #'docstring))
- #'(define-syntax name
- (syntax-rules ()
- docstring
- ((_ . pattern) template)))))))
-
-(define-syntax let*
- (lambda (x)
- (syntax-case x ()
- ((let* ((x v) ...) e1 e2 ...)
- (and-map identifier? #'(x ...))
- (let f ((bindings #'((x v) ...)))
- (if (null? bindings)
- #'(let () e1 e2 ...)
- (with-syntax ((body (f (cdr bindings)))
- (binding (car bindings)))
- #'(let (binding) body))))))))
-
-(define-syntax quasiquote
- (let ()
- (define (quasi p lev)
- (syntax-case p (unquote quasiquote)
- ((unquote p)
- (if (= lev 0)
- #'("value" p)
- (quasicons #'("quote" unquote) (quasi #'(p) (- lev 1)))))
- ((quasiquote p) (quasicons #'("quote" quasiquote) (quasi #'(p) (+ lev 1))))
- ((p . q)
- (syntax-case #'p (unquote unquote-splicing)
- ((unquote p ...)
- (if (= lev 0)
- (quasilist* #'(("value" p) ...) (quasi #'q lev))
- (quasicons
- (quasicons #'("quote" unquote) (quasi #'(p ...) (- lev 1)))
- (quasi #'q lev))))
- ((unquote-splicing p ...)
- (if (= lev 0)
- (quasiappend #'(("value" p) ...) (quasi #'q lev))
- (quasicons
- (quasicons #'("quote" unquote-splicing) (quasi #'(p ...) (- lev 1)))
- (quasi #'q lev))))
- (_ (quasicons (quasi #'p lev) (quasi #'q lev)))))
- (#(x ...) (quasivector (vquasi #'(x ...) lev)))
- (p #'("quote" p))))
- (define (vquasi p lev)
- (syntax-case p ()
- ((p . q)
- (syntax-case #'p (unquote unquote-splicing)
- ((unquote p ...)
- (if (= lev 0)
- (quasilist* #'(("value" p) ...) (vquasi #'q lev))
- (quasicons
- (quasicons #'("quote" unquote) (quasi #'(p ...) (- lev 1)))
- (vquasi #'q lev))))
- ((unquote-splicing p ...)
- (if (= lev 0)
- (quasiappend #'(("value" p) ...) (vquasi #'q lev))
- (quasicons
- (quasicons
- #'("quote" unquote-splicing)
- (quasi #'(p ...) (- lev 1)))
- (vquasi #'q lev))))
- (_ (quasicons (quasi #'p lev) (vquasi #'q lev)))))
- (() #'("quote" ()))))
- (define (quasicons x y)
- (with-syntax ((x x) (y y))
- (syntax-case #'y ()
- (("quote" dy)
- (syntax-case #'x ()
- (("quote" dx) #'("quote" (dx . dy)))
- (_ (if (null? #'dy) #'("list" x) #'("list*" x y)))))
- (("list" . stuff) #'("list" x . stuff))
- (("list*" . stuff) #'("list*" x . stuff))
- (_ #'("list*" x y)))))
- (define (quasiappend x y)
- (syntax-case y ()
- (("quote" ())
- (cond
- ((null? x) #'("quote" ()))
- ((null? (cdr x)) (car x))
- (else (with-syntax (((p ...) x)) #'("append" p ...)))))
- (_
- (cond
- ((null? x) y)
- (else (with-syntax (((p ...) x) (y y)) #'("append" p ... y)))))))
- (define (quasilist* x y)
- (let f ((x x))
- (if (null? x)
- y
- (quasicons (car x) (f (cdr x))))))
- (define (quasivector x)
- (syntax-case x ()
- (("quote" (x ...)) #'("quote" #(x ...)))
- (_
- (let f ((y x) (k (lambda (ls) #`("vector" #,@ls))))
- (syntax-case y ()
- (("quote" (y ...)) (k #'(("quote" y) ...)))
- (("list" y ...) (k #'(y ...)))
- (("list*" y ... z) (f #'z (lambda (ls) (k (append #'(y ...) ls)))))
- (else #`("list->vector" #,x)))))))
- (define (emit x)
- (syntax-case x ()
- (("quote" x) #''x)
- (("list" x ...) #`(list #,@(map emit #'(x ...))))
- ;; could emit list* for 3+ arguments if implementation supports
- ;; list*
- (("list*" x ... y)
- (let f ((x* #'(x ...)))
- (if (null? x*)
- (emit #'y)
- #`(cons #,(emit (car x*)) #,(f (cdr x*))))))
- (("append" x ...) #`(append #,@(map emit #'(x ...))))
- (("vector" x ...) #`(vector #,@(map emit #'(x ...))))
- (("list->vector" x) #`(list->vector #,(emit #'x)))
- (("value" x) #'x)))
- (lambda (x)
- (syntax-case x ()
- ;; convert to intermediate language, combining introduced (but
- ;; not unquoted source) quote expressions where possible and
- ;; choosing optimal construction code otherwise, then emit
- ;; Scheme code corresponding to the intermediate language forms.
- ((_ e) (emit (quasi #'e 0)))))))
-
-(define-syntax include
- (lambda (x)
- (define read-file
- (lambda (fn dir k)
- (let* ((p (open-input-file
- (cond ((absolute-file-name? fn)
- fn)
- (dir
- (in-vicinity dir fn))
- (else
- (syntax-violation
- 'include
- "relative file name only allowed when the include form is in a file"
- x)))))
- (enc (file-encoding p)))
-
- ;; Choose the input encoding deterministically.
- (set-port-encoding! p (or enc "UTF-8"))
-
- (let f ((x (read p))
- (result '()))
- (if (eof-object? x)
- (begin
- (close-input-port p)
- (reverse result))
- (f (read p)
- (cons (datum->syntax k x) result)))))))
- (let* ((src (syntax-source x))
- (file (and src (assq-ref src 'filename)))
- (dir (and (string? file) (dirname file))))
- (syntax-case x ()
- ((k filename)
- (let ((fn (syntax->datum #'filename)))
- (with-syntax (((exp ...) (read-file fn dir #'filename)))
- #'(begin exp ...))))))))
-
-(define-syntax include-from-path
- (lambda (x)
- (syntax-case x ()
- ((k filename)
- (let ((fn (syntax->datum #'filename)))
- (with-syntax ((fn (datum->syntax
- #'filename
- (or (%search-load-path fn)
- (syntax-violation 'include-from-path
- "file not found in path"
- x #'filename)))))
- #'(include fn)))))))
-
-(define-syntax unquote
- (lambda (x)
- (syntax-violation 'unquote
- "expression not valid outside of quasiquote"
- x)))
-
-(define-syntax unquote-splicing
- (lambda (x)
- (syntax-violation 'unquote-splicing
- "expression not valid outside of quasiquote"
- x)))
-
-(define (make-variable-transformer proc)
- (if (procedure? proc)
- (let ((trans (lambda (x)
- #((macro-type . variable-transformer))
- (proc x))))
- (set-procedure-property! trans 'variable-transformer #t)
- trans)
- (error "variable transformer not a procedure" proc)))
-
-(define-syntax identifier-syntax
- (lambda (xx)
- (syntax-case xx (set!)
- ((_ e)
- #'(lambda (x)
- #((macro-type . identifier-syntax))
- (syntax-case x ()
- (id
- (identifier? #'id)
- #'e)
- ((_ x (... ...))
- #'(e x (... ...))))))
- ((_ (id exp1) ((set! var val) exp2))
- (and (identifier? #'id) (identifier? #'var))
- #'(make-variable-transformer
- (lambda (x)
- #((macro-type . variable-transformer))
- (syntax-case x (set!)
- ((set! var val) #'exp2)
- ((id x (... ...)) #'(exp1 x (... ...)))
- (id (identifier? #'id) #'exp1))))))))
-
-(define-syntax define*
- (lambda (x)
- (syntax-case x ()
- ((_ (id . args) b0 b1 ...)
- #'(define id (lambda* args b0 b1 ...)))
- ((_ id val) (identifier? #'id)
- #'(define id val)))))
-;;;; q.scm --- Queues
-;;;;
-;;;; Copyright (C) 1995, 2001, 2004, 2006 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary:
-
-;;; Q: Based on the interface to
-;;;
-;;; "queue.scm" Queues/Stacks for Scheme
-;;; Written by Andrew Wilcox (awilcox@astro.psu.edu) on April 1, 1992.
-
-;;; {Q}
-;;;
-;;; A list is just a bunch of cons pairs that follows some constrains,
-;;; right? Association lists are the same. Hash tables are just
-;;; vectors and association lists. You can print them, read them,
-;;; write them as constants, pun them off as other data structures
-;;; etc. This is good. This is lisp. These structures are fast and
-;;; compact and easy to manipulate arbitrarily because of their
-;;; simple, regular structure and non-disjointedness (associations
-;;; being lists and so forth).
-;;;
-;;; So I figured, queues should be the same -- just a "subtype" of cons-pair
-;;; structures in general.
-;;;
-;;; A queue is a cons pair:
-;;; ( <the-q> . <last-pair> )
-;;;
-;;; <the-q> is a list of things in the q. New elements go at the end
-;;; of that list.
-;;;
-;;; <last-pair> is #f if the q is empty, and otherwise is the last
-;;; pair of <the-q>.
-;;;
-;;; q's print nicely, but alas, they do not read well because the
-;;; eq?-ness of <last-pair> and (last-pair <the-q>) is lost by read.
-;;;
-;;; All the functions that aren't explicitly defined to return
-;;; something else (a queue element; a boolean value) return the queue
-;;; object itself.
-
-;;; Code:
-
-(define-module (ice-9 q)
- \:export (sync-q! make-q q? q-empty? q-empty-check q-front q-rear
- q-remove! q-push! enq! q-pop! deq! q-length))
-
-;;; sync-q!
-;;; The procedure
-;;;
-;;; (sync-q! q)
-;;;
-;;; recomputes and resets the <last-pair> component of a queue.
-;;;
-(define (sync-q! q)
- (set-cdr! q (if (pair? (car q)) (last-pair (car q))
- #f))
- q)
-
-;;; make-q
-;;; return a new q.
-;;;
-(define (make-q) (cons '() #f))
-
-;;; q? obj
-;;; Return true if obj is a Q.
-;;; An object is a queue if it is equal? to '(() . #f)
-;;; or it is a pair P with (list? (car P))
-;;; and (eq? (cdr P) (last-pair (car P))).
-;;;
-(define (q? obj)
- (and (pair? obj)
- (if (pair? (car obj))
- (eq? (cdr obj) (last-pair (car obj)))
- (and (null? (car obj))
- (not (cdr obj))))))
-
-;;; q-empty? obj
-;;;
-(define (q-empty? obj) (null? (car obj)))
-
-;;; q-empty-check q
-;;; Throw a q-empty exception if Q is empty.
-(define (q-empty-check q) (if (q-empty? q) (throw 'q-empty q)))
-
-;;; q-front q
-;;; Return the first element of Q.
-(define (q-front q) (q-empty-check q) (caar q))
-
-;;; q-rear q
-;;; Return the last element of Q.
-(define (q-rear q) (q-empty-check q) (cadr q))
-
-;;; q-remove! q obj
-;;; Remove all occurences of obj from Q.
-(define (q-remove! q obj)
- (set-car! q (delq! obj (car q)))
- (sync-q! q))
-
-;;; q-push! q obj
-;;; Add obj to the front of Q
-(define (q-push! q obj)
- (let ((h (cons obj (car q))))
- (set-car! q h)
- (or (cdr q) (set-cdr! q h)))
- q)
-
-;;; enq! q obj
-;;; Add obj to the rear of Q
-(define (enq! q obj)
- (let ((h (cons obj '())))
- (if (null? (car q))
- (set-car! q h)
- (set-cdr! (cdr q) h))
- (set-cdr! q h))
- q)
-
-;;; q-pop! q
-;;; Take the front of Q and return it.
-(define (q-pop! q)
- (q-empty-check q)
- (let ((it (caar q))
- (next (cdar q)))
- (if (null? next)
- (set-cdr! q #f))
- (set-car! q next)
- it))
-
-;;; deq! q
-;;; Take the front of Q and return it.
-(define deq! q-pop!)
-
-;;; q-length q
-;;; Return the number of enqueued elements.
-;;;
-(define (q-length q) (length (car q)))
-
-;;; q.scm ends here
-;; Quasisyntax in terms of syntax-case.
-;;
-;; Code taken from
-;; <http://www.het.brown.edu/people/andre/macros/index.html>;
-;; Copyright (c) 2006 Andre van Tonder. All Rights Reserved.
-;;
-;; Permission is hereby granted, free of charge, to any person
-;; obtaining a copy of this software and associated documentation
-;; files (the "Software"), to deal in the Software without
-;; restriction, including without limitation the rights to use, copy,
-;; modify, merge, publish, distribute, sublicense, and/or sell copies
-;; of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-;;
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-;;
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-;;=========================================================
-;;
-;; To make nested unquote-splicing behave in a useful way,
-;; the R5RS-compatible extension of quasiquote in appendix B
-;; of the following paper is here ported to quasisyntax:
-;;
-;; Alan Bawden - Quasiquotation in Lisp
-;; http://citeseer.ist.psu.edu/bawden99quasiquotation.html
-;;
-;; The algorithm converts a quasisyntax expression to an
-;; equivalent with-syntax expression.
-;; For example:
-;;
-;; (quasisyntax (set! #,a #,b))
-;; ==> (with-syntax ((t0 a)
-;; (t1 b))
-;; (syntax (set! t0 t1)))
-;;
-;; (quasisyntax (list #,@args))
-;; ==> (with-syntax (((t ...) args))
-;; (syntax (list t ...)))
-;;
-;; Note that quasisyntax is expanded first, before any
-;; ellipses act. For example:
-;;
-;; (quasisyntax (f ((b #,a) ...))
-;; ==> (with-syntax ((t a))
-;; (syntax (f ((b t) ...))))
-;;
-;; so that
-;;
-;; (let-syntax ((test-ellipses-over-unsyntax
-;; (lambda (e)
-;; (let ((a (syntax a)))
-;; (with-syntax (((b ...) (syntax (1 2 3))))
-;; (quasisyntax
-;; (quote ((b #,a) ...))))))))
-;; (test-ellipses-over-unsyntax))
-;;
-;; ==> ((1 a) (2 a) (3 a))
-(define-syntax quasisyntax
- (lambda (e)
-
- ;; Expand returns a list of the form
- ;; [template[t/e, ...] (replacement ...)]
- ;; Here template[t/e ...] denotes the original template
- ;; with unquoted expressions e replaced by fresh
- ;; variables t, followed by the appropriate ellipses
- ;; if e is also spliced.
- ;; The second part of the return value is the list of
- ;; replacements, each of the form (t e) if e is just
- ;; unquoted, or ((t ...) e) if e is also spliced.
- ;; This will be the list of bindings of the resulting
- ;; with-syntax expression.
-
- (define (expand x level)
- (syntax-case x (quasisyntax unsyntax unsyntax-splicing)
- ((quasisyntax e)
- (with-syntax (((k _) x) ;; original identifier must be copied
- ((e* reps) (expand (syntax e) (+ level 1))))
- (syntax ((k e*) reps))))
- ((unsyntax e)
- (= level 0)
- (with-syntax (((t) (generate-temporaries '(t))))
- (syntax (t ((t e))))))
- (((unsyntax e ...) . r)
- (= level 0)
- (with-syntax (((r* (rep ...)) (expand (syntax r) 0))
- ((t ...) (generate-temporaries (syntax (e ...)))))
- (syntax ((t ... . r*)
- ((t e) ... rep ...)))))
- (((unsyntax-splicing e ...) . r)
- (= level 0)
- (with-syntax (((r* (rep ...)) (expand (syntax r) 0))
- ((t ...) (generate-temporaries (syntax (e ...)))))
- (with-syntax ((((t ...) ...) (syntax ((t (... ...)) ...))))
- (syntax ((t ... ... . r*)
- (((t ...) e) ... rep ...))))))
- ((k . r)
- (and (> level 0)
- (identifier? (syntax k))
- (or (free-identifier=? (syntax k) (syntax unsyntax))
- (free-identifier=? (syntax k) (syntax unsyntax-splicing))))
- (with-syntax (((r* reps) (expand (syntax r) (- level 1))))
- (syntax ((k . r*) reps))))
- ((h . t)
- (with-syntax (((h* (rep1 ...)) (expand (syntax h) level))
- ((t* (rep2 ...)) (expand (syntax t) level)))
- (syntax ((h* . t*)
- (rep1 ... rep2 ...)))))
- (#(e ...)
- (with-syntax ((((e* ...) reps)
- (expand (vector->list (syntax #(e ...))) level)))
- (syntax (#(e* ...) reps))))
- (other
- (syntax (other ())))))
-
- (syntax-case e ()
- ((_ template)
- (with-syntax (((template* replacements) (expand (syntax template) 0)))
- (syntax
- (with-syntax replacements (syntax template*))))))))
-
-(define-syntax unsyntax
- (lambda (e)
- (syntax-violation 'unsyntax "Invalid expression" e)))
-
-(define-syntax unsyntax-splicing
- (lambda (e)
- (syntax-violation 'unsyntax "Invalid expression" e)))
-;;;; r4rs.scm --- definitions needed for libguile to be R4RS compliant
-;;;; Jim Blandy <jimb@cyclic.com> --- October 1996
-
-;;;; Copyright (C) 1996, 1997, 1998, 2000, 2001, 2006, 2010, 2011, 2012 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(eval-when (compile)
- (set-current-module (resolve-module '(guile))))
-
-
-;;;; apply and call-with-current-continuation
-
-;;; The deal with these is that they are the procedural wrappers around the
-;;; primitives of Guile's language. There are about 20 different kinds of
-;;; expression in Guile, and e.g. @apply is one of them. (It has to be that way
-;;; to preserve tail recursion.)
-;;;
-;;; Usually we recognize (apply foo bar) to be an instance of @apply, but in the
-;;; case that apply is passed to apply, or we're bootstrapping, we need a
-;;; trampoline -- and here they are.
-(define (apply fun . args)
- (@apply fun (apply:nconc2last args)))
-(define (call-with-current-continuation proc)
- (@call-with-current-continuation proc))
-(define (call-with-values producer consumer)
- (@call-with-values producer consumer))
-(define (dynamic-wind in thunk out)
- "All three arguments must be 0-argument procedures.
-Guard @var{in} is called, then @var{thunk}, then
-guard @var{out}.
-
-If, any time during the execution of @var{thunk}, the
-continuation of the @code{dynamic_wind} expression is escaped
-non-locally, @var{out} is called. If the continuation of
-the dynamic-wind is re-entered, @var{in} is called. Thus
-@var{in} and @var{out} may be called any number of
-times.
-@lisp
- (define x 'normal-binding)
-@result{} x
- (define a-cont
- (call-with-current-continuation
- (lambda (escape)
- (let ((old-x x))
- (dynamic-wind
- ;; in-guard:
- ;;
- (lambda () (set! x 'special-binding))
-
- ;; thunk
- ;;
- (lambda () (display x) (newline)
- (call-with-current-continuation escape)
- (display x) (newline)
- x)
-
- ;; out-guard:
- ;;
- (lambda () (set! x old-x)))))))
-
-;; Prints:
-special-binding
-;; Evaluates to:
-@result{} a-cont
-x
-@result{} normal-binding
- (a-cont #f)
-;; Prints:
-special-binding
-;; Evaluates to:
-@result{} a-cont ;; the value of the (define a-cont...)
-x
-@result{} normal-binding
-a-cont
-@result{} special-binding
-@end lisp"
- (@dynamic-wind in (thunk) out))
-
-
-;;;; Basic Port Code
-
-;;; Specifically, the parts of the low-level port code that are written in
-;;; Scheme rather than C.
-;;;
-;;; WARNING: the parts of this interface that refer to file ports
-;;; are going away. It would be gone already except that it is used
-;;; "internally" in a few places.
-
-
-;;; OPEN_READ, OPEN_WRITE, and OPEN_BOTH are used to request the
-;;; proper mode to open files in.
-;;;
-;;; If we want to support systems that do CRLF->LF translation, like
-;;; Windows, then we should have a symbol in scmconfig.h made visible
-;;; to the Scheme level that we can test here, and autoconf magic to
-;;; #define it when appropriate. Windows will probably just have a
-;;; hand-generated scmconfig.h file.
-(define OPEN_READ "r")
-(define OPEN_WRITE "w")
-(define OPEN_BOTH "r+")
-
-(define *null-device* "/dev/null")
-
-(define (open-input-file str)
- "Takes a string naming an existing file and returns an input port
-capable of delivering characters from the file. If the file
-cannot be opened, an error is signalled."
- (open-file str OPEN_READ))
-
-(define (open-output-file str)
- "Takes a string naming an output file to be created and returns an
-output port capable of writing characters to a new file by that
-name. If the file cannot be opened, an error is signalled. If a
-file with the given name already exists, the effect is unspecified."
- (open-file str OPEN_WRITE))
-
-(define (open-io-file str)
- "Open file with name STR for both input and output."
- (open-file str OPEN_BOTH))
-
-(define (call-with-input-file str proc)
- "PROC should be a procedure of one argument, and STR should be a
-string naming a file. The file must
-already exist. These procedures call PROC
-with one argument: the port obtained by opening the named file for
-input or output. If the file cannot be opened, an error is
-signalled. If the procedure returns, then the port is closed
-automatically and the values yielded by the procedure are returned.
-If the procedure does not return, then the port will not be closed
-automatically unless it is possible to prove that the port will
-never again be used for a read or write operation."
- (let ((p (open-input-file str)))
- (call-with-values
- (lambda () (proc p))
- (lambda vals
- (close-input-port p)
- (apply values vals)))))
-
-(define (call-with-output-file str proc)
- "PROC should be a procedure of one argument, and STR should be a
-string naming a file. The behaviour is unspecified if the file
-already exists. These procedures call PROC
-with one argument: the port obtained by opening the named file for
-input or output. If the file cannot be opened, an error is
-signalled. If the procedure returns, then the port is closed
-automatically and the values yielded by the procedure are returned.
-If the procedure does not return, then the port will not be closed
-automatically unless it is possible to prove that the port will
-never again be used for a read or write operation."
- (let ((p (open-output-file str)))
- (call-with-values
- (lambda () (proc p))
- (lambda vals
- (close-output-port p)
- (apply values vals)))))
-
-(define (with-input-from-port port thunk)
- (let* ((swaports (lambda () (set! port (set-current-input-port port)))))
- (dynamic-wind swaports thunk swaports)))
-
-(define (with-output-to-port port thunk)
- (let* ((swaports (lambda () (set! port (set-current-output-port port)))))
- (dynamic-wind swaports thunk swaports)))
-
-(define (with-error-to-port port thunk)
- (let* ((swaports (lambda () (set! port (set-current-error-port port)))))
- (dynamic-wind swaports thunk swaports)))
-
-(define (with-input-from-file file thunk)
- "THUNK must be a procedure of no arguments, and FILE must be a
-string naming a file. The file must already exist. The file is opened for
-input, an input port connected to it is made
-the default value returned by `current-input-port',
-and the THUNK is called with no arguments.
-When the THUNK returns, the port is closed and the previous
-default is restored. Returns the values yielded by THUNK. If an
-escape procedure is used to escape from the continuation of these
-procedures, their behavior is implementation dependent."
- (call-with-input-file file
- (lambda (p) (with-input-from-port p thunk))))
-
-(define (with-output-to-file file thunk)
- "THUNK must be a procedure of no arguments, and FILE must be a
-string naming a file. The effect is unspecified if the file already exists.
-The file is opened for output, an output port connected to it is made
-the default value returned by `current-output-port',
-and the THUNK is called with no arguments.
-When the THUNK returns, the port is closed and the previous
-default is restored. Returns the values yielded by THUNK. If an
-escape procedure is used to escape from the continuation of these
-procedures, their behavior is implementation dependent."
- (call-with-output-file file
- (lambda (p) (with-output-to-port p thunk))))
-
-(define (with-error-to-file file thunk)
- "THUNK must be a procedure of no arguments, and FILE must be a
-string naming a file. The effect is unspecified if the file already exists.
-The file is opened for output, an output port connected to it is made
-the default value returned by `current-error-port',
-and the THUNK is called with no arguments.
-When the THUNK returns, the port is closed and the previous
-default is restored. Returns the values yielded by THUNK. If an
-escape procedure is used to escape from the continuation of these
-procedures, their behavior is implementation dependent."
- (call-with-output-file file
- (lambda (p) (with-error-to-port p thunk))))
-
-(define (with-input-from-string string thunk)
- "THUNK must be a procedure of no arguments.
-The test of STRING is opened for
-input, an input port connected to it is made,
-and the THUNK is called with no arguments.
-When the THUNK returns, the port is closed.
-Returns the values yielded by THUNK. If an
-escape procedure is used to escape from the continuation of these
-procedures, their behavior is implementation dependent."
- (call-with-input-string string
- (lambda (p) (with-input-from-port p thunk))))
-
-(define (with-output-to-string thunk)
- "Calls THUNK and returns its output as a string."
- (call-with-output-string
- (lambda (p) (with-output-to-port p thunk))))
-
-(define (with-error-to-string thunk)
- "Calls THUNK and returns its error output as a string."
- (call-with-output-string
- (lambda (p) (with-error-to-port p thunk))))
-
-(define the-eof-object (call-with-input-string "" (lambda (p) (read-char p))))
-;;;; Copyright (C) 2000, 2001, 2006, 2010 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;;; R5RS bindings
-
-(define-module (ice-9 r5rs)
- \:export (scheme-report-environment
- ;;transcript-on
- ;;transcript-off
- )
- \:re-export (interaction-environment
-
- call-with-input-file call-with-output-file
- with-input-from-file with-output-to-file
- open-input-file open-output-file
- close-input-port close-output-port
-
- load))
-
-(module-use! (module-public-interface (current-module))
- (resolve-interface '(ice-9 safe-r5rs)))
-
-(define scheme-report-interface (module-public-interface (current-module)))
-
-(define (scheme-report-environment n)
- (if (not (= n 5))
- (scm-error 'misc-error 'scheme-report-environment
- "~A is not a valid version"
- (list n)
- '()))
- scheme-report-interface)
-;;; r6rs-libraries.scm --- Support for the R6RS `library' and `import' forms
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-;; This file is included from boot-9.scm and assumes the existence of (and
-;; expands into) procedures and syntactic forms defined therein.
-
-(define (resolve-r6rs-interface import-spec)
- (define (make-custom-interface mod)
- (let ((iface (make-module)))
- (set-module-kind! iface 'custom-interface)
- (set-module-name! iface (module-name mod))
- iface))
- (define (sym? x) (symbol? (syntax->datum x)))
-
- (syntax-case import-spec (library only except prefix rename srfi)
- ;; (srfi n ...) -> (srfi srfi-n ...)
- ((library (srfi colon-n rest ... (version ...)))
- (and (and-map sym? #'(srfi rest ...))
- (symbol? (syntax->datum #'colon-n))
- (eqv? (string-ref (symbol->string (syntax->datum #'colon-n)) 0) #\:))
- (let ((srfi-n (string->symbol
- (string-append
- "srfi-"
- (substring (symbol->string (syntax->datum #'colon-n))
- 1)))))
- (resolve-r6rs-interface
- (syntax-case #'(rest ...) ()
- (()
- #`(library (srfi #,srfi-n (version ...))))
- ((name rest ...)
- ;; SRFI 97 says that the first identifier after the colon-n
- ;; is used for the libraries name, so it must be ignored.
- #`(library (srfi #,srfi-n rest ... (version ...))))))))
-
- ((library (name name* ... (version ...)))
- (and-map sym? #'(name name* ...))
- (resolve-interface (syntax->datum #'(name name* ...))
- #\version (syntax->datum #'(version ...))))
-
- ((library (name name* ...))
- (and-map sym? #'(name name* ...))
- (resolve-r6rs-interface #'(library (name name* ... ()))))
-
- ((only import-set identifier ...)
- (and-map sym? #'(identifier ...))
- (let* ((mod (resolve-r6rs-interface #'import-set))
- (iface (make-custom-interface mod)))
- (for-each (lambda (sym)
- (module-add! iface sym
- (or (module-local-variable mod sym)
- (error "no binding `~A' in module ~A"
- sym mod))))
- (syntax->datum #'(identifier ...)))
- iface))
-
- ((except import-set identifier ...)
- (and-map sym? #'(identifier ...))
- (let* ((mod (resolve-r6rs-interface #'import-set))
- (iface (make-custom-interface mod)))
- (module-for-each (lambda (sym var) (module-add! iface sym var)) mod)
- (for-each (lambda (sym)
- (if (module-local-variable iface sym)
- (module-remove! iface sym)
- (error "no binding `~A' in module ~A" sym mod)))
- (syntax->datum #'(identifier ...)))
- iface))
-
- ((prefix import-set identifier)
- (sym? #'identifier)
- (let* ((mod (resolve-r6rs-interface #'import-set))
- (iface (make-custom-interface mod))
- (pre (syntax->datum #'identifier)))
- (module-for-each (lambda (sym var)
- (module-add! iface (symbol-append pre sym) var))
- mod)
- iface))
-
- ((rename import-set (from to) ...)
- (and (and-map sym? #'(from ...)) (and-map sym? #'(to ...)))
- (let* ((mod (resolve-r6rs-interface #'import-set))
- (iface (make-custom-interface mod)))
- (module-for-each (lambda (sym var) (module-add! iface sym var)) mod)
- (let lp ((in (syntax->datum #'((from . to) ...))) (out '()))
- (cond
- ((null? in)
- (for-each
- (lambda (pair)
- (if (module-local-variable iface (car pair))
- (error "duplicate binding for `~A' in module ~A"
- (car pair) mod)
- (module-add! iface (car pair) (cdr pair))))
- out)
- iface)
- (else
- (let ((var (or (module-local-variable mod (caar in))
- (error "no binding `~A' in module ~A"
- (caar in) mod))))
- (module-remove! iface (caar in))
- (lp (cdr in) (acons (cdar in) var out))))))))
-
- ((name name* ... (version ...))
- (and-map sym? #'(name name* ...))
- (resolve-r6rs-interface #'(library (name name* ... (version ...)))))
-
- ((name name* ...)
- (and-map sym? #'(name name* ...))
- (resolve-r6rs-interface #'(library (name name* ... ()))))))
-
-(define-syntax library
- (lambda (stx)
- (define (compute-exports ifaces specs)
- (define (re-export? sym)
- (or-map (lambda (iface) (module-local-variable iface sym)) ifaces))
- (define (replace? sym)
- (module-local-variable the-scm-module sym))
-
- (let lp ((specs specs) (e '()) (r '()) (x '()))
- (syntax-case specs (rename)
- (() (values e r x))
- (((rename (from to) ...) . rest)
- (and (and-map identifier? #'(from ...))
- (and-map identifier? #'(to ...)))
- (let lp2 ((in #'((from . to) ...)) (e e) (r r) (x x))
- (syntax-case in ()
- (() (lp #'rest e r x))
- (((from . to) . in)
- (cond
- ((re-export? (syntax->datum #'from))
- (lp2 #'in e (cons #'(from . to) r) x))
- ((replace? (syntax->datum #'from))
- (lp2 #'in e r (cons #'(from . to) x)))
- (else
- (lp2 #'in (cons #'(from . to) e) r x)))))))
- ((id . rest)
- (identifier? #'id)
- (let ((sym (syntax->datum #'id)))
- (cond
- ((re-export? sym)
- (lp #'rest e (cons #'id r) x))
- ((replace? sym)
- (lp #'rest e r (cons #'id x)))
- (else
- (lp #'rest (cons #'id e) r x))))))))
-
- (syntax-case stx (export import)
- ((_ (name name* ...)
- (export espec ...)
- (import ispec ...)
- body ...)
- (and-map identifier? #'(name name* ...))
- ;; Add () as the version.
- #'(library (name name* ... ())
- (export espec ...)
- (import ispec ...)
- body ...))
-
- ((_ (name name* ... (version ...))
- (export espec ...)
- (import ispec ...)
- body ...)
- (and-map identifier? #'(name name* ...))
- (call-with-values
- (lambda ()
- (compute-exports
- (map (lambda (im)
- (syntax-case im (for)
- ((for import-set import-level ...)
- (resolve-r6rs-interface #'import-set))
- (import-set (resolve-r6rs-interface #'import-set))))
- #'(ispec ...))
- #'(espec ...)))
- (lambda (exports re-exports replacements)
- (with-syntax (((e ...) exports)
- ((r ...) re-exports)
- ((x ...) replacements))
- ;; It would be nice to push the module that was current before the
- ;; definition, and pop it after the library definition, but I
- ;; actually can't see a way to do that. Helper procedures perhaps,
- ;; around a fluid that is rebound in save-module-excursion? Patches
- ;; welcome!
- #'(begin
- (define-module (name name* ...)
- #\pure
- #\version (version ...))
- (import ispec)
- ...
- (export e ...)
- (re-export r ...)
- (export! x ...)
- (@@ @@ (name name* ...) body)
- ...))))))))
-
-(define-syntax import
- (lambda (stx)
- (define (strip-for import-set)
- (syntax-case import-set (for)
- ((for import-set import-level ...)
- #'import-set)
- (import-set
- #'import-set)))
- (syntax-case stx ()
- ((_ import-set ...)
- (with-syntax (((library-reference ...) (map strip-for #'(import-set ...))))
- #'(eval-when (expand load eval)
- (let ((iface (resolve-r6rs-interface 'library-reference)))
- (call-with-deferred-observers
- (lambda ()
- (module-use-interfaces! (current-module) (list iface)))))
- ...
- (if #f #f)))))))
-;;; installed-scm-file
-
-;;;; Copyright (C) 1997, 1999, 2000, 2001, 2006, 2010, 2013,
-;;;; 2014 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-;;; This is the Scheme part of the module for delimited I/O. It's
-;;; similar to (scsh rdelim) but somewhat incompatible.
-
-(define-module (ice-9 rdelim)
- #\export (read-line
- read-line!
- read-delimited
- read-delimited!
- read-string
- read-string!
- %read-delimited!
- %read-line
- write-line))
-
-
-(%init-rdelim-builtins)
-
-(define* (read-line! string #\optional (port current-input-port))
- ;; corresponds to SCM_LINE_INCREMENTORS in libguile.
- (define scm-line-incrementors "\n")
- (let* ((rv (%read-delimited! scm-line-incrementors
- string
- #t
- port))
- (terminator (car rv))
- (nchars (cdr rv)))
- (cond ((and (= nchars 0)
- (eof-object? terminator))
- terminator)
- ((not terminator) #f)
- (else nchars))))
-
-(define* (read-delimited! delims buf #\optional
- (port (current-input-port)) (handle-delim 'trim)
- (start 0) (end (string-length buf)))
- (let* ((rv (%read-delimited! delims
- buf
- (not (eq? handle-delim 'peek))
- port
- start
- end))
- (terminator (car rv))
- (nchars (cdr rv)))
- (cond ((or (not terminator) ; buffer filled
- (eof-object? terminator))
- (if (zero? nchars)
- (if (eq? handle-delim 'split)
- (cons terminator terminator)
- terminator)
- (if (eq? handle-delim 'split)
- (cons nchars terminator)
- nchars)))
- (else
- (case handle-delim
- ((trim peek) nchars)
- ((concat) (string-set! buf (+ nchars start) terminator)
- (+ nchars 1))
- ((split) (cons nchars terminator))
- (else (error "unexpected handle-delim value: "
- handle-delim)))))))
-
-(define* (read-delimited delims #\optional (port (current-input-port))
- (handle-delim 'trim))
- (let loop ((substrings '())
- (total-chars 0)
- (buf-size 100)) ; doubled each time through.
- (let* ((buf (make-string buf-size))
- (rv (%read-delimited! delims
- buf
- (not (eq? handle-delim 'peek))
- port))
- (terminator (car rv))
- (nchars (cdr rv))
- (new-total (+ total-chars nchars)))
- (cond
- ((not terminator)
- ;; buffer filled.
- (loop (cons (substring buf 0 nchars) substrings)
- new-total
- (* buf-size 2)))
- ((and (eof-object? terminator) (zero? new-total))
- (if (eq? handle-delim 'split)
- (cons terminator terminator)
- terminator))
- (else
- (let ((joined
- (string-concatenate-reverse
- (cons (substring buf 0 nchars) substrings))))
- (case handle-delim
- ((concat)
- (if (eof-object? terminator)
- joined
- (string-append joined (string terminator))))
- ((trim peek) joined)
- ((split) (cons joined terminator))
- (else (error "unexpected handle-delim value: "
- handle-delim)))))))))
-
-(define-syntax-rule (check-arg exp message arg ...)
- (unless exp
- (error message arg ...)))
-
-(define (index? n)
- (and (integer? n) (exact? n) (>= n 0)))
-
-(define* (read-string! buf #\optional
- (port (current-input-port))
- (start 0) (end (string-length buf)))
- "Read all of the characters out of PORT and write them to BUF.
-Returns the number of characters read.
-
-This function only reads out characters from PORT if it will be able to
-write them to BUF. That is to say, if BUF is smaller than the number of
-available characters, then BUF will be filled, and characters will be
-left in the port."
- (check-arg (string? buf) "not a string" buf)
- (check-arg (index? start) "bad index" start)
- (check-arg (index? end) "bad index" end)
- (check-arg (<= start end) "start beyond end" start end)
- (check-arg (<= end (string-length buf)) "end beyond string length" end)
- (let lp ((n start))
- (if (< n end)
- (let ((c (read-char port)))
- (if (eof-object? c)
- (- n start)
- (begin
- (string-set! buf n c)
- (lp (1+ n)))))
- (- n start))))
-
-(define* read-string
- (case-lambda*
- "Read all of the characters out of PORT and return them as a string.
-If the COUNT argument is present, treat it as a limit to the number of
-characters to read. By default, there is no limit."
- ((#\optional (port (current-input-port)))
- ;; Fast path.
- ;; This creates more garbage than using 'string-set!' as in
- ;; 'read-string!', but currently that is faster nonetheless.
- (let loop ((chars '()))
- (let ((char (read-char port)))
- (if (eof-object? char)
- (list->string (reverse! chars))
- (loop (cons char chars))))))
- ((port count)
- ;; Slower path.
- (let loop ((chars '())
- (total 0))
- (let ((char (read-char port)))
- (if (or (eof-object? char) (>= total count))
- (list->string (reverse chars))
- (loop (cons char chars) (+ 1 total))))))))
-
-
-;;; read-line [PORT [HANDLE-DELIM]] reads a newline-terminated string
-;;; from PORT. The return value depends on the value of HANDLE-DELIM,
-;;; which may be one of the symbols `trim', `concat', `peek' and
-;;; `split'. If it is `trim' (the default), the trailing newline is
-;;; removed and the string is returned. If `concat', the string is
-;;; returned with the trailing newline intact. If `peek', the newline
-;;; is left in the input port buffer and the string is returned. If
-;;; `split', the newline is split from the string and read-line
-;;; returns a pair consisting of the truncated string and the newline.
-
-(define* (read-line #\optional (port (current-input-port))
- (handle-delim 'trim))
- (let* ((line/delim (%read-line port))
- (line (car line/delim))
- (delim (cdr line/delim)))
- (case handle-delim
- ((trim) line)
- ((split) line/delim)
- ((concat) (if (and (string? line) (char? delim))
- (string-append line (string delim))
- line))
- ((peek) (if (char? delim)
- (unread-char delim port))
- line)
- (else
- (error "unexpected handle-delim value: " handle-delim)))))
-;;;; SRFI-8
-
-;;; Copyright (C) 2000, 2001, 2004, 2006, 2010, 2011 Free Software Foundation, Inc.
-;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (ice-9 receive)
- #\export (receive))
-
-(define-syntax-rule (receive vars vals . body)
- (call-with-values (lambda () vals)
- (lambda vars . body)))
-
-(cond-expand-provide (current-module) '(srfi-8))
-;;;; Copyright (C) 1997, 1999, 2001, 2004, 2005, 2006, 2008, 2010 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary:
-
-;; These procedures are exported:
-;; (match:count match)
-;; (match:string match)
-;; (match:prefix match)
-;; (match:suffix match)
-;; (regexp-match? match)
-;; (regexp-quote string)
-;; (match:start match . submatch-num)
-;; (match:end match . submatch-num)
-;; (match:substring match . submatch-num)
-;; (string-match pattern str . start)
-;; (regexp-substitute port match . items)
-;; (fold-matches regexp string init proc . flags)
-;; (list-matches regexp string . flags)
-;; (regexp-substitute/global port regexp string . items)
-
-;;; Code:
-
-;;;; POSIX regex support functions.
-
-(define-module (ice-9 regex)
- #\export (match:count match:string match:prefix match:suffix
- regexp-match? regexp-quote match:start match:end match:substring
- string-match regexp-substitute fold-matches list-matches
- regexp-substitute/global))
-
-;; References:
-;;
-;; POSIX spec:
-;; http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html
-
-;;; FIXME:
-;;; It is not clear what should happen if a `match' function
-;;; is passed a `match number' which is out of bounds for the
-;;; regexp match: return #f, or throw an error? These routines
-;;; throw an out-of-range error.
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;; These procedures are not defined in SCSH, but I found them useful.
-
-(define (match:count match)
- (- (vector-length match) 1))
-
-(define (match:string match)
- (vector-ref match 0))
-
-(define (match:prefix match)
- (substring (match:string match) 0 (match:start match 0)))
-
-(define (match:suffix match)
- (substring (match:string match) (match:end match 0)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;; SCSH compatibility routines.
-
-(define (regexp-match? match)
- (and (vector? match)
- (string? (vector-ref match 0))
- (let loop ((i 1))
- (cond ((>= i (vector-length match)) #t)
- ((and (pair? (vector-ref match i))
- (integer? (car (vector-ref match i)))
- (integer? (cdr (vector-ref match i))))
- (loop (+ 1 i)))
- (else #f)))))
-
-;; * . \ ^ $ and [ are special in both regexp/basic and regexp/extended and
-;; can be backslash escaped.
-;;
-;; ( ) + ? { } and | are special in regexp/extended so must be quoted. But
-;; that can't be done with a backslash since in regexp/basic where they're
-;; not special, adding a backslash makes them become special. Character
-;; class forms [(] etc are used instead.
-;;
-;; ) is not special when not preceded by a (, and * and ? are not special at
-;; the start of a string, but we quote all of these always, so the result
-;; can be concatenated or merged into some larger regexp.
-;;
-;; ] is not special outside a [ ] character class, so doesn't need to be
-;; quoted.
-;;
-(define (regexp-quote string)
- (call-with-output-string
- (lambda (p)
- (string-for-each (lambda (c)
- (case c
- ((#\* #\. #\\ #\^ #\$ #\[)
- (write-char #\\ p)
- (write-char c p))
- ((#\( #\) #\+ #\? #\{ #\} #\|)
- (write-char #\[ p)
- (write-char c p)
- (write-char #\] p))
- (else
- (write-char c p))))
- string))))
-
-(define* (match:start match #\optional (n 0))
- (let ((start (car (vector-ref match (1+ n)))))
- (if (= start -1) #f start)))
-
-(define* (match:end match #\optional (n 0))
- (let* ((end (cdr (vector-ref match (1+ n)))))
- (if (= end -1) #f end)))
-
-(define* (match:substring match #\optional (n 0))
- (let* ((start (match:start match n))
- (end (match:end match n)))
- (and start end (substring (match:string match) start end))))
-
-(define (string-match pattern str . args)
- (let ((rx (make-regexp pattern))
- (start (if (pair? args) (car args) 0)))
- (regexp-exec rx str start)))
-
-(define (regexp-substitute port match . items)
- ;; If `port' is #f, send output to a string.
- (if (not port)
- (call-with-output-string
- (lambda (p)
- (apply regexp-substitute p match items)))
-
- ;; Otherwise, process each substitution argument in `items'.
- (for-each (lambda (obj)
- (cond ((string? obj) (display obj port))
- ((integer? obj) (display (match:substring match obj) port))
- ((eq? 'pre obj) (display (match:prefix match) port))
- ((eq? 'post obj) (display (match:suffix match) port))
- (else (error 'wrong-type-arg obj))))
- items)))
-
-;;; If we call fold-matches, below, with a regexp that can match the
-;;; empty string, it's not obvious what "all the matches" means. How
-;;; many empty strings are there in the string "a"? Our answer:
-;;;
-;;; This function applies PROC to every non-overlapping, maximal
-;;; match of REGEXP in STRING.
-;;;
-;;; "non-overlapping": There are two non-overlapping matches of "" in
-;;; "a" --- one before the `a', and one after. There are three
-;;; non-overlapping matches of "q|x*" in "aqb": the empty strings
-;;; before `a' and after `b', and `q'. The two empty strings before
-;;; and after `q' don't count, because they overlap with the match of
-;;; "q".
-;;;
-;;; "maximal": There are three distinct maximal matches of "x*" in
-;;; "axxxb": one before the `a', one covering `xxx', and one after the
-;;; `b'. Around or within `xxx', only the match covering all three
-;;; x's counts, because the rest are not maximal.
-
-(define* (fold-matches regexp string init proc #\optional (flags 0))
- (let ((regexp (if (regexp? regexp) regexp (make-regexp regexp))))
- (let loop ((start 0)
- (value init)
- (abuts #f)) ; True if start abuts a previous match.
- (define bol (if (zero? start) 0 regexp/notbol))
- (let ((m (if (> start (string-length string)) #f
- (regexp-exec regexp string start (logior flags bol)))))
- (cond
- ((not m) value)
- ((and (= (match:start m) (match:end m)) abuts)
- ;; We matched an empty string, but that would overlap the
- ;; match immediately before. Try again at a position
- ;; further to the right.
- (loop (+ start 1) value #f))
- (else
- (loop (match:end m) (proc m value) #t)))))))
-
-(define* (list-matches regexp string #\optional (flags 0))
- (reverse! (fold-matches regexp string '() cons flags)))
-
-(define (regexp-substitute/global port regexp string . items)
-
- ;; If `port' is #f, send output to a string.
- (if (not port)
- (call-with-output-string
- (lambda (p)
- (apply regexp-substitute/global p regexp string items)))
-
- ;; Walk the set of non-overlapping, maximal matches.
- (let next-match ((matches (list-matches regexp string))
- (start 0))
- (if (null? matches)
- (display (substring string start) port)
- (let ((m (car matches)))
-
- ;; Process all of the items for this match. Don't use
- ;; for-each, because we need to make sure 'post at the
- ;; end of the item list is a tail call.
- (let next-item ((items items))
-
- (define (do-item item)
- (cond
- ((string? item) (display item port))
- ((integer? item) (display (match:substring m item) port))
- ((procedure? item) (display (item m) port))
- ((eq? item 'pre)
- (display
- (substring string start (match:start m))
- port))
- ((eq? item 'post)
- (next-match (cdr matches) (match:end m)))
- (else (error 'wrong-type-arg item))))
-
- (if (pair? items)
- (if (null? (cdr items))
- (do-item (car items)) ; This is a tail call.
- (begin
- (do-item (car items)) ; This is not.
- (next-item (cdr items)))))))))))
-;;;; runq.scm --- the runq data structure
-;;;;
-;;;; Copyright (C) 1996, 2001, 2006, 2010 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary:
-
-;;; One way to schedule parallel computations in a serial environment is
-;;; to explicitly divide each task up into small, finite execution time,
-;;; strips. Then you interleave the execution of strips from various
-;;; tasks to achieve a kind of parallelism. Runqs are a handy data
-;;; structure for this style of programming.
-;;;
-;;; We use thunks (nullary procedures) and lists of thunks to represent
-;;; strips. By convention, the return value of a strip-thunk must either
-;;; be another strip or the value #f.
-;;;
-;;; A runq is a procedure that manages a queue of strips. Called with no
-;;; arguments, it processes one strip from the queue. Called with
-;;; arguments, the arguments form a control message for the queue. The
-;;; first argument is a symbol which is the message selector.
-;;;
-;;; A strip is processed this way: If the strip is a thunk, the thunk is
-;;; called -- if it returns a strip, that strip is added back to the
-;;; queue. To process a strip which is a list of thunks, the CAR of that
-;;; list is called. After a call to that CAR, there are 0, 1, or 2 strips
-;;; -- perhaps one returned by the thunk, and perhaps the CDR of the
-;;; original strip if that CDR is not nil. The runq puts whichever of
-;;; these strips exist back on the queue. (The exact order in which
-;;; strips are put back on the queue determines the scheduling behavior of
-;;; a particular queue -- it's a parameter.)
-
-;;; Code:
-
-(define-module (ice-9 runq)
- \:use-module (ice-9 q)
- \:export (runq-control make-void-runq make-fair-runq
- make-exclusive-runq make-subordinate-runq-to strip-sequence
- fair-strip-subtask))
-
-;;;;
-;;; (runq-control q msg . args)
-;;;
-;;; processes in the default way the control messages that
-;;; can be sent to a runq. Q should be an ordinary
-;;; Q (see utils/q.scm).
-;;;
-;;; The standard runq messages are:
-;;;
-;;; 'add! strip0 strip1... ;; to enqueue one or more strips
-;;; 'enqueue! strip0 strip1... ;; to enqueue one or more strips
-;;; 'push! strip0 ... ;; add strips to the front of the queue
-;;; 'empty? ;; true if it is
-;;; 'length ;; how many strips in the queue?
-;;; 'kill! ;; empty the queue
-;;; else ;; throw 'not-understood
-;;;
-(define (runq-control q msg . args)
- (case msg
- ((add!) (for-each (lambda (t) (enq! q t)) args) '*unspecified*)
- ((enqueue!) (for-each (lambda (t) (enq! q t)) args) '*unspecified*)
- ((push!) (for-each (lambda (t) (q-push! q t)) args) '*unspecified*)
- ((empty?) (q-empty? q))
- ((length) (q-length q))
- ((kill!) (set! q (make-q)))
- (else (throw 'not-understood msg args))))
-
-(define (run-strip thunk) (catch #t thunk (lambda ign (warn 'runq-strip thunk ign) #f)))
-
-;;;;
-;;; make-void-runq
-;;;
-;;; Make a runq that discards all messages except "length", for which
-;;; it returns 0.
-;;;
-(define (make-void-runq)
- (lambda opts
- (and opts
- (apply-to-args opts
- (lambda (msg . args)
- (case msg
- ((length) 0)
- (else #f)))))))
-
-;;;;
-;;; (make-fair-runq)
-;;;
-;;; Returns a runq procedure.
-;;; Called with no arguments, the procedure processes one strip from the queue.
-;;; Called with arguments, it uses runq-control.
-;;;
-;;; In a fair runq, if a strip returns a new strip X, X is added
-;;; to the end of the queue, meaning it will be the last to execute
-;;; of all the remaining procedures.
-;;;
-(define (make-fair-runq)
- (letrec ((q (make-q))
- (self
- (lambda ctl
- (if ctl
- (apply runq-control q ctl)
- (and (not (q-empty? q))
- (let ((next-strip (deq! q)))
- (cond
- ((procedure? next-strip) (let ((k (run-strip next-strip)))
- (and k (enq! q k))))
- ((pair? next-strip) (let ((k (run-strip (car next-strip))))
- (and k (enq! q k)))
- (if (not (null? (cdr next-strip)))
- (enq! q (cdr next-strip)))))
- self))))))
- self))
-
-
-;;;;
-;;; (make-exclusive-runq)
-;;;
-;;; Returns a runq procedure.
-;;; Called with no arguments, the procedure processes one strip from the queue.
-;;; Called with arguments, it uses runq-control.
-;;;
-;;; In an exclusive runq, if a strip W returns a new strip X, X is added
-;;; to the front of the queue, meaning it will be the next to execute
-;;; of all the remaining procedures.
-;;;
-;;; An exception to this occurs if W was the CAR of a list of strips.
-;;; In that case, after the return value of W is pushed onto the front
-;;; of the queue, the CDR of the list of strips is pushed in front
-;;; of that (if the CDR is not nil). This way, the rest of the thunks
-;;; in the list that contained W have priority over the return value of W.
-;;;
-(define (make-exclusive-runq)
- (letrec ((q (make-q))
- (self
- (lambda ctl
- (if ctl
- (apply runq-control q ctl)
- (and (not (q-empty? q))
- (let ((next-strip (deq! q)))
- (cond
- ((procedure? next-strip) (let ((k (run-strip next-strip)))
- (and k (q-push! q k))))
- ((pair? next-strip) (let ((k (run-strip (car next-strip))))
- (and k (q-push! q k)))
- (if (not (null? (cdr next-strip)))
- (q-push! q (cdr next-strip)))))
- self))))))
- self))
-
-
-;;;;
-;;; (make-subordinate-runq-to superior basic-inferior)
-;;;
-;;; Returns a runq proxy for the runq basic-inferior.
-;;;
-;;; The proxy watches for operations on the basic-inferior that cause
-;;; a transition from a queue length of 0 to a non-zero length and
-;;; vice versa. While the basic-inferior queue is not empty,
-;;; the proxy installs a task on the superior runq. Each strip
-;;; of that task processes N strips from the basic-inferior where
-;;; N is the length of the basic-inferior queue when the proxy
-;;; strip is entered. [Countless scheduling variations are possible.]
-;;;
-(define (make-subordinate-runq-to superior-runq basic-runq)
- (let ((runq-task (cons #f #f)))
- (set-car! runq-task
- (lambda ()
- (if (basic-runq 'empty?)
- (set-cdr! runq-task #f)
- (do ((n (basic-runq 'length) (1- n)))
- ((<= n 0) #f)
- (basic-runq)))))
- (letrec ((self
- (lambda ctl
- (if (not ctl)
- (let ((answer (basic-runq)))
- (self 'empty?)
- answer)
- (begin
- (case (car ctl)
- ((suspend) (set-cdr! runq-task #f))
- (else (let ((answer (apply basic-runq ctl)))
- (if (and (not (cdr runq-task)) (not (basic-runq 'empty?)))
- (begin
- (set-cdr! runq-task runq-task)
- (superior-runq 'add! runq-task)))
- answer))))))))
- self)))
-
-;;;;
-;;; (define fork-strips (lambda args args))
-;;; Return a strip that starts several strips in
-;;; parallel. If this strip is enqueued on a fair
-;;; runq, strips of the parallel subtasks will run
-;;; round-robin style.
-;;;
-
-
-;;;;
-;;; (strip-sequence . strips)
-;;;
-;;; Returns a new strip which is the concatenation of the argument strips.
-;;;
-(define (strip-sequence . strips)
- (lambda ()
- (let loop ((st (let ((a strips)) (set! strips #f) a)))
- (and (not (null? st))
- (let ((then ((car st))))
- (if then
- (lambda () (loop (cons then (cdr st))))
- (lambda () (loop (cdr st)))))))))
-
-
-;;;;
-;;; (fair-strip-subtask . initial-strips)
-;;;
-;;; Returns a new strip which is the synchronos, fair,
-;;; parallel execution of the argument strips.
-;;;
-;;;
-;;;
-(define (fair-strip-subtask . initial-strips)
- (let ((st (make-fair-runq)))
- (apply st 'add! initial-strips)
- st))
-
-;;; runq.scm ends here
-;;; installed-scm-file
-
-;;;; Copyright (C) 2001, 2006 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-;;; This is the Scheme part of (ice-9 rw), which is a subset of
-;;; (scsh rw).
-
-(define-module (ice-9 rw)
- \:export (read-string!/partial write-string/partial))
-
-(%init-rw-builtins)
-;;;; Copyright (C) 2000, 2001, 2004, 2006, 2010 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;;; Safe subset of R5RS bindings
-
-(define-module (ice-9 safe-r5rs)
- \:re-export (eqv? eq? equal?
- number? complex? real? rational? integer?
- exact? inexact?
- = < > <= >=
- zero? positive? negative? odd? even?
- max min
- + * - /
- abs
- quotient remainder modulo
- gcd lcm
- numerator denominator
- rationalize
- floor ceiling truncate round
- exp log sin cos tan asin acos atan
- sqrt
- expt
- make-rectangular make-polar real-part imag-part magnitude angle
- exact->inexact inexact->exact
-
- number->string string->number
-
- boolean?
- not
-
- pair?
- cons car cdr
- set-car! set-cdr!
- caar cadr cdar cddr
- caaar caadr cadar caddr cdaar cdadr cddar cdddr
- caaaar caaadr caadar caaddr cadaar cadadr caddar cadddr
- cdaaar cdaadr cdadar cdaddr cddaar cddadr cdddar cddddr
- null?
- list?
- list
- length
- append
- reverse
- list-tail list-ref
- memq memv member
- assq assv assoc
-
- symbol?
- symbol->string string->symbol
-
- char?
- char=? char<? char>? char<=? char>=?
- char-ci=? char-ci<? char-ci>? char-ci<=? char-ci>=?
- char-alphabetic? char-numeric? char-whitespace?
- char-upper-case? char-lower-case?
- char->integer integer->char
- char-upcase
- char-downcase
-
- string?
- make-string
- string
- string-length
- string-ref string-set!
- string=? string-ci=?
- string<? string>? string<=? string>=?
- string-ci<? string-ci>? string-ci<=? string-ci>=?
- substring
- string-length
- string-append
- string->list list->string
- string-copy string-fill!
-
- vector?
- make-vector
- vector
- vector-length
- vector-ref vector-set!
- vector->list list->vector
- vector-fill!
-
- procedure?
- apply
- map
- for-each
- force
-
- call-with-current-continuation
-
- values
- call-with-values
- dynamic-wind
-
- eval
-
- input-port? output-port?
- current-input-port current-output-port
-
- read
- read-char
- peek-char
- eof-object?
- char-ready?
-
- write
- display
- newline
- write-char
-
- ;;transcript-on
- ;;transcript-off
- )
-
- \:export (null-environment))
-
-(define null-interface (resolve-interface '(ice-9 null)))
-
-(module-use! (module-public-interface (current-module))
- null-interface)
-
-(define (null-environment n)
- (if (not (= n 5))
- (scm-error 'misc-error 'null-environment
- "~A is not a valid version"
- (list n)
- '()))
- ;; Note that we need to create a *fresh* interface
- (let ((interface (make-module 31)))
- (set-module-kind! interface 'interface)
- (module-use! interface null-interface)
- interface))
-;;;; Copyright (C) 2000, 2001, 2006 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;;; Safe subset of R5RS bindings
-
-(define-module (ice-9 safe)
- \:export (safe-environment make-safe-module))
-
-(define safe-r5rs-interface (resolve-interface '(ice-9 safe-r5rs)))
-
-(define (safe-environment n)
- (if (not (= n 5))
- (scm-error 'misc-error 'safe-environment
- "~A is not a valid version"
- (list n)
- '()))
- safe-r5rs-interface)
-
-(define (make-safe-module)
- (make-module 1021 (list safe-r5rs-interface)))
-;;; -*- mode: scheme; coding: utf-8; -*-
-
-;;;; Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010
-;;;; Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-
-;;; Commentary:
-
-;;; An older approack to debugging, in which the user installs a pre-unwind
-;;; handler that saves the stack at the time of the error. The last stack can
-;;; then be debugged later.
-;;;
-
-;;; Code:
-
-(define-module (ice-9 save-stack)
- ;; Replace deprecated root-module bindings, if present.
- #\replace (stack-saved?
- the-last-stack
- save-stack))
-
-;; FIXME: stack-saved? is broken in the presence of threads.
-(define stack-saved? #f)
-
-(define the-last-stack (make-fluid))
-
-(define (save-stack . narrowing)
- (if (not stack-saved?)
- (begin
- (let ((stacks (fluid-ref %stacks)))
- (fluid-set! the-last-stack
- ;; (make-stack obj inner outer inner outer ...)
- ;;
- ;; In this case, cut away the make-stack frame, the
- ;; save-stack frame, and then narrow as specified by the
- ;; user, delimited by the nearest start-stack invocation,
- ;; if any.
- (apply make-stack #t
- 2
- (if (pair? stacks) (cdar stacks) 0)
- narrowing)))
- (set! stack-saved? #t))))
-;;;; Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010
-;;;; Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-(define-module (ice-9 scm-style-repl)
- #\use-module (ice-9 save-stack)
-
- #\export (scm-repl-silent
- scm-repl-print-unspecified
- scm-repl-verbose
- scm-repl-prompt)
-
- ;; #\replace, as with deprecated code enabled these will be in the root env
- #\replace (assert-repl-silence
- assert-repl-print-unspecified
- assert-repl-verbosity
-
- default-pre-unwind-handler
- bad-throw
- error-catching-loop
- error-catching-repl
- scm-style-repl
- handle-system-error))
-
-(define scm-repl-silent #f)
-(define (assert-repl-silence v) (set! scm-repl-silent v))
-
-(define scm-repl-print-unspecified #f)
-(define (assert-repl-print-unspecified v) (set! scm-repl-print-unspecified v))
-
-(define scm-repl-verbose #f)
-(define (assert-repl-verbosity v) (set! scm-repl-verbose v))
-
-(define scm-repl-prompt "guile> ")
-
-
-
-;; bad-throw is the hook that is called upon a throw to a an unhandled
-;; key (unless the throw has four arguments, in which case
-;; it's usually interpreted as an error throw.)
-;; If the key has a default handler (a throw-handler-default property),
-;; it is applied to the throw.
-;;
-(define (bad-throw key . args)
- (let ((default (symbol-property key 'throw-handler-default)))
- (or (and default (apply default key args))
- (apply error "unhandled-exception:" key args))))
-
-
-
-(define (default-pre-unwind-handler key . args)
- ;; Narrow by two more frames: this one, and the throw handler.
- (save-stack 2)
- (apply throw key args))
-
-
-
-(define has-shown-debugger-hint? #f)
-
-(define (error-catching-loop thunk)
- (let ((status #f)
- (interactive #t))
- (define (loop first)
- (let ((next
- (catch #t
-
- (lambda ()
- (call-with-unblocked-asyncs
- (lambda ()
- (first)
-
- ;; This line is needed because mark
- ;; doesn't do closures quite right.
- ;; Unreferenced locals should be
- ;; collected.
- (set! first #f)
- (let loop ((v (thunk)))
- (loop (thunk)))
- #f)))
-
- (lambda (key . args)
- (case key
- ((quit)
- (set! status args)
- #f)
-
- ((switch-repl)
- (apply throw 'switch-repl args))
-
- ((abort)
- ;; This is one of the closures that require
- ;; (set! first #f) above
- ;;
- (lambda ()
- (run-hook abort-hook)
- (force-output (current-output-port))
- (display "ABORT: " (current-error-port))
- (write args (current-error-port))
- (newline (current-error-port))
- (if interactive
- (begin
- (if (and
- (not has-shown-debugger-hint?)
- (not (memq 'backtrace
- (debug-options-interface)))
- (stack? (fluid-ref the-last-stack)))
- (begin
- (newline (current-error-port))
- (display
- "Type \"(backtrace)\" to get more information or \"(debug)\" to enter the debugger.\n"
- (current-error-port))
- (set! has-shown-debugger-hint? #t)))
- (force-output (current-error-port)))
- (begin
- (primitive-exit 1)))
- (set! stack-saved? #f)))
-
- (else
- ;; This is the other cons-leak closure...
- (lambda ()
- (cond ((= (length args) 4)
- (apply handle-system-error key args))
- (else
- (apply bad-throw key args)))))))
-
- default-pre-unwind-handler)))
-
- (if next (loop next) status)))
- (set! ensure-batch-mode! (lambda ()
- (set! interactive #f)
- (restore-signals)))
- (set! batch-mode? (lambda () (not interactive)))
- (call-with-blocked-asyncs
- (lambda () (loop (lambda () #t))))))
-
-(define (error-catching-repl r e p)
- (error-catching-loop
- (lambda ()
- (call-with-values (lambda () (e (r)))
- (lambda the-values (for-each p the-values))))))
-
-(define (scm-style-repl)
- (letrec (
- (start-gc-rt #f)
- (start-rt #f)
- (repl-report-start-timing (lambda ()
- (set! start-gc-rt (gc-run-time))
- (set! start-rt (get-internal-run-time))))
- (repl-report (lambda ()
- (display ";;; ")
- (display (inexact->exact
- (* 1000 (/ (- (get-internal-run-time) start-rt)
- internal-time-units-per-second))))
- (display " msec (")
- (display (inexact->exact
- (* 1000 (/ (- (gc-run-time) start-gc-rt)
- internal-time-units-per-second))))
- (display " msec in gc)\n")))
-
- (consume-trailing-whitespace
- (lambda ()
- (let ((ch (peek-char)))
- (cond
- ((eof-object? ch))
- ((or (char=? ch #\space) (char=? ch #\tab))
- (read-char)
- (consume-trailing-whitespace))
- ((char=? ch #\newline)
- (read-char))))))
- (-read (lambda ()
- (let ((val
- (let ((prompt (cond ((string? scm-repl-prompt)
- scm-repl-prompt)
- ((thunk? scm-repl-prompt)
- (scm-repl-prompt))
- (scm-repl-prompt "> ")
- (else ""))))
- (repl-reader prompt))))
-
- ;; As described in R4RS, the READ procedure updates the
- ;; port to point to the first character past the end of
- ;; the external representation of the object. This
- ;; means that it doesn't consume the newline typically
- ;; found after an expression. This means that, when
- ;; debugging Guile with GDB, GDB gets the newline, which
- ;; it often interprets as a "continue" command, making
- ;; breakpoints kind of useless. So, consume any
- ;; trailing newline here, as well as any whitespace
- ;; before it.
- ;; But not if EOF, for control-D.
- (if (not (eof-object? val))
- (consume-trailing-whitespace))
- (run-hook after-read-hook)
- (if (eof-object? val)
- (begin
- (repl-report-start-timing)
- (if scm-repl-verbose
- (begin
- (newline)
- (display ";;; EOF -- quitting")
- (newline)))
- (quit 0)))
- val)))
-
- (-eval (lambda (sourc)
- (repl-report-start-timing)
- (run-hook before-eval-hook sourc)
- (let ((val (start-stack 'repl-stack
- ;; If you change this procedure
- ;; (primitive-eval), please also
- ;; modify the repl-stack case in
- ;; save-stack so that stack cutting
- ;; continues to work.
- (primitive-eval sourc))))
- (run-hook after-eval-hook sourc)
- val)))
-
-
- (-print (let ((maybe-print (lambda (result)
- (if (or scm-repl-print-unspecified
- (not (unspecified? result)))
- (begin
- (write result)
- (newline))))))
- (lambda (result)
- (if (not scm-repl-silent)
- (begin
- (run-hook before-print-hook result)
- (maybe-print result)
- (run-hook after-print-hook result)
- (if scm-repl-verbose
- (repl-report))
- (force-output))))))
-
- (-quit (lambda (args)
- (if scm-repl-verbose
- (begin
- (display ";;; QUIT executed, repl exitting")
- (newline)
- (repl-report)))
- args)))
-
- (let ((status (error-catching-repl -read
- -eval
- -print)))
- (-quit status))))
-
-(define (handle-system-error key . args)
- (let ((cep (current-error-port)))
- (cond ((not (stack? (fluid-ref the-last-stack))))
- ((memq 'backtrace (debug-options-interface))
- (let ((highlights (if (or (eq? key 'wrong-type-arg)
- (eq? key 'out-of-range))
- (list-ref args 3)
- '())))
- (run-hook before-backtrace-hook)
- (newline cep)
- (display "Backtrace:\n")
- (display-backtrace (fluid-ref the-last-stack) cep
- #f #f highlights)
- (newline cep)
- (run-hook after-backtrace-hook))))
- (run-hook before-error-hook)
- (apply display-error (fluid-ref the-last-stack) cep args)
- (run-hook after-error-hook)
- (force-output cep)
- (throw 'abort key)))
-;;;; Copyright (C) 2003, 2006 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary:
-
-;; (serialize FORM1 ...) and (parallelize FORM1 ...) are useful when
-;; you don't trust the thread safety of most of your program, but
-;; where you have some section(s) of code which you consider can run
-;; in parallel to other sections.
-;;
-;; They "flag" (with dynamic extent) sections of code to be of
-;; "serial" or "parallel" nature and have the single effect of
-;; preventing a serial section from being run in parallel with any
-;; serial section (including itself).
-;;
-;; Both serialize and parallelize can be nested. If so, the
-;; inner-most construct is in effect.
-;;
-;; NOTE 1: A serial section can run in parallel with a parallel
-;; section.
-;;
-;; NOTE 2: If a serial section S is "interrupted" by a parallel
-;; section P in the following manner: S = S1 P S2, S2 is not
-;; guaranteed to be resumed by the same thread that previously
-;; executed S1.
-;;
-;; WARNING: Spawning new threads within a serial section have
-;; undefined effects. It is OK, though, to spawn threads in unflagged
-;; sections of code where neither serialize or parallelize is in
-;; effect.
-;;
-;; A typical usage is when Guile is used as scripting language in some
-;; application doing heavy computations. If each thread is
-;; encapsulated with a serialize form, you can then put a parallelize
-;; form around the code performing the heavy computations (typically a
-;; C code primitive), enabling the computations to run in parallel
-;; while the scripting code runs single-threadedly.
-;;
-
-;;; Code:
-
-(define-module (ice-9 serialize)
- \:use-module (ice-9 threads)
- \:export (call-with-serialization
- call-with-parallelization)
- \:export-syntax (serialize
- parallelize))
-
-
-(define serialization-mutex (make-mutex))
-(define admin-mutex (make-mutex))
-(define owner #f)
-
-(define (call-with-serialization thunk)
- (let ((outer-owner #f))
- (dynamic-wind
- (lambda ()
- (lock-mutex admin-mutex)
- (set! outer-owner owner)
- (if (not (eqv? outer-owner (dynamic-root)))
- (begin
- (unlock-mutex admin-mutex)
- (lock-mutex serialization-mutex)
- (set! owner (dynamic-root)))
- (unlock-mutex admin-mutex)))
- thunk
- (lambda ()
- (lock-mutex admin-mutex)
- (if (not (eqv? outer-owner (dynamic-root)))
- (begin
- (set! owner #f)
- (unlock-mutex serialization-mutex)))
- (unlock-mutex admin-mutex)))))
-
-(define-macro (serialize . forms)
- `(call-with-serialization (lambda () ,@forms)))
-
-(define (call-with-parallelization thunk)
- (let ((outer-owner #f))
- (dynamic-wind
- (lambda ()
- (lock-mutex admin-mutex)
- (set! outer-owner owner)
- (if (eqv? outer-owner (dynamic-root))
- (begin
- (set! owner #f)
- (unlock-mutex serialization-mutex)))
- (unlock-mutex admin-mutex))
- thunk
- (lambda ()
- (lock-mutex admin-mutex)
- (if (eqv? outer-owner (dynamic-root))
- (begin
- (unlock-mutex admin-mutex)
- (lock-mutex serialization-mutex)
- (set! owner outer-owner))
- (unlock-mutex admin-mutex))))))
-
-(define-macro (parallelize . forms)
- `(call-with-parallelization (lambda () ,@forms)))
-;;;; Copyright (C) 1997, 2000, 2001, 2003, 2006, 2009, 2010, 2011,
-;;;; 2012 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-(define-module (ice-9 session)
- #\use-module (ice-9 documentation)
- #\use-module (ice-9 regex)
- #\use-module (ice-9 rdelim)
- #\use-module (ice-9 match)
- #\export (help
- add-value-help-handler! remove-value-help-handler!
- add-name-help-handler! remove-name-help-handler!
- apropos-hook
- apropos apropos-internal apropos-fold apropos-fold-accessible
- apropos-fold-exported apropos-fold-all source arity
- procedure-arguments
- module-commentary))
-
-
-
-(define *value-help-handlers*
- `(,(lambda (name value)
- (object-documentation value))))
-
-(define (add-value-help-handler! proc)
- "Adds a handler for performing `help' on a value.
-
-`proc' will be called as (PROC NAME VALUE). `proc' should return #t to
-indicate that it has performed help, a string to override the default
-object documentation, or #f to try the other handlers, potentially
-falling back on the normal behavior for `help'."
- (set! *value-help-handlers* (cons proc *value-help-handlers*)))
-
-(define (remove-value-help-handler! proc)
- "Removes a handler for performing `help' on a value."
- (set! *value-help-handlers* (delete! proc *value-help-handlers*)))
-
-(define (try-value-help name value)
- (or-map (lambda (proc) (proc name value)) *value-help-handlers*))
-
-
-(define *name-help-handlers* '())
-
-(define (add-name-help-handler! proc)
- "Adds a handler for performing `help' on a name.
-
-`proc' will be called with the unevaluated name as its argument. That is
-to say, when the user calls `(help FOO)', the name is FOO, exactly as
-the user types it.
-
-`proc' should return #t to indicate that it has performed help, a string
-to override the default object documentation, or #f to try the other
-handlers, potentially falling back on the normal behavior for `help'."
- (set! *name-help-handlers* (cons proc *name-help-handlers*)))
-
-(define (remove-name-help-handler! proc)
- "Removes a handler for performing `help' on a name."
- (set! *name-help-handlers* (delete! proc *name-help-handlers*)))
-
-(define (try-name-help name)
- (or-map (lambda (proc) (proc name)) *name-help-handlers*))
-
-
-;;; Documentation
-;;;
-(define-macro (help . exp)
- "(help [NAME])
-Prints useful information. Try `(help)'."
- (cond ((not (= (length exp) 1))
- (help-usage)
- '(begin))
- ((not (provided? 'regex))
- (display "`help' depends on the `regex' feature.
-You don't seem to have regular expressions installed.\n")
- '(begin))
- (else
- (let ((name (car exp))
- (not-found (lambda (type x)
- (simple-format #t "No ~A found for ~A\n"
- type x))))
- (cond
-
- ;; User-specified
- ((try-name-help name)
- => (lambda (x) (if (not (eq? x #t)) (display x))))
-
- ;; SYMBOL
- ((symbol? name)
- (help-doc name
- (simple-format
- #f "^~A$"
- (regexp-quote (symbol->string name)))))
-
- ;; "STRING"
- ((string? name)
- (help-doc name name))
-
- ;; (unquote SYMBOL)
- ((and (list? name)
- (= (length name) 2)
- (eq? (car name) 'unquote))
- (let ((doc (try-value-help (cadr name)
- (module-ref (current-module)
- (cadr name)))))
- (cond ((not doc) (not-found 'documentation (cadr name)))
- ((eq? doc #t)) ;; pass
- (else (write-line doc)))))
-
- ;; (quote SYMBOL)
- ((and (list? name)
- (= (length name) 2)
- (eq? (car name) 'quote)
- (symbol? (cadr name)))
- (cond ((search-documentation-files (cadr name))
- => write-line)
- (else (not-found 'documentation (cadr name)))))
-
- ;; (SYM1 SYM2 ...)
- ((and (list? name)
- (and-map symbol? name)
- (not (null? name))
- (not (eq? (car name) 'quote)))
- (cond ((module-commentary name)
- => (lambda (doc)
- (display name) (write-line " commentary:")
- (write-line doc)))
- (else (not-found 'commentary name))))
-
- ;; unrecognized
- (else
- (help-usage)))
- '(begin)))))
-
-(define (module-filename name) ; fixme: better way? / done elsewhere?
- (let* ((name (map symbol->string name))
- (reverse-name (reverse name))
- (leaf (car reverse-name))
- (dir-hint-module-name (reverse (cdr reverse-name)))
- (dir-hint (apply string-append
- (map (lambda (elt)
- (string-append elt "/"))
- dir-hint-module-name))))
- (%search-load-path (in-vicinity dir-hint leaf))))
-
-(define (module-commentary name)
- (cond ((module-filename name) => file-commentary)
- (else #f)))
-
-(define (help-doc term regexp)
- (let ((entries (apropos-fold (lambda (module name object data)
- (cons (list module
- name
- (try-value-help name object)
- (cond ((procedure? object)
- "a procedure")
- (else
- "an object")))
- data))
- '()
- regexp
- apropos-fold-exported))
- (module car)
- (name cadr)
- (doc caddr)
- (type cadddr))
- (cond ((not (null? entries))
- (let ((first? #t)
- (undocumented-entries '())
- (documented-entries '())
- (documentations '()))
-
- (for-each (lambda (entry)
- (let ((entry-summary (simple-format
- #f "~S: ~S\n"
- (module-name (module entry))
- (name entry))))
- (if (doc entry)
- (begin
- (set! documented-entries
- (cons entry-summary documented-entries))
- ;; *fixme*: Use `describe' when we have GOOPS?
- (set! documentations
- (cons (simple-format
- #f "`~S' is ~A in the ~S module.\n\n~A\n"
- (name entry)
- (type entry)
- (module-name (module entry))
- (doc entry))
- documentations)))
- (set! undocumented-entries
- (cons entry-summary
- undocumented-entries)))))
- entries)
-
- (if (and (not (null? documented-entries))
- (or (> (length documented-entries) 1)
- (not (null? undocumented-entries))))
- (begin
- (display "Documentation found for:\n")
- (for-each (lambda (entry) (display entry))
- documented-entries)
- (set! first? #f)))
-
- (for-each (lambda (entry)
- (if first?
- (set! first? #f)
- (newline))
- (display entry))
- documentations)
-
- (if (not (null? undocumented-entries))
- (begin
- (if first?
- (set! first? #f)
- (newline))
- (display "No documentation found for:\n")
- (for-each (lambda (entry) (display entry))
- undocumented-entries)))))
- ((search-documentation-files term)
- => (lambda (doc)
- (write-line "Documentation from file:")
- (write-line doc)))
- (else
- ;; no matches
- (display "Did not find any object ")
- (simple-format #t
- (if (symbol? term)
- "named `~A'\n"
- "matching regexp \"~A\"\n")
- term)))))
-
-(define (help-usage)
- (display "Usage: (help NAME) gives documentation about objects named NAME (a symbol)
- (help REGEXP) ditto for objects with names matching REGEXP (a string)
- (help 'NAME) gives documentation for NAME, even if it is not an object
- (help ,EXPR) gives documentation for object returned by EXPR
- (help (my module)) gives module commentary for `(my module)'
- (help) gives this text
-
-`help' searches among bindings exported from loaded modules, while
-`apropos' searches among bindings visible from the \"current\" module.
-
-Examples: (help help)
- (help cons)
- (help \"output-string\")
-
-Other useful sources of helpful information:
-
-(apropos STRING)
-(arity PROCEDURE)
-(name PROCEDURE-OR-MACRO)
-(source PROCEDURE-OR-MACRO)
-
-Tools:
-
-(backtrace) ;show backtrace from last error
-(debug) ;enter the debugger
-(trace [PROCEDURE]) ;trace procedure (no arg => show)
-(untrace [PROCEDURE]) ;untrace (no arg => untrace all)
-
-(OPTIONSET-options 'full) ;display option information
-(OPTIONSET-enable 'OPTION)
-(OPTIONSET-disable 'OPTION)
-(OPTIONSET-set! OPTION VALUE)
-
-where OPTIONSET is one of debug, read, eval, print
-
-"))
-
-;;; {Apropos}
-;;;
-;;; Author: Roland Orre <orre@nada.kth.se>
-;;;
-
-;; Two arguments: the module, and the pattern, as a string.
-;;
-(define apropos-hook (make-hook 2))
-
-(define (apropos rgx . options)
- "Search for bindings: apropos regexp {options= 'full 'shadow 'value}"
- (run-hook apropos-hook (current-module) rgx)
- (if (zero? (string-length rgx))
- "Empty string not allowed"
- (let* ((match (make-regexp rgx))
- (uses (module-uses (current-module)))
- (modules (cons (current-module)
- (if (and (not (null? uses))
- (eq? (module-name (car uses))
- 'duplicates))
- (cdr uses)
- uses)))
- (separator #\tab)
- (shadow (member 'shadow options))
- (value (member 'value options)))
- (cond ((member 'full options)
- (set! shadow #t)
- (set! value #t)))
- (for-each
- (lambda (module)
- (let* ((name (module-name module))
- (obarray (module-obarray module)))
- ;; XXX - should use hash-fold here
- (hash-for-each
- (lambda (symbol variable)
- (cond ((regexp-exec match (symbol->string symbol))
- (display name)
- (display ": ")
- (display symbol)
- (cond ((variable-bound? variable)
- (let ((val (variable-ref variable)))
- (cond ((or (procedure? val) value)
- (display separator)
- (display val)))))
- (else
- (display separator)
- (display "(unbound)")))
- (if (and shadow
- (not (eq? (module-ref module symbol)
- (module-ref (current-module) symbol))))
- (display " shadowed"))
- (newline))))
- obarray)))
- modules))))
-
-(define (apropos-internal rgx)
- "Return a list of accessible variable names."
- (apropos-fold (lambda (module name var data)
- (cons name data))
- '()
- rgx
- (apropos-fold-accessible (current-module))))
-
-(define (apropos-fold proc init rgx folder)
- "Folds PROCEDURE over bindings matching third arg REGEXP.
-
-Result is
-
- (PROCEDURE MODULE1 NAME1 VALUE1
- (PROCEDURE MODULE2 NAME2 VALUE2
- ...
- (PROCEDURE MODULEn NAMEn VALUEn INIT)))
-
-where INIT is the second arg to `apropos-fold'.
-
-Fourth arg FOLDER is one of
-
- (apropos-fold-accessible MODULE) ;fold over bindings accessible in MODULE
- apropos-fold-exported ;fold over all exported bindings
- apropos-fold-all ;fold over all bindings"
- (run-hook apropos-hook (current-module) rgx)
- (let ((match (make-regexp rgx))
- (recorded (make-hash-table)))
- (let ((fold-module
- (lambda (module data)
- (let* ((obarray-filter
- (lambda (name val data)
- (if (and (regexp-exec match (symbol->string name))
- (not (hashq-get-handle recorded name)))
- (begin
- (hashq-set! recorded name #t)
- (proc module name val data))
- data)))
- (module-filter
- (lambda (name var data)
- (if (variable-bound? var)
- (obarray-filter name (variable-ref var) data)
- data))))
- (cond (module (hash-fold module-filter
- data
- (module-obarray module)))
- (else data))))))
- (folder fold-module init))))
-
-(define (make-fold-modules init-thunk traverse extract)
- "Return procedure capable of traversing a forest of modules.
-The forest traversed is the image of the forest generated by root
-modules returned by INIT-THUNK and the generator TRAVERSE.
-It is an image under the mapping EXTRACT."
- (lambda (fold-module init)
- (let* ((table (make-hash-table 31))
- (first? (lambda (obj)
- (let* ((handle (hash-create-handle! table obj #t))
- (first? (cdr handle)))
- (set-cdr! handle #f)
- first?))))
- (let rec ((data init)
- (modules (init-thunk)))
- (do ((modules modules (cdr modules))
- (data data (if (first? (car modules))
- (rec (fold-module (extract (car modules)) data)
- (traverse (car modules)))
- data)))
- ((null? modules) data))))))
-
-(define (apropos-fold-accessible module)
- (make-fold-modules (lambda () (list module))
- module-uses
- identity))
-
-(define (root-modules)
- (submodules (resolve-module '() #f)))
-
-(define (submodules mod)
- (hash-map->list (lambda (k v) v) (module-submodules mod)))
-
-(define apropos-fold-exported
- (make-fold-modules root-modules submodules module-public-interface))
-
-(define apropos-fold-all
- (make-fold-modules root-modules submodules identity))
-
-(define (source obj)
- (cond ((procedure? obj) (procedure-source obj))
- ((macro? obj) (procedure-source (macro-transformer obj)))
- (else #f)))
-
-(define (arity obj)
- (define (display-arg-list arg-list)
- (display #\`)
- (display (car arg-list))
- (let loop ((ls (cdr arg-list)))
- (cond ((null? ls)
- (display #\'))
- ((not (pair? ls))
- (display "', the rest in `")
- (display ls)
- (display #\'))
- (else
- (if (pair? (cdr ls))
- (display "', `")
- (display "' and `"))
- (display (car ls))
- (loop (cdr ls))))))
- (define (display-arg-list/summary arg-list type)
- (let ((len (length arg-list)))
- (display len)
- (display " ")
- (display type)
- (if (> len 1)
- (display " arguments: ")
- (display " argument: "))
- (display-arg-list arg-list)))
- (cond
- ((procedure-property obj 'arglist)
- => (lambda (arglist)
- (let ((required-args (car arglist))
- (optional-args (cadr arglist))
- (keyword-args (caddr arglist))
- (allow-other-keys? (cadddr arglist))
- (rest-arg (car (cddddr arglist)))
- (need-punctuation #f))
- (cond ((not (null? required-args))
- (display-arg-list/summary required-args "required")
- (set! need-punctuation #t)))
- (cond ((not (null? optional-args))
- (if need-punctuation (display ", "))
- (display-arg-list/summary optional-args "optional")
- (set! need-punctuation #t)))
- (cond ((not (null? keyword-args))
- (if need-punctuation (display ", "))
- (display-arg-list/summary keyword-args "keyword")
- (set! need-punctuation #t)))
- (cond (allow-other-keys?
- (if need-punctuation (display ", "))
- (display "other keywords allowed")
- (set! need-punctuation #t)))
- (cond (rest-arg
- (if need-punctuation (display ", "))
- (display "the rest in `")
- (display rest-arg)
- (display "'"))))))
- (else
- (let ((arity (procedure-minimum-arity obj)))
- (display (car arity))
- (cond ((caddr arity)
- (display " or more"))
- ((not (zero? (cadr arity)))
- (display " required and ")
- (display (cadr arity))
- (display " optional")))
- (if (and (not (caddr arity))
- (= (car arity) 1)
- (<= (cadr arity) 1))
- (display " argument")
- (display " arguments")))))
- (display ".\n"))
-
-
-(define (procedure-arguments proc)
- "Return an alist describing the arguments that `proc' accepts, or `#f'
-if the information cannot be obtained.
-
-The alist keys that are currently defined are `required', `optional',
-`keyword', `allow-other-keys?', and `rest'."
- (cond
- ((procedure-property proc 'arglist)
- => (match-lambda
- ((req opt keyword aok? rest)
- `((required . ,(if (number? req)
- (make-list req '_)
- req))
- (optional . ,(if (number? opt)
- (make-list opt '_)
- opt))
- (keyword . ,keyword)
- (allow-other-keys? . ,aok?)
- (rest . ,rest)))))
- ((procedure-source proc)
- => cadr)
- (((@ (system vm program) program?) proc)
- ((@ (system vm program) program-arguments-alist) proc))
- (else #f)))
-
-
-;;; session.scm ends here
-;;;; slib.scm --- definitions needed to get SLIB to work with Guile
-;;;;
-;;;; Copyright (C) 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2006, 2007, 2013 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Look for slib.init in the $datadir, in /usr/share, and finally in
-;;; the load path. It's not usually in the load path on common distros,
-;;; but it could be if the user put it there. The init file takes care
-;;; of defining the module.
-
-(let ((try-load (lambda (dir)
- (let ((init (string-append dir "/slib/guile.init")))
- (and (file-exists? init)
- (begin
- (load init)
- #t))))))
- (or (try-load (assq-ref %guile-build-info 'datadir))
- (try-load "/usr/share")
- (load-from-path "slib/guile.init")))
-;;; installed-scm-file
-
-;;;; Copyright (C) 2001, 2006, 2010 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-(define-module (ice-9 stack-catch)
- #\use-module (ice-9 save-stack)
- #\export (stack-catch))
-
-(define (stack-catch key thunk handler)
- "Like @code{catch}, invoke @var{thunk} in the dynamic context of
-@var{handler} for exceptions matching @var{key}, but also save the
-current stack state in the @var{the-last-stack} fluid, for the purpose
-of debugging or re-throwing of an error. If thunk throws to the
-symbol @var{key}, then @var{handler} is invoked this way:\n
-@example
- (handler key args ...)
-@end example\n
-@var{key} is a symbol or #t.\n
-@var{thunk} takes no arguments. If @var{thunk} returns normally, that
-is the return value of @code{catch}.\n
-Handler is invoked outside the scope of its own @code{catch}. If
-@var{handler} again throws to the same key, a new handler from further
-up the call chain is invoked.\n
-If the key is @code{#t}, then a throw to @emph{any} symbol will match
-this call to @code{catch}."
- (catch key
- thunk
- handler
- (lambda (key . args)
- ;; Narrow by two more frames: this one, and the throw handler.
- (save-stack 2)
- (apply throw key args))))
-;;;; streams.scm --- general lazy streams
-;;;; -*- Scheme -*-
-
-;;;; Copyright (C) 1999, 2001, 2004, 2006 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;; the basic stream operations are inspired by
-;; (i.e. ripped off) Scheme48's `stream' package,
-;; modulo stream-empty? -> stream-null? renaming.
-
-(define-module (ice-9 streams)
- \:export (make-stream
- stream-car stream-cdr stream-null?
- list->stream vector->stream port->stream
- stream->list stream->reversed-list
- stream->list&length stream->reversed-list&length
- stream->vector
- stream-fold stream-for-each stream-map))
-
-;; Use:
-;;
-;; (make-stream producer initial-state)
-;; - PRODUCER is a function of one argument, the current state.
-;; it should return either a pair or an atom (i.e. anything that
-;; is not a pair). if PRODUCER returns a pair, then the car of the pair
-;; is the stream's head value, and the cdr is the state to be fed
-;; to PRODUCER later. if PRODUCER returns an atom, then the stream is
-;; considered depleted.
-;;
-;; (stream-car stream)
-;; (stream-cdr stream)
-;; (stream-null? stream)
-;; - yes.
-;;
-;; (list->stream list)
-;; (vector->stream vector)
-;; - make a stream with the same contents as LIST/VECTOR.
-;;
-;; (port->stream port read)
-;; - makes a stream of values which are obtained by READing from PORT.
-;;
-;; (stream->list stream)
-;; - returns a list with the same contents as STREAM.
-;;
-;; (stream->reversed-list stream)
-;; - as above, except the contents are in reversed order.
-;;
-;; (stream->list&length stream)
-;; (stream->reversed-list&length stream)
-;; - multiple-valued versions of the above two, the second value is the
-;; length of the resulting list (so you get it for free).
-;;
-;; (stream->vector stream)
-;; - yes.
-;;
-;; (stream-fold proc init stream0 ...)
-;; - PROC must take (+ 1 <number-of-stream-arguments>) arguments, like this:
-;; (PROC car0 ... init). *NOTE*: the INIT argument is last, not first.
-;; I don't have any preference either way, but it's consistent with
-;; `fold[lr]' procedures from SRFI-1. PROC is applied to successive
-;; elements of the given STREAM(s) and to the value of the previous
-;; invocation (INIT on the first invocation). the last result from PROC
-;; is returned.
-;;
-;; (stream-for-each proc stream0 ...)
-;; - like `for-each' we all know and love.
-;;
-;; (stream-map proc stream0 ...)
-;; - like `map', except returns a stream of results, and not a list.
-
-;; Code:
-
-(define (make-stream m state)
- (delay
- (let ((o (m state)))
- (if (pair? o)
- (cons (car o)
- (make-stream m (cdr o)))
- '()))))
-
-(define (stream-car stream)
- "Returns the first element in STREAM. This is equivalent to `car'."
- (car (force stream)))
-
-(define (stream-cdr stream)
- "Returns the first tail of STREAM. Equivalent to `(force (cdr STREAM))'."
- (cdr (force stream)))
-
-(define (stream-null? stream)
- "Returns `#t' if STREAM is the end-of-stream marker; otherwise
-returns `#f'. This is equivalent to `null?', but should be used
-whenever testing for the end of a stream."
- (null? (force stream)))
-
-(define (list->stream l)
- "Returns a newly allocated stream whose elements are the elements of
-LIST. Equivalent to `(apply stream LIST)'."
- (make-stream
- (lambda (l) l)
- l))
-
-(define (vector->stream v)
- (make-stream
- (let ((len (vector-length v)))
- (lambda (i)
- (or (= i len)
- (cons (vector-ref v i) (+ 1 i)))))
- 0))
-
-(define (stream->reversed-list&length stream)
- (let loop ((s stream) (acc '()) (len 0))
- (if (stream-null? s)
- (values acc len)
- (loop (stream-cdr s) (cons (stream-car s) acc) (+ 1 len)))))
-
-(define (stream->reversed-list stream)
- (call-with-values
- (lambda () (stream->reversed-list&length stream))
- (lambda (l len) l)))
-
-(define (stream->list&length stream)
- (call-with-values
- (lambda () (stream->reversed-list&length stream))
- (lambda (l len) (values (reverse! l) len))))
-
-(define (stream->list stream)
- "Returns a newly allocated list whose elements are the elements of STREAM.
-If STREAM has infinite length this procedure will not terminate."
- (reverse! (stream->reversed-list stream)))
-
-(define (stream->vector stream)
- (call-with-values
- (lambda () (stream->reversed-list&length stream))
- (lambda (l len)
- (let ((v (make-vector len)))
- (let loop ((i 0) (l l))
- (if (not (null? l))
- (begin
- (vector-set! v (- len i 1) (car l))
- (loop (+ 1 i) (cdr l)))))
- v))))
-
-(define (stream-fold f init stream . rest)
- (if (null? rest) ;fast path
- (stream-fold-one f init stream)
- (stream-fold-many f init (cons stream rest))))
-
-(define (stream-fold-one f r stream)
- (if (stream-null? stream)
- r
- (stream-fold-one f (f (stream-car stream) r) (stream-cdr stream))))
-
-(define (stream-fold-many f r streams)
- (if (or-map stream-null? streams)
- r
- (stream-fold-many f
- (apply f (let recur ((cars
- (map stream-car streams)))
- (if (null? cars)
- (list r)
- (cons (car cars)
- (recur (cdr cars))))))
- (map stream-cdr streams))))
-
-(define (stream-for-each f stream . rest)
- (if (null? rest) ;fast path
- (stream-for-each-one f stream)
- (stream-for-each-many f (cons stream rest))))
-
-(define (stream-for-each-one f stream)
- (if (not (stream-null? stream))
- (begin
- (f (stream-car stream))
- (stream-for-each-one f (stream-cdr stream)))))
-
-(define (stream-for-each-many f streams)
- (if (not (or-map stream-null? streams))
- (begin
- (apply f (map stream-car streams))
- (stream-for-each-many f (map stream-cdr streams)))))
-
-(define (stream-map f stream . rest)
- "Returns a newly allocated stream, each element being the result of
-invoking F with the corresponding elements of the STREAMs
-as its arguments."
- (if (null? rest) ;fast path
- (make-stream (lambda (s)
- (or (stream-null? s)
- (cons (f (stream-car s)) (stream-cdr s))))
- stream)
- (make-stream (lambda (streams)
- (or (or-map stream-null? streams)
- (cons (apply f (map stream-car streams))
- (map stream-cdr streams))))
- (cons stream rest))))
-
-(define (port->stream port read)
- (make-stream (lambda (p)
- (let ((o (read p)))
- (or (eof-object? o)
- (cons o p))))
- port))
-
-;;; streams.scm ends here
-;;;; string-fun.scm --- string manipulation functions
-;;;;
-;;;; Copyright (C) 1995, 1996, 1997, 1999, 2001, 2006 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-(define-module (ice-9 string-fun)
- \:export (split-after-char split-before-char split-discarding-char
- split-after-char-last split-before-char-last
- split-discarding-char-last split-before-predicate
- split-after-predicate split-discarding-predicate
- separate-fields-discarding-char separate-fields-after-char
- separate-fields-before-char string-prefix-predicate string-prefix=?
- sans-surrounding-whitespace sans-trailing-whitespace
- sans-leading-whitespace sans-final-newline has-trailing-newline?))
-
-;;;;
-;;;
-;;; Various string funcitons, particularly those that take
-;;; advantage of the "shared substring" capability.
-;;;
-
-;;; {String Fun: Dividing Strings Into Fields}
-;;;
-;;; The names of these functions are very regular.
-;;; Here is a grammar of a call to one of these:
-;;;
-;;; <string-function-invocation>
-;;; := (<action>-<seperator-disposition>-<seperator-determination> <seperator-param> <str> <ret>)
-;;;
-;;; <str> = the string
-;;;
-;;; <ret> = The continuation. String functions generally return
-;;; multiple values by passing them to this procedure.
-;;;
-;;; <action> = split
-;;; | separate-fields
-;;;
-;;; "split" means to divide a string into two parts.
-;;; <ret> will be called with two arguments.
-;;;
-;;; "separate-fields" means to divide a string into as many
-;;; parts as possible. <ret> will be called with
-;;; however many fields are found.
-;;;
-;;; <seperator-disposition> = before
-;;; | after
-;;; | discarding
-;;;
-;;; "before" means to leave the seperator attached to
-;;; the beginning of the field to its right.
-;;; "after" means to leave the seperator attached to
-;;; the end of the field to its left.
-;;; "discarding" means to discard seperators.
-;;;
-;;; Other dispositions might be handy. For example, "isolate"
-;;; could mean to treat the separator as a field unto itself.
-;;;
-;;; <seperator-determination> = char
-;;; | predicate
-;;;
-;;; "char" means to use a particular character as field seperator.
-;;; "predicate" means to check each character using a particular predicate.
-;;;
-;;; Other determinations might be handy. For example, "character-set-member".
-;;;
-;;; <seperator-param> = A parameter that completes the meaning of the determinations.
-;;; For example, if the determination is "char", then this parameter
-;;; says which character. If it is "predicate", the parameter is the
-;;; predicate.
-;;;
-;;;
-;;; For example:
-;;;
-;;; (separate-fields-discarding-char #\, "foo, bar, baz, , bat" list)
-;;; => ("foo" " bar" " baz" " " " bat")
-;;;
-;;; (split-after-char #\- 'an-example-of-split list)
-;;; => ("an-" "example-of-split")
-;;;
-;;; As an alternative to using a determination "predicate", or to trying to do anything
-;;; complicated with these functions, consider using regular expressions.
-;;;
-
-(define (split-after-char char str ret)
- (let ((end (cond
- ((string-index str char) => 1+)
- (else (string-length str)))))
- (ret (substring str 0 end)
- (substring str end))))
-
-(define (split-before-char char str ret)
- (let ((end (or (string-index str char)
- (string-length str))))
- (ret (substring str 0 end)
- (substring str end))))
-
-(define (split-discarding-char char str ret)
- (let ((end (string-index str char)))
- (if (not end)
- (ret str "")
- (ret (substring str 0 end)
- (substring str (1+ end))))))
-
-(define (split-after-char-last char str ret)
- (let ((end (cond
- ((string-rindex str char) => 1+)
- (else 0))))
- (ret (substring str 0 end)
- (substring str end))))
-
-(define (split-before-char-last char str ret)
- (let ((end (or (string-rindex str char) 0)))
- (ret (substring str 0 end)
- (substring str end))))
-
-(define (split-discarding-char-last char str ret)
- (let ((end (string-rindex str char)))
- (if (not end)
- (ret str "")
- (ret (substring str 0 end)
- (substring str (1+ end))))))
-
-(define (split-before-predicate pred str ret)
- (let loop ((n 0))
- (cond
- ((= n (string-length str)) (ret str ""))
- ((not (pred (string-ref str n))) (loop (1+ n)))
- (else (ret (substring str 0 n)
- (substring str n))))))
-(define (split-after-predicate pred str ret)
- (let loop ((n 0))
- (cond
- ((= n (string-length str)) (ret str ""))
- ((not (pred (string-ref str n))) (loop (1+ n)))
- (else (ret (substring str 0 (1+ n))
- (substring str (1+ n)))))))
-
-(define (split-discarding-predicate pred str ret)
- (let loop ((n 0))
- (cond
- ((= n (string-length str)) (ret str ""))
- ((not (pred (string-ref str n))) (loop (1+ n)))
- (else (ret (substring str 0 n)
- (substring str (1+ n)))))))
-
-(define (separate-fields-discarding-char ch str ret)
- (let loop ((fields '())
- (str str))
- (cond
- ((string-rindex str ch)
- => (lambda (w) (loop (cons (substring str (+ 1 w)) fields)
- (substring str 0 w))))
- (else (apply ret str fields)))))
-
-(define (separate-fields-after-char ch str ret)
- (reverse
- (let loop ((fields '())
- (str str))
- (cond
- ((string-index str ch)
- => (lambda (w) (loop (cons (substring str 0 (+ 1 w)) fields)
- (substring str (+ 1 w)))))
- (else (apply ret str fields))))))
-
-(define (separate-fields-before-char ch str ret)
- (let loop ((fields '())
- (str str))
- (cond
- ((string-rindex str ch)
- => (lambda (w) (loop (cons (substring str w) fields)
- (substring str 0 w))))
- (else (apply ret str fields)))))
-
-
-;;; {String Fun: String Prefix Predicates}
-;;;
-;;; Very simple:
-;;;
-;;; (define-public ((string-prefix-predicate pred?) prefix str)
-;;; (and (<= (string-length prefix) (string-length str))
-;;; (pred? prefix (substring str 0 (string-length prefix)))))
-;;;
-;;; (define-public string-prefix=? (string-prefix-predicate string=?))
-;;;
-
-(define (string-prefix-predicate pred?)
- (lambda (prefix str)
- (and (<= (string-length prefix) (string-length str))
- (pred? prefix (substring str 0 (string-length prefix))))))
-
-(define string-prefix=? (string-prefix-predicate string=?))
-
-
-;;; {String Fun: Strippers}
-;;;
-;;; <stripper> = sans-<removable-part>
-;;;
-;;; <removable-part> = surrounding-whitespace
-;;; | trailing-whitespace
-;;; | leading-whitespace
-;;; | final-newline
-;;;
-
-(define (sans-surrounding-whitespace s)
- (let ((st 0)
- (end (string-length s)))
- (while (and (< st (string-length s))
- (char-whitespace? (string-ref s st)))
- (set! st (1+ st)))
- (while (and (< 0 end)
- (char-whitespace? (string-ref s (1- end))))
- (set! end (1- end)))
- (if (< end st)
- ""
- (substring s st end))))
-
-(define (sans-trailing-whitespace s)
- (let ((st 0)
- (end (string-length s)))
- (while (and (< 0 end)
- (char-whitespace? (string-ref s (1- end))))
- (set! end (1- end)))
- (if (< end st)
- ""
- (substring s st end))))
-
-(define (sans-leading-whitespace s)
- (let ((st 0)
- (end (string-length s)))
- (while (and (< st (string-length s))
- (char-whitespace? (string-ref s st)))
- (set! st (1+ st)))
- (if (< end st)
- ""
- (substring s st end))))
-
-(define (sans-final-newline str)
- (cond
- ((= 0 (string-length str))
- str)
-
- ((char=? #\nl (string-ref str (1- (string-length str))))
- (substring str 0 (1- (string-length str))))
-
- (else str)))
-
-;;; {String Fun: has-trailing-newline?}
-;;;
-
-(define (has-trailing-newline? str)
- (and (< 0 (string-length str))
- (char=? #\nl (string-ref str (1- (string-length str))))))
-
-
-
-;;; {String Fun: with-regexp-parts}
-
-;;; This relies on the older, hairier regexp interface, which we don't
-;;; particularly want to implement, and it's not used anywhere, so
-;;; we're just going to drop it for now.
-;;; (define-public (with-regexp-parts regexp fields str return fail)
-;;; (let ((parts (regexec regexp str fields)))
-;;; (if (number? parts)
-;;; (fail parts)
-;;; (apply return parts))))
-
-;;;; Copyright (C) 1997, 2000, 2001, 2002, 2003, 2006, 2010 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-(define-module (ice-9 syncase)
- ;; FIXME re-export other procs
- #\export (datum->syntax-object syntax-object->datum
- sc-expand))
-
-(issue-deprecation-warning
- "Syntax-case macros are now a part of Guile core; importing (ice-9 syncase) is no longer necessary.")
-
-(define datum->syntax-object datum->syntax)
-(define syntax-object->datum syntax->datum)
-(define sc-expand macroexpand)
-
-;;; Hack to make syncase macros work in the slib module
-;; FIXME wingo is this still necessary?
-;; (let ((m (nested-ref the-root-module '(%app modules ice-9 slib))))
-;; (if m
-;; (set-object-property! (module-local-variable m 'define)
-;; '*sc-expander*
-;; '(define))))
-;; Copyright (C) 1991, 1992, 1993, 1994, 1995, 2006 Free Software Foundation, Inc.
-;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;;; "test.scm" Test correctness of scheme implementations.
-;;; Author: Aubrey Jaffer
-;;; Modified: Mikael Djurfeldt (Removed tests which Guile deliberately
-;;; won't pass. Made the tests (test-cont), (test-sc4), and
-;;; (test-delay) start to run automatically.
-
-;;; This includes examples from
-;;; William Clinger and Jonathan Rees, editors.
-;;; Revised^4 Report on the Algorithmic Language Scheme
-;;; and the IEEE specification.
-
-;;; The input tests read this file expecting it to be named
-;;; "test.scm", so you'll have to run it from the ice-9 source
-;;; directory, or copy this file elsewhere
-;;; Files `tmp1', `tmp2' and `tmp3' will be created in the course of running
-;;; these tests. You may need to delete them in order to run
-;;; "test.scm" more than once.
-
-;;; There are three optional tests:
-;;; (TEST-CONT) tests multiple returns from call-with-current-continuation
-;;;
-;;; (TEST-SC4) tests procedures required by R4RS but not by IEEE
-;;;
-;;; (TEST-DELAY) tests DELAY and FORCE, which are not required by
-;;; either standard.
-
-;;; If you are testing a R3RS version which does not have `list?' do:
-;;; (define list? #f)
-
-;;; send corrections or additions to jaffer@ai.mit.edu or
-;;; Aubrey Jaffer, 84 Pleasant St., Wakefield MA 01880, USA
-
-(define cur-section '())(define errs '())
-(define SECTION (lambda args
- (display "SECTION") (write args) (newline)
- (set! cur-section args) #t))
-(define record-error (lambda (e) (set! errs (cons (list cur-section e) errs))))
-
-(define test
- (lambda (expect fun . args)
- (write (cons fun args))
- (display " ==> ")
- ((lambda (res)
- (write res)
- (newline)
- (cond ((not (equal? expect res))
- (record-error (list res expect (cons fun args)))
- (display " BUT EXPECTED ")
- (write expect)
- (newline)
- #f)
- (else #t)))
- (if (procedure? fun) (apply fun args) (car args)))))
-(define (report-errs)
- (newline)
- (if (null? errs) (display "Passed all tests")
- (begin
- (display "errors were:")
- (newline)
- (display "(SECTION (got expected (call)))")
- (newline)
- (for-each (lambda (l) (write l) (newline))
- errs)))
- (newline))
-
-(SECTION 2 1);; test that all symbol characters are supported.
-;'(+ - ... !.. $.+ %.- &.! *.: /:. :+. <-. =. >. ?. ~. _. ^.)
-
-(SECTION 3 4)
-(define disjoint-type-functions
- (list boolean? char? null? number? pair? procedure? string? symbol? vector?))
-(define type-examples
- (list
- #t #f #\a '() 9739 '(test) record-error "test" "" 'test '#() '#(a b c) ))
-(define i 1)
-(for-each (lambda (x) (display (make-string i #\space))
- (set! i (+ 3 i))
- (write x)
- (newline))
- disjoint-type-functions)
-(define type-matrix
- (map (lambda (x)
- (let ((t (map (lambda (f) (f x)) disjoint-type-functions)))
- (write t)
- (write x)
- (newline)
- t))
- type-examples))
-(SECTION 4 1 2)
-(test '(quote a) 'quote (quote 'a))
-(test '(quote a) 'quote ''a)
-(SECTION 4 1 3)
-(test 12 (if #f + *) 3 4)
-(SECTION 4 1 4)
-(test 8 (lambda (x) (+ x x)) 4)
-(define reverse-subtract
- (lambda (x y) (- y x)))
-(test 3 reverse-subtract 7 10)
-(define add4
- (let ((x 4))
- (lambda (y) (+ x y))))
-(test 10 add4 6)
-(test '(3 4 5 6) (lambda x x) 3 4 5 6)
-(test '(5 6) (lambda (x y . z) z) 3 4 5 6)
-(SECTION 4 1 5)
-(test 'yes 'if (if (> 3 2) 'yes 'no))
-(test 'no 'if (if (> 2 3) 'yes 'no))
-(test '1 'if (if (> 3 2) (- 3 2) (+ 3 2)))
-(SECTION 4 1 6)
-(define x 2)
-(test 3 'define (+ x 1))
-(set! x 4)
-(test 5 'set! (+ x 1))
-(SECTION 4 2 1)
-(test 'greater 'cond (cond ((> 3 2) 'greater)
- ((< 3 2) 'less)))
-(test 'equal 'cond (cond ((> 3 3) 'greater)
- ((< 3 3) 'less)
- (else 'equal)))
-(test 2 'cond (cond ((assv 'b '((a 1) (b 2))) => cadr)
- (else #f)))
-(test 'composite 'case (case (* 2 3)
- ((2 3 5 7) 'prime)
- ((1 4 6 8 9) 'composite)))
-(test 'consonant 'case (case (car '(c d))
- ((a e i o u) 'vowel)
- ((w y) 'semivowel)
- (else 'consonant)))
-(test #t 'and (and (= 2 2) (> 2 1)))
-(test #f 'and (and (= 2 2) (< 2 1)))
-(test '(f g) 'and (and 1 2 'c '(f g)))
-(test #t 'and (and))
-(test #t 'or (or (= 2 2) (> 2 1)))
-(test #t 'or (or (= 2 2) (< 2 1)))
-(test #f 'or (or #f #f #f))
-(test #f 'or (or))
-(test '(b c) 'or (or (memq 'b '(a b c)) (+ 3 0)))
-(SECTION 4 2 2)
-(test 6 'let (let ((x 2) (y 3)) (* x y)))
-(test 35 'let (let ((x 2) (y 3)) (let ((x 7) (z (+ x y))) (* z x))))
-(test 70 'let* (let ((x 2) (y 3)) (let* ((x 7) (z (+ x y))) (* z x))))
-(test #t 'letrec (letrec ((even?
- (lambda (n) (if (zero? n) #t (odd? (- n 1)))))
- (odd?
- (lambda (n) (if (zero? n) #f (even? (- n 1))))))
- (even? 88)))
-(define x 34)
-(test 5 'let (let ((x 3)) (define x 5) x))
-(test 34 'let x)
-(test 6 'let (let () (define x 6) x))
-(test 34 'let x)
-(test 7 'let* (let* ((x 3)) (define x 7) x))
-(test 34 'let* x)
-(test 8 'let* (let* () (define x 8) x))
-(test 34 'let* x)
-(test 9 'letrec (letrec () (define x 9) x))
-(test 34 'letrec x)
-(test 10 'letrec (letrec ((x 3)) (define x 10) x))
-(test 34 'letrec x)
-(SECTION 4 2 3)
-(define x 0)
-(test 6 'begin (begin (set! x 5) (+ x 1)))
-(SECTION 4 2 4)
-(test '#(0 1 2 3 4) 'do (do ((vec (make-vector 5))
- (i 0 (+ i 1)))
- ((= i 5) vec)
- (vector-set! vec i i)))
-(test 25 'do (let ((x '(1 3 5 7 9)))
- (do ((x x (cdr x))
- (sum 0 (+ sum (car x))))
- ((null? x) sum))))
-(test 1 'let (let foo () 1))
-(test '((6 1 3) (-5 -2)) 'let
- (let loop ((numbers '(3 -2 1 6 -5))
- (nonneg '())
- (neg '()))
- (cond ((null? numbers) (list nonneg neg))
- ((negative? (car numbers))
- (loop (cdr numbers)
- nonneg
- (cons (car numbers) neg)))
- (else
- (loop (cdr numbers)
- (cons (car numbers) nonneg)
- neg)))))
-(SECTION 4 2 6)
-(test '(list 3 4) 'quasiquote `(list ,(+ 1 2) 4))
-(test '(list a (quote a)) 'quasiquote (let ((name 'a)) `(list ,name ',name)))
-(test '(a 3 4 5 6 b) 'quasiquote `(a ,(+ 1 2) ,@(map abs '(4 -5 6)) b))
-(test '((foo 7) . cons)
- 'quasiquote
- `((foo ,(- 10 3)) ,@(cdr '(c)) . ,(car '(cons))))
-
-;;; sqt is defined here because not all implementations are required to
-;;; support it.
-(define (sqt x)
- (do ((i 0 (+ i 1)))
- ((> (* i i) x) (- i 1))))
-
-(test '#(10 5 2 4 3 8) 'quasiquote `#(10 5 ,(sqt 4) ,@(map sqt '(16 9)) 8))
-(test 5 'quasiquote `,(+ 2 3))
-(test '(a `(b ,(+ 1 2) ,(foo 4 d) e) f)
- 'quasiquote `(a `(b ,(+ 1 2) ,(foo ,(+ 1 3) d) e) f))
-(test '(a `(b ,x ,'y d) e) 'quasiquote
- (let ((name1 'x) (name2 'y)) `(a `(b ,,name1 ,',name2 d) e)))
-(test '(list 3 4) 'quasiquote (quasiquote (list (unquote (+ 1 2)) 4)))
-(test '`(list ,(+ 1 2) 4) 'quasiquote '(quasiquote (list (unquote (+ 1 2)) 4)))
-(SECTION 5 2 1)
-(define add3 (lambda (x) (+ x 3)))
-(test 6 'define (add3 3))
-(define first car)
-(test 1 'define (first '(1 2)))
-(SECTION 5 2 2)
-(test 45 'define
- (let ((x 5))
- (define foo (lambda (y) (bar x y)))
- (define bar (lambda (a b) (+ (* a b) a)))
- (foo (+ x 3))))
-(define x 34)
-(define (foo) (define x 5) x)
-(test 5 foo)
-(test 34 'define x)
-(define foo (lambda () (define x 5) x))
-(test 5 foo)
-(test 34 'define x)
-(define (foo x) ((lambda () (define x 5) x)) x)
-(test 88 foo 88)
-(test 4 foo 4)
-(test 34 'define x)
-(SECTION 6 1)
-(test #f not #t)
-(test #f not 3)
-(test #f not (list 3))
-(test #t not #f)
-(test #f not '())
-(test #f not (list))
-(test #f not 'nil)
-
-(test #t boolean? #f)
-(test #f boolean? 0)
-(test #f boolean? '())
-(SECTION 6 2)
-(test #t eqv? 'a 'a)
-(test #f eqv? 'a 'b)
-(test #t eqv? 2 2)
-(test #t eqv? '() '())
-(test #t eqv? '10000 '10000)
-(test #f eqv? (cons 1 2)(cons 1 2))
-(test #f eqv? (lambda () 1) (lambda () 2))
-(test #f eqv? #f 'nil)
-(let ((p (lambda (x) x)))
- (test #t eqv? p p))
-(define gen-counter
- (lambda ()
- (let ((n 0))
- (lambda () (set! n (+ n 1)) n))))
-(let ((g (gen-counter))) (test #t eqv? g g))
-(test #f eqv? (gen-counter) (gen-counter))
-(letrec ((f (lambda () (if (eqv? f g) 'f 'both)))
- (g (lambda () (if (eqv? f g) 'g 'both))))
- (test #f eqv? f g))
-
-(test #t eq? 'a 'a)
-(test #f eq? (list 'a) (list 'a))
-(test #t eq? '() '())
-(test #t eq? car car)
-(let ((x '(a))) (test #t eq? x x))
-(let ((x '#())) (test #t eq? x x))
-(let ((x (lambda (x) x))) (test #t eq? x x))
-
-(test #t equal? 'a 'a)
-(test #t equal? '(a) '(a))
-(test #t equal? '(a (b) c) '(a (b) c))
-(test #t equal? "abc" "abc")
-(test #t equal? 2 2)
-(test #t equal? (make-vector 5 'a) (make-vector 5 'a))
-(SECTION 6 3)
-(test '(a b c d e) 'dot '(a . (b . (c . (d . (e . ()))))))
-(define x (list 'a 'b 'c))
-(define y x)
-(and list? (test #t list? y))
-(set-cdr! x 4)
-(test '(a . 4) 'set-cdr! x)
-(test #t eqv? x y)
-(test '(a b c . d) 'dot '(a . (b . (c . d))))
-(and list? (test #f list? y))
-(and list? (let ((x (list 'a))) (set-cdr! x x) (test #f 'list? (list? x))))
-
-(test #t pair? '(a . b))
-(test #t pair? '(a . 1))
-(test #t pair? '(a b c))
-(test #f pair? '())
-(test #f pair? '#(a b))
-
-(test '(a) cons 'a '())
-(test '((a) b c d) cons '(a) '(b c d))
-(test '("a" b c) cons "a" '(b c))
-(test '(a . 3) cons 'a 3)
-(test '((a b) . c) cons '(a b) 'c)
-
-(test 'a car '(a b c))
-(test '(a) car '((a) b c d))
-(test 1 car '(1 . 2))
-
-(test '(b c d) cdr '((a) b c d))
-(test 2 cdr '(1 . 2))
-
-(test '(a 7 c) list 'a (+ 3 4) 'c)
-(test '() list)
-
-(test 3 length '(a b c))
-(test 3 length '(a (b) (c d e)))
-(test 0 length '())
-
-(test '(x y) append '(x) '(y))
-(test '(a b c d) append '(a) '(b c d))
-(test '(a (b) (c)) append '(a (b)) '((c)))
-(test '() append)
-(test '(a b c . d) append '(a b) '(c . d))
-(test 'a append '() 'a)
-
-(test '(c b a) reverse '(a b c))
-(test '((e (f)) d (b c) a) reverse '(a (b c) d (e (f))))
-
-(test 'c list-ref '(a b c d) 2)
-
-(test '(a b c) memq 'a '(a b c))
-(test '(b c) memq 'b '(a b c))
-(test '#f memq 'a '(b c d))
-(test '#f memq (list 'a) '(b (a) c))
-(test '((a) c) member (list 'a) '(b (a) c))
-(test '(101 102) memv 101 '(100 101 102))
-
-(define e '((a 1) (b 2) (c 3)))
-(test '(a 1) assq 'a e)
-(test '(b 2) assq 'b e)
-(test #f assq 'd e)
-(test #f assq (list 'a) '(((a)) ((b)) ((c))))
-(test '((a)) assoc (list 'a) '(((a)) ((b)) ((c))))
-(test '(5 7) assv 5 '((2 3) (5 7) (11 13)))
-(SECTION 6 4)
-(test #t symbol? 'foo)
-(test #t symbol? (car '(a b)))
-(test #f symbol? "bar")
-(test #t symbol? 'nil)
-(test #f symbol? '())
-(test #f symbol? #f)
-;;; But first, what case are symbols in? Determine the standard case:
-(define char-standard-case char-upcase)
-(if (string=? (symbol->string 'A) "a")
- (set! char-standard-case char-downcase))
-;;; Not for Guile
-;(test #t 'standard-case
-; (string=? (symbol->string 'a) (symbol->string 'A)))
-;(test #t 'standard-case
-; (or (string=? (symbol->string 'a) "A")
-; (string=? (symbol->string 'A) "a")))
-(define (str-copy s)
- (let ((v (make-string (string-length s))))
- (do ((i (- (string-length v) 1) (- i 1)))
- ((< i 0) v)
- (string-set! v i (string-ref s i)))))
-(define (string-standard-case s)
- (set! s (str-copy s))
- (do ((i 0 (+ 1 i))
- (sl (string-length s)))
- ((>= i sl) s)
- (string-set! s i (char-standard-case (string-ref s i)))))
-;;; Not for Guile
-;(test (string-standard-case "flying-fish") symbol->string 'flying-fish)
-;(test (string-standard-case "martin") symbol->string 'Martin)
-(test "Malvina" symbol->string (string->symbol "Malvina"))
-;;; Not for Guile
-;(test #t 'standard-case (eq? 'a 'A))
-
-(define x (string #\a #\b))
-(define y (string->symbol x))
-(string-set! x 0 #\c)
-(test "cb" 'string-set! x)
-(test "ab" symbol->string y)
-(test y string->symbol "ab")
-
-;;; Not for Guile
-;(test #t eq? 'mISSISSIppi 'mississippi)
-;(test #f 'string->symbol (eq? 'bitBlt (string->symbol "bitBlt")))
-(test 'JollyWog string->symbol (symbol->string 'JollyWog))
-
-(SECTION 6 5 5)
-(test #t number? 3)
-(test #t complex? 3)
-(test #t real? 3)
-(test #t rational? 3)
-(test #t integer? 3)
-
-(test #t exact? 3)
-(test #f inexact? 3)
-
-(test #t = 22 22 22)
-(test #t = 22 22)
-(test #f = 34 34 35)
-(test #f = 34 35)
-(test #t > 3 -6246)
-(test #f > 9 9 -2424)
-(test #t >= 3 -4 -6246)
-(test #t >= 9 9)
-(test #f >= 8 9)
-(test #t < -1 2 3 4 5 6 7 8)
-(test #f < -1 2 3 4 4 5 6 7)
-(test #t <= -1 2 3 4 5 6 7 8)
-(test #t <= -1 2 3 4 4 5 6 7)
-(test #f < 1 3 2)
-(test #f >= 1 3 2)
-
-(test #t zero? 0)
-(test #f zero? 1)
-(test #f zero? -1)
-(test #f zero? -100)
-(test #t positive? 4)
-(test #f positive? -4)
-(test #f positive? 0)
-(test #f negative? 4)
-(test #t negative? -4)
-(test #f negative? 0)
-(test #t odd? 3)
-(test #f odd? 2)
-(test #f odd? -4)
-(test #t odd? -1)
-(test #f even? 3)
-(test #t even? 2)
-(test #t even? -4)
-(test #f even? -1)
-
-(test 38 max 34 5 7 38 6)
-(test -24 min 3 5 5 330 4 -24)
-
-(test 7 + 3 4)
-(test '3 + 3)
-(test 0 +)
-(test 4 * 4)
-(test 1 *)
-
-(test -1 - 3 4)
-(test -3 - 3)
-(test 7 abs -7)
-(test 7 abs 7)
-(test 0 abs 0)
-
-(test 5 quotient 35 7)
-(test -5 quotient -35 7)
-(test -5 quotient 35 -7)
-(test 5 quotient -35 -7)
-(test 1 modulo 13 4)
-(test 1 remainder 13 4)
-(test 3 modulo -13 4)
-(test -1 remainder -13 4)
-(test -3 modulo 13 -4)
-(test 1 remainder 13 -4)
-(test -1 modulo -13 -4)
-(test -1 remainder -13 -4)
-(define (divtest n1 n2)
- (= n1 (+ (* n2 (quotient n1 n2))
- (remainder n1 n2))))
-(test #t divtest 238 9)
-(test #t divtest -238 9)
-(test #t divtest 238 -9)
-(test #t divtest -238 -9)
-
-(test 4 gcd 0 4)
-(test 4 gcd -4 0)
-(test 4 gcd 32 -36)
-(test 0 gcd)
-(test 288 lcm 32 -36)
-(test 1 lcm)
-
-;;;;From: fred@sce.carleton.ca (Fred J Kaudel)
-;;; Modified by jaffer.
-(define (test-inexact)
- (define f3.9 (string->number "3.9"))
- (define f4.0 (string->number "4.0"))
- (define f-3.25 (string->number "-3.25"))
- (define f.25 (string->number ".25"))
- (define f4.5 (string->number "4.5"))
- (define f3.5 (string->number "3.5"))
- (define f0.0 (string->number "0.0"))
- (define f0.8 (string->number "0.8"))
- (define f1.0 (string->number "1.0"))
- (define wto write-test-obj)
- (define dto display-test-obj)
- (define lto load-test-obj)
- (newline)
- (display ";testing inexact numbers; ")
- (newline)
- (SECTION 6 5 5)
- (test #t inexact? f3.9)
- (test #t 'inexact? (inexact? (max f3.9 4)))
- (test f4.0 'max (max f3.9 4))
- (test f4.0 'exact->inexact (exact->inexact 4))
- (test (- f4.0) round (- f4.5))
- (test (- f4.0) round (- f3.5))
- (test (- f4.0) round (- f3.9))
- (test f0.0 round f0.0)
- (test f0.0 round f.25)
- (test f1.0 round f0.8)
- (test f4.0 round f3.5)
- (test f4.0 round f4.5)
- (set! write-test-obj (list f.25 f-3.25));.25 inexact errors less likely.
- (set! display-test-obj (list f.25 f-3.25));.3 often has such errors (~10^-13)
- (set! load-test-obj (list 'define 'foo (list 'quote write-test-obj)))
- (test #t call-with-output-file
- "tmp3"
- (lambda (test-file)
- (write-char #\; test-file)
- (display write-test-obj test-file)
- (newline test-file)
- (write load-test-obj test-file)
- (output-port? test-file)))
- (check-test-file "tmp3")
- (set! write-test-obj wto)
- (set! display-test-obj dto)
- (set! load-test-obj lto)
- (let ((x (string->number "4195835.0"))
- (y (string->number "3145727.0")))
- (test #t 'pentium-fdiv-bug (> f1.0 (- x (* (/ x y) y)))))
- (report-errs))
-
-(define (test-bignum)
- (define tb
- (lambda (n1 n2)
- (= n1 (+ (* n2 (quotient n1 n2))
- (remainder n1 n2)))))
- (newline)
- (display ";testing bignums; ")
- (newline)
- (SECTION 6 5 5)
- (test 0 modulo -2177452800 86400)
- (test 0 modulo 2177452800 -86400)
- (test 0 modulo 2177452800 86400)
- (test 0 modulo -2177452800 -86400)
- (test #t 'remainder (tb 281474976710655 65535))
- (test #t 'remainder (tb 281474976710654 65535))
- (SECTION 6 5 6)
- (test 281474976710655 string->number "281474976710655")
- (test "281474976710655" number->string 281474976710655)
- (report-errs))
-
-(SECTION 6 5 6)
-(test "0" number->string 0)
-(test "100" number->string 100)
-(test "100" number->string 256 16)
-(test 100 string->number "100")
-(test 256 string->number "100" 16)
-(test #f string->number "")
-(test #f string->number ".")
-(test #f string->number "d")
-(test #f string->number "D")
-(test #f string->number "i")
-(test #f string->number "I")
-(test #f string->number "3i")
-(test #f string->number "3I")
-(test #f string->number "33i")
-(test #f string->number "33I")
-(test #f string->number "3.3i")
-(test #f string->number "3.3I")
-(test #f string->number "-")
-(test #f string->number "+")
-
-(SECTION 6 6)
-(test #t eqv? '#\space #\Space)
-(test #t eqv? #\space '#\Space)
-(test #t char? #\a)
-(test #t char? #\()
-(test #t char? #\space)
-(test #t char? '#\newline)
-
-(test #f char=? #\A #\B)
-(test #f char=? #\a #\b)
-(test #f char=? #\9 #\0)
-(test #t char=? #\A #\A)
-
-(test #t char<? #\A #\B)
-(test #t char<? #\a #\b)
-(test #f char<? #\9 #\0)
-(test #f char<? #\A #\A)
-
-(test #f char>? #\A #\B)
-(test #f char>? #\a #\b)
-(test #t char>? #\9 #\0)
-(test #f char>? #\A #\A)
-
-(test #t char<=? #\A #\B)
-(test #t char<=? #\a #\b)
-(test #f char<=? #\9 #\0)
-(test #t char<=? #\A #\A)
-
-(test #f char>=? #\A #\B)
-(test #f char>=? #\a #\b)
-(test #t char>=? #\9 #\0)
-(test #t char>=? #\A #\A)
-
-(test #f char-ci=? #\A #\B)
-(test #f char-ci=? #\a #\B)
-(test #f char-ci=? #\A #\b)
-(test #f char-ci=? #\a #\b)
-(test #f char-ci=? #\9 #\0)
-(test #t char-ci=? #\A #\A)
-(test #t char-ci=? #\A #\a)
-
-(test #t char-ci<? #\A #\B)
-(test #t char-ci<? #\a #\B)
-(test #t char-ci<? #\A #\b)
-(test #t char-ci<? #\a #\b)
-(test #f char-ci<? #\9 #\0)
-(test #f char-ci<? #\A #\A)
-(test #f char-ci<? #\A #\a)
-
-(test #f char-ci>? #\A #\B)
-(test #f char-ci>? #\a #\B)
-(test #f char-ci>? #\A #\b)
-(test #f char-ci>? #\a #\b)
-(test #t char-ci>? #\9 #\0)
-(test #f char-ci>? #\A #\A)
-(test #f char-ci>? #\A #\a)
-
-(test #t char-ci<=? #\A #\B)
-(test #t char-ci<=? #\a #\B)
-(test #t char-ci<=? #\A #\b)
-(test #t char-ci<=? #\a #\b)
-(test #f char-ci<=? #\9 #\0)
-(test #t char-ci<=? #\A #\A)
-(test #t char-ci<=? #\A #\a)
-
-(test #f char-ci>=? #\A #\B)
-(test #f char-ci>=? #\a #\B)
-(test #f char-ci>=? #\A #\b)
-(test #f char-ci>=? #\a #\b)
-(test #t char-ci>=? #\9 #\0)
-(test #t char-ci>=? #\A #\A)
-(test #t char-ci>=? #\A #\a)
-
-(test #t char-alphabetic? #\a)
-(test #t char-alphabetic? #\A)
-(test #t char-alphabetic? #\z)
-(test #t char-alphabetic? #\Z)
-(test #f char-alphabetic? #\0)
-(test #f char-alphabetic? #\9)
-(test #f char-alphabetic? #\space)
-(test #f char-alphabetic? #\;)
-
-(test #f char-numeric? #\a)
-(test #f char-numeric? #\A)
-(test #f char-numeric? #\z)
-(test #f char-numeric? #\Z)
-(test #t char-numeric? #\0)
-(test #t char-numeric? #\9)
-(test #f char-numeric? #\space)
-(test #f char-numeric? #\;)
-
-(test #f char-whitespace? #\a)
-(test #f char-whitespace? #\A)
-(test #f char-whitespace? #\z)
-(test #f char-whitespace? #\Z)
-(test #f char-whitespace? #\0)
-(test #f char-whitespace? #\9)
-(test #t char-whitespace? #\space)
-(test #f char-whitespace? #\;)
-
-(test #f char-upper-case? #\0)
-(test #f char-upper-case? #\9)
-(test #f char-upper-case? #\space)
-(test #f char-upper-case? #\;)
-
-(test #f char-lower-case? #\0)
-(test #f char-lower-case? #\9)
-(test #f char-lower-case? #\space)
-(test #f char-lower-case? #\;)
-
-(test #\. integer->char (char->integer #\.))
-(test #\A integer->char (char->integer #\A))
-(test #\a integer->char (char->integer #\a))
-(test #\A char-upcase #\A)
-(test #\A char-upcase #\a)
-(test #\a char-downcase #\A)
-(test #\a char-downcase #\a)
-(SECTION 6 7)
-(test #t string? "The word \"recursion\\\" has many meanings.")
-(test #t string? "")
-(define f (make-string 3 #\*))
-(test "?**" 'string-set! (begin (string-set! f 0 #\?) f))
-(test "abc" string #\a #\b #\c)
-(test "" string)
-(test 3 string-length "abc")
-(test #\a string-ref "abc" 0)
-(test #\c string-ref "abc" 2)
-(test 0 string-length "")
-(test "" substring "ab" 0 0)
-(test "" substring "ab" 1 1)
-(test "" substring "ab" 2 2)
-(test "a" substring "ab" 0 1)
-(test "b" substring "ab" 1 2)
-(test "ab" substring "ab" 0 2)
-(test "foobar" string-append "foo" "bar")
-(test "foo" string-append "foo")
-(test "foo" string-append "foo" "")
-(test "foo" string-append "" "foo")
-(test "" string-append)
-(test "" make-string 0)
-(test #t string=? "" "")
-(test #f string<? "" "")
-(test #f string>? "" "")
-(test #t string<=? "" "")
-(test #t string>=? "" "")
-(test #t string-ci=? "" "")
-(test #f string-ci<? "" "")
-(test #f string-ci>? "" "")
-(test #t string-ci<=? "" "")
-(test #t string-ci>=? "" "")
-
-(test #f string=? "A" "B")
-(test #f string=? "a" "b")
-(test #f string=? "9" "0")
-(test #t string=? "A" "A")
-
-(test #t string<? "A" "B")
-(test #t string<? "a" "b")
-(test #f string<? "9" "0")
-(test #f string<? "A" "A")
-
-(test #f string>? "A" "B")
-(test #f string>? "a" "b")
-(test #t string>? "9" "0")
-(test #f string>? "A" "A")
-
-(test #t string<=? "A" "B")
-(test #t string<=? "a" "b")
-(test #f string<=? "9" "0")
-(test #t string<=? "A" "A")
-
-(test #f string>=? "A" "B")
-(test #f string>=? "a" "b")
-(test #t string>=? "9" "0")
-(test #t string>=? "A" "A")
-
-(test #f string-ci=? "A" "B")
-(test #f string-ci=? "a" "B")
-(test #f string-ci=? "A" "b")
-(test #f string-ci=? "a" "b")
-(test #f string-ci=? "9" "0")
-(test #t string-ci=? "A" "A")
-(test #t string-ci=? "A" "a")
-
-(test #t string-ci<? "A" "B")
-(test #t string-ci<? "a" "B")
-(test #t string-ci<? "A" "b")
-(test #t string-ci<? "a" "b")
-(test #f string-ci<? "9" "0")
-(test #f string-ci<? "A" "A")
-(test #f string-ci<? "A" "a")
-
-(test #f string-ci>? "A" "B")
-(test #f string-ci>? "a" "B")
-(test #f string-ci>? "A" "b")
-(test #f string-ci>? "a" "b")
-(test #t string-ci>? "9" "0")
-(test #f string-ci>? "A" "A")
-(test #f string-ci>? "A" "a")
-
-(test #t string-ci<=? "A" "B")
-(test #t string-ci<=? "a" "B")
-(test #t string-ci<=? "A" "b")
-(test #t string-ci<=? "a" "b")
-(test #f string-ci<=? "9" "0")
-(test #t string-ci<=? "A" "A")
-(test #t string-ci<=? "A" "a")
-
-(test #f string-ci>=? "A" "B")
-(test #f string-ci>=? "a" "B")
-(test #f string-ci>=? "A" "b")
-(test #f string-ci>=? "a" "b")
-(test #t string-ci>=? "9" "0")
-(test #t string-ci>=? "A" "A")
-(test #t string-ci>=? "A" "a")
-(SECTION 6 8)
-(test #t vector? '#(0 (2 2 2 2) "Anna"))
-(test #t vector? '#())
-(test '#(a b c) vector 'a 'b 'c)
-(test '#() vector)
-(test 3 vector-length '#(0 (2 2 2 2) "Anna"))
-(test 0 vector-length '#())
-(test 8 vector-ref '#(1 1 2 3 5 8 13 21) 5)
-(test '#(0 ("Sue" "Sue") "Anna") 'vector-set
- (let ((vec (vector 0 '(2 2 2 2) "Anna")))
- (vector-set! vec 1 '("Sue" "Sue"))
- vec))
-(test '#(hi hi) make-vector 2 'hi)
-(test '#() make-vector 0)
-(test '#() make-vector 0 'a)
-(SECTION 6 9)
-(test #t procedure? car)
-(test #f procedure? 'car)
-(test #t procedure? (lambda (x) (* x x)))
-(test #f procedure? '(lambda (x) (* x x)))
-(test #t call-with-current-continuation procedure?)
-(test 7 apply + (list 3 4))
-(test 7 apply (lambda (a b) (+ a b)) (list 3 4))
-(test 17 apply + 10 (list 3 4))
-(test '() apply list '())
-(define compose (lambda (f g) (lambda args (f (apply g args)))))
-(test 30 (compose sqt *) 12 75)
-
-(test '(b e h) map cadr '((a b) (d e) (g h)))
-(test '(5 7 9) map + '(1 2 3) '(4 5 6))
-(test '#(0 1 4 9 16) 'for-each
- (let ((v (make-vector 5)))
- (for-each (lambda (i) (vector-set! v i (* i i)))
- '(0 1 2 3 4))
- v))
-(test -3 call-with-current-continuation
- (lambda (exit)
- (for-each (lambda (x) (if (negative? x) (exit x)))
- '(54 0 37 -3 245 19))
- #t))
-(define list-length
- (lambda (obj)
- (call-with-current-continuation
- (lambda (return)
- (letrec ((r (lambda (obj) (cond ((null? obj) 0)
- ((pair? obj) (+ (r (cdr obj)) 1))
- (else (return #f))))))
- (r obj))))))
-(test 4 list-length '(1 2 3 4))
-(test #f list-length '(a b . c))
-(test '() map cadr '())
-
-;;; This tests full conformance of call-with-current-continuation. It
-;;; is a separate test because some schemes do not support call/cc
-;;; other than escape procedures. I am indebted to
-;;; raja@copper.ucs.indiana.edu (Raja Sooriamurthi) for fixing this
-;;; code. The function leaf-eq? compares the leaves of 2 arbitrary
-;;; trees constructed of conses.
-(define (next-leaf-generator obj eot)
- (letrec ((return #f)
- (cont (lambda (x)
- (recur obj)
- (set! cont (lambda (x) (return eot)))
- (cont #f)))
- (recur (lambda (obj)
- (if (pair? obj)
- (for-each recur obj)
- (call-with-current-continuation
- (lambda (c)
- (set! cont c)
- (return obj)))))))
- (lambda () (call-with-current-continuation
- (lambda (ret) (set! return ret) (cont #f))))))
-(define (leaf-eq? x y)
- (let* ((eot (list 'eot))
- (xf (next-leaf-generator x eot))
- (yf (next-leaf-generator y eot)))
- (letrec ((loop (lambda (x y)
- (cond ((not (eq? x y)) #f)
- ((eq? eot x) #t)
- (else (loop (xf) (yf)))))))
- (loop (xf) (yf)))))
-(define (test-cont)
- (newline)
- (display ";testing continuations; ")
- (newline)
- (SECTION 6 9)
- (test #t leaf-eq? '(a (b (c))) '((a) b c))
- (test #f leaf-eq? '(a (b (c))) '((a) b c d))
- (report-errs))
-
-;;; Test Optional R4RS DELAY syntax and FORCE procedure
-(define (test-delay)
- (newline)
- (display ";testing DELAY and FORCE; ")
- (newline)
- (SECTION 6 9)
- (test 3 'delay (force (delay (+ 1 2))))
- (test '(3 3) 'delay (let ((p (delay (+ 1 2))))
- (list (force p) (force p))))
- (test 2 'delay (letrec ((a-stream
- (letrec ((next (lambda (n)
- (cons n (delay (next (+ n 1)))))))
- (next 0)))
- (head car)
- (tail (lambda (stream) (force (cdr stream)))))
- (head (tail (tail a-stream)))))
- (letrec ((count 0)
- (p (delay (begin (set! count (+ count 1))
- (if (> count x)
- count
- (force p)))))
- (x 5))
- (test 6 force p)
- (set! x 10)
- (test 6 force p))
- (test 3 'force
- (letrec ((p (delay (if c 3 (begin (set! c #t) (+ (force p) 1)))))
- (c #f))
- (force p)))
- (report-errs))
-
-(SECTION 6 10 1)
-(test #t input-port? (current-input-port))
-(test #t output-port? (current-output-port))
-(test #t call-with-input-file "test.scm" input-port?)
-(define this-file (open-input-file "test.scm"))
-(test #t input-port? this-file)
-(SECTION 6 10 2)
-(test #\; peek-char this-file)
-(test #\; read-char this-file)
-(test '(define cur-section '()) read this-file)
-(test #\( peek-char this-file)
-(test '(define errs '()) read this-file)
-(close-input-port this-file)
-(close-input-port this-file)
-(define (check-test-file name)
- (define test-file (open-input-file name))
- (test #t 'input-port?
- (call-with-input-file
- name
- (lambda (test-file)
- (test load-test-obj read test-file)
- (test #t eof-object? (peek-char test-file))
- (test #t eof-object? (read-char test-file))
- (input-port? test-file))))
- (test #\; read-char test-file)
- (test display-test-obj read test-file)
- (test load-test-obj read test-file)
- (close-input-port test-file))
-(SECTION 6 10 3)
-(define write-test-obj
- '(#t #f #\a () 9739 -3 . #((test) "te \" \" st" "" test #() b c)))
-(define display-test-obj
- '(#t #f a () 9739 -3 . #((test) te " " st test #() b c)))
-(define load-test-obj
- (list 'define 'foo (list 'quote write-test-obj)))
-(test #t call-with-output-file
- "tmp1"
- (lambda (test-file)
- (write-char #\; test-file)
- (display write-test-obj test-file)
- (newline test-file)
- (write load-test-obj test-file)
- (output-port? test-file)))
-(check-test-file "tmp1")
-
-(define test-file (open-output-file "tmp2"))
-(write-char #\; test-file)
-(display write-test-obj test-file)
-(newline test-file)
-(write load-test-obj test-file)
-(test #t output-port? test-file)
-(close-output-port test-file)
-(check-test-file "tmp2")
-(define (test-sc4)
- (newline)
- (display ";testing scheme 4 functions; ")
- (newline)
- (SECTION 6 7)
- (test '(#\P #\space #\l) string->list "P l")
- (test '() string->list "")
- (test "1\\\"" list->string '(#\1 #\\ #\"))
- (test "" list->string '())
- (SECTION 6 8)
- (test '(dah dah didah) vector->list '#(dah dah didah))
- (test '() vector->list '#())
- (test '#(dididit dah) list->vector '(dididit dah))
- (test '#() list->vector '())
- (SECTION 6 10 4)
- (load "tmp1")
- (test write-test-obj 'load foo)
- (report-errs))
-
-(report-errs)
-(if (and (string->number "0.0") (inexact? (string->number "0.0")))
- (test-inexact))
-
-(let ((n (string->number "281474976710655")))
- (if (and n (exact? n))
- (test-bignum)))
-(newline)
-(test-cont)
-(newline)
-(test-sc4)
-(newline)
-(test-delay)
-(newline)
-"last item in file"
-;;;; Copyright (C) 1996, 1998, 2001, 2002, 2003, 2006, 2010, 2011,
-;;;; 2012 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-;;;; ----------------------------------------------------------------
-;;;; threads.scm -- User-level interface to Guile's thread system
-;;;; 4 March 1996, Anthony Green <green@cygnus.com>
-;;;; Modified 5 October 1996, MDJ <djurfeldt@nada.kth.se>
-;;;; Modified 6 April 2001, ttn
-;;;; ----------------------------------------------------------------
-;;;;
-
-;;; Commentary:
-
-;; This module is documented in the Guile Reference Manual.
-;; Briefly, one procedure is exported: `%thread-handler';
-;; as well as four macros: `make-thread', `begin-thread',
-;; `with-mutex' and `monitor'.
-
-;;; Code:
-
-(define-module (ice-9 threads)
- #\use-module (ice-9 futures)
- #\use-module (ice-9 match)
- #\export (begin-thread
- parallel
- letpar
- make-thread
- with-mutex
- monitor
-
- par-map
- par-for-each
- n-par-map
- n-par-for-each
- n-for-each-par-map
- %thread-handler))
-
-
-
-;;; Macros first, so that the procedures expand correctly.
-
-(define-syntax-rule (begin-thread e0 e1 ...)
- (call-with-new-thread
- (lambda () e0 e1 ...)
- %thread-handler))
-
-(define-syntax parallel
- (lambda (x)
- (syntax-case x ()
- ((_ e0 ...)
- (with-syntax (((tmp0 ...) (generate-temporaries (syntax (e0 ...)))))
- #'(let ((tmp0 (future e0))
- ...)
- (values (touch tmp0) ...)))))))
-
-(define-syntax-rule (letpar ((v e) ...) b0 b1 ...)
- (call-with-values
- (lambda () (parallel e ...))
- (lambda (v ...)
- b0 b1 ...)))
-
-(define-syntax-rule (make-thread proc arg ...)
- (call-with-new-thread
- (lambda () (proc arg ...))
- %thread-handler))
-
-(define-syntax-rule (with-mutex m e0 e1 ...)
- (let ((x m))
- (dynamic-wind
- (lambda () (lock-mutex x))
- (lambda () (begin e0 e1 ...))
- (lambda () (unlock-mutex x)))))
-
-(define-syntax-rule (monitor first rest ...)
- (with-mutex (make-mutex)
- first rest ...))
-
-(define (par-mapper mapper cons)
- (lambda (proc . lists)
- (let loop ((lists lists))
- (match lists
- (((heads tails ...) ...)
- (let ((tail (future (loop tails)))
- (head (apply proc heads)))
- (cons head (touch tail))))
- (_
- '())))))
-
-(define par-map (par-mapper map cons))
-(define par-for-each (par-mapper for-each (const *unspecified*)))
-
-(define (n-par-map n proc . arglists)
- (let* ((m (make-mutex))
- (threads '())
- (results (make-list (length (car arglists))))
- (result results))
- (do ((i 0 (+ 1 i)))
- ((= i n)
- (for-each join-thread threads)
- results)
- (set! threads
- (cons (begin-thread
- (let loop ()
- (lock-mutex m)
- (if (null? result)
- (unlock-mutex m)
- (let ((args (map car arglists))
- (my-result result))
- (set! arglists (map cdr arglists))
- (set! result (cdr result))
- (unlock-mutex m)
- (set-car! my-result (apply proc args))
- (loop)))))
- threads)))))
-
-(define (n-par-for-each n proc . arglists)
- (let ((m (make-mutex))
- (threads '()))
- (do ((i 0 (+ 1 i)))
- ((= i n)
- (for-each join-thread threads))
- (set! threads
- (cons (begin-thread
- (let loop ()
- (lock-mutex m)
- (if (null? (car arglists))
- (unlock-mutex m)
- (let ((args (map car arglists)))
- (set! arglists (map cdr arglists))
- (unlock-mutex m)
- (apply proc args)
- (loop)))))
- threads)))))
-
-;;; The following procedure is motivated by the common and important
-;;; case where a lot of work should be done, (not too much) in parallel,
-;;; but the results need to be handled serially (for example when
-;;; writing them to a file).
-;;;
-(define (n-for-each-par-map n s-proc p-proc . arglists)
- "Using N parallel processes, apply S-PROC in serial order on the results
-of applying P-PROC on ARGLISTS."
- (let* ((m (make-mutex))
- (threads '())
- (no-result '(no-value))
- (results (make-list (length (car arglists)) no-result))
- (result results))
- (do ((i 0 (+ 1 i)))
- ((= i n)
- (for-each join-thread threads))
- (set! threads
- (cons (begin-thread
- (let loop ()
- (lock-mutex m)
- (cond ((null? results)
- (unlock-mutex m))
- ((not (eq? (car results) no-result))
- (let ((arg (car results)))
- ;; stop others from choosing to process results
- (set-car! results no-result)
- (unlock-mutex m)
- (s-proc arg)
- (lock-mutex m)
- (set! results (cdr results))
- (unlock-mutex m)
- (loop)))
- ((null? result)
- (unlock-mutex m))
- (else
- (let ((args (map car arglists))
- (my-result result))
- (set! arglists (map cdr arglists))
- (set! result (cdr result))
- (unlock-mutex m)
- (set-car! my-result (apply p-proc args))
- (loop))))))
- threads)))))
-
-(define (thread-handler tag . args)
- (let ((n (length args))
- (p (current-error-port)))
- (display "In thread:" p)
- (newline p)
- (if (>= n 3)
- (display-error #f
- p
- (car args)
- (cadr args)
- (caddr args)
- (if (= n 4)
- (cadddr args)
- '()))
- (begin
- (display "uncaught throw to " p)
- (display tag p)
- (display ": " p)
- (display args p)
- (newline p)))
- #f))
-
-;;; Set system thread handler
-(define %thread-handler thread-handler)
-
-;;; threads.scm ends here
-;;;; Copyright (C) 2001, 2004, 2006 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary:
-
-;; This module exports a single macro: `time'.
-;; Usage: (time exp)
-;;
-;; Example:
-;; guile> (time (sleep 3))
-;; clock utime stime cutime cstime gctime
-;; 3.01 0.00 0.00 0.00 0.00 0.00
-;; 0
-
-;;; Code:
-
-(define-module (ice-9 time)
- \:use-module (ice-9 format)
- \:export (time))
-
-(define (time-proc proc)
- (let* ((gc-start (gc-run-time))
- (tms-start (times))
- (result (proc))
- (tms-end (times))
- (gc-end (gc-run-time)))
- ;; FIXME: We would probably like format ~f to accept rationals, but
- ;; currently it doesn't so we force to a flonum with exact->inexact.
- (define (get proc start end)
- (exact->inexact (/ (- (proc end) (proc start)) internal-time-units-per-second)))
- (display "clock utime stime cutime cstime gctime\n")
- (format #t "~5,2F ~5,2F ~5,2F ~6,2F ~6,2F ~6,2F\n"
- (get tms:clock tms-start tms-end)
- (get tms:utime tms-start tms-end)
- (get tms:stime tms-start tms-end)
- (get tms:cutime tms-start tms-end)
- (get tms:cstime tms-start tms-end)
- (get identity gc-start gc-end))
- result))
-
-(define-macro (time exp)
- `((@@ (ice-9 time) time-proc) (lambda () ,exp)))
-
-;;; time.scm ends here
-;;; -*- mode: scheme; coding: utf-8; -*-
-
-;;;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-;;;; 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-(define-module (ice-9 top-repl)
- #\use-module (ice-9 top-repl)
- #\use-module ((system repl repl) #\select (start-repl))
-
- ;; #\replace, as with deprecated code enabled these will be in the root env
- #\replace (top-repl))
-
-(define call-with-sigint
- (if (not (provided? 'posix))
- (lambda (thunk) (thunk))
- (lambda (thunk)
- (let ((handler #f))
- (dynamic-wind
- (lambda ()
- (set! handler
- (sigaction SIGINT
- (lambda (sig)
- (scm-error 'signal #f "User interrupt" '()
- (list sig))))))
- thunk
- (lambda ()
- (if handler
- ;; restore Scheme handler, SIG_IGN or SIG_DFL.
- (sigaction SIGINT (car handler) (cdr handler))
- ;; restore original C handler.
- (sigaction SIGINT #f))))))))
-
-(define (top-repl)
- (let ((guile-user-module (resolve-module '(guile-user))))
-
- ;; Use some convenient modules (in reverse order)
-
- (set-current-module guile-user-module)
- (process-use-modules
- (append
- '(((ice-9 r5rs))
- ((ice-9 session)))
- (if (provided? 'regex)
- '(((ice-9 regex)))
- '())
- (if (provided? 'threads)
- '(((ice-9 threads)))
- '())))
-
- (call-with-sigint
- (lambda ()
- (and (defined? 'setlocale)
- (catch 'system-error
- (lambda ()
- (setlocale LC_ALL ""))
- (lambda (key subr fmt args errno)
- (format (current-error-port)
- "warning: failed to install locale: ~a~%"
- (strerror (car errno))))))
-
- (let ((status (start-repl (current-language))))
- (run-hook exit-hook)
- status)))))
-;; unicode
-
-;;;; Copyright (C) 2014 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software: you can redistribute it and/or modify
-;;;; it under the terms of the GNU Lesser General Public License as
-;;;; published by the Free Software Foundation, either version 3 of the
-;;;; License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;;;; GNU Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library. If not, see
-;;;; <http://www.gnu.org/licenses/>.
-;;;;
-
-(define-module (ice-9 unicode)
- #\export (formal-name->char
- char->formal-name))
-
-(eval-when (expand load eval)
- (load-extension (string-append "libguile-" (effective-version))
- "scm_init_unicode"))
-;;; -*- mode: scheme; coding: utf-8; -*-
-;;;
-;;; Copyright (C) 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (ice-9 vlist)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-9)
- #\use-module (srfi srfi-9 gnu)
- #\use-module (srfi srfi-26)
- #\use-module (ice-9 format)
-
- #\export (vlist? vlist-cons vlist-head vlist-tail vlist-null?
- vlist-null list->vlist vlist-ref vlist-drop vlist-take
- vlist-length vlist-fold vlist-fold-right vlist-map
- vlist-unfold vlist-unfold-right vlist-append
- vlist-reverse vlist-filter vlist-delete vlist->list
- vlist-for-each
- block-growth-factor
-
- vhash? vhash-cons vhash-consq vhash-consv
- vhash-assoc vhash-assq vhash-assv
- vhash-delete vhash-delq vhash-delv
- vhash-fold vhash-fold-right
- vhash-fold* vhash-foldq* vhash-foldv*
- alist->vhash))
-
-;;; Author: Ludovic Courtès <ludo@gnu.org>
-;;;
-;;; Commentary:
-;;;
-;;; This module provides an implementations of vlists, a functional list-like
-;;; data structure described by Phil Bagwell in "Fast Functional Lists,
-;;; Hash-Lists, Dequeues and Variable-Length Arrays", EPFL Technical Report,
-;;; 2002.
-;;;
-;;; The idea is to store vlist elements in increasingly large contiguous blocks
-;;; (implemented as vectors here). These blocks are linked to one another using
-;;; a pointer to the next block (called `block-base' here) and an offset within
-;;; that block (`block-offset' here). The size of these blocks form a geometric
-;;; series with ratio `block-growth-factor'.
-;;;
-;;; In the best case (e.g., using a vlist returned by `list->vlist'),
-;;; elements from the first half of an N-element vlist are accessed in O(1)
-;;; (assuming `block-growth-factor' is 2), and `vlist-length' takes only
-;;; O(ln(N)). Furthermore, the data structure improves data locality since
-;;; vlist elements are adjacent, which plays well with caches.
-;;;
-;;; Code:
-
-
-;;;
-;;; VList Blocks and Block Descriptors.
-;;;
-
-(define block-growth-factor
- (make-fluid 2))
-
-(define-inlinable (make-block base offset size hash-tab?)
- ;; Return a block (and block descriptor) of SIZE elements pointing to
- ;; BASE at OFFSET. If HASH-TAB? is true, we also reserve space for a
- ;; "hash table". Note: We use `next-free' instead of `last-used' as
- ;; suggested by Bagwell.
- (if hash-tab?
- (vector (make-vector (* size 3) #f)
- base offset size 0)
- (vector (make-vector size)
- base offset size 0)))
-
-(define-syntax-rule (define-block-accessor name index)
- (define-inlinable (name block)
- (vector-ref block index)))
-
-(define-block-accessor block-content 0)
-(define-block-accessor block-base 1)
-(define-block-accessor block-offset 2)
-(define-block-accessor block-size 3)
-(define-block-accessor block-next-free 4)
-
-(define-inlinable (block-hash-table? block)
- (< (block-size block) (vector-length (block-content block))))
-
-(define-inlinable (set-block-next-free! block next-free)
- (vector-set! block 4 next-free))
-
-(define-inlinable (block-append! block value offset)
- ;; This is not thread-safe. To fix it, see Section 2.8 of the paper.
- (and (< offset (block-size block))
- (= offset (block-next-free block))
- (begin
- (set-block-next-free! block (1+ offset))
- (vector-set! (block-content block) offset value)
- #t)))
-
-;; Return the item at slot OFFSET.
-(define-inlinable (block-ref content offset)
- (vector-ref content offset))
-
-;; Return the offset of the next item in the hash bucket, after the one
-;; at OFFSET.
-(define-inlinable (block-hash-table-next-offset content size offset)
- (vector-ref content (+ size size offset)))
-
-;; Save the offset of the next item in the hash bucket, after the one
-;; at OFFSET.
-(define-inlinable (block-hash-table-set-next-offset! content size offset
- next-offset)
- (vector-set! content (+ size size offset) next-offset))
-
-;; Returns the index of the last entry stored in CONTENT with
-;; SIZE-modulo hash value KHASH.
-(define-inlinable (block-hash-table-ref content size khash)
- (vector-ref content (+ size khash)))
-
-(define-inlinable (block-hash-table-set! content size khash offset)
- (vector-set! content (+ size khash) offset))
-
-;; Add hash table information for the item recently added at OFFSET,
-;; with SIZE-modulo hash KHASH.
-(define-inlinable (block-hash-table-add! content size khash offset)
- (block-hash-table-set-next-offset! content size offset
- (block-hash-table-ref content size khash))
- (block-hash-table-set! content size khash offset))
-
-(define block-null
- ;; The null block.
- (make-block #f 0 0 #f))
-
-
-;;;
-;;; VLists.
-;;;
-
-(define-record-type <vlist>
- ;; A vlist is just a base+offset pair pointing to a block.
-
- ;; XXX: Allocating a <vlist> record in addition to the block at each
- ;; `vlist-cons' call is inefficient. However, Bagwell's hack to avoid it
- ;; (Section 2.2) would require GC_ALL_INTERIOR_POINTERS, which would be a
- ;; performance hit for everyone.
- (make-vlist base offset)
- vlist?
- (base vlist-base)
- (offset vlist-offset))
-
-(set-record-type-printer! <vlist>
- (lambda (vl port)
- (cond ((vlist-null? vl)
- (format port "#<vlist ()>"))
- ((vhash? vl)
- (format port "#<vhash ~x ~a pairs>"
- (object-address vl)
- (vlist-length vl)))
- (else
- (format port "#<vlist ~a>"
- (vlist->list vl))))))
-
-
-(define vlist-null
- ;; The empty vlist.
- (make-vlist block-null 0))
-
-;; Asserting that something is a vlist is actually a win if your next
-;; step is to call record accessors, because that causes CSE to
-;; eliminate the type checks in those accessors.
-;;
-(define-inlinable (assert-vlist val)
- (unless (vlist? val)
- (throw 'wrong-type-arg
- #f
- "Not a vlist: ~S"
- (list val)
- (list val))))
-
-(define-inlinable (block-cons item vlist hash-tab?)
- (let ((base (vlist-base vlist))
- (offset (1+ (vlist-offset vlist))))
- (cond
- ((block-append! base item offset)
- ;; Fast path: We added the item directly to the block.
- (make-vlist base offset))
- (else
- ;; Slow path: Allocate a new block.
- (let* ((size (block-size base))
- (base (make-block
- base
- (1- offset)
- (cond
- ((zero? size) 1)
- ((< offset size) 1) ;; new vlist head
- (else (* (fluid-ref block-growth-factor) size)))
- hash-tab?)))
- (set-block-next-free! base 1)
- (vector-set! (block-content base) 0 item)
- (make-vlist base 0))))))
-
-(define (vlist-cons item vlist)
- "Return a new vlist with ITEM as its head and VLIST as its
-tail."
- ;; Note: Although the result of `vlist-cons' on a vhash is a valid
- ;; vlist, it is not a valid vhash. The new item does not get a hash
- ;; table entry. If we allocate a new block, the new block will not
- ;; have a hash table. Perhaps we can do something more sensible here,
- ;; but this is a hot function, so there are performance impacts.
- (assert-vlist vlist)
- (block-cons item vlist #f))
-
-(define (vlist-head vlist)
- "Return the head of VLIST."
- (assert-vlist vlist)
- (let ((base (vlist-base vlist))
- (offset (vlist-offset vlist)))
- (block-ref (block-content base) offset)))
-
-(define (vlist-tail vlist)
- "Return the tail of VLIST."
- (assert-vlist vlist)
- (let ((base (vlist-base vlist))
- (offset (vlist-offset vlist)))
- (if (> offset 0)
- (make-vlist base (- offset 1))
- (make-vlist (block-base base)
- (block-offset base)))))
-
-(define (vlist-null? vlist)
- "Return true if VLIST is empty."
- (assert-vlist vlist)
- (let ((base (vlist-base vlist)))
- (and (not (block-base base))
- (= 0 (block-size base)))))
-
-
-;;;
-;;; VList Utilities.
-;;;
-
-(define (list->vlist lst)
- "Return a new vlist whose contents correspond to LST."
- (vlist-reverse (fold vlist-cons vlist-null lst)))
-
-(define (vlist-fold proc init vlist)
- "Fold over VLIST, calling PROC for each element."
- ;; FIXME: Handle multiple lists.
- (assert-vlist vlist)
- (let loop ((base (vlist-base vlist))
- (offset (vlist-offset vlist))
- (result init))
- (if (eq? base block-null)
- result
- (let* ((next (- offset 1))
- (done? (< next 0)))
- (loop (if done? (block-base base) base)
- (if done? (block-offset base) next)
- (proc (block-ref (block-content base) offset) result))))))
-
-(define (vlist-fold-right proc init vlist)
- "Fold over VLIST, calling PROC for each element, starting from
-the last element."
- (assert-vlist vlist)
- (let loop ((index (1- (vlist-length vlist)))
- (result init))
- (if (< index 0)
- result
- (loop (1- index)
- (proc (vlist-ref vlist index) result)))))
-
-(define (vlist-reverse vlist)
- "Return a new VLIST whose content are those of VLIST in reverse
-order."
- (vlist-fold vlist-cons vlist-null vlist))
-
-(define (vlist-map proc vlist)
- "Map PROC over the elements of VLIST and return a new vlist."
- (vlist-fold (lambda (item result)
- (vlist-cons (proc item) result))
- vlist-null
- (vlist-reverse vlist)))
-
-(define (vlist->list vlist)
- "Return a new list whose contents match those of VLIST."
- (vlist-fold-right cons '() vlist))
-
-(define (vlist-ref vlist index)
- "Return the element at index INDEX in VLIST."
- (assert-vlist vlist)
- (let loop ((index index)
- (base (vlist-base vlist))
- (offset (vlist-offset vlist)))
- (if (<= index offset)
- (block-ref (block-content base) (- offset index))
- (loop (- index offset 1)
- (block-base base)
- (block-offset base)))))
-
-(define (vlist-drop vlist count)
- "Return a new vlist that does not contain the COUNT first elements of
-VLIST."
- (assert-vlist vlist)
- (let loop ((count count)
- (base (vlist-base vlist))
- (offset (vlist-offset vlist)))
- (if (<= count offset)
- (make-vlist base (- offset count))
- (loop (- count offset 1)
- (block-base base)
- (block-offset base)))))
-
-(define (vlist-take vlist count)
- "Return a new vlist that contains only the COUNT first elements of
-VLIST."
- (let loop ((count count)
- (vlist vlist)
- (result vlist-null))
- (if (= 0 count)
- (vlist-reverse result)
- (loop (- count 1)
- (vlist-tail vlist)
- (vlist-cons (vlist-head vlist) result)))))
-
-(define (vlist-filter pred vlist)
- "Return a new vlist containing all the elements from VLIST that
-satisfy PRED."
- (vlist-fold-right (lambda (e v)
- (if (pred e)
- (vlist-cons e v)
- v))
- vlist-null
- vlist))
-
-(define* (vlist-delete x vlist #\optional (equal? equal?))
- "Return a new vlist corresponding to VLIST without the elements
-EQUAL? to X."
- (vlist-filter (lambda (e)
- (not (equal? e x)))
- vlist))
-
-(define (vlist-length vlist)
- "Return the length of VLIST."
- (assert-vlist vlist)
- (let loop ((base (vlist-base vlist))
- (len (vlist-offset vlist)))
- (if (eq? base block-null)
- len
- (loop (block-base base)
- (+ len 1 (block-offset base))))))
-
-(define* (vlist-unfold p f g seed
- #\optional (tail-gen (lambda (x) vlist-null)))
- "Return a new vlist. See the description of SRFI-1 `unfold' for details."
- (let uf ((seed seed))
- (if (p seed)
- (tail-gen seed)
- (vlist-cons (f seed)
- (uf (g seed))))))
-
-(define* (vlist-unfold-right p f g seed #\optional (tail vlist-null))
- "Return a new vlist. See the description of SRFI-1 `unfold-right' for
-details."
- (let uf ((seed seed) (lis tail))
- (if (p seed)
- lis
- (uf (g seed) (vlist-cons (f seed) lis)))))
-
-(define (vlist-append . vlists)
- "Append the given lists."
- (if (null? vlists)
- vlist-null
- (fold-right (lambda (vlist result)
- (vlist-fold-right (lambda (e v)
- (vlist-cons e v))
- result
- vlist))
- vlist-null
- vlists)))
-
-(define (vlist-for-each proc vlist)
- "Call PROC on each element of VLIST. The result is unspecified."
- (vlist-fold (lambda (item x)
- (proc item))
- (if #f #f)
- vlist))
-
-
-;;;
-;;; Hash Lists, aka. `VHash'.
-;;;
-
-;; Assume keys K1 and K2, H = hash(K1) = hash(K2), and two values V1 and V2
-;; associated with K1 and K2, respectively. The resulting layout is a
-;; follows:
-;;
-;; ,--------------------.
-;; 0| ,-> (K1 . V1) | Vlist array
-;; 1| | |
-;; 2| | (K2 . V2) |
-;; 3| | |
-;; size +-|------------------+
-;; 0| | | Hash table
-;; 1| | |
-;; 2| +-- O <------------- H
-;; 3| | |
-;; size * 2 +-|------------------+
-;; 0| `-> 2 | Chain links
-;; 1| |
-;; 2| #f |
-;; 3| |
-;; size * 3 `--------------------'
-;;
-;; The backing store for the vhash is partitioned into three areas: the
-;; vlist part, the hash table part, and the chain links part. In this
-;; example we have a hash H which, when indexed into the hash table
-;; part, indicates that a value with this hash can be found at offset 0
-;; in the vlist part. The corresponding index (in this case, 0) of the
-;; chain links array holds the index of the next element in this block
-;; with this hash value, or #f if we reached the end of the chain.
-;;
-;; This API potentially requires users to repeat which hash function and
-;; which equality predicate to use. This can lead to unpredictable
-;; results if they are used in consistenly, e.g., between `vhash-cons'
-;; and `vhash-assoc', which is undesirable, as argued in
-;; http://savannah.gnu.org/bugs/?22159 . OTOH, two arguments can be
-;; made in favor of this API:
-;;
-;; - It's consistent with how alists are handled in SRFI-1.
-;;
-;; - In practice, users will probably consistenly use either the `q',
-;; the `v', or the plain variant (`vlist-cons' and `vlist-assoc'
-;; without any optional argument), i.e., they will rarely explicitly
-;; pass a hash function or equality predicate.
-
-(define (vhash? obj)
- "Return true if OBJ is a hash list."
- (and (vlist? obj)
- (block-hash-table? (vlist-base obj))))
-
-(define* (vhash-cons key value vhash #\optional (hash hash))
- "Return a new hash list based on VHASH where KEY is associated
-with VALUE. Use HASH to compute KEY's hash."
- (assert-vlist vhash)
- ;; We should also assert that it is a hash table. Need to check the
- ;; performance impacts of that. Also, vlist-null is a valid hash
- ;; table, which does not pass vhash?. A bug, perhaps.
- (let* ((vhash (block-cons (cons key value) vhash #t))
- (base (vlist-base vhash))
- (offset (vlist-offset vhash))
- (size (block-size base))
- (khash (hash key size))
- (content (block-content base)))
- (block-hash-table-add! content size khash offset)
- vhash))
-
-(define vhash-consq (cut vhash-cons <> <> <> hashq))
-(define vhash-consv (cut vhash-cons <> <> <> hashv))
-
-(define-inlinable (%vhash-fold* proc init key vhash equal? hash)
- ;; Fold over all the values associated with KEY in VHASH.
- (define (visit-block base max-offset result)
- (let* ((size (block-size base))
- (content (block-content base))
- (khash (hash key size)))
- (let loop ((offset (block-hash-table-ref content size khash))
- (result result))
- (if offset
- (loop (block-hash-table-next-offset content size offset)
- (if (and (<= offset max-offset)
- (equal? key (car (block-ref content offset))))
- (proc (cdr (block-ref content offset)) result)
- result))
- (let ((next-block (block-base base)))
- (if (> (block-size next-block) 0)
- (visit-block next-block (block-offset base) result)
- result))))))
-
- (assert-vlist vhash)
- (if (> (block-size (vlist-base vhash)) 0)
- (visit-block (vlist-base vhash)
- (vlist-offset vhash)
- init)
- init))
-
-(define* (vhash-fold* proc init key vhash
- #\optional (equal? equal?) (hash hash))
- "Fold over all the values associated with KEY in VHASH, with each
-call to PROC having the form ‘(proc value result)’, where
-RESULT is the result of the previous call to PROC and INIT the
-value of RESULT for the first call to PROC."
- (%vhash-fold* proc init key vhash equal? hash))
-
-(define (vhash-foldq* proc init key vhash)
- "Same as ‘vhash-fold*’, but using ‘hashq’ and ‘eq?’."
- (%vhash-fold* proc init key vhash eq? hashq))
-
-(define (vhash-foldv* proc init key vhash)
- "Same as ‘vhash-fold*’, but using ‘hashv’ and ‘eqv?’."
- (%vhash-fold* proc init key vhash eqv? hashv))
-
-(define-inlinable (%vhash-assoc key vhash equal? hash)
- ;; A specialization of `vhash-fold*' that stops when the first value
- ;; associated with KEY is found or when the end-of-list is reached. Inline to
- ;; make sure `vhash-assq' gets to use the `eq?' instruction instead of calling
- ;; the `eq?' subr.
- (define (visit-block base max-offset)
- (let* ((size (block-size base))
- (content (block-content base))
- (khash (hash key size)))
- (let loop ((offset (block-hash-table-ref content size khash)))
- (if offset
- (if (and (<= offset max-offset)
- (equal? key (car (block-ref content offset))))
- (block-ref content offset)
- (loop (block-hash-table-next-offset content size offset)))
- (let ((next-block (block-base base)))
- (and (> (block-size next-block) 0)
- (visit-block next-block (block-offset base))))))))
-
- (assert-vlist vhash)
- (and (> (block-size (vlist-base vhash)) 0)
- (visit-block (vlist-base vhash)
- (vlist-offset vhash))))
-
-(define* (vhash-assoc key vhash #\optional (equal? equal?) (hash hash))
- "Return the first key/value pair from VHASH whose key is equal to
-KEY according to the EQUAL? equality predicate."
- (%vhash-assoc key vhash equal? hash))
-
-(define (vhash-assq key vhash)
- "Return the first key/value pair from VHASH whose key is ‘eq?’ to
-KEY."
- (%vhash-assoc key vhash eq? hashq))
-
-(define (vhash-assv key vhash)
- "Return the first key/value pair from VHASH whose key is ‘eqv?’ to
-KEY."
- (%vhash-assoc key vhash eqv? hashv))
-
-(define* (vhash-delete key vhash #\optional (equal? equal?) (hash hash))
- "Remove all associations from VHASH with KEY, comparing keys
-with EQUAL?."
- (if (vhash-assoc key vhash equal? hash)
- (vlist-fold (lambda (k+v result)
- (let ((k (car k+v))
- (v (cdr k+v)))
- (if (equal? k key)
- result
- (vhash-cons k v result hash))))
- vlist-null
- vhash)
- vhash))
-
-(define vhash-delq (cut vhash-delete <> <> eq? hashq))
-(define vhash-delv (cut vhash-delete <> <> eqv? hashv))
-
-(define (vhash-fold proc init vhash)
- "Fold over the key/pair elements of VHASH from left to right, with
-each call to PROC having the form ‘(PROC key value result)’,
-where RESULT is the result of the previous call to PROC and
-INIT the value of RESULT for the first call to PROC."
- (vlist-fold (lambda (key+value result)
- (proc (car key+value) (cdr key+value)
- result))
- init
- vhash))
-
-(define (vhash-fold-right proc init vhash)
- "Fold over the key/pair elements of VHASH from right to left, with
-each call to PROC having the form ‘(PROC key value result)’,
-where RESULT is the result of the previous call to PROC and
-INIT the value of RESULT for the first call to PROC."
- (vlist-fold-right (lambda (key+value result)
- (proc (car key+value) (cdr key+value)
- result))
- init
- vhash))
-
-(define* (alist->vhash alist #\optional (hash hash))
- "Return the vhash corresponding to ALIST, an association list."
- (fold-right (lambda (pair result)
- (vhash-cons (car pair) (cdr pair) result hash))
- vlist-null
- alist))
-
-;;; vlist.scm ends here
-;;; installed-scm-file
-
-;;;; Copyright (C) 2003, 2006, 2014 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-(define-module (ice-9 weak-vector)
- \:export (make-weak-vector list->weak-vector weak-vector weak-vector?
- weak-vector-length weak-vector-ref weak-vector-set!
- make-weak-key-alist-vector
- make-weak-value-alist-vector
- make-doubly-weak-alist-vector
- weak-key-alist-vector?
- weak-value-alist-vector?
- doubly-weak-alist-vector?) ; C
- )
-
-(%init-weaks-builtins) ; defined in libguile/weaks.c
-;;; Guile Virtual Machine Assembly
-
-;; Copyright (C) 2001, 2009, 2010, 2011 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language assembly)
- #\use-module (rnrs bytevectors)
- #\use-module (system base pmatch)
- #\use-module (system vm instruction)
- #\use-module ((srfi srfi-1) #\select (fold))
- #\export (byte-length
- addr+ align-program align-code align-block
- assembly-pack assembly-unpack
- object->assembly assembly->object))
-
-;; len, metalen
-(define *program-header-len* (+ 4 4))
-
-;; lengths are encoded in 3 bytes
-(define *len-len* 3)
-
-
-(define (byte-length assembly)
- (pmatch assembly
- ((,inst . _) (guard (>= (instruction-length inst) 0))
- (+ 1 (instruction-length inst)))
- ((load-number ,str)
- (+ 1 *len-len* (string-length str)))
- ((load-string ,str)
- (+ 1 *len-len* (string-length str)))
- ((load-wide-string ,str)
- (+ 1 *len-len* (* 4 (string-length str))))
- ((load-symbol ,str)
- (+ 1 *len-len* (string-length str)))
- ((load-array ,bv)
- (+ 1 *len-len* (bytevector-length bv)))
- ((load-program ,labels ,len ,meta . ,code)
- (+ 1 *program-header-len* len (if meta (1- (byte-length meta)) 0)))
- (,label (guard (not (pair? label)))
- 0)
- (else (error "unknown instruction" assembly))))
-
-
-(define *program-alignment* 8)
-
-(define (addr+ addr code)
- (fold (lambda (x len) (+ (byte-length x) len))
- addr
- code))
-
-(define (code-alignment addr alignment header-len)
- (make-list (modulo (- alignment
- (modulo (+ addr header-len) alignment))
- alignment)
- '(nop)))
-
-(define (align-block addr)
- '())
-
-(define (align-code code addr alignment header-len)
- `(,@(code-alignment addr alignment header-len)
- ,code))
-
-(define (align-program prog addr)
- (align-code prog addr *program-alignment* 1))
-
-;;;
-;;; Code compress/decompression
-;;;
-
-(define *abbreviations*
- '(((make-int8 0) . (make-int8:0))
- ((make-int8 1) . (make-int8:1))))
-
-(define *expansions*
- (map (lambda (x) (cons (cdr x) (car x))) *abbreviations*))
-
-(define (assembly-pack code)
- (or (assoc-ref *abbreviations* code)
- code))
-
-(define (assembly-unpack code)
- (or (assoc-ref *expansions* code)
- code))
-
-
-;;;
-;;; Encoder/decoder
-;;;
-
-(define (object->assembly x)
- (cond ((eq? x #t) `(make-true))
- ((eq? x #f) `(make-false))
- ((eq? x #nil) `(make-nil))
- ((null? x) `(make-eol))
- ((and (integer? x) (exact? x))
- (cond ((and (<= -128 x) (< x 128))
- (assembly-pack `(make-int8 ,(modulo x 256))))
- ((and (<= -32768 x) (< x 32768))
- (let ((n (if (< x 0) (+ x 65536) x)))
- `(make-int16 ,(quotient n 256) ,(modulo n 256))))
- ((and (<= 0 x #xffffffffffffffff))
- `(make-uint64 ,@(bytevector->u8-list
- (let ((bv (make-bytevector 8)))
- (bytevector-u64-set! bv 0 x (endianness big))
- bv))))
- ((and (<= 0 (+ x #x8000000000000000) #x7fffffffffffffff))
- `(make-int64 ,@(bytevector->u8-list
- (let ((bv (make-bytevector 8)))
- (bytevector-s64-set! bv 0 x (endianness big))
- bv))))
- (else #f)))
- ((char? x)
- (cond ((<= (char->integer x) #xff)
- `(make-char8 ,(char->integer x)))
- (else
- `(make-char32 ,(char->integer x)))))
- (else #f)))
-
-(define (assembly->object code)
- (pmatch code
- ((make-true) #t)
- ((make-false) #f) ;; FIXME: Same as the `else' case!
- ((make-nil) #nil)
- ((make-eol) '())
- ((make-int8 ,n)
- (if (< n 128) n (- n 256)))
- ((make-int16 ,n1 ,n2)
- (let ((n (+ (* n1 256) n2)))
- (if (< n 32768) n (- n 65536))))
- ((make-uint64 ,n1 ,n2 ,n3 ,n4 ,n5 ,n6 ,n7 ,n8)
- (bytevector-u64-ref
- (u8-list->bytevector (list n1 n2 n3 n4 n5 n6 n7 n8))
- 0
- (endianness big)))
- ((make-int64 ,n1 ,n2 ,n3 ,n4 ,n5 ,n6 ,n7 ,n8)
- (bytevector-s64-ref
- (u8-list->bytevector (list n1 n2 n3 n4 n5 n6 n7 n8))
- 0
- (endianness big)))
- ((make-char8 ,n)
- (integer->char n))
- ((make-char32 ,n1 ,n2 ,n3 ,n4)
- (integer->char (+ (* n1 #x1000000)
- (* n2 #x10000)
- (* n3 #x100)
- n4)))
- ((load-string ,s) s)
- ((load-symbol ,s) (string->symbol s))
- (else #f)))
-;;; Guile VM assembler
-
-;; Copyright (C) 2001, 2009, 2010, 2011, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language assembly compile-bytecode)
- #\use-module (system base pmatch)
- #\use-module (system base target)
- #\use-module (language assembly)
- #\use-module (system vm instruction)
- #\use-module (rnrs bytevectors)
- #\use-module ((srfi srfi-1) #\select (fold))
- #\export (compile-bytecode))
-
-(define (compile-bytecode assembly env . opts)
- (define-syntax-rule (define-inline1 (proc arg) body body* ...)
- (define-syntax proc
- (syntax-rules ()
- ((_ (arg-expr (... ...)))
- (let ((x (arg-expr (... ...))))
- (proc x)))
- ((_ arg)
- (begin body body* ...)))))
-
- (define (fill-bytecode bv target-endianness)
- (let ((pos 0))
- (define-inline1 (write-byte b)
- (bytevector-u8-set! bv pos b)
- (set! pos (1+ pos)))
- (define u32-bv (make-bytevector 4))
- (define-inline1 (write-int24-be x)
- (bytevector-s32-set! u32-bv 0 x (endianness big))
- (bytevector-u8-set! bv pos (bytevector-u8-ref u32-bv 1))
- (bytevector-u8-set! bv (+ pos 1) (bytevector-u8-ref u32-bv 2))
- (bytevector-u8-set! bv (+ pos 2) (bytevector-u8-ref u32-bv 3))
- (set! pos (+ pos 3)))
- (define-inline1 (write-uint32-be x)
- (bytevector-u32-set! bv pos x (endianness big))
- (set! pos (+ pos 4)))
- (define-inline1 (write-uint32 x)
- (bytevector-u32-set! bv pos x target-endianness)
- (set! pos (+ pos 4)))
- (define-inline1 (write-loader-len len)
- (bytevector-u8-set! bv pos (ash len -16))
- (bytevector-u8-set! bv (+ pos 1) (logand (ash len -8) 255))
- (bytevector-u8-set! bv (+ pos 2) (logand len 255))
- (set! pos (+ pos 3)))
- (define-inline1 (write-latin1-string s)
- (let ((len (string-length s)))
- (write-loader-len len)
- (let lp ((i 0))
- (if (< i len)
- (begin
- (bytevector-u8-set! bv (+ pos i)
- (char->integer (string-ref s i)))
- (lp (1+ i)))))
- (set! pos (+ pos len))))
- (define-inline1 (write-bytevector bv*)
- (let ((len (bytevector-length bv*)))
- (write-loader-len len)
- (bytevector-copy! bv* 0 bv pos len)
- (set! pos (+ pos len))))
- (define-inline1 (write-wide-string s)
- (write-bytevector (string->utf32 s target-endianness)))
- (define-inline1 (write-break label)
- (let ((offset (- (assq-ref labels label) (+ (get-addr) 3))))
- (cond ((>= offset (ash 1 23)) (error "jump too far forward" offset))
- ((< offset (- (ash 1 23))) (error "jump too far backwards" offset))
- (else (write-int24-be offset)))))
-
- (define (write-bytecode asm labels address emit-opcode?)
- ;; Write ASM's bytecode to BV. If EMIT-OPCODE? is false, don't
- ;; emit bytecode for the first opcode encountered. Assume code
- ;; starts at ADDRESS (an integer). LABELS is assumed to be an
- ;; alist mapping labels to addresses.
- (define get-addr
- (let ((start pos))
- (lambda ()
- (+ address (- pos start)))))
- (define (write-break label)
- (let ((offset (- (assq-ref labels label) (+ (get-addr) 3))))
- (cond ((>= offset (ash 1 23)) (error "jump too far forward" offset))
- ((< offset (- (ash 1 23))) (error "jump too far backwards" offset))
- (else (write-int24-be offset)))))
-
- (let ((inst (car asm))
- (args (cdr asm)))
- (let ((opcode (instruction->opcode inst))
- (len (instruction-length inst)))
- (if emit-opcode?
- (write-byte opcode))
- (pmatch asm
- ((load-program ,labels ,length ,meta . ,code)
- (write-uint32 length)
- (write-uint32 (if meta (1- (byte-length meta)) 0))
- (fold (lambda (asm address)
- (let ((start pos))
- (write-bytecode asm labels address #t)
- (+ address (- pos start))))
- 0
- code)
- (if meta
- ;; Don't emit the `load-program' byte for metadata. Note that
- ;; META's bytecode meets the alignment requirements of
- ;; `scm_objcode', thanks to the alignment computed in `(language
- ;; assembly)'.
- (write-bytecode meta '() 0 #f)))
- ((make-char32 ,x) (write-uint32-be x))
- ((load-number ,str) (write-latin1-string str))
- ((load-string ,str) (write-latin1-string str))
- ((load-wide-string ,str) (write-wide-string str))
- ((load-symbol ,str) (write-latin1-string str))
- ((load-array ,bv) (write-bytevector bv))
- ((br ,l) (write-break l))
- ((br-if ,l) (write-break l))
- ((br-if-not ,l) (write-break l))
- ((br-if-eq ,l) (write-break l))
- ((br-if-not-eq ,l) (write-break l))
- ((br-if-null ,l) (write-break l))
- ((br-if-not-null ,l) (write-break l))
- ((br-if-nargs-ne ,hi ,lo ,l) (write-byte hi) (write-byte lo) (write-break l))
- ((br-if-nargs-lt ,hi ,lo ,l) (write-byte hi) (write-byte lo) (write-break l))
- ((br-if-nargs-gt ,hi ,lo ,l) (write-byte hi) (write-byte lo) (write-break l))
- ((bind-optionals/shuffle-or-br ,nreq-hi ,nreq-lo
- ,nreq-and-nopt-hi ,nreq-and-nopt-lo
- ,ntotal-hi ,ntotal-lo
- ,l)
- (write-byte nreq-hi)
- (write-byte nreq-lo)
- (write-byte nreq-and-nopt-hi)
- (write-byte nreq-and-nopt-lo)
- (write-byte ntotal-hi)
- (write-byte ntotal-lo)
- (write-break l))
- ((mv-call ,n ,l) (write-byte n) (write-break l))
- ((prompt ,escape-only? ,l) (write-byte escape-only?) (write-break l))
- (else
- (cond
- ((< len 0)
- (error "unhanded variable-length instruction" asm))
- ((not (= (length args) len))
- (error "bad number of args to instruction" asm len))
- (else
- (for-each (lambda (x) (write-byte x)) args))))))))
-
- ;; Don't emit the `load-program' byte.
- (write-bytecode assembly '() 0 #f)
- (if (= pos (bytevector-length bv))
- (values bv env env)
- (error "failed to fill bytevector" bv pos
- (bytevector-length bv)))))
-
- (pmatch assembly
- ((load-program ,labels ,length ,meta . ,code)
- (fill-bytecode (make-bytevector (+ 4 4 length
- (if meta
- (1- (byte-length meta))
- 0)))
- (target-endianness)))
-
- (else (error "bad assembly" assembly))))
-;;; Guile VM code converters
-
-;; Copyright (C) 2001, 2009, 2010, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language assembly decompile-bytecode)
- #\use-module (system vm instruction)
- #\use-module (system base pmatch)
- #\use-module (srfi srfi-4)
- #\use-module (rnrs bytevectors)
- #\use-module (language assembly)
- #\use-module ((system vm objcode) #\select (byte-order))
- #\export (decompile-bytecode))
-
-(define (decompile-bytecode x env opts)
- (let ((i 0) (size (u8vector-length x)))
- (define (pop)
- (let ((b (cond ((< i size) (u8vector-ref x i))
- ((= i size) #f)
- (else (error "tried to decode too many bytes")))))
- (if b (set! i (1+ i)))
- b))
- (let ((ret (decode-load-program pop)))
- (if (= i size)
- (values ret env)
- (error "bad bytecode: only decoded ~a out of ~a bytes" i size)))))
-
-(define (br-instruction? x)
- (memq x '(br br-if br-if-not br-if-eq br-if-not-eq br-if-null br-if-not-null)))
-(define (br-nargs-instruction? x)
- (memq x '(br-if-nargs-ne br-if-nargs-lt br-if-nargs-gt br-if-nargs-lt/non-kw)))
-
-(define (bytes->s24 a b c)
- (let ((x (+ (ash a 16) (ash b 8) c)))
- (if (zero? (logand (ash 1 23) x))
- x
- (- x (ash 1 24)))))
-
-;; FIXME: this is a little-endian disassembly!!!
-(define (decode-load-program pop)
- (let* ((a (pop)) (b (pop)) (c (pop)) (d (pop))
- (e (pop)) (f (pop)) (g (pop)) (h (pop))
- (len (+ a (ash b 8) (ash c 16) (ash d 24)))
- (metalen (+ e (ash f 8) (ash g 16) (ash h 24)))
- (labels '())
- (i 0))
- (define (ensure-label rel1 rel2 rel3)
- (let ((where (+ i (bytes->s24 rel1 rel2 rel3))))
- (or (assv-ref labels where)
- (begin
- (let ((l (gensym ":L")))
- (set! labels (acons where l labels))
- l)))))
- (define (sub-pop) ;; ...records. ha. ha.
- (let ((b (cond ((< i len) (pop))
- ((= i len) #f)
- (else (error "tried to decode too many bytes")))))
- (if b (set! i (1+ i)))
- b))
- (let lp ((out '()))
- (cond ((> i len)
- (error "error decoding program -- read too many bytes" out))
- ((= i len)
- `(load-program ,(map (lambda (x) (cons (cdr x) (car x)))
- (reverse labels))
- ,len
- ,(if (zero? metalen) #f (decode-load-program pop))
- ,@(reverse! out)))
- (else
- (let ((exp (decode-bytecode sub-pop)))
- (pmatch exp
- ((,br ,rel1 ,rel2 ,rel3) (guard (br-instruction? br))
- (lp (cons `(,br ,(ensure-label rel1 rel2 rel3)) out)))
- ((,br ,hi ,lo ,rel1 ,rel2 ,rel3) (guard (br-nargs-instruction? br))
- (lp (cons `(,br ,hi ,lo ,(ensure-label rel1 rel2 rel3)) out)))
- ((bind-optionals/shuffle-or-br ,nreq-hi ,nreq-lo
- ,nreq-and-nopt-hi ,nreq-and-nopt-lo
- ,ntotal-hi ,ntotal-lo
- ,rel1 ,rel2 ,rel3)
- (lp (cons `(bind-optionals/shuffle-or-br
- ,nreq-hi ,nreq-lo
- ,nreq-and-nopt-hi ,nreq-and-nopt-lo
- ,ntotal-hi ,ntotal-lo
- ,(ensure-label rel1 rel2 rel3))
- out)))
- ((mv-call ,n ,rel1 ,rel2 ,rel3)
- (lp (cons `(mv-call ,n ,(ensure-label rel1 rel2 rel3)) out)))
- ((prompt ,n0 ,rel1 ,rel2 ,rel3)
- (lp (cons `(prompt ,n0 ,(ensure-label rel1 rel2 rel3)) out)))
- (else
- (lp (cons exp out))))))))))
-
-(define (decode-bytecode pop)
- (and=> (pop)
- (lambda (opcode)
- (let ((inst (opcode->instruction opcode)))
- (cond
- ((eq? inst 'load-program)
- (decode-load-program pop))
-
- ((< (instruction-length inst) 0)
- ;; the negative length indicates a variable length
- ;; instruction
- (let* ((make-sequence
- (if (or (memq inst '(load-array load-wide-string)))
- make-bytevector
- make-string))
- (sequence-set!
- (if (or (memq inst '(load-array load-wide-string)))
- bytevector-u8-set!
- (lambda (str pos value)
- (string-set! str pos (integer->char value)))))
- (len (let* ((a (pop)) (b (pop)) (c (pop)))
- (+ (ash a 16) (ash b 8) c)))
- (seq (make-sequence len)))
- (let lp ((i 0))
- (if (= i len)
- `(,inst ,(if (eq? inst 'load-wide-string)
- (utf32->string seq (native-endianness))
- seq))
- (begin
- (sequence-set! seq i (pop))
- (lp (1+ i)))))))
- (else
- ;; fixed length
- (let lp ((n (instruction-length inst)) (out (list inst)))
- (if (zero? n)
- (reverse! out)
- (lp (1- n) (cons (pop) out))))))))))
-;;; Guile VM code converters
-
-;; Copyright (C) 2001, 2009, 2010, 2012, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language assembly disassemble)
- #\use-module (ice-9 format)
- #\use-module (srfi srfi-1)
- #\use-module (system vm instruction)
- #\use-module (system vm program)
- #\use-module (system base pmatch)
- #\use-module (language assembly)
- #\use-module (system base compile)
- #\export (disassemble))
-
-(define (disassemble x)
- (format #t "Disassembly of ~A:\n\n" x)
- (call-with-values
- (lambda () (decompile x #\from 'value #\to 'assembly))
- disassemble-load-program))
-
-(define (disassemble-load-program asm env)
- (pmatch asm
- ((load-program ,labels ,len ,meta . ,code)
- (let ((objs (and env (assq-ref env 'objects)))
- (free-vars (and env (assq-ref env 'free-vars)))
- (meta (and env (assq-ref env 'meta)))
- (blocs (and env (assq-ref env 'blocs)))
- (srcs (and env (assq-ref env 'sources))))
- (let lp ((pos 0) (code code) (programs '()))
- (cond
- ((null? code)
- (newline)
- (for-each
- (lambda (sym+asm)
- (format #t "Embedded program ~A:\n\n" (car sym+asm))
- (disassemble-load-program (cdr sym+asm) '()))
- (reverse! programs)))
- (else
- (let* ((asm (car code))
- (len (byte-length asm))
- (end (+ pos len)))
- (pmatch asm
- ((load-program . _)
- (let ((sym (gensym "")))
- (print-info pos `(load-program ,sym) #f #f)
- (lp (+ pos (byte-length asm)) (cdr code)
- (acons sym asm programs))))
- ((nop)
- (lp (+ pos (byte-length asm)) (cdr code) programs))
- (else
- (print-info pos asm
- ;; FIXME: code-annotation for whether it's
- ;; an arg or not, currently passing nargs=-1
- (code-annotation end asm objs -1 blocs
- labels)
- (and=> (and srcs (assq end srcs)) source->string))
- (lp (+ pos (byte-length asm)) (cdr code) programs)))))))
-
- (if (pair? free-vars)
- (disassemble-free-vars free-vars))
- (if meta
- (disassemble-meta meta))
-
- ;; Disassemble other bytecode in it
- ;; FIXME: something about the module.
- (if objs
- (for-each
- (lambda (x)
- (if (program? x)
- (begin (display "----------------------------------------\n")
- (disassemble x))))
- (cdr (vector->list objs))))))
- (else
- (error "bad load-program form" asm))))
-
-(define (disassemble-free-vars free-vars)
- (display "Free variables:\n\n")
- (fold (lambda (free-var i)
- (print-info i free-var #f #f)
- (+ 1 i))
- 0
- free-vars))
-
-(define-macro (unless test . body)
- `(if (not ,test) (begin ,@body)))
-
-(define *uninteresting-props* '(name))
-
-(define (disassemble-meta meta)
- (let ((props (filter (lambda (x)
- (not (memq (car x) *uninteresting-props*)))
- (cdddr meta))))
- (unless (null? props)
- (display "Properties:\n\n")
- (for-each (lambda (x) (print-info #f x #f #f)) props)
- (newline))))
-
-(define (source->string src)
- (format #f "~a:~a:~a" (or (source:file src) "(unknown file)")
- (source:line-for-user src) (source:column src)))
-
-(define (make-int16 byte1 byte2)
- (+ (* byte1 256) byte2))
-
-(define (code-annotation end-addr code objs nargs blocs labels)
- (let* ((code (assembly-unpack code))
- (inst (car code))
- (args (cdr code)))
- (case inst
- ((list vector)
- (list "~a element~:p" (apply make-int16 args)))
- ((br br-if br-if-eq br-if-not br-if-not-eq br-if-not-null br-if-null)
- (list "-> ~A" (assq-ref labels (car args))))
- ((br-if-nargs-ne br-if-nargs-lt br-if-nargs-gt)
- (list "-> ~A" (assq-ref labels (caddr args))))
- ((bind-optionals/shuffle-or-br)
- (list "-> ~A" (assq-ref labels (car (last-pair args)))))
- ((object-ref)
- (and objs (list "~s" (vector-ref objs (car args)))))
- ((local-ref local-boxed-ref local-set local-boxed-set)
- (and blocs
- (let lp ((bindings (list-ref blocs (car args))))
- (and (pair? bindings)
- (let ((b (car bindings)))
- (if (and (< (binding:start (car bindings)) end-addr)
- (>= (binding:end (car bindings)) end-addr))
- (list "`~a'~@[ (arg)~]"
- (binding:name b) (< (binding:index b) nargs))
- (lp (cdr bindings))))))))
- ((assert-nargs-ee/locals assert-nargs-ge/locals)
- (list "~a arg~:p, ~a local~:p"
- (logand (car args) #x7) (ash (car args) -3)))
- ((free-ref free-boxed-ref free-boxed-set)
- ;; FIXME: we can do better than this
- (list "(closure variable)"))
- ((toplevel-ref toplevel-set)
- (and objs
- (let ((v (vector-ref objs (car args))))
- (if (and (variable? v) (variable-bound? v))
- (list "~s" (variable-ref v))
- (list "`~s'" v)))))
- ((mv-call)
- (list "MV -> ~A" (assq-ref labels (cadr args))))
- ((prompt)
- ;; the H is for handler
- (list "H -> ~A" (assq-ref labels (cadr args))))
- (else
- (and=> (assembly->object code)
- (lambda (obj) (list "~s" obj)))))))
-
-;; i am format's daddy.
-(define (print-info addr info extra src)
- (format #t "~4@S ~32S~@[;; ~1{~@?~}~]~@[~61t at ~a~]\n" addr info extra src))
-;;; Guile Virtual Machine Assembly
-
-;; Copyright (C) 2001, 2009, 2010, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language assembly spec)
- #\use-module (system base language)
- #\use-module (language assembly compile-bytecode)
- #\use-module (language assembly decompile-bytecode)
- #\export (assembly))
-
-(define-language assembly
- #\title "Guile Virtual Machine Assembly Language"
- #\reader (lambda (port env) (read port))
- #\printer write
- #\parser read ;; fixme: make a verifier?
- #\compilers `((bytecode . ,compile-bytecode))
- #\decompilers `((bytecode . ,decompile-bytecode))
- #\for-humans? #f
- )
-;;; Brainfuck for GNU Guile
-
-;; Copyright (C) 2009, 2013 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language brainfuck compile-scheme)
- #\export (compile-scheme))
-
-;; Compilation of Brainfuck to Scheme is pretty straight-forward. For all of
-;; brainfuck's instructions, there are basic representations in Scheme we
-;; only have to generate.
-;;
-;; Brainfuck's pointer and data-tape are stored in the variables pointer and
-;; tape, where tape is a vector of integer values initially set to zero. Pointer
-;; starts out at position 0.
-;; Our tape is thus of finite length, with an address range of 0..n for
-;; some defined upper bound n depending on the length of our tape.
-
-
-;; Define the length to use for the tape.
-
-(define tape-size 30000)
-
-
-;; This compiles a whole brainfuck program. This constructs a Scheme code like:
-;; (let ((pointer 0)
-;; (tape (make-vector tape-size 0)))
-;; (begin
-;; <body>
-;; (write-char #\newline)))
-;;
-;; So first the pointer and tape variables are set up correctly, then the
-;; program's body is executed in this context, and finally we output an
-;; additional newline character in case the program does not output one.
-;;
-;; TODO: Find out and explain the details about env, the three return values and
-;; how to use the options. Implement options to set the tape-size, maybe.
-
-(define (compile-scheme exp env opts)
- (values
- `(let ((pointer 0)
- (tape (make-vector ,tape-size 0)))
- ,@(compile-body (cdr exp))
- (write-char #\newline))
- env
- env))
-
-
-;; Compile a list of instructions to get a list of Scheme codes. As we always
-;; strip off the car of the instructions-list and cons the result onto the
-;; result-list, it will get out in reversed order first; so we have to (reverse)
-;; it on return.
-
-(define (compile-body instructions)
- (let iterate ((cur instructions)
- (result '()))
- (if (null? cur)
- (reverse result)
- (let ((compiled (compile-instruction (car cur))))
- (iterate (cdr cur) (cons compiled result))))))
-
-
-;; Compile a single instruction to Scheme, using the direct representations
-;; all of Brainfuck's instructions have.
-
-(define (compile-instruction ins)
- (case (car ins)
-
- ;; Pointer moval >< is done simply by something like:
- ;; (set! pointer (+ pointer +-1))
- ((<bf-move>)
- (let ((dir (cadr ins)))
- `(set! pointer (+ pointer ,dir))))
-
- ;; Cell increment +- is done as:
- ;; (vector-set! tape pointer (+ (vector-ref tape pointer) +-1))
- ((<bf-increment>)
- (let ((inc (cadr ins)))
- `(vector-set! tape pointer (+ (vector-ref tape pointer) ,inc))))
-
- ;; Output . is done by converting the cell's integer value to a character
- ;; first and then printing out this character:
- ;; (write-char (integer->char (vector-ref tape pointer)))
- ((<bf-print>)
- '(write-char (integer->char (vector-ref tape pointer))))
-
- ;; Input , is done similarly, read in a character, get its ASCII code and
- ;; store it into the current cell:
- ;; (vector-set! tape pointer (char->integer (read-char)))
- ((<bf-read>)
- '(vector-set! tape pointer (char->integer (read-char))))
-
- ;; For loops [...] we use a named let construction to execute the body until
- ;; the current cell gets zero. The body is compiled via a recursive call
- ;; back to (compile-body).
- ;; (let iterate ()
- ;; (if (not (= (vector-ref! tape pointer) 0))
- ;; (begin
- ;; <body>
- ;; (iterate))))
- ((<bf-loop>)
- `(let iterate ()
- (if (not (= (vector-ref tape pointer) 0))
- (begin
- ,@(compile-body (cdr ins))
- (iterate)))))
-
- (else (error "unknown brainfuck instruction " (car ins)))))
-;;; Brainfuck for GNU Guile
-
-;; Copyright (C) 2009 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-;; 02110-1301 USA
-
-;;; Commentary:
-
-;; Brainfuck is a simple language that mostly mimics the operations of a
-;; Turing machine. This file implements a compiler from Brainfuck to
-;; Guile's Tree-IL.
-
-;;; Code:
-
-(define-module (language brainfuck compile-tree-il)
- #\use-module (system base pmatch)
- #\use-module (language tree-il)
- #\export (compile-tree-il))
-
-;; Compilation of Brainfuck is pretty straight-forward. For all of
-;; brainfuck's instructions, there are basic representations in Tree-IL
-;; we only have to generate.
-;;
-;; Brainfuck's pointer and data-tape are stored in the variables pointer and
-;; tape, where tape is a vector of integer values initially set to zero. Pointer
-;; starts out at position 0.
-;; Our tape is thus of finite length, with an address range of 0..n for
-;; some defined upper bound n depending on the length of our tape.
-
-
-;; Define the length to use for the tape.
-
-(define tape-size 30000)
-
-
-;; This compiles a whole brainfuck program. This constructs a Tree-IL
-;; code equivalent to Scheme code like this:
-;;
-;; (let ((pointer 0)
-;; (tape (make-vector tape-size 0)))
-;; (begin
-;; <body>
-;; (write-char #\newline)))
-;;
-;; So first the pointer and tape variables are set up correctly, then the
-;; program's body is executed in this context, and finally we output an
-;; additional newline character in case the program does not output one.
-;;
-;; The fact that we are compiling to Guile primitives gives this
-;; implementation a number of interesting characteristics. First, the
-;; values of the tape cells do not underflow or overflow. We could make
-;; them do otherwise via compiling calls to "modulo" at certain points.
-;;
-;; In addition, tape overruns or underruns will be detected, and will
-;; throw an error, whereas a number of Brainfuck compilers do not detect
-;; this.
-;;
-;; Note that we're generating the S-expression representation of
-;; Tree-IL, then using parse-tree-il to turn it into the actual Tree-IL
-;; data structures. This makes the compiler more pleasant to look at,
-;; but we do lose is the ability to propagate source information. Since
-;; Brainfuck is so obtuse anyway, this shouldn't matter ;-)
-;;
-;; `compile-tree-il' takes as its input the read expression, the
-;; environment, and some compile options. It returns the compiled
-;; expression, the environment appropriate for the next pass of the
-;; compiler -- in our case, just the environment unchanged -- and the
-;; continuation environment.
-;;
-;; The normal use of a continuation environment is if compiling one
-;; expression changes the environment, and that changed environment
-;; should be passed to the next compiled expression -- for example,
-;; changing the current module. But Brainfuck is incapable of that, so
-;; for us, the continuation environment is just the same environment we
-;; got in.
-;;
-;; FIXME: perhaps use options or the env to set the tape-size?
-
-(define (compile-tree-il exp env opts)
- (values
- (parse-tree-il
- `(let (pointer tape) (pointer tape)
- ((const 0)
- (apply (primitive make-vector) (const ,tape-size) (const 0)))
- ,(compile-body exp)))
- env
- env))
-
-
-;; Compile a list of instructions to a Tree-IL expression.
-
-(define (compile-body instructions)
- (let lp ((in instructions) (out '()))
- (define (emit x)
- (lp (cdr in) (cons x out)))
- (cond
- ((null? in)
- ;; No more input, build our output.
- (cond
- ((null? out) '(void)) ; no output
- ((null? (cdr out)) (car out)) ; single expression
- (else `(begin ,@(reverse out)))) ; sequence
- )
- (else
- (pmatch (car in)
-
- ;; Pointer moves >< are done simply by something like:
- ;; (set! pointer (+ pointer +-1))
- ((<bf-move> ,dir)
- (emit `(set! (lexical pointer)
- (apply (primitive +) (lexical pointer) (const ,dir)))))
-
- ;; Cell increment +- is done as:
- ;; (vector-set! tape pointer (+ (vector-ref tape pointer) +-1))
- ((<bf-increment> ,inc)
- (emit `(apply (primitive vector-set!) (lexical tape) (lexical pointer)
- (apply (primitive +)
- (apply (primitive vector-ref)
- (lexical tape) (lexical pointer))
- (const ,inc)))))
-
- ;; Output . is done by converting the cell's integer value to a
- ;; character first and then printing out this character:
- ;; (write-char (integer->char (vector-ref tape pointer)))
- ((<bf-print>)
- (emit `(apply (primitive write-char)
- (apply (primitive integer->char)
- (apply (primitive vector-ref)
- (lexical tape) (lexical pointer))))))
-
- ;; Input , is done similarly, read in a character, get its ASCII
- ;; code and store it into the current cell:
- ;; (vector-set! tape pointer (char->integer (read-char)))
- ((<bf-read>)
- (emit `(apply (primitive vector-set!)
- (lexical tape) (lexical pointer)
- (apply (primitive char->integer)
- (apply (primitive read-char))))))
-
- ;; For loops [...] we use a letrec construction to execute the body until
- ;; the current cell gets zero. The body is compiled via a recursive call
- ;; back to (compile-body).
- ;; (let iterate ()
- ;; (if (not (= (vector-ref! tape pointer) 0))
- ;; (begin
- ;; <body>
- ;; (iterate))))
- ;;
- ;; Indeed, letrec is the only way we have to loop in Tree-IL.
- ;; Note that this does not mean that the closure must actually
- ;; be created; later passes can compile tail-recursive letrec
- ;; calls into inline code with gotos. Admittedly, that part of
- ;; the compiler is not yet in place, but it will be, and in the
- ;; meantime the code is still reasonably efficient.
- ((<bf-loop> . ,body)
- (let ((iterate (gensym)))
- (emit `(letrec (iterate) (,iterate)
- ((lambda ()
- (lambda-case
- ((() #f #f #f () ())
- (if (apply (primitive =)
- (apply (primitive vector-ref)
- (lexical tape) (lexical pointer))
- (const 0))
- (void)
- (begin ,(compile-body body)
- (apply (lexical ,iterate)))))
- #f)))
- (apply (lexical ,iterate))))))
-
- (else (error "unknown brainfuck instruction" (car in))))))))
-;;; Brainfuck for GNU Guile.
-
-;; Copyright (C) 2009, 2013 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-;; 02110-1301 USA
-
-;;; Code:
-
-(define-module (language brainfuck parse)
- #\export (read-brainfuck))
-
-; Purpose of the parse module is to read in brainfuck in text form and produce
-; the corresponding tree representing the brainfuck code.
-;
-; Each object (representing basically a single instruction) is structured like:
-; (<instruction> [arguments])
-; where <instruction> is a symbolic name representing the type of instruction
-; and the optional arguments represent further data (for instance, the body of
-; a [...] loop as a number of nested instructions).
-
-
-; While reading a number of instructions in sequence, all of them are cons'ed
-; onto a list of instructions; thus this list gets out in reverse order.
-; Additionally, for "comment characters" (everything not an instruction) we
-; generate <bf-nop> NOP instructions.
-;
-; This routine reverses a list of instructions and removes all <bf-nop>'s on the
-; way to fix these two issues for a read-in list.
-
-(define (reverse-without-nops lst)
- (let iterate ((cur lst)
- (result '()))
- (if (null? cur)
- result
- (let ((head (car cur))
- (tail (cdr cur)))
- (if (eq? (car head) '<bf-nop>)
- (iterate tail result)
- (iterate tail (cons head result)))))))
-
-
-; Read in a set of instructions until a terminating ] character is found (or
-; end of file is reached). This is used both for loop bodies and whole
-; programs, so that a program has to be either terminated by EOF or an
-; additional ], too.
-;
-; For instance, the basic program so just echo one character would be:
-; ,.]
-
-(define (read-brainfuck p)
- (let iterate ((parsed '()))
- (let ((chr (read-char p)))
- (cond
- ((eof-object? chr)
- (let ((parsed (reverse-without-nops parsed)))
- (if (null? parsed)
- chr ;; pass on the EOF object
- parsed)))
- ((eqv? chr #\])
- (reverse-without-nops parsed))
- (else
- (iterate (cons (process-input-char chr p) parsed)))))))
-
-
-; This routine processes a single character of input and builds the
-; corresponding instruction. Loop bodies are read by recursively calling
-; back (read-brainfuck).
-;
-; For the poiner movement commands >< and the cell increment/decrement +-
-; commands, we only use one instruction form each and specify the direction of
-; the pointer/value increment using an argument to the instruction form.
-
-(define (process-input-char chr p)
- (case chr
- ((#\>) '(<bf-move> 1))
- ((#\<) '(<bf-move> -1))
- ((#\+) '(<bf-increment> 1))
- ((#\-) '(<bf-increment> -1))
- ((#\.) '(<bf-print>))
- ((#\,) '(<bf-read>))
- ((#\[) `(<bf-loop> ,@(read-brainfuck p)))
- (else '(<bf-nop>))))
-;;; Brainfuck for GNU Guile.
-
-;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-;; 02110-1301 USA
-
-;;; Code:
-
-(define-module (language brainfuck spec)
- #\use-module (language brainfuck compile-tree-il)
- #\use-module (language brainfuck compile-scheme)
- #\use-module (language brainfuck parse)
- #\use-module (system base language)
- #\export (brainfuck))
-
-
-; The new language is integrated into Guile via this (define-language)
-; specification in the special module (language [lang] spec).
-; Provided is a parser-routine in #\reader, a output routine in #\printer
-; and one or more compiler routines (as target-language - routine pairs)
-; in #\compilers. This is the basic set of fields needed to specify a new
-; language.
-
-(define-language brainfuck
- #\title "Brainfuck"
- #\reader (lambda (port env) (read-brainfuck port))
- #\compilers `((tree-il . ,compile-tree-il)
- (scheme . ,compile-scheme))
- #\printer write
- )
-;;; Guile Lowlevel Intermediate Language
-
-;; Copyright (C) 2001, 2009, 2010, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language bytecode spec)
- #\use-module (system base language)
- #\use-module (system vm objcode)
- #\export (bytecode))
-
-(define (compile-objcode x e opts)
- (values (bytecode->objcode x) e e))
-
-(define (decompile-objcode x e opts)
- (values (objcode->bytecode x) e))
-
-(define-language bytecode
- #\title "Guile Bytecode Vectors"
- #\reader (lambda (port env) (read port))
- #\printer write
- #\compilers `((objcode . ,compile-objcode))
- #\decompilers `((objcode . ,decompile-objcode))
- #\for-humans? #f
- )
-;;; ECMAScript for Guile
-
-;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language ecmascript array)
- #\use-module (oop goops)
- #\use-module (language ecmascript base)
- #\use-module (language ecmascript function)
- #\export (*array-prototype* new-array))
-
-
-(define-class <js-array-object> (<js-object>)
- (vector #\init-value #() #\accessor js-array-vector #\init-keyword #\vector))
-
-(define (new-array . vals)
- (let ((o (make <js-array-object> #\class "Array"
- #\prototype *array-prototype*)))
- (pput o 'length (length vals))
- (let ((vect (js-array-vector o)))
- (let lp ((i 0) (vals vals))
- (cond ((not (null? vals))
- (vector-set! vect i (car vals))
- (lp (1+ i) (cdr vals)))
- (else o))))))
-
-(define *array-prototype* (make <js-object> #\class "Array"
- #\value new-array
- #\constructor new-array))
-
-(hashq-set! *program-wrappers* new-array *array-prototype*)
-
-(pput *array-prototype* 'prototype *array-prototype*)
-(pput *array-prototype* 'constructor new-array)
-
-(define-method (pget (o <js-array-object>) p)
- (cond ((and (integer? p) (exact? p) (>= p 0))
- (let ((v (js-array-vector o)))
- (if (< p (vector-length v))
- (vector-ref v p)
- (next-method))))
- ((or (and (symbol? p) (eq? p 'length))
- (and (string? p) (string=? p "length")))
- (vector-length (js-array-vector o)))
- (else (next-method))))
-
-(define-method (pput (o <js-array-object>) p v)
- (cond ((and (integer? p) (exact? p) (>= 0 p))
- (let ((vect (js-array-vector o)))
- (if (< p (vector-length vect))
- (vector-set! vect p v)
- ;; Fixme: round up to powers of 2?
- (let ((new (make-vector (1+ p) 0)))
- (vector-move-left! vect 0 (vector-length vect) new 0)
- (set! (js-array-vector o) new)
- (vector-set! new p v)))))
- ((or (and (symbol? p) (eq? p 'length))
- (and (string? p) (string=? p "length")))
- (let ((vect (js-array-vector o)))
- (let ((new (make-vector (->uint32 v) 0)))
- (vector-move-left! vect 0 (min (vector-length vect) (->uint32 v))
- new 0)
- (set! (js-array-vector o) new))))
- (else (next-method))))
-
-(define-js-method *array-prototype* (toString)
- (format #f "~A" (js-array-vector this)))
-
-(define-js-method *array-prototype* (concat . rest)
- (let* ((len (apply + (->uint32 (pget this 'length))
- (map (lambda (x) (->uint32 (pget x 'length)))
- rest)))
- (rv (make-vector len 0)))
- (let lp ((objs (cons this rest)) (i 0))
- (cond ((null? objs) (make <js-array-object> #\class "Array"
- #\prototype *array-prototype*
- #\vector rv))
- ((is-a? (car objs) <js-array-object>)
- (let ((v (js-array-vector (car objs))))
- (vector-move-left! v 0 (vector-length v)
- rv i)
- (lp (cdr objs) (+ i (vector-length v)))))
- (else
- (error "generic array concats not yet implemented"))))))
-
-(define-js-method *array-prototype* (join . separator)
- (let lp ((i (1- (->uint32 (pget this 'length)))) (l '()))
- (if (< i 0)
- (string-join l (if separator (->string (car separator)) ","))
- (lp (1+ i)
- (cons (->string (pget this i)) l)))))
-
-(define-js-method *array-prototype* (pop)
- (let ((len (->uint32 (pget this 'length))))
- (if (zero? len)
- *undefined*
- (let ((ret (pget this (1- len))))
- (pput this 'length (1- len))
- ret))))
-
-(define-js-method *array-prototype* (push . args)
- (let lp ((args args))
- (if (null? args)
- (->uint32 (pget this 'length))
- (begin (pput this (->uint32 (pget this 'length)) (car args))
- (lp (cdr args))))))
-;;; ECMAScript for Guile
-
-;; Copyright (C) 2009, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language ecmascript base)
- #\use-module (oop goops)
- #\export (*undefined* *this*
- <js-object> *object-prototype*
- js-prototype js-props js-prop-attrs js-value js-constructor js-class
- pget prop-keys prop-attrs prop-has-attr? pput has-property? pdel
-
- object->string object->number object->value/string
- object->value/number object->value
-
- ->primitive ->boolean ->number ->integer ->int32 ->uint32
- ->uint16 ->string ->object
-
- call/this* call/this lambda/this define-js-method
-
- new-object new))
-
-(define *undefined* ((@@ (oop goops) make-unbound)))
-(define *this* (make-fluid))
-
-(define-class <js-object> ()
- (prototype #\getter js-prototype #\init-keyword #\prototype
- #\init-thunk (lambda () *object-prototype*))
- (props #\getter js-props #\init-form (make-hash-table 7))
- (prop-attrs #\getter js-prop-attrs #\init-value #f)
- (value #\getter js-value #\init-value #f #\init-keyword #\value)
- (constructor #\getter js-constructor #\init-value #f #\init-keyword #\constructor)
- (class #\getter js-class #\init-value "Object" #\init-keyword #\class))
-
-(define-method (prop-keys (o <js-object>))
- (hash-map->list (lambda (k v) k) (js-props o)))
-
-(define-method (pget (o <js-object>) (p <string>))
- (pget o (string->symbol p)))
-
-(define-method (pget (o <js-object>) p)
- (let ((h (hashq-get-handle (js-props o) p)))
- (if h
- (cdr h)
- (let ((proto (js-prototype o)))
- (if proto
- (pget proto p)
- *undefined*)))))
-
-(define-method (prop-attrs (o <js-object>) p)
- (or (let ((attrs (js-prop-attrs o)))
- (and attrs (hashq-ref (js-prop-attrs o) p)))
- (let ((proto (js-prototype o)))
- (if proto
- (prop-attrs proto p)
- '()))))
-
-(define-method (prop-has-attr? (o <js-object>) p attr)
- (memq attr (prop-attrs o p)))
-
-(define-method (pput (o <js-object>) p v)
- (if (prop-has-attr? o p 'ReadOnly)
- (throw 'ReferenceError o p)
- (hashq-set! (js-props o) p v)))
-
-(define-method (pput (o <js-object>) (p <string>) v)
- (pput o (string->symbol p) v))
-
-(define-method (pdel (o <js-object>) p)
- (if (prop-has-attr? o p 'DontDelete)
- #f
- (begin
- (pput o p *undefined*)
- #t)))
-
-(define-method (pdel (o <js-object>) (p <string>) v)
- (pdel o (string->symbol p)))
-
-(define-method (has-property? (o <js-object>) p)
- (if (hashq-get-handle (js-props o) p)
- #t
- (let ((proto (js-prototype o)))
- (if proto
- (has-property? proto p)
- #f))))
-
-(define (call/this* this f)
- (with-fluid* *this* this f))
-
-(define-macro (call/this this f . args)
- `(with-fluid* *this* ,this (lambda () (,f . ,args))))
-(define-macro (lambda/this formals . body)
- `(lambda ,formals (let ((this (fluid-ref *this*))) . ,body)))
-(define-macro (define-js-method object name-and-args . body)
- `(pput ,object ',(car name-and-args) (lambda/this ,(cdr name-and-args) . ,body)))
-
-(define *object-prototype* #f)
-(set! *object-prototype* (make <js-object>))
-
-(define-js-method *object-prototype* (toString)
- (format #f "[object ~A]" (js-class this)))
-(define-js-method *object-prototype* (toLocaleString . args)
- ((pget *object-prototype* 'toString)))
-(define-js-method *object-prototype* (valueOf)
- this)
-(define-js-method *object-prototype* (hasOwnProperty p)
- (and (hashq-get-handle (js-props this) p) #t))
-(define-js-method *object-prototype* (isPrototypeOf v)
- (eq? this (js-prototype v)))
-(define-js-method *object-prototype* (propertyIsEnumerable p)
- (and (hashq-get-handle (js-props this) p)
- (not (prop-has-attr? this p 'DontEnum))))
-
-(define (object->string o error?)
- (let ((toString (pget o 'toString)))
- (if (procedure? toString)
- (let ((x (call/this o toString)))
- (if (and error? (is-a? x <js-object>))
- (throw 'TypeError o 'default-value)
- x))
- (if error?
- (throw 'TypeError o 'default-value)
- o))))
-
-(define (object->number o error?)
- (let ((valueOf (pget o 'valueOf)))
- (if (procedure? valueOf)
- (let ((x (call/this o valueOf)))
- (if (and error? (is-a? x <js-object>))
- (throw 'TypeError o 'default-value)
- x))
- (if error?
- (throw 'TypeError o 'default-value)
- o))))
-
-(define (object->value/string o)
- (if (is-a? o <js-object>)
- (object->number o #t)
- o))
-
-(define (object->value/number o)
- (if (is-a? o <js-object>)
- (object->string o #t)
- o))
-
-(define (object->value o)
- ;; FIXME: if it's a date, we should try numbers first
- (object->value/string o))
-
-(define (->primitive x)
- (if (is-a? x <js-object>)
- (object->value x)
- x))
-
-(define (->boolean x)
- (not (or (not x) (null? x) (eq? x *undefined*)
- (and (number? x) (or (zero? x) (nan? x)))
- (and (string? x) (= (string-length x) 0)))))
-
-(define (->number x)
- (cond ((number? x) x)
- ((boolean? x) (if x 1 0))
- ((null? x) 0)
- ((eq? x *undefined*) +nan.0)
- ((is-a? x <js-object>) (object->number x #t))
- ((string? x) (string->number x))
- (else (throw 'TypeError x '->number))))
-
-(define (->integer x)
- (let ((n (->number x)))
- (cond ((nan? n) 0)
- ((zero? n) n)
- ((inf? n) n)
- (else (inexact->exact (round n))))))
-
-(define (->int32 x)
- (let ((n (->number x)))
- (if (or (nan? n) (zero? n) (inf? n))
- 0
- (let ((m (logand (1- (ash 1 32)) (inexact->exact (round n)))))
- (if (negative? n)
- (- m (ash 1 32))
- m)))))
-
-(define (->uint32 x)
- (let ((n (->number x)))
- (if (or (nan? n) (zero? n) (inf? n))
- 0
- (logand (1- (ash 1 32)) (inexact->exact (round n))))))
-
-(define (->uint16 x)
- (let ((n (->number x)))
- (if (or (nan? n) (zero? n) (inf? n))
- 0
- (logand (1- (ash 1 16)) (inexact->exact (round n))))))
-
-(define (->string x)
- (cond ((eq? x *undefined*) "undefined")
- ((null? x) "null")
- ((boolean? x) (if x "true" "false"))
- ((string? x) x)
- ((number? x)
- (cond ((nan? x) "NaN")
- ((zero? x) "0")
- ((inf? x) "Infinity")
- (else (number->string x))))
- (else (->string (object->value/string x)))))
-
-(define (->object x)
- (cond ((eq? x *undefined*) (throw 'TypeError x '->object))
- ((null? x) (throw 'TypeError x '->object))
- ((boolean? x) (make <js-object> #\prototype Boolean #\value x))
- ((number? x) (make <js-object> #\prototype String #\value x))
- ((string? x) (make <js-object> #\prototype Number #\value x))
- (else x)))
-
-(define (new-object . pairs)
- (let ((o (make <js-object>)))
- (map (lambda (pair)
- (pput o (car pair) (cdr pair)))
- pairs)
- o))
-(slot-set! *object-prototype* 'constructor new-object)
-
-(define-method (new o . initargs)
- (let ((ctor (js-constructor o)))
- (if (not ctor)
- (throw 'TypeError 'new o)
- (let ((o (make <js-object>
- #\prototype (or (js-prototype o) *object-prototype*))))
- (let ((new-o (call/this o apply ctor initargs)))
- (if (is-a? new-o <js-object>)
- new-o
- o))))))
-;;; ECMAScript for Guile
-
-;; Copyright (C) 2009, 2011 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language ecmascript compile-tree-il)
- #\use-module (language tree-il)
- #\use-module (ice-9 receive)
- #\use-module (system base pmatch)
- #\use-module (srfi srfi-1)
- #\export (compile-tree-il))
-
-(define-syntax-rule (-> (type arg ...))
- `(type ,arg ...))
-
-(define-syntax-rule (@implv sym)
- (-> (@ '(language ecmascript impl) 'sym)))
-
-(define-syntax-rule (@impl sym arg ...)
- (-> (apply (@implv sym) arg ...)))
-
-(define (empty-lexical-environment)
- '())
-
-(define (econs name gensym env)
- (acons name (-> (lexical name gensym)) env))
-
-(define (lookup name env)
- (or (assq-ref env name)
- (-> (toplevel name))))
-
-(define (compile-tree-il exp env opts)
- (values
- (parse-tree-il
- (-> (begin (@impl js-init)
- (comp exp (empty-lexical-environment)))))
- env
- env))
-
-(define (location x)
- (and (pair? x)
- (let ((props (source-properties x)))
- (and (not (null? props))
- props))))
-
-;; for emacs:
-;; (put 'pmatch/source 'scheme-indent-function 1)
-
-(define-syntax-rule (pmatch/source x clause ...)
- (let ((x x))
- (let ((res (pmatch x
- clause ...)))
- (let ((loc (location x)))
- (if loc
- (set-source-properties! res (location x))))
- res)))
-
-(define current-return-tag (make-parameter #f))
-
-(define (return expr)
- (-> (abort (or (current-return-tag) (error "return outside function"))
- (list expr)
- (-> (const '())))))
-
-(define (with-return-prompt body-thunk)
- (let ((tag (gensym "return")))
- (parameterize ((current-return-tag
- (-> (lexical 'return tag))))
- (-> (let '(return) (list tag)
- (list (-> (apply (-> (primitive 'make-prompt-tag)))))
- (-> (prompt (current-return-tag)
- (body-thunk)
- (let ((val (gensym "val")))
- (-> (lambda-case
- `(((k val) #f #f #f () (,(gensym) ,val))
- ,(-> (lexical 'val val)))))))))))))
-
-(define (comp x e)
- (let ((l (location x)))
- (define (let1 what proc)
- (let ((sym (gensym)))
- (-> (let (list sym) (list sym) (list what)
- (proc sym)))))
- (define (begin1 what proc)
- (let1 what (lambda (v)
- (-> (begin (proc v)
- (-> (lexical v v)))))))
- (pmatch/source x
- (null
- ;; FIXME, null doesn't have much relation to EOL...
- (-> (const '())))
- (true
- (-> (const #t)))
- (false
- (-> (const #f)))
- ((number ,num)
- (-> (const num)))
- ((string ,str)
- (-> (const str)))
- (this
- (@impl get-this))
- ((+ ,a)
- (-> (apply (-> (primitive '+))
- (@impl ->number (comp a e))
- (-> (const 0)))))
- ((- ,a)
- (-> (apply (-> (primitive '-)) (-> (const 0)) (comp a e))))
- ((~ ,a)
- (@impl bitwise-not (comp a e)))
- ((! ,a)
- (@impl logical-not (comp a e)))
- ((+ ,a ,b)
- (-> (apply (-> (primitive '+)) (comp a e) (comp b e))))
- ((- ,a ,b)
- (-> (apply (-> (primitive '-)) (comp a e) (comp b e))))
- ((/ ,a ,b)
- (-> (apply (-> (primitive '/)) (comp a e) (comp b e))))
- ((* ,a ,b)
- (-> (apply (-> (primitive '*)) (comp a e) (comp b e))))
- ((% ,a ,b)
- (@impl mod (comp a e) (comp b e)))
- ((<< ,a ,b)
- (@impl shift (comp a e) (comp b e)))
- ((>> ,a ,b)
- (@impl shift (comp a e) (comp `(- ,b) e)))
- ((< ,a ,b)
- (-> (apply (-> (primitive '<)) (comp a e) (comp b e))))
- ((<= ,a ,b)
- (-> (apply (-> (primitive '<=)) (comp a e) (comp b e))))
- ((> ,a ,b)
- (-> (apply (-> (primitive '>)) (comp a e) (comp b e))))
- ((>= ,a ,b)
- (-> (apply (-> (primitive '>=)) (comp a e) (comp b e))))
- ((in ,a ,b)
- (@impl has-property? (comp a e) (comp b e)))
- ((== ,a ,b)
- (-> (apply (-> (primitive 'equal?)) (comp a e) (comp b e))))
- ((!= ,a ,b)
- (-> (apply (-> (primitive 'not))
- (-> (apply (-> (primitive 'equal?))
- (comp a e) (comp b e))))))
- ((=== ,a ,b)
- (-> (apply (-> (primitive 'eqv?)) (comp a e) (comp b e))))
- ((!== ,a ,b)
- (-> (apply (-> (primitive 'not))
- (-> (apply (-> (primitive 'eqv?))
- (comp a e) (comp b e))))))
- ((& ,a ,b)
- (@impl band (comp a e) (comp b e)))
- ((^ ,a ,b)
- (@impl bxor (comp a e) (comp b e)))
- ((bor ,a ,b)
- (@impl bior (comp a e) (comp b e)))
- ((and ,a ,b)
- (-> (if (@impl ->boolean (comp a e))
- (comp b e)
- (-> (const #f)))))
- ((or ,a ,b)
- (let1 (comp a e)
- (lambda (v)
- (-> (if (@impl ->boolean (-> (lexical v v)))
- (-> (lexical v v))
- (comp b e))))))
- ((if ,test ,then ,else)
- (-> (if (@impl ->boolean (comp test e))
- (comp then e)
- (comp else e))))
- ((if ,test ,then)
- (-> (if (@impl ->boolean (comp test e))
- (comp then e)
- (@implv *undefined*))))
- ((postinc (ref ,foo))
- (begin1 (comp `(ref ,foo) e)
- (lambda (var)
- (-> (set! (lookup foo e)
- (-> (apply (-> (primitive '+))
- (-> (lexical var var))
- (-> (const 1)))))))))
- ((postinc (pref ,obj ,prop))
- (let1 (comp obj e)
- (lambda (objvar)
- (begin1 (@impl pget
- (-> (lexical objvar objvar))
- (-> (const prop)))
- (lambda (tmpvar)
- (@impl pput
- (-> (lexical objvar objvar))
- (-> (const prop))
- (-> (apply (-> (primitive '+))
- (-> (lexical tmpvar tmpvar))
- (-> (const 1))))))))))
- ((postinc (aref ,obj ,prop))
- (let1 (comp obj e)
- (lambda (objvar)
- (let1 (comp prop e)
- (lambda (propvar)
- (begin1 (@impl pget
- (-> (lexical objvar objvar))
- (-> (lexical propvar propvar)))
- (lambda (tmpvar)
- (@impl pput
- (-> (lexical objvar objvar))
- (-> (lexical propvar propvar))
- (-> (apply (-> (primitive '+))
- (-> (lexical tmpvar tmpvar))
- (-> (const 1))))))))))))
- ((postdec (ref ,foo))
- (begin1 (comp `(ref ,foo) e)
- (lambda (var)
- (-> (set (lookup foo e)
- (-> (apply (-> (primitive '-))
- (-> (lexical var var))
- (-> (const 1)))))))))
- ((postdec (pref ,obj ,prop))
- (let1 (comp obj e)
- (lambda (objvar)
- (begin1 (@impl pget
- (-> (lexical objvar objvar))
- (-> (const prop)))
- (lambda (tmpvar)
- (@impl pput
- (-> (lexical objvar objvar))
- (-> (const prop))
- (-> (apply (-> (primitive '-))
- (-> (lexical tmpvar tmpvar))
- (-> (const 1))))))))))
- ((postdec (aref ,obj ,prop))
- (let1 (comp obj e)
- (lambda (objvar)
- (let1 (comp prop e)
- (lambda (propvar)
- (begin1 (@impl pget
- (-> (lexical objvar objvar))
- (-> (lexical propvar propvar)))
- (lambda (tmpvar)
- (@impl pput
- (-> (lexical objvar objvar))
- (-> (lexical propvar propvar))
- (-> (inline
- '- (-> (lexical tmpvar tmpvar))
- (-> (const 1))))))))))))
- ((preinc (ref ,foo))
- (let ((v (lookup foo e)))
- (-> (begin
- (-> (set! v
- (-> (apply (-> (primitive '+))
- v
- (-> (const 1))))))
- v))))
- ((preinc (pref ,obj ,prop))
- (let1 (comp obj e)
- (lambda (objvar)
- (begin1 (-> (apply (-> (primitive '+))
- (@impl pget
- (-> (lexical objvar objvar))
- (-> (const prop)))
- (-> (const 1))))
- (lambda (tmpvar)
- (@impl pput (-> (lexical objvar objvar))
- (-> (const prop))
- (-> (lexical tmpvar tmpvar))))))))
- ((preinc (aref ,obj ,prop))
- (let1 (comp obj e)
- (lambda (objvar)
- (let1 (comp prop e)
- (lambda (propvar)
- (begin1 (-> (apply (-> (primitive '+))
- (@impl pget
- (-> (lexical objvar objvar))
- (-> (lexical propvar propvar)))
- (-> (const 1))))
- (lambda (tmpvar)
- (@impl pput
- (-> (lexical objvar objvar))
- (-> (lexical propvar propvar))
- (-> (lexical tmpvar tmpvar))))))))))
- ((predec (ref ,foo))
- (let ((v (lookup foo e)))
- (-> (begin
- (-> (set! v
- (-> (apply (-> (primitive '-))
- v
- (-> (const 1))))))
- v))))
- ((predec (pref ,obj ,prop))
- (let1 (comp obj e)
- (lambda (objvar)
- (begin1 (-> (apply (-> (primitive '-))
- (@impl pget
- (-> (lexical objvar objvar))
- (-> (const prop)))
- (-> (const 1))))
- (lambda (tmpvar)
- (@impl pput
- (-> (lexical objvar objvar))
- (-> (const prop))
- (-> (lexical tmpvar tmpvar))))))))
- ((predec (aref ,obj ,prop))
- (let1 (comp obj e)
- (lambda (objvar)
- (let1 (comp prop e)
- (lambda (propvar)
- (begin1 (-> (apply (-> (primitive '-))
- (@impl pget
- (-> (lexical objvar objvar))
- (-> (lexical propvar propvar)))
- (-> (const 1))))
- (lambda (tmpvar)
- (@impl pput
- (-> (lexical objvar objvar))
- (-> (lexical propvar propvar))
- (-> (lexical tmpvar tmpvar))))))))))
- ((ref ,id)
- (lookup id e))
- ((var . ,forms)
- `(begin
- ,@(map (lambda (form)
- (pmatch form
- ((,x ,y)
- (-> (define x (comp y e))))
- ((,x)
- (-> (define x (@implv *undefined*))))
- (else (error "bad var form" form))))
- forms)))
- ((begin)
- (-> (void)))
- ((begin ,form)
- (comp form e))
- ((begin . ,forms)
- `(begin ,@(map (lambda (x) (comp x e)) forms)))
- ((lambda ,formals ,body)
- (let ((syms (map (lambda (x)
- (gensym (string-append (symbol->string x) " ")))
- formals)))
- `(lambda ()
- (lambda-case
- ((() ,formals #f #f ,(map (lambda (x) (@implv *undefined*)) formals) ,syms)
- ,(with-return-prompt
- (lambda ()
- (comp-body e body formals syms))))))))
- ((call/this ,obj ,prop . ,args)
- (@impl call/this*
- obj
- (-> (lambda '()
- `(lambda-case
- ((() #f #f #f () ())
- (apply ,(@impl pget obj prop) ,@args)))))))
- ((call (pref ,obj ,prop) ,args)
- (comp `(call/this ,(comp obj e)
- ,(-> (const prop))
- ,@(map (lambda (x) (comp x e)) args))
- e))
- ((call (aref ,obj ,prop) ,args)
- (comp `(call/this ,(comp obj e)
- ,(comp prop e)
- ,@(map (lambda (x) (comp x e)) args))
- e))
- ((call ,proc ,args)
- `(apply ,(comp proc e)
- ,@(map (lambda (x) (comp x e)) args)))
- ((return ,expr)
- (return (comp expr e)))
- ((array . ,args)
- `(apply ,(@implv new-array)
- ,@(map (lambda (x) (comp x e)) args)))
- ((object . ,args)
- `(apply ,(@implv new-object)
- ,@(map (lambda (x)
- (pmatch x
- ((,prop ,val)
- (-> (apply (-> (primitive 'cons))
- (-> (const prop))
- (comp val e))))
- (else
- (error "bad prop-val pair" x))))
- args)))
- ((pref ,obj ,prop)
- (@impl pget
- (comp obj e)
- (-> (const prop))))
- ((aref ,obj ,index)
- (@impl pget
- (comp obj e)
- (comp index e)))
- ((= (ref ,name) ,val)
- (let ((v (lookup name e)))
- (-> (begin
- (-> (set! v (comp val e)))
- v))))
- ((= (pref ,obj ,prop) ,val)
- (@impl pput
- (comp obj e)
- (-> (const prop))
- (comp val e)))
- ((= (aref ,obj ,prop) ,val)
- (@impl pput
- (comp obj e)
- (comp prop e)
- (comp val e)))
- ((+= ,what ,val)
- (comp `(= ,what (+ ,what ,val)) e))
- ((-= ,what ,val)
- (comp `(= ,what (- ,what ,val)) e))
- ((/= ,what ,val)
- (comp `(= ,what (/ ,what ,val)) e))
- ((*= ,what ,val)
- (comp `(= ,what (* ,what ,val)) e))
- ((%= ,what ,val)
- (comp `(= ,what (% ,what ,val)) e))
- ((>>= ,what ,val)
- (comp `(= ,what (>> ,what ,val)) e))
- ((<<= ,what ,val)
- (comp `(= ,what (<< ,what ,val)) e))
- ((>>>= ,what ,val)
- (comp `(= ,what (>>> ,what ,val)) e))
- ((&= ,what ,val)
- (comp `(= ,what (& ,what ,val)) e))
- ((bor= ,what ,val)
- (comp `(= ,what (bor ,what ,val)) e))
- ((^= ,what ,val)
- (comp `(= ,what (^ ,what ,val)) e))
- ((new ,what ,args)
- (@impl new
- (map (lambda (x) (comp x e))
- (cons what args))))
- ((delete (pref ,obj ,prop))
- (@impl pdel
- (comp obj e)
- (-> (const prop))))
- ((delete (aref ,obj ,prop))
- (@impl pdel
- (comp obj e)
- (comp prop e)))
- ((void ,expr)
- (-> (begin
- (comp expr e)
- (@implv *undefined*))))
- ((typeof ,expr)
- (@impl typeof
- (comp expr e)))
- ((do ,statement ,test)
- (let ((%loop (gensym "%loop "))
- (%continue (gensym "%continue ")))
- (let ((e (econs '%loop %loop (econs '%continue %continue e))))
- (-> (letrec '(%loop %continue) (list %loop %continue)
- (list (-> (lambda '()
- (-> (lambda-case
- `((() #f #f #f () ())
- ,(-> (begin
- (comp statement e)
- (-> (apply (-> (lexical '%continue %continue)))))))))))
- (-> (lambda '()
- (-> (lambda-case
- `((() #f #f #f () ())
- ,(-> (if (@impl ->boolean (comp test e))
- (-> (apply (-> (lexical '%loop %loop))))
- (@implv *undefined*)))))))))
- (-> (apply (-> (lexical '%loop %loop)))))))))
- ((while ,test ,statement)
- (let ((%continue (gensym "%continue ")))
- (let ((e (econs '%continue %continue e)))
- (-> (letrec '(%continue) (list %continue)
- (list (-> (lambda '()
- (-> (lambda-case
- `((() #f #f #f () ())
- ,(-> (if (@impl ->boolean (comp test e))
- (-> (begin (comp statement e)
- (-> (apply (-> (lexical '%continue %continue))))))
- (@implv *undefined*)))))))))
- (-> (apply (-> (lexical '%continue %continue)))))))))
-
- ((for ,init ,test ,inc ,statement)
- (let ((%continue (gensym "%continue ")))
- (let ((e (econs '%continue %continue e)))
- (-> (letrec '(%continue) (list %continue)
- (list (-> (lambda '()
- (-> (lambda-case
- `((() #f #f #f () ())
- ,(-> (if (if test
- (@impl ->boolean (comp test e))
- (comp 'true e))
- (-> (begin (comp statement e)
- (comp (or inc '(begin)) e)
- (-> (apply (-> (lexical '%continue %continue))))))
- (@implv *undefined*)))))))))
- (-> (begin (comp (or init '(begin)) e)
- (-> (apply (-> (lexical '%continue %continue)))))))))))
-
- ((for-in ,var ,object ,statement)
- (let ((%enum (gensym "%enum "))
- (%continue (gensym "%continue ")))
- (let ((e (econs '%enum %enum (econs '%continue %continue e))))
- (-> (letrec '(%enum %continue) (list %enum %continue)
- (list (@impl make-enumerator (comp object e))
- (-> (lambda '()
- (-> (lambda-case
- `((() #f #f #f () ())
- (-> (if (@impl ->boolean
- (@impl pget
- (-> (lexical '%enum %enum))
- (-> (const 'length))))
- (-> (begin
- (comp `(= ,var (call/this ,(-> (lexical '%enum %enum))
- ,(-> (const 'pop))))
- e)
- (comp statement e)
- (-> (apply (-> (lexical '%continue %continue))))))
- (@implv *undefined*)))))))))
- (-> (apply (-> (lexical '%continue %continue)))))))))
-
- ((block ,x)
- (comp x e))
- (else
- (error "compilation not yet implemented:" x)))))
-
-(define (comp-body e body formals formal-syms)
- (define (process)
- (let lp ((in body) (out '()) (rvars '()))
- (pmatch in
- (((var (,x) . ,morevars) . ,rest)
- (lp `((var . ,morevars) . ,rest)
- out
- (if (or (memq x rvars) (memq x formals))
- rvars
- (cons x rvars))))
- (((var (,x ,y) . ,morevars) . ,rest)
- (lp `((var . ,morevars) . ,rest)
- `((= (ref ,x) ,y) . ,out)
- (if (or (memq x rvars) (memq x formals))
- rvars
- (cons x rvars))))
- (((var) . ,rest)
- (lp rest out rvars))
- ((,x . ,rest) (guard (and (pair? x) (eq? (car x) 'lambda)))
- (lp rest
- (cons x out)
- rvars))
- ((,x . ,rest) (guard (pair? x))
- (receive (sub-out rvars)
- (lp x '() rvars)
- (lp rest
- (cons sub-out out)
- rvars)))
- ((,x . ,rest)
- (lp rest
- (cons x out)
- rvars))
- (()
- (values (reverse! out)
- rvars)))))
- (receive (out rvars)
- (process)
- (let* ((names (reverse rvars))
- (syms (map (lambda (x)
- (gensym (string-append (symbol->string x) " ")))
- names))
- (e (fold econs (fold econs e formals formal-syms) names syms)))
- (-> (let names syms (map (lambda (x) (@implv *undefined*)) names)
- (comp out e))))))
-;;; ECMAScript for Guile
-
-;; Copyright (C) 2009 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language ecmascript function)
- #\use-module (oop goops)
- #\use-module (language ecmascript base)
- #\export (*function-prototype* *program-wrappers*))
-
-
-(define-class <js-program-wrapper> (<js-object>))
-
-(define *program-wrappers* (make-doubly-weak-hash-table 31))
-
-(define *function-prototype* (make <js-object> #\class "Function"
- #\value (lambda args *undefined*)))
-
-(define-js-method *function-prototype* (toString)
- (format #f "~A" (js-value this)))
-
-(define-js-method *function-prototype* (apply this-arg array)
- (cond ((or (null? array) (eq? array *undefined*))
- (call/this this-arg (js-value this)))
- ((is-a? array <js-array-object>)
- (call/this this-arg
- (lambda ()
- (apply (js-value this)
- (vector->list (js-array-vector array))))))
- (else
- (throw 'TypeError 'apply array))))
-
-(define-js-method *function-prototype* (call this-arg . args)
- (call/this this-arg
- (lambda ()
- (apply (js-value this) args))))
-
-(define-method (pget (o <applicable>) p)
- (let ((wrapper (hashq-ref *program-wrappers* o)))
- (if wrapper
- (pget wrapper p)
- (pget *function-prototype* p))))
-
-(define-method (pput (o <applicable>) p v)
- (let ((wrapper (hashq-ref *program-wrappers* o)))
- (if wrapper
- (pput wrapper p v)
- (let ((wrapper (make <js-program-wrapper> #\value o #\class "Function"
- #\prototype *function-prototype*)))
- (hashq-set! *program-wrappers* o wrapper)
- (pput wrapper p v)))))
-
-(define-method (js-prototype (o <applicable>))
- (let ((wrapper (hashq-ref *program-wrappers* o)))
- (if wrapper
- (js-prototype wrapper)
- #f)))
-
-(define-method (js-constructor (o <applicable>))
- (let ((wrapper (hashq-ref *program-wrappers* o)))
- (if wrapper
- (js-constructor wrapper)
- #f)))
-;;; ECMAScript for Guile
-
-;; Copyright (C) 2009 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language ecmascript impl)
- #\use-module (oop goops)
- #\use-module (language ecmascript base)
- #\use-module (language ecmascript function)
- #\use-module (language ecmascript array)
- #\re-export (*undefined* *this* call/this*
- pget pput pdel has-property?
- ->boolean ->number
- new-object new new-array)
- #\export (js-init get-this
- typeof
- bitwise-not logical-not
- shift
- mod
- band bxor bior
- make-enumerator))
-
-
-(define-class <js-module-object> (<js-object>)
- (module #\init-form (current-module) #\init-keyword #\module
- #\getter js-module))
-(define-method (pget (o <js-module-object>) (p <string>))
- (pget o (string->symbol p)))
-(define-method (pget (o <js-module-object>) (p <symbol>))
- (let ((v (module-variable (js-module o) p)))
- (if v
- (variable-ref v)
- (next-method))))
-(define-method (pput (o <js-module-object>) (p <string>) v)
- (pput o (string->symbol p) v))
-(define-method (pput (o <js-module-object>) (p <symbol>) v)
- (module-define! (js-module o) p v))
-(define-method (prop-attrs (o <js-module-object>) (p <symbol>))
- (cond ((module-local-variable (js-module o) p) '())
- ((module-variable (js-module o) p) '(DontDelete ReadOnly))
- (else (next-method))))
-(define-method (prop-attrs (o <js-module-object>) (p <string>))
- (prop-attrs o (string->symbol p)))
-(define-method (prop-keys (o <js-module-object>))
- (append (hash-map->list (lambda (k v) k) (module-obarray (js-module o)))
- (next-method)))
-
-;; we could make a renamer, but having obj['foo-bar'] should be enough
-(define (js-require modstr)
- (make <js-module-object> #\module
- (resolve-interface (map string->symbol (string-split modstr #\.)))))
-
-(define-class <js-global-object> (<js-module-object>))
-(define-method (js-module (o <js-global-object>))
- (current-module))
-
-(define (init-js-bindings! mod)
- (module-define! mod 'NaN +nan.0)
- (module-define! mod 'Infinity +inf.0)
- (module-define! mod 'undefined *undefined*)
- (module-define! mod 'require js-require)
- ;; isNAN, isFinite, parseFloat, parseInt, eval
- ;; decodeURI, decodeURIComponent, encodeURI, encodeURIComponent
- ;; Object Function Array String Boolean Number Date RegExp Error EvalError
- ;; RangeError ReferenceError SyntaxError TypeError URIError
- (module-define! mod 'Object *object-prototype*)
- (module-define! mod 'Array *array-prototype*))
-
-(define (js-init)
- (cond ((get-this))
- (else
- (fluid-set! *this* (make <js-global-object>))
- (init-js-bindings! (current-module)))))
-
-(define (get-this)
- (fluid-ref *this*))
-
-(define (typeof x)
- (cond ((eq? x *undefined*) "undefined")
- ((null? x) "object")
- ((boolean? x) "boolean")
- ((number? x) "number")
- ((string? x) "string")
- ((procedure? x) "function")
- ((is-a? x <js-object>) "object")
- (else "scm")))
-
-(define bitwise-not lognot)
-(define (logical-not x)
- (not (->boolean (->primitive x))))
-
-(define shift ash)
-
-(define band logand)
-(define bxor logxor)
-(define bior logior)
-
-(define mod modulo)
-
-(define-method (+ (a <string>) (b <string>))
- (string-append a b))
-
-(define-method (+ (a <string>) b)
- (string-append a (->string b)))
-
-(define-method (+ a (b <string>))
- (string-append (->string a) b))
-
-(define-method (+ a b)
- (+ (->number a) (->number b)))
-
-(define-method (- a b)
- (- (->number a) (->number b)))
-
-(define-method (* a b)
- (* (->number a) (->number b)))
-
-(define-method (/ a b)
- (/ (->number a) (->number b)))
-
-(define-method (< a b)
- (< (->number a) (->number b)))
-(define-method (< (a <string>) (b <string>))
- (string< a b))
-
-(define-method (<= a b)
- (<= (->number a) (->number b)))
-(define-method (<= (a <string>) (b <string>))
- (string<= a b))
-
-(define-method (>= a b)
- (>= (->number a) (->number b)))
-(define-method (>= (a <string>) (b <string>))
- (string>= a b))
-
-(define-method (> a b)
- (> (->number a) (->number b)))
-(define-method (> (a <string>) (b <string>))
- (string> a b))
-
-(define (obj-and-prototypes o)
- (if o
- (cons o (obj-and-prototypes (js-prototype o)))
- '()))
-
-(define (make-enumerator obj)
- (let ((props (make-hash-table 23)))
- (for-each (lambda (o)
- (for-each (lambda (k) (hashq-set! props k #t))
- (prop-keys o)))
- (obj-and-prototypes obj))
- (apply new-array (filter (lambda (p)
- (not (prop-has-attr? obj p 'DontEnum)))
- (hash-map->list (lambda (k v) k) props)))))
-;;; ECMAScript for Guile
-
-;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language ecmascript parse)
- #\use-module (system base lalr)
- #\use-module (language ecmascript tokenize)
- #\export (read-ecmascript read-ecmascript/1 make-parser))
-
-(define* (syntax-error message #\optional token)
- (if (lexical-token? token)
- (throw 'syntax-error #f message
- (and=> (lexical-token-source token)
- source-location->source-properties)
- (or (lexical-token-value token)
- (lexical-token-category token))
- #f)
- (throw 'syntax-error #f message #f token #f)))
-
-(define (read-ecmascript port)
- (let ((parse (make-parser)))
- (parse (make-tokenizer port) syntax-error)))
-
-(define (read-ecmascript/1 port)
- (let ((parse (make-parser)))
- (parse (make-tokenizer/1 port) syntax-error)))
-
-(define *eof-object*
- (call-with-input-string "" read-char))
-
-(define (make-parser)
- ;; Return a fresh ECMAScript parser. Parsers produced by `lalr-scm' are now
- ;; stateful (e.g., they won't invoke the tokenizer any more once it has
- ;; returned `*eoi*'), hence the need to instantiate new parsers.
-
- (lalr-parser
- ;; terminal (i.e. input) token types
- (lbrace rbrace lparen rparen lbracket rbracket dot semicolon comma <
- > <= >= == != === !== + - * % ++ -- << >> >>> & bor ^ ! ~ && or ?
- colon = += -= *= %= <<= >>= >>>= &= bor= ^= / /=
-
- break else new var case finally return void catch for switch while
- continue function this with default if throw delete in try do
- instanceof typeof null true false
-
- Identifier StringLiteral NumericLiteral RegexpLiteral)
-
-
- (Program (SourceElements) \: $1
- (*eoi*) \: *eof-object*)
-
- ;;
- ;; Verily, here we define statements. Expressions are defined
- ;; afterwards.
- ;;
-
- (SourceElement (Statement) \: $1
- (FunctionDeclaration) \: $1)
-
- (FunctionDeclaration (function Identifier lparen rparen lbrace FunctionBody rbrace) \: `(var (,$2 (lambda () ,$6)))
- (function Identifier lparen FormalParameterList rparen lbrace FunctionBody rbrace) \: `(var (,$2 (lambda ,$4 ,$7))))
- (FunctionExpression (function lparen rparen lbrace FunctionBody rbrace) \: `(lambda () ,$5)
- (function Identifier lparen rparen lbrace FunctionBody rbrace) \: `(lambda () ,$6)
- (function lparen FormalParameterList rparen lbrace FunctionBody rbrace) \: `(lambda ,$3 ,$6)
- (function Identifier lparen FormalParameterList rparen lbrace FunctionBody rbrace) \: `(lambda ,$4 ,$7))
- (FormalParameterList (Identifier) \: `(,$1)
- (FormalParameterList comma Identifier) \: `(,@$1 ,$3))
- (SourceElements (SourceElement) \: $1
- (SourceElements SourceElement) \: (if (and (pair? $1) (eq? (car $1) 'begin))
- `(begin ,@(cdr $1) ,$2)
- `(begin ,$1 ,$2)))
- (FunctionBody (SourceElements) \: $1
- () \: '(begin))
-
- (Statement (Block) \: $1
- (VariableStatement) \: $1
- (EmptyStatement) \: $1
- (ExpressionStatement) \: $1
- (IfStatement) \: $1
- (IterationStatement) \: $1
- (ContinueStatement) \: $1
- (BreakStatement) \: $1
- (ReturnStatement) \: $1
- (WithStatement) \: $1
- (LabelledStatement) \: $1
- (SwitchStatement) \: $1
- (ThrowStatement) \: $1
- (TryStatement) \: $1)
-
- (Block (lbrace StatementList rbrace) \: `(block ,$2))
- (StatementList (Statement) \: $1
- (StatementList Statement) \: (if (and (pair? $1) (eq? (car $1) 'begin))
- `(begin ,@(cdr $1) ,$2)
- `(begin ,$1 ,$2)))
-
- (VariableStatement (var VariableDeclarationList) \: `(var ,@$2))
- (VariableDeclarationList (VariableDeclaration) \: `(,$1)
- (VariableDeclarationList comma VariableDeclaration) \: `(,@$1 ,$2))
- (VariableDeclarationListNoIn (VariableDeclarationNoIn) \: `(,$1)
- (VariableDeclarationListNoIn comma VariableDeclarationNoIn) \: `(,@$1 ,$2))
- (VariableDeclaration (Identifier) \: `(,$1)
- (Identifier Initialiser) \: `(,$1 ,$2))
- (VariableDeclarationNoIn (Identifier) \: `(,$1)
- (Identifier Initialiser) \: `(,$1 ,$2))
- (Initialiser (= AssignmentExpression) \: $2)
- (InitialiserNoIn (= AssignmentExpressionNoIn) \: $2)
-
- (EmptyStatement (semicolon) \: '(begin))
-
- (ExpressionStatement (Expression semicolon) \: $1)
-
- (IfStatement (if lparen Expression rparen Statement else Statement) \: `(if ,$3 ,$5 ,$7)
- (if lparen Expression rparen Statement) \: `(if ,$3 ,$5))
-
- (IterationStatement (do Statement while lparen Expression rparen semicolon) \: `(do ,$2 ,$5)
-
- (while lparen Expression rparen Statement) \: `(while ,$3 ,$5)
-
- (for lparen semicolon semicolon rparen Statement) \: `(for #f #f #f ,$6)
- (for lparen semicolon semicolon Expression rparen Statement) \: `(for #f #f ,$5 ,$7)
- (for lparen semicolon Expression semicolon rparen Statement) \: `(for #f ,$4 #f ,$7)
- (for lparen semicolon Expression semicolon Expression rparen Statement) \: `(for #f ,$4 ,$6 ,$8)
-
- (for lparen ExpressionNoIn semicolon semicolon rparen Statement) \: `(for ,$3 #f #f ,$7)
- (for lparen ExpressionNoIn semicolon semicolon Expression rparen Statement) \: `(for ,$3 #f ,$6 ,$8)
- (for lparen ExpressionNoIn semicolon Expression semicolon rparen Statement) \: `(for ,$3 ,$5 #f ,$8)
- (for lparen ExpressionNoIn semicolon Expression semicolon Expression rparen Statement) \: `(for ,$3 ,$5 ,$7 ,$9)
-
- (for lparen var VariableDeclarationListNoIn semicolon semicolon rparen Statement) \: `(for (var ,@$4) #f #f ,$8)
- (for lparen var VariableDeclarationListNoIn semicolon semicolon Expression rparen Statement) \: `(for (var ,@$4) #f ,$7 ,$9)
- (for lparen var VariableDeclarationListNoIn semicolon Expression semicolon rparen Statement) \: `(for (var ,@$4) ,$6 #f ,$9)
- (for lparen var VariableDeclarationListNoIn semicolon Expression semicolon Expression rparen Statement) \: `(for (var ,@$4) ,$6 ,$8 ,$10)
-
- (for lparen LeftHandSideExpression in Expression rparen Statement) \: `(for-in ,$3 ,$5 ,$7)
- (for lparen var VariableDeclarationNoIn in Expression rparen Statement) \: `(begin (var ,$4) (for-in (ref ,@$4) ,$6 ,$8)))
-
- (ContinueStatement (continue Identifier semicolon) \: `(continue ,$2)
- (continue semicolon) \: `(continue))
-
- (BreakStatement (break Identifier semicolon) \: `(break ,$2)
- (break semicolon) \: `(break))
-
- (ReturnStatement (return Expression semicolon) \: `(return ,$2)
- (return semicolon) \: `(return))
-
- (WithStatement (with lparen Expression rparen Statement) \: `(with ,$3 ,$5))
-
- (SwitchStatement (switch lparen Expression rparen CaseBlock) \: `(switch ,$3 ,@$5))
- (CaseBlock (lbrace rbrace) \: '()
- (lbrace CaseClauses rbrace) \: $2
- (lbrace CaseClauses DefaultClause rbrace) \: `(,@$2 ,@$3)
- (lbrace DefaultClause rbrace) \: `(,$2)
- (lbrace DefaultClause CaseClauses rbrace) \: `(,@$2 ,@$3))
- (CaseClauses (CaseClause) \: `(,$1)
- (CaseClauses CaseClause) \: `(,@$1 ,$2))
- (CaseClause (case Expression colon) \: `(case ,$2)
- (case Expression colon StatementList) \: `(case ,$2 ,$4))
- (DefaultClause (default colon) \: `(default)
- (default colon StatementList) \: `(default ,$3))
-
- (LabelledStatement (Identifier colon Statement) \: `(label ,$1 ,$3))
-
- (ThrowStatement (throw Expression semicolon) \: `(throw ,$2))
-
- (TryStatement (try Block Catch) \: `(try ,$2 ,$3 #f)
- (try Block Finally) \: `(try ,$2 #f ,$3)
- (try Block Catch Finally) \: `(try ,$2 ,$3 ,$4))
- (Catch (catch lparen Identifier rparen Block) \: `(catch ,$3 ,$5))
- (Finally (finally Block) \: `(finally ,$2))
-
- ;;
- ;; As promised, expressions. We build up to Expression bottom-up, so
- ;; as to get operator precedence right.
- ;;
-
- (PrimaryExpression (this) \: 'this
- (null) \: 'null
- (true) \: 'true
- (false) \: 'false
- (Identifier) \: `(ref ,$1)
- (StringLiteral) \: `(string ,$1)
- (RegexpLiteral) \: `(regexp ,$1)
- (NumericLiteral) \: `(number ,$1)
- (dot NumericLiteral) \: `(number ,(string->number (string-append "." (number->string $2))))
- (ArrayLiteral) \: $1
- (ObjectLiteral) \: $1
- (lparen Expression rparen) \: $2)
-
- (ArrayLiteral (lbracket rbracket) \: '(array)
- (lbracket Elision rbracket) \: '(array ,@$2)
- (lbracket ElementList rbracket) \: `(array ,@$2)
- (lbracket ElementList comma rbracket) \: `(array ,@$2)
- (lbracket ElementList comma Elision rbracket) \: `(array ,@$2))
- (ElementList (AssignmentExpression) \: `(,$1)
- (Elision AssignmentExpression) \: `(,@$1 ,$2)
- (ElementList comma AssignmentExpression) \: `(,@$1 ,$3)
- (ElementList comma Elision AssignmentExpression) \: `(,@$1 ,@$3 ,$4))
- (Elision (comma) \: '((number 0))
- (Elision comma) \: `(,@$1 (number 0)))
-
- (ObjectLiteral (lbrace rbrace) \: `(object)
- (lbrace PropertyNameAndValueList rbrace) \: `(object ,@$2))
- (PropertyNameAndValueList (PropertyName colon AssignmentExpression) \: `((,$1 ,$3))
- (PropertyNameAndValueList comma PropertyName colon AssignmentExpression) \: `(,@$1 (,$3 ,$5)))
- (PropertyName (Identifier) \: $1
- (StringLiteral) \: (string->symbol $1)
- (NumericLiteral) \: $1)
-
- (MemberExpression (PrimaryExpression) \: $1
- (FunctionExpression) \: $1
- (MemberExpression lbracket Expression rbracket) \: `(aref ,$1 ,$3)
- (MemberExpression dot Identifier) \: `(pref ,$1 ,$3)
- (new MemberExpression Arguments) \: `(new ,$2 ,$3))
-
- (NewExpression (MemberExpression) \: $1
- (new NewExpression) \: `(new ,$2 ()))
-
- (CallExpression (MemberExpression Arguments) \: `(call ,$1 ,$2)
- (CallExpression Arguments) \: `(call ,$1 ,$2)
- (CallExpression lbracket Expression rbracket) \: `(aref ,$1 ,$3)
- (CallExpression dot Identifier) \: `(pref ,$1 ,$3))
- (Arguments (lparen rparen) \: '()
- (lparen ArgumentList rparen) \: $2)
- (ArgumentList (AssignmentExpression) \: `(,$1)
- (ArgumentList comma AssignmentExpression) \: `(,@$1 ,$3))
-
- (LeftHandSideExpression (NewExpression) \: $1
- (CallExpression) \: $1)
-
- (PostfixExpression (LeftHandSideExpression) \: $1
- (LeftHandSideExpression ++) \: `(postinc ,$1)
- (LeftHandSideExpression --) \: `(postdec ,$1))
-
- (UnaryExpression (PostfixExpression) \: $1
- (delete UnaryExpression) \: `(delete ,$2)
- (void UnaryExpression) \: `(void ,$2)
- (typeof UnaryExpression) \: `(typeof ,$2)
- (++ UnaryExpression) \: `(preinc ,$2)
- (-- UnaryExpression) \: `(predec ,$2)
- (+ UnaryExpression) \: `(+ ,$2)
- (- UnaryExpression) \: `(- ,$2)
- (~ UnaryExpression) \: `(~ ,$2)
- (! UnaryExpression) \: `(! ,$2))
-
- (MultiplicativeExpression (UnaryExpression) \: $1
- (MultiplicativeExpression * UnaryExpression) \: `(* ,$1 ,$3)
- (MultiplicativeExpression / UnaryExpression) \: `(/ ,$1 ,$3)
- (MultiplicativeExpression % UnaryExpression) \: `(% ,$1 ,$3))
-
- (AdditiveExpression (MultiplicativeExpression) \: $1
- (AdditiveExpression + MultiplicativeExpression) \: `(+ ,$1 ,$3)
- (AdditiveExpression - MultiplicativeExpression) \: `(- ,$1 ,$3))
-
- (ShiftExpression (AdditiveExpression) \: $1
- (ShiftExpression << MultiplicativeExpression) \: `(<< ,$1 ,$3)
- (ShiftExpression >> MultiplicativeExpression) \: `(>> ,$1 ,$3)
- (ShiftExpression >>> MultiplicativeExpression) \: `(>>> ,$1 ,$3))
-
- (RelationalExpression (ShiftExpression) \: $1
- (RelationalExpression < ShiftExpression) \: `(< ,$1 ,$3)
- (RelationalExpression > ShiftExpression) \: `(> ,$1 ,$3)
- (RelationalExpression <= ShiftExpression) \: `(<= ,$1 ,$3)
- (RelationalExpression >= ShiftExpression) \: `(>= ,$1 ,$3)
- (RelationalExpression instanceof ShiftExpression) \: `(instanceof ,$1 ,$3)
- (RelationalExpression in ShiftExpression) \: `(in ,$1 ,$3))
-
- (RelationalExpressionNoIn (ShiftExpression) \: $1
- (RelationalExpressionNoIn < ShiftExpression) \: `(< ,$1 ,$3)
- (RelationalExpressionNoIn > ShiftExpression) \: `(> ,$1 ,$3)
- (RelationalExpressionNoIn <= ShiftExpression) \: `(<= ,$1 ,$3)
- (RelationalExpressionNoIn >= ShiftExpression) \: `(>= ,$1 ,$3)
- (RelationalExpressionNoIn instanceof ShiftExpression) \: `(instanceof ,$1 ,$3))
-
- (EqualityExpression (RelationalExpression) \: $1
- (EqualityExpression == RelationalExpression) \: `(== ,$1 ,$3)
- (EqualityExpression != RelationalExpression) \: `(!= ,$1 ,$3)
- (EqualityExpression === RelationalExpression) \: `(=== ,$1 ,$3)
- (EqualityExpression !== RelationalExpression) \: `(!== ,$1 ,$3))
-
- (EqualityExpressionNoIn (RelationalExpressionNoIn) \: $1
- (EqualityExpressionNoIn == RelationalExpressionNoIn) \: `(== ,$1 ,$3)
- (EqualityExpressionNoIn != RelationalExpressionNoIn) \: `(!= ,$1 ,$3)
- (EqualityExpressionNoIn === RelationalExpressionNoIn) \: `(=== ,$1 ,$3)
- (EqualityExpressionNoIn !== RelationalExpressionNoIn) \: `(!== ,$1 ,$3))
-
- (BitwiseANDExpression (EqualityExpression) \: $1
- (BitwiseANDExpression & EqualityExpression) \: `(& ,$1 ,$3))
- (BitwiseANDExpressionNoIn (EqualityExpressionNoIn) \: $1
- (BitwiseANDExpressionNoIn & EqualityExpressionNoIn) \: `(& ,$1 ,$3))
-
- (BitwiseXORExpression (BitwiseANDExpression) \: $1
- (BitwiseXORExpression ^ BitwiseANDExpression) \: `(^ ,$1 ,$3))
- (BitwiseXORExpressionNoIn (BitwiseANDExpressionNoIn) \: $1
- (BitwiseXORExpressionNoIn ^ BitwiseANDExpressionNoIn) \: `(^ ,$1 ,$3))
-
- (BitwiseORExpression (BitwiseXORExpression) \: $1
- (BitwiseORExpression bor BitwiseXORExpression) \: `(bor ,$1 ,$3))
- (BitwiseORExpressionNoIn (BitwiseXORExpressionNoIn) \: $1
- (BitwiseORExpressionNoIn bor BitwiseXORExpressionNoIn) \: `(bor ,$1 ,$3))
-
- (LogicalANDExpression (BitwiseORExpression) \: $1
- (LogicalANDExpression && BitwiseORExpression) \: `(and ,$1 ,$3))
- (LogicalANDExpressionNoIn (BitwiseORExpressionNoIn) \: $1
- (LogicalANDExpressionNoIn && BitwiseORExpressionNoIn) \: `(and ,$1 ,$3))
-
- (LogicalORExpression (LogicalANDExpression) \: $1
- (LogicalORExpression or LogicalANDExpression) \: `(or ,$1 ,$3))
- (LogicalORExpressionNoIn (LogicalANDExpressionNoIn) \: $1
- (LogicalORExpressionNoIn or LogicalANDExpressionNoIn) \: `(or ,$1 ,$3))
-
- (ConditionalExpression (LogicalORExpression) \: $1
- (LogicalORExpression ? AssignmentExpression colon AssignmentExpression) \: `(if ,$1 ,$3 ,$5))
- (ConditionalExpressionNoIn (LogicalORExpressionNoIn) \: $1
- (LogicalORExpressionNoIn ? AssignmentExpressionNoIn colon AssignmentExpressionNoIn) \: `(if ,$1 ,$3 ,$5))
-
- (AssignmentExpression (ConditionalExpression) \: $1
- (LeftHandSideExpression AssignmentOperator AssignmentExpression) \: `(,$2 ,$1 ,$3))
- (AssignmentExpressionNoIn (ConditionalExpressionNoIn) \: $1
- (LeftHandSideExpression AssignmentOperator AssignmentExpressionNoIn) \: `(,$2 ,$1 ,$3))
- (AssignmentOperator (=) \: '=
- (*=) \: '*=
- (/=) \: '/=
- (%=) \: '%=
- (+=) \: '+=
- (-=) \: '-=
- (<<=) \: '<<=
- (>>=) \: '>>=
- (>>>=) \: '>>>=
- (&=) \: '&=
- (^=) \: '^=
- (bor=) \: 'bor=)
-
- (Expression (AssignmentExpression) \: $1
- (Expression comma AssignmentExpression) \: `(begin ,$1 ,$3))
- (ExpressionNoIn (AssignmentExpressionNoIn) \: $1
- (ExpressionNoIn comma AssignmentExpressionNoIn) \: `(begin ,$1 ,$3))))
-;;; ECMAScript specification for Guile
-
-;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language ecmascript spec)
- #\use-module (system base language)
- #\use-module (language ecmascript parse)
- #\use-module (language ecmascript compile-tree-il)
- #\export (ecmascript))
-
-;;;
-;;; Language definition
-;;;
-
-(define-language ecmascript
- #\title "ECMAScript"
- #\reader (lambda (port env) (read-ecmascript/1 port))
- #\compilers `((tree-il . ,compile-tree-il))
- ;; a pretty-printer would be interesting.
- #\printer write
- )
-;;; ECMAScript for Guile
-
-;; Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language ecmascript tokenize)
- #\use-module (ice-9 rdelim)
- #\use-module ((srfi srfi-1) #\select (unfold-right))
- #\use-module (system base lalr)
- #\export (next-token make-tokenizer make-tokenizer/1 tokenize tokenize/1))
-
-(define (syntax-error what loc form . args)
- (throw 'syntax-error #f what
- (and=> loc source-location->source-properties)
- form #f args))
-
-(define (port-source-location port)
- (make-source-location (port-filename port)
- (port-line port)
- (port-column port)
- (false-if-exception (ftell port))
- #f))
-
-;; taken from SSAX, sorta
-(define (read-until delims port loc)
- (if (eof-object? (peek-char port))
- (syntax-error "EOF while reading a token" loc #f)
- (let ((token (read-delimited delims port 'peek)))
- (if (eof-object? (peek-char port))
- (syntax-error "EOF while reading a token" loc token)
- token))))
-
-(define (char-hex? c)
- (and (not (eof-object? c))
- (or (char-numeric? c)
- (memv c '(#\a #\b #\c #\d #\e #\f))
- (memv c '(#\A #\B #\C #\D #\E #\F)))))
-
-(define (digit->number c)
- (- (char->integer c) (char->integer #\0)))
-
-(define (hex->number c)
- (if (char-numeric? c)
- (digit->number c)
- (+ 10 (- (char->integer (char-downcase c)) (char->integer #\a)))))
-
-(define (read-slash port loc div?)
- (let ((c1 (begin
- (read-char port)
- (peek-char port))))
- (cond
- ((eof-object? c1)
- ;; hmm. error if we're not looking for a div? ?
- (make-lexical-token '/ loc #f))
- ((char=? c1 #\/)
- (read-line port)
- (next-token port div?))
- ((char=? c1 #\*)
- (read-char port)
- (let lp ((c (read-char port)))
- (cond
- ((eof-object? c)
- (syntax-error "EOF while in multi-line comment" loc #f))
- ((char=? c #\*)
- (if (eqv? (peek-char port) #\/)
- (begin
- (read-char port)
- (next-token port div?))
- (lp (read-char port))))
- (else
- (lp (read-char port))))))
- (div?
- (case c1
- ((#\=) (read-char port) (make-lexical-token '/= loc #f))
- (else (make-lexical-token '/ loc #f))))
- (else
- (read-regexp port loc)))))
-
-(define (read-regexp port loc)
- ;; first slash already read
- (let ((terms (string #\/ #\\ #\nl #\cr)))
- (let lp ((str (read-until terms port loc)) (head ""))
- (let ((terminator (peek-char port)))
- (cond
- ((char=? terminator #\/)
- (read-char port)
- ;; flags
- (let lp ((c (peek-char port)) (flags '()))
- (if (or (eof-object? c)
- (not (or (char-alphabetic? c)
- (char-numeric? c)
- (char=? c #\$)
- (char=? c #\_))))
- (make-lexical-token 'RegexpLiteral loc
- (cons (string-append head str)
- (reverse flags)))
- (begin (read-char port)
- (lp (peek-char port) (cons c flags))))))
- ((char=? terminator #\\)
- (read-char port)
- (let ((echar (read-char port)))
- (lp (read-until terms port loc)
- (string-append head str (string #\\ echar)))))
- (else
- (syntax-error "regexp literals may not contain newlines"
- loc str)))))))
-
-(define (read-string port loc)
- (let ((c (read-char port)))
- (let ((terms (string c #\\ #\nl #\cr)))
- (define (read-escape port)
- (let ((c (read-char port)))
- (case c
- ((#\' #\" #\\) c)
- ((#\b) #\bs)
- ((#\f) #\np)
- ((#\n) #\nl)
- ((#\r) #\cr)
- ((#\t) #\tab)
- ((#\v) #\vt)
- ((#\0)
- (let ((next (peek-char port)))
- (cond
- ((eof-object? next) #\nul)
- ((char-numeric? next)
- (syntax-error "octal escape sequences are not supported"
- loc #f))
- (else #\nul))))
- ((#\x)
- (let* ((a (read-char port))
- (b (read-char port)))
- (cond
- ((and (char-hex? a) (char-hex? b))
- (integer->char (+ (* 16 (hex->number a)) (hex->number b))))
- (else
- (syntax-error "bad hex character escape" loc (string a b))))))
- ((#\u)
- (let* ((a (read-char port))
- (b (read-char port))
- (c (read-char port))
- (d (read-char port)))
- (integer->char (string->number (string a b c d) 16))))
- (else
- c))))
- (let lp ((str (read-until terms port loc)))
- (let ((terminator (peek-char port)))
- (cond
- ((char=? terminator c)
- (read-char port)
- (make-lexical-token 'StringLiteral loc str))
- ((char=? terminator #\\)
- (read-char port)
- (let ((echar (read-escape port)))
- (lp (string-append str (string echar)
- (read-until terms port loc)))))
- (else
- (syntax-error "string literals may not contain newlines"
- loc str))))))))
-
-(define *keywords*
- '(("break" . break)
- ("else" . else)
- ("new" . new)
- ("var" . var)
- ("case" . case)
- ("finally" . finally)
- ("return" . return)
- ("void" . void)
- ("catch" . catch)
- ("for" . for)
- ("switch" . switch)
- ("while" . while)
- ("continue" . continue)
- ("function" . function)
- ("this" . this)
- ("with" . with)
- ("default" . default)
- ("if" . if)
- ("throw" . throw)
- ("delete" . delete)
- ("in" . in)
- ("try" . try)
- ("do" . do)
- ("instanceof" . instanceof)
- ("typeof" . typeof)
-
- ;; these aren't exactly keywords, but hey
- ("null" . null)
- ("true" . true)
- ("false" . false)))
-
-(define *future-reserved-words*
- '(("abstract" . abstract)
- ("enum" . enum)
- ("int" . int)
- ("short" . short)
- ("boolean" . boolean)
- ("export" . export)
- ("interface" . interface)
- ("static" . static)
- ("byte" . byte)
- ("extends" . extends)
- ("long" . long)
- ("super" . super)
- ("char" . char)
- ("final" . final)
- ("native" . native)
- ("synchronized" . synchronized)
- ("class" . class)
- ("float" . float)
- ("package" . package)
- ("throws" . throws)
- ("const" . const)
- ("goto" . goto)
- ("private" . private)
- ("transient" . transient)
- ("debugger" . debugger)
- ("implements" . implements)
- ("protected" . protected)
- ("volatile" . volatile)
- ("double" . double)
- ("import" . import)
- ("public" . public)))
-
-(define (read-identifier port loc)
- (let lp ((c (peek-char port)) (chars '()))
- (if (or (eof-object? c)
- (not (or (char-alphabetic? c)
- (char-numeric? c)
- (char=? c #\$)
- (char=? c #\_))))
- (let ((word (list->string (reverse chars))))
- (cond ((assoc-ref *keywords* word)
- => (lambda (x) (make-lexical-token x loc #f)))
- ((assoc-ref *future-reserved-words* word)
- (syntax-error "word is reserved for the future, dude."
- loc word))
- (else (make-lexical-token 'Identifier loc
- (string->symbol word)))))
- (begin (read-char port)
- (lp (peek-char port) (cons c chars))))))
-
-(define (read-numeric port loc)
- (let* ((c0 (if (char=? (peek-char port) #\.)
- #\0
- (read-char port)))
- (c1 (peek-char port)))
- (cond
- ((eof-object? c1) (digit->number c0))
- ((and (char=? c0 #\0) (or (char=? c1 #\x) (char=? c1 #\X)))
- (read-char port)
- (let ((c (peek-char port)))
- (if (not (char-hex? c))
- (syntax-error "bad digit reading hexadecimal number"
- loc c))
- (let lp ((c c) (acc 0))
- (cond ((char-hex? c)
- (read-char port)
- (lp (peek-char port)
- (+ (* 16 acc) (hex->number c))))
- (else
- acc)))))
- ((and (char=? c0 #\0) (char-numeric? c1))
- (let lp ((c c1) (acc 0))
- (cond ((eof-object? c) acc)
- ((char-numeric? c)
- (if (or (char=? c #\8) (char=? c #\9))
- (syntax-error "invalid digit in octal sequence"
- loc c))
- (read-char port)
- (lp (peek-char port)
- (+ (* 8 acc) (digit->number c))))
- (else
- acc))))
- (else
- (let lp ((c1 c1) (acc (digit->number c0)))
- (cond
- ((eof-object? c1) acc)
- ((char-numeric? c1)
- (read-char port)
- (lp (peek-char port)
- (+ (* 10 acc) (digit->number c1))))
- ((or (char=? c1 #\e) (char=? c1 #\E))
- (read-char port)
- (let ((add (let ((c (peek-char port)))
- (cond ((eof-object? c)
- (syntax-error "error reading exponent: EOF"
- loc #f))
- ((char=? c #\+) (read-char port) +)
- ((char=? c #\-) (read-char port) -)
- ((char-numeric? c) +)
- (else
- (syntax-error "error reading exponent: non-digit"
- loc c))))))
- (let lp ((c (peek-char port)) (e 0))
- (cond ((and (not (eof-object? c)) (char-numeric? c))
- (read-char port)
- (lp (peek-char port) (add (* 10 e) (digit->number c))))
- (else
- (* (if (negative? e) (* acc 1.0) acc) (expt 10 e)))))))
- ((char=? c1 #\.)
- (read-char port)
- (let lp2 ((c (peek-char port)) (dec 0.0) (n -1))
- (cond ((and (not (eof-object? c)) (char-numeric? c))
- (read-char port)
- (lp2 (peek-char port)
- (+ dec (* (digit->number c) (expt 10 n)))
- (1- n)))
- (else
- ;; loop back to catch an exponential part
- (lp c (+ acc dec))))))
- (else
- acc)))))))
-
-(define *punctuation*
- '(("{" . lbrace)
- ("}" . rbrace)
- ("(" . lparen)
- (")" . rparen)
- ("[" . lbracket)
- ("]" . rbracket)
- ("." . dot)
- (";" . semicolon)
- ("," . comma)
- ("<" . <)
- (">" . >)
- ("<=" . <=)
- (">=" . >=)
- ("==" . ==)
- ("!=" . !=)
- ("===" . ===)
- ("!==" . !==)
- ("+" . +)
- ("-" . -)
- ("*" . *)
- ("%" . %)
- ("++" . ++)
- ("--" . --)
- ("<<" . <<)
- (">>" . >>)
- (">>>" . >>>)
- ("&" . &)
- ("|" . bor)
- ("^" . ^)
- ("!" . !)
- ("~" . ~)
- ("&&" . &&)
- ("||" . or)
- ("?" . ?)
- (":" . colon)
- ("=" . =)
- ("+=" . +=)
- ("-=" . -=)
- ("*=" . *=)
- ("%=" . %=)
- ("<<=" . <<=)
- (">>=" . >>=)
- (">>>=" . >>>=)
- ("&=" . &=)
- ("|=" . bor=)
- ("^=" . ^=)))
-
-(define *div-punctuation*
- '(("/" . /)
- ("/=" . /=)))
-
-;; node ::= (char (symbol | #f) node*)
-(define read-punctuation
- (let ((punc-tree (let lp ((nodes '()) (puncs *punctuation*))
- (cond ((null? puncs)
- nodes)
- ((assv-ref nodes (string-ref (caar puncs) 0))
- => (lambda (node-tail)
- (if (= (string-length (caar puncs)) 1)
- (set-car! node-tail (cdar puncs))
- (set-cdr! node-tail
- (lp (cdr node-tail)
- `((,(substring (caar puncs) 1)
- . ,(cdar puncs))))))
- (lp nodes (cdr puncs))))
- (else
- (lp (cons (list (string-ref (caar puncs) 0) #f) nodes)
- puncs))))))
- (lambda (port loc)
- (let lp ((c (peek-char port)) (tree punc-tree) (candidate #f))
- (cond
- ((assv-ref tree c)
- => (lambda (node-tail)
- (read-char port)
- (lp (peek-char port) (cdr node-tail) (car node-tail))))
- (candidate
- (make-lexical-token candidate loc #f))
- (else
- (syntax-error "bad syntax: character not allowed" loc c)))))))
-
-(define (next-token port div?)
- (let ((c (peek-char port))
- (loc (port-source-location port)))
- (case c
- ((#\ht #\vt #\np #\space #\x00A0) ; whitespace
- (read-char port)
- (next-token port div?))
- ((#\newline #\cr) ; line break
- (read-char port)
- (next-token port div?))
- ((#\/)
- ;; division, single comment, double comment, or regexp
- (read-slash port loc div?))
- ((#\" #\') ; string literal
- (read-string port loc))
- (else
- (cond
- ((eof-object? c)
- '*eoi*)
- ((or (char-alphabetic? c)
- (char=? c #\$)
- (char=? c #\_))
- ;; reserved word or identifier
- (read-identifier port loc))
- ((char-numeric? c)
- ;; numeric -- also accept . FIXME, requires lookahead
- (make-lexical-token 'NumericLiteral loc (read-numeric port loc)))
- (else
- ;; punctuation
- (read-punctuation port loc)))))))
-
-(define (make-tokenizer port)
- (let ((div? #f))
- (lambda ()
- (let ((tok (next-token port div?)))
- (set! div? (and (lexical-token? tok)
- (let ((cat (lexical-token-category tok)))
- (or (eq? cat 'Identifier)
- (eq? cat 'NumericLiteral)
- (eq? cat 'StringLiteral)))))
- tok))))
-
-(define (make-tokenizer/1 port)
- (let ((div? #f)
- (eoi? #f)
- (stack '()))
- (lambda ()
- (if eoi?
- '*eoi*
- (let ((tok (next-token port div?)))
- (case (if (lexical-token? tok) (lexical-token-category tok) tok)
- ((lparen)
- (set! stack (cons tok stack)))
- ((rparen)
- (if (and (pair? stack)
- (eq? (lexical-token-category (car stack)) 'lparen))
- (set! stack (cdr stack))
- (syntax-error "unexpected right parenthesis"
- (lexical-token-source tok)
- #f)))
- ((lbracket)
- (set! stack (cons tok stack)))
- ((rbracket)
- (if (and (pair? stack)
- (eq? (lexical-token-category (car stack)) 'lbracket))
- (set! stack (cdr stack))
- (syntax-error "unexpected right bracket"
- (lexical-token-source tok)
- #f)))
- ((lbrace)
- (set! stack (cons tok stack)))
- ((rbrace)
- (if (and (pair? stack)
- (eq? (lexical-token-category (car stack)) 'lbrace))
- (set! stack (cdr stack))
- (syntax-error "unexpected right brace"
- (lexical-token-source tok)
- #f)))
- ((semicolon)
- (set! eoi? (null? stack))))
- (set! div? (and (lexical-token? tok)
- (let ((cat (lexical-token-category tok)))
- (or (eq? cat 'Identifier)
- (eq? cat 'NumericLiteral)
- (eq? cat 'StringLiteral)))))
- tok)))))
-
-(define (tokenize port)
- (let ((next (make-tokenizer port)))
- (let lp ((out '()))
- (let ((tok (next)))
- (if (eq? tok '*eoi*)
- (reverse! out)
- (lp (cons tok out)))))))
-
-(define (tokenize/1 port)
- (let ((next (make-tokenizer/1 port)))
- (let lp ((out '()))
- (let ((tok (next)))
- (if (eq? tok '*eoi*)
- (reverse! out)
- (lp (cons tok out)))))))
-
-;;; Guile Emacs Lisp
-
-;;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language elisp bindings)
- #\export (make-bindings
- mark-global-needed!
- map-globals-needed
- with-lexical-bindings
- with-dynamic-bindings
- get-lexical-binding))
-
-;;; This module defines routines to handle analysis of symbol bindings
-;;; used during elisp compilation. This data allows to collect the
-;;; symbols, for which globals need to be created, or mark certain
-;;; symbols as lexically bound.
-;;;
-;;; Needed globals are stored in an association-list that stores a list
-;;; of symbols for each module they are needed in.
-;;;
-;;; The lexical bindings of symbols are stored in a hash-table that
-;;; associates symbols to fluids; those fluids are used in the
-;;; with-lexical-binding and with-dynamic-binding routines to associate
-;;; symbols to different bindings over a dynamic extent.
-
-;;; Record type used to hold the data necessary.
-
-(define bindings-type
- (make-record-type 'bindings '(needed-globals lexical-bindings)))
-
-;;; Construct an 'empty' instance of the bindings data structure to be
-;;; used at the start of a fresh compilation.
-
-(define (make-bindings)
- ((record-constructor bindings-type) '() (make-hash-table)))
-
-;;; Mark that a given symbol is needed as global in the specified
-;;; slot-module.
-
-(define (mark-global-needed! bindings sym module)
- (let* ((old-needed ((record-accessor bindings-type 'needed-globals)
- bindings))
- (old-in-module (or (assoc-ref old-needed module) '()))
- (new-in-module (if (memq sym old-in-module)
- old-in-module
- (cons sym old-in-module)))
- (new-needed (assoc-set! old-needed module new-in-module)))
- ((record-modifier bindings-type 'needed-globals)
- bindings
- new-needed)))
-
-;;; Cycle through all globals needed in order to generate the code for
-;;; their creation or some other analysis.
-
-(define (map-globals-needed bindings proc)
- (let ((needed ((record-accessor bindings-type 'needed-globals)
- bindings)))
- (let iterate-modules ((mod-tail needed)
- (mod-result '()))
- (if (null? mod-tail)
- mod-result
- (iterate-modules
- (cdr mod-tail)
- (let* ((aentry (car mod-tail))
- (module (car aentry))
- (symbols (cdr aentry)))
- (let iterate-symbols ((sym-tail symbols)
- (sym-result mod-result))
- (if (null? sym-tail)
- sym-result
- (iterate-symbols (cdr sym-tail)
- (cons (proc module (car sym-tail))
- sym-result))))))))))
-
-;;; Get the current lexical binding (gensym it should refer to in the
-;;; current scope) for a symbol or #f if it is dynamically bound.
-
-(define (get-lexical-binding bindings sym)
- (let* ((lex ((record-accessor bindings-type 'lexical-bindings)
- bindings))
- (slot (hash-ref lex sym #f)))
- (if slot
- (fluid-ref slot)
- #f)))
-
-;;; Establish a binding or mark a symbol as dynamically bound for the
-;;; extent of calling proc.
-
-(define (with-symbol-bindings bindings syms targets proc)
- (if (or (not (list? syms))
- (not (and-map symbol? syms)))
- (error "can't bind non-symbols" syms))
- (let ((lex ((record-accessor bindings-type 'lexical-bindings)
- bindings)))
- (for-each (lambda (sym)
- (if (not (hash-ref lex sym))
- (hash-set! lex sym (make-fluid))))
- syms)
- (with-fluids* (map (lambda (sym) (hash-ref lex sym)) syms)
- targets
- proc)))
-
-(define (with-lexical-bindings bindings syms targets proc)
- (if (or (not (list? targets))
- (not (and-map symbol? targets)))
- (error "invalid targets for lexical binding" targets)
- (with-symbol-bindings bindings syms targets proc)))
-
-(define (with-dynamic-bindings bindings syms proc)
- (with-symbol-bindings bindings
- syms
- (map (lambda (el) #f) syms)
- proc))
-;;; Guile Emacs Lisp
-
-;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-;;
-;; You should have received a copy of the GNU General Public License
-;; along with this program; see the file COPYING. If not, write to
-;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
-
-;;; Code:
-
-(define-module (language elisp compile-tree-il)
- #\use-module (language elisp bindings)
- #\use-module (language elisp runtime)
- #\use-module (language tree-il)
- #\use-module (system base pmatch)
- #\use-module (system base compile)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-8)
- #\use-module (srfi srfi-11)
- #\use-module (srfi srfi-26)
- #\export (compile-tree-il
- compile-progn
- compile-if
- compile-defconst
- compile-defvar
- compile-setq
- compile-let
- compile-lexical-let
- compile-flet
- compile-let*
- compile-lexical-let*
- compile-flet*
- compile-without-void-checks
- compile-with-always-lexical
- compile-guile-ref
- compile-guile-primitive
- compile-while
- compile-function
- compile-defmacro
- compile-defun
- #{compile-\`}
- compile-quote))
-
-;;; Certain common parameters (like the bindings data structure or
-;;; compiler options) are not always passed around but accessed using
-;;; fluids to simulate dynamic binding (hey, this is about elisp).
-
-;;; The bindings data structure to keep track of symbol binding related
-;;; data.
-
-(define bindings-data (make-fluid))
-
-;;; Store for which symbols (or all/none) void checks are disabled.
-
-(define disable-void-check (make-fluid))
-
-;;; Store which symbols (or all/none) should always be bound lexically,
-;;; even with ordinary let and as lambda arguments.
-
-(define always-lexical (make-fluid))
-
-;;; Find the source properties of some parsed expression if there are
-;;; any associated with it.
-
-(define (location x)
- (and (pair? x)
- (let ((props (source-properties x)))
- (and (not (null? props))
- props))))
-
-;;; Values to use for Elisp's nil and t.
-
-(define (nil-value loc)
- (make-const loc (@ (language elisp runtime) nil-value)))
-
-(define (t-value loc)
- (make-const loc (@ (language elisp runtime) t-value)))
-
-;;; Modules that contain the value and function slot bindings.
-
-(define runtime '(language elisp runtime))
-
-(define value-slot (@ (language elisp runtime) value-slot-module))
-
-(define function-slot (@ (language elisp runtime) function-slot-module))
-
-;;; The backquoting works the same as quasiquotes in Scheme, but the
-;;; forms are named differently; to make easy adaptions, we define these
-;;; predicates checking for a symbol being the car of an
-;;; unquote/unquote-splicing/backquote form.
-
-(define (unquote? sym)
- (and (symbol? sym) (eq? sym '#{\,})))
-
-(define (unquote-splicing? sym)
- (and (symbol? sym) (eq? sym '#{\,\@})))
-
-;;; Build a call to a primitive procedure nicely.
-
-(define (call-primitive loc sym . args)
- (make-application loc (make-primitive-ref loc sym) args))
-
-;;; Error reporting routine for syntax/compilation problems or build
-;;; code for a runtime-error output.
-
-(define (report-error loc . args)
- (apply error args))
-
-(define (runtime-error loc msg . args)
- (make-application loc
- (make-primitive-ref loc 'error)
- (cons (make-const loc msg) args)))
-
-;;; Generate code to ensure a global symbol is there for further use of
-;;; a given symbol. In general during the compilation, those needed are
-;;; only tracked with the bindings data structure. Afterwards, however,
-;;; for all those needed symbols the globals are really generated with
-;;; this routine.
-
-(define (generate-ensure-global loc sym module)
- (make-application loc
- (make-module-ref loc runtime 'ensure-fluid! #t)
- (list (make-const loc module)
- (make-const loc sym))))
-
-(define (ensuring-globals loc bindings body)
- (make-sequence
- loc
- `(,@(map-globals-needed (fluid-ref bindings)
- (lambda (mod sym)
- (generate-ensure-global loc sym mod)))
- ,body)))
-
-;;; Build a construct that establishes dynamic bindings for certain
-;;; variables. We may want to choose between binding with fluids and
-;;; with-fluids* and using just ordinary module symbols and
-;;; setting/reverting their values with a dynamic-wind.
-
-(define (let-dynamic loc syms module vals body)
- (call-primitive
- loc
- 'with-fluids*
- (make-application loc
- (make-primitive-ref loc 'list)
- (map (lambda (sym)
- (make-module-ref loc module sym #t))
- syms))
- (make-application loc (make-primitive-ref loc 'list) vals)
- (make-lambda loc
- '()
- (make-lambda-case #f '() #f #f #f '() '() body #f))))
-
-;;; Handle access to a variable (reference/setting) correctly depending
-;;; on whether it is currently lexically or dynamically bound. lexical
-;;; access is done only for references to the value-slot module!
-
-(define (access-variable loc
- sym
- module
- handle-global
- handle-lexical
- handle-dynamic)
- (let ((lexical (get-lexical-binding (fluid-ref bindings-data) sym)))
- (cond
- (lexical (handle-lexical lexical))
- ((equal? module function-slot) (handle-global))
- (else (handle-dynamic)))))
-
-;;; Generate code to reference a variable. For references in the
-;;; value-slot module, we may want to generate a lexical reference
-;;; instead if the variable has a lexical binding.
-
-(define (reference-variable loc sym module)
- (access-variable
- loc
- sym
- module
- (lambda () (make-module-ref loc module sym #t))
- (lambda (lexical) (make-lexical-ref loc lexical lexical))
- (lambda ()
- (mark-global-needed! (fluid-ref bindings-data) sym module)
- (call-primitive loc
- 'fluid-ref
- (make-module-ref loc module sym #t)))))
-
-;;; Generate code to set a variable. Just as with reference-variable, in
-;;; case of a reference to value-slot, we want to generate a lexical set
-;;; when the variable has a lexical binding.
-
-(define (set-variable! loc sym module value)
- (access-variable
- loc
- sym
- module
- (lambda ()
- (make-application
- loc
- (make-module-ref loc runtime 'set-variable! #t)
- (list (make-const loc module) (make-const loc sym) value)))
- (lambda (lexical) (make-lexical-set loc lexical lexical value))
- (lambda ()
- (mark-global-needed! (fluid-ref bindings-data) sym module)
- (call-primitive loc
- 'fluid-set!
- (make-module-ref loc module sym #t)
- value))))
-
-;;; Process the bindings part of a let or let* expression; that is,
-;;; check for correctness and bring it to the form ((sym1 . val1) (sym2
-;;; . val2) ...).
-
-(define (process-let-bindings loc bindings)
- (map
- (lambda (b)
- (if (symbol? b)
- (cons b 'nil)
- (if (or (not (list? b))
- (not (= (length b) 2)))
- (report-error
- loc
- "expected symbol or list of 2 elements in let")
- (if (not (symbol? (car b)))
- (report-error loc "expected symbol in let")
- (cons (car b) (cadr b))))))
- bindings))
-
-;;; Split the let bindings into a list to be done lexically and one
-;;; dynamically. A symbol will be bound lexically if and only if: We're
-;;; processing a lexical-let (i.e. module is 'lexical), OR we're
-;;; processing a value-slot binding AND the symbol is already lexically
-;;; bound or is always lexical, OR we're processing a function-slot
-;;; binding.
-
-(define (bind-lexically? sym module)
- (or (eq? module 'lexical)
- (eq? module function-slot)
- (and (equal? module value-slot)
- (let ((always (fluid-ref always-lexical)))
- (or (eq? always 'all)
- (memq sym always)
- (get-lexical-binding (fluid-ref bindings-data) sym))))))
-
-(define (split-let-bindings bindings module)
- (let iterate ((tail bindings)
- (lexical '())
- (dynamic '()))
- (if (null? tail)
- (values (reverse lexical) (reverse dynamic))
- (if (bind-lexically? (caar tail) module)
- (iterate (cdr tail) (cons (car tail) lexical) dynamic)
- (iterate (cdr tail) lexical (cons (car tail) dynamic))))))
-
-;;; Compile let and let* expressions. The code here is used both for
-;;; let/let* and flet/flet*, just with a different bindings module.
-;;;
-;;; A special module value 'lexical means that we're doing a lexical-let
-;;; instead and the bindings should not be saved to globals at all but
-;;; be done with the lexical framework instead.
-
-;;; Let is done with a single call to let-dynamic binding them locally
-;;; to new values all "at once". If there is at least one variable to
-;;; bind lexically among the bindings, we first do a let for all of them
-;;; to evaluate all values before any bindings take place, and then call
-;;; let-dynamic for the variables to bind dynamically.
-
-(define (generate-let loc module bindings body)
- (let ((bind (process-let-bindings loc bindings)))
- (call-with-values
- (lambda () (split-let-bindings bind module))
- (lambda (lexical dynamic)
- (for-each (lambda (sym)
- (mark-global-needed! (fluid-ref bindings-data)
- sym
- module))
- (map car dynamic))
- (let ((make-values (lambda (for)
- (map (lambda (el) (compile-expr (cdr el)))
- for)))
- (make-body (lambda ()
- (make-sequence loc (map compile-expr body)))))
- (if (null? lexical)
- (let-dynamic loc (map car dynamic) module
- (make-values dynamic) (make-body))
- (let* ((lexical-syms (map (lambda (el) (gensym)) lexical))
- (dynamic-syms (map (lambda (el) (gensym)) dynamic))
- (all-syms (append lexical-syms dynamic-syms))
- (vals (append (make-values lexical)
- (make-values dynamic))))
- (make-let loc
- all-syms
- all-syms
- vals
- (with-lexical-bindings
- (fluid-ref bindings-data)
- (map car lexical) lexical-syms
- (lambda ()
- (if (null? dynamic)
- (make-body)
- (let-dynamic loc
- (map car dynamic)
- module
- (map
- (lambda (sym)
- (make-lexical-ref loc
- sym
- sym))
- dynamic-syms)
- (make-body)))))))))))))
-
-;;; Let* is compiled to a cascaded set of "small lets" for each binding
-;;; in turn so that each one already sees the preceding bindings.
-
-(define (generate-let* loc module bindings body)
- (let ((bind (process-let-bindings loc bindings)))
- (begin
- (for-each (lambda (sym)
- (if (not (bind-lexically? sym module))
- (mark-global-needed! (fluid-ref bindings-data)
- sym
- module)))
- (map car bind))
- (let iterate ((tail bind))
- (if (null? tail)
- (make-sequence loc (map compile-expr body))
- (let ((sym (caar tail))
- (value (compile-expr (cdar tail))))
- (if (bind-lexically? sym module)
- (let ((target (gensym)))
- (make-let loc
- `(,target)
- `(,target)
- `(,value)
- (with-lexical-bindings
- (fluid-ref bindings-data)
- `(,sym)
- `(,target)
- (lambda () (iterate (cdr tail))))))
- (let-dynamic loc
- `(,(caar tail))
- module
- `(,value)
- (iterate (cdr tail))))))))))
-
-;;; Split the argument list of a lambda expression into required,
-;;; optional and rest arguments and also check it is actually valid.
-;;; Additionally, we create a list of all "local variables" (that is,
-;;; required, optional and rest arguments together) and also this one
-;;; split into those to be bound lexically and dynamically. Returned is
-;;; as multiple values: required optional rest lexical dynamic
-
-(define (bind-arg-lexical? arg)
- (let ((always (fluid-ref always-lexical)))
- (or (eq? always 'all)
- (memq arg always))))
-
-(define (split-lambda-arguments loc args)
- (let iterate ((tail args)
- (mode 'required)
- (required '())
- (optional '())
- (lexical '())
- (dynamic '()))
- (cond
- ((null? tail)
- (let ((final-required (reverse required))
- (final-optional (reverse optional))
- (final-lexical (reverse lexical))
- (final-dynamic (reverse dynamic)))
- (values final-required
- final-optional
- #f
- final-lexical
- final-dynamic)))
- ((and (eq? mode 'required)
- (eq? (car tail) '&optional))
- (iterate (cdr tail) 'optional required optional lexical dynamic))
- ((eq? (car tail) '&rest)
- (if (or (null? (cdr tail))
- (not (null? (cddr tail))))
- (report-error loc "expected exactly one symbol after &rest")
- (let* ((rest (cadr tail))
- (rest-lexical (bind-arg-lexical? rest))
- (final-required (reverse required))
- (final-optional (reverse optional))
- (final-lexical (reverse (if rest-lexical
- (cons rest lexical)
- lexical)))
- (final-dynamic (reverse (if rest-lexical
- dynamic
- (cons rest dynamic)))))
- (values final-required
- final-optional
- rest
- final-lexical
- final-dynamic))))
- (else
- (if (not (symbol? (car tail)))
- (report-error loc
- "expected symbol in argument list, got"
- (car tail))
- (let* ((arg (car tail))
- (bind-lexical (bind-arg-lexical? arg))
- (new-lexical (if bind-lexical
- (cons arg lexical)
- lexical))
- (new-dynamic (if bind-lexical
- dynamic
- (cons arg dynamic))))
- (case mode
- ((required) (iterate (cdr tail) mode
- (cons arg required) optional
- new-lexical new-dynamic))
- ((optional) (iterate (cdr tail) mode
- required (cons arg optional)
- new-lexical new-dynamic))
- (else
- (error "invalid mode in split-lambda-arguments"
- mode)))))))))
-
-;;; Compile a lambda expression. One thing we have to be aware of is
-;;; that lambda arguments are usually dynamically bound, even when a
-;;; lexical binding is intact for a symbol. For symbols that are marked
-;;; as 'always lexical,' however, we lexically bind here as well, and
-;;; thus we get them out of the let-dynamic call and register a lexical
-;;; binding for them (the lexical target variable is already there,
-;;; namely the real lambda argument from TreeIL).
-
-(define (compile-lambda loc args body)
- (if (not (list? args))
- (report-error loc "expected list for argument-list" args))
- (if (null? body)
- (report-error loc "function body must not be empty"))
- (receive (required optional rest lexical dynamic)
- (split-lambda-arguments loc args)
- (define (process-args args)
- (define (find-pairs pairs filter)
- (lset-intersection (lambda (name+sym x)
- (eq? (car name+sym) x))
- pairs
- filter))
- (let* ((syms (map (lambda (x) (gensym)) args))
- (pairs (map cons args syms))
- (lexical-pairs (find-pairs pairs lexical))
- (dynamic-pairs (find-pairs pairs dynamic)))
- (values syms pairs lexical-pairs dynamic-pairs)))
- (let*-values (((required-syms
- required-pairs
- required-lex-pairs
- required-dyn-pairs)
- (process-args required))
- ((optional-syms
- optional-pairs
- optional-lex-pairs
- optional-dyn-pairs)
- (process-args optional))
- ((rest-syms rest-pairs rest-lex-pairs rest-dyn-pairs)
- (process-args (if rest (list rest) '())))
- ((the-rest-sym) (if rest (car rest-syms) #f))
- ((all-syms) (append required-syms
- optional-syms
- rest-syms))
- ((all-lex-pairs) (append required-lex-pairs
- optional-lex-pairs
- rest-lex-pairs))
- ((all-dyn-pairs) (append required-dyn-pairs
- optional-dyn-pairs
- rest-dyn-pairs)))
- (for-each (lambda (sym)
- (mark-global-needed! (fluid-ref bindings-data)
- sym
- value-slot))
- dynamic)
- (with-dynamic-bindings
- (fluid-ref bindings-data)
- dynamic
- (lambda ()
- (with-lexical-bindings
- (fluid-ref bindings-data)
- (map car all-lex-pairs)
- (map cdr all-lex-pairs)
- (lambda ()
- (make-lambda
- loc
- '()
- (make-lambda-case
- #f
- required
- optional
- rest
- #f
- (map (lambda (x) (nil-value loc)) optional)
- all-syms
- (let ((compiled-body
- (make-sequence loc (map compile-expr body))))
- (make-sequence
- loc
- (list
- (if rest
- (make-conditional
- loc
- (call-primitive loc
- 'null?
- (make-lexical-ref loc
- rest
- the-rest-sym))
- (make-lexical-set loc
- rest
- the-rest-sym
- (nil-value loc))
- (make-void loc))
- (make-void loc))
- (if (null? dynamic)
- compiled-body
- (let-dynamic loc
- dynamic
- value-slot
- (map (lambda (name-sym)
- (make-lexical-ref
- loc
- (car name-sym)
- (cdr name-sym)))
- all-dyn-pairs)
- compiled-body)))))
- #f)))))))))
-
-;;; Handle the common part of defconst and defvar, that is, checking for
-;;; a correct doc string and arguments as well as maybe in the future
-;;; handling the docstring somehow.
-
-(define (handle-var-def loc sym doc)
- (cond
- ((not (symbol? sym)) (report-error loc "expected symbol, got" sym))
- ((> (length doc) 1) (report-error loc "too many arguments to defvar"))
- ((and (not (null? doc)) (not (string? (car doc))))
- (report-error loc "expected string as third argument of defvar, got"
- (car doc)))
- ;; TODO: Handle doc string if present.
- (else #t)))
-
-;;; Handle macro and special operator bindings.
-
-(define (find-operator sym type)
- (and
- (symbol? sym)
- (module-defined? (resolve-interface function-slot) sym)
- (let* ((op (module-ref (resolve-module function-slot) sym))
- (op (if (fluid? op) (fluid-ref op) op)))
- (if (and (pair? op) (eq? (car op) type))
- (cdr op)
- #f))))
-
-;;; See if a (backquoted) expression contains any unquotes.
-
-(define (contains-unquotes? expr)
- (if (pair? expr)
- (if (or (unquote? (car expr)) (unquote-splicing? (car expr)))
- #t
- (or (contains-unquotes? (car expr))
- (contains-unquotes? (cdr expr))))
- #f))
-
-;;; Process a backquoted expression by building up the needed
-;;; cons/append calls. For splicing, it is assumed that the expression
-;;; spliced in evaluates to a list. The emacs manual does not really
-;;; state either it has to or what to do if it does not, but Scheme
-;;; explicitly forbids it and this seems reasonable also for elisp.
-
-(define (unquote-cell? expr)
- (and (list? expr) (= (length expr) 2) (unquote? (car expr))))
-
-(define (unquote-splicing-cell? expr)
- (and (list? expr) (= (length expr) 2) (unquote-splicing? (car expr))))
-
-(define (process-backquote loc expr)
- (if (contains-unquotes? expr)
- (if (pair? expr)
- (if (or (unquote-cell? expr) (unquote-splicing-cell? expr))
- (compile-expr (cadr expr))
- (let* ((head (car expr))
- (processed-tail (process-backquote loc (cdr expr)))
- (head-is-list-2 (and (list? head)
- (= (length head) 2)))
- (head-unquote (and head-is-list-2
- (unquote? (car head))))
- (head-unquote-splicing (and head-is-list-2
- (unquote-splicing?
- (car head)))))
- (if head-unquote-splicing
- (call-primitive loc
- 'append
- (compile-expr (cadr head))
- processed-tail)
- (call-primitive loc 'cons
- (if head-unquote
- (compile-expr (cadr head))
- (process-backquote loc head))
- processed-tail))))
- (report-error loc
- "non-pair expression contains unquotes"
- expr))
- (make-const loc expr)))
-
-;;; Temporarily update a list of symbols that are handled specially
-;;; (disabled void check or always lexical) for compiling body. We need
-;;; to handle special cases for already all / set to all and the like.
-
-(define (with-added-symbols loc fluid syms body)
- (if (null? body)
- (report-error loc "symbol-list construct has empty body"))
- (if (not (or (eq? syms 'all)
- (and (list? syms) (and-map symbol? syms))))
- (report-error loc "invalid symbol list" syms))
- (let ((old (fluid-ref fluid))
- (make-body (lambda ()
- (make-sequence loc (map compile-expr body)))))
- (if (eq? old 'all)
- (make-body)
- (let ((new (if (eq? syms 'all)
- 'all
- (append syms old))))
- (with-fluids ((fluid new))
- (make-body))))))
-
-;;; Special operators
-
-(defspecial progn (loc args)
- (make-sequence loc (map compile-expr args)))
-
-(defspecial if (loc args)
- (pmatch args
- ((,cond ,then . ,else)
- (make-conditional loc
- (compile-expr cond)
- (compile-expr then)
- (if (null? else)
- (nil-value loc)
- (make-sequence loc
- (map compile-expr else)))))))
-
-(defspecial defconst (loc args)
- (pmatch args
- ((,sym ,value . ,doc)
- (if (handle-var-def loc sym doc)
- (make-sequence loc
- (list (set-variable! loc
- sym
- value-slot
- (compile-expr value))
- (make-const loc sym)))))))
-
-(defspecial defvar (loc args)
- (pmatch args
- ((,sym) (make-const loc sym))
- ((,sym ,value . ,doc)
- (if (handle-var-def loc sym doc)
- (make-sequence
- loc
- (list
- (make-conditional
- loc
- (make-conditional
- loc
- (call-primitive
- loc
- 'module-bound?
- (call-primitive loc
- 'resolve-interface
- (make-const loc value-slot))
- (make-const loc sym))
- (call-primitive loc
- 'fluid-bound?
- (make-module-ref loc value-slot sym #t))
- (make-const loc #f))
- (make-void loc)
- (set-variable! loc sym value-slot (compile-expr value)))
- (make-const loc sym)))))))
-
-(defspecial setq (loc args)
- (define (car* x) (if (null? x) '() (car x)))
- (define (cdr* x) (if (null? x) '() (cdr x)))
- (define (cadr* x) (car* (cdr* x)))
- (define (cddr* x) (cdr* (cdr* x)))
- (make-sequence
- loc
- (let loop ((args args) (last (nil-value loc)))
- (if (null? args)
- (list last)
- (let ((sym (car args))
- (val (compile-expr (cadr* args))))
- (if (not (symbol? sym))
- (report-error loc "expected symbol in setq")
- (cons
- (set-variable! loc sym value-slot val)
- (loop (cddr* args)
- (reference-variable loc sym value-slot)))))))))
-
-(defspecial let (loc args)
- (pmatch args
- ((,bindings . ,body)
- (generate-let loc value-slot bindings body))))
-
-(defspecial lexical-let (loc args)
- (pmatch args
- ((,bindings . ,body)
- (generate-let loc 'lexical bindings body))))
-
-(defspecial flet (loc args)
- (pmatch args
- ((,bindings . ,body)
- (generate-let loc function-slot bindings body))))
-
-(defspecial let* (loc args)
- (pmatch args
- ((,bindings . ,body)
- (generate-let* loc value-slot bindings body))))
-
-(defspecial lexical-let* (loc args)
- (pmatch args
- ((,bindings . ,body)
- (generate-let* loc 'lexical bindings body))))
-
-(defspecial flet* (loc args)
- (pmatch args
- ((,bindings . ,body)
- (generate-let* loc function-slot bindings body))))
-
-;;; Temporarily set symbols as always lexical only for the lexical scope
-;;; of a construct.
-
-(defspecial with-always-lexical (loc args)
- (pmatch args
- ((,syms . ,body)
- (with-added-symbols loc always-lexical syms body))))
-
-;;; guile-ref allows building TreeIL's module references from within
-;;; elisp as a way to access data within the Guile universe. The module
-;;; and symbol referenced are static values, just like (@ module symbol)
-;;; does!
-
-(defspecial guile-ref (loc args)
- (pmatch args
- ((,module ,sym) (guard (and (list? module) (symbol? sym)))
- (make-module-ref loc module sym #t))))
-
-;;; guile-primitive allows to create primitive references, which are
-;;; still a little faster.
-
-(defspecial guile-primitive (loc args)
- (pmatch args
- ((,sym)
- (make-primitive-ref loc sym))))
-
-;;; A while construct is transformed into a tail-recursive loop like
-;;; this:
-;;;
-;;; (letrec ((iterate (lambda ()
-;;; (if condition
-;;; (begin body
-;;; (iterate))
-;;; #nil))))
-;;; (iterate))
-;;;
-;;; As letrec is not directly accessible from elisp, while is
-;;; implemented here instead of with a macro.
-
-(defspecial while (loc args)
- (pmatch args
- ((,condition . ,body)
- (let* ((itersym (gensym))
- (compiled-body (map compile-expr body))
- (iter-call (make-application loc
- (make-lexical-ref loc
- 'iterate
- itersym)
- (list)))
- (full-body (make-sequence loc
- `(,@compiled-body ,iter-call)))
- (lambda-body (make-conditional loc
- (compile-expr condition)
- full-body
- (nil-value loc)))
- (iter-thunk (make-lambda loc
- '()
- (make-lambda-case #f
- '()
- #f
- #f
- #f
- '()
- '()
- lambda-body
- #f))))
- (make-letrec loc
- #f
- '(iterate)
- (list itersym)
- (list iter-thunk)
- iter-call)))))
-
-(defspecial function (loc args)
- (pmatch args
- (((lambda ,args . ,body))
- (compile-lambda loc args body))
- ((,sym) (guard (symbol? sym))
- (reference-variable loc sym function-slot))))
-
-(defspecial defmacro (loc args)
- (pmatch args
- ((,name ,args . ,body)
- (if (not (symbol? name))
- (report-error loc "expected symbol as macro name" name)
- (let* ((tree-il
- (make-sequence
- loc
- (list
- (set-variable!
- loc
- name
- function-slot
- (make-application
- loc
- (make-module-ref loc '(guile) 'cons #t)
- (list (make-const loc 'macro)
- (compile-lambda loc args body))))
- (make-const loc name)))))
- (compile (ensuring-globals loc bindings-data tree-il)
- #\from 'tree-il
- #\to 'value)
- tree-il)))))
-
-(defspecial defun (loc args)
- (pmatch args
- ((,name ,args . ,body)
- (if (not (symbol? name))
- (report-error loc "expected symbol as function name" name)
- (make-sequence loc
- (list (set-variable! loc
- name
- function-slot
- (compile-lambda loc
- args
- body))
- (make-const loc name)))))))
-
-(defspecial #{\`} (loc args)
- (pmatch args
- ((,val)
- (process-backquote loc val))))
-
-(defspecial quote (loc args)
- (pmatch args
- ((,val)
- (make-const loc val))))
-
-;;; Compile a compound expression to Tree-IL.
-
-(define (compile-pair loc expr)
- (let ((operator (car expr))
- (arguments (cdr expr)))
- (cond
- ((find-operator operator 'special-operator)
- => (lambda (special-operator-function)
- (special-operator-function loc arguments)))
- ((find-operator operator 'macro)
- => (lambda (macro-function)
- (compile-expr (apply macro-function arguments))))
- (else
- (make-application loc
- (if (symbol? operator)
- (reference-variable loc
- operator
- function-slot)
- (compile-expr operator))
- (map compile-expr arguments))))))
-
-;;; Compile a symbol expression. This is a variable reference or maybe
-;;; some special value like nil.
-
-(define (compile-symbol loc sym)
- (case sym
- ((nil) (nil-value loc))
- ((t) (t-value loc))
- (else (reference-variable loc sym value-slot))))
-
-;;; Compile a single expression to TreeIL.
-
-(define (compile-expr expr)
- (let ((loc (location expr)))
- (cond
- ((symbol? expr)
- (compile-symbol loc expr))
- ((pair? expr)
- (compile-pair loc expr))
- (else (make-const loc expr)))))
-
-;;; Process the compiler options.
-;;; FIXME: Why is '(()) passed as options by the REPL?
-
-(define (valid-symbol-list-arg? value)
- (or (eq? value 'all)
- (and (list? value) (and-map symbol? value))))
-
-(define (process-options! opt)
- (if (and (not (null? opt))
- (not (equal? opt '(()))))
- (if (null? (cdr opt))
- (report-error #f "Invalid compiler options" opt)
- (let ((key (car opt))
- (value (cadr opt)))
- (case key
- ((#\warnings) ; ignore
- #f)
- ((#\always-lexical)
- (if (valid-symbol-list-arg? value)
- (fluid-set! always-lexical value)
- (report-error #f
- "Invalid value for #\always-lexical"
- value)))
- (else (report-error #f
- "Invalid compiler option"
- key)))))))
-
-;;; Entry point for compilation to TreeIL. This creates the bindings
-;;; data structure, and after compiling the main expression we need to
-;;; make sure all globals for symbols used during the compilation are
-;;; created using the generate-ensure-global function.
-
-(define (compile-tree-il expr env opts)
- (values
- (with-fluids ((bindings-data (make-bindings))
- (disable-void-check '())
- (always-lexical '()))
- (process-options! opts)
- (let ((compiled (compile-expr expr)))
- (ensuring-globals (location expr) bindings-data compiled)))
- env
- env))
-;;; Guile Emacs Lisp
-
-;;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language elisp lexer)
- #\use-module (ice-9 regex)
- #\export (get-lexer get-lexer/1))
-
-;;; This is the lexical analyzer for the elisp reader. It is
-;;; hand-written instead of using some generator. I think this is the
-;;; best solution because of all that fancy escape sequence handling and
-;;; the like.
-;;;
-;;; Characters are handled internally as integers representing their
-;;; code value. This is necessary because elisp allows a lot of fancy
-;;; modifiers that set certain high-range bits and the resulting values
-;;; would not fit into a real Scheme character range. Additionally,
-;;; elisp wants characters as integers, so we just do the right thing...
-;;;
-;;; TODO: #@count comments
-
-;;; Report an error from the lexer (that is, invalid input given).
-
-(define (lexer-error port msg . args)
- (apply error msg args))
-
-;;; In a character, set a given bit. This is just some bit-wise or'ing
-;;; on the characters integer code and converting back to character.
-
-(define (set-char-bit chr bit)
- (logior chr (ash 1 bit)))
-
-;;; Check if a character equals some other. This is just like char=?
-;;; except that the tested one could be EOF in which case it simply
-;;; isn't equal.
-
-(define (is-char? tested should-be)
- (and (not (eof-object? tested))
- (char=? tested should-be)))
-
-;;; For a character (as integer code), find the real character it
-;;; represents or #\nul if out of range. This is used to work with
-;;; Scheme character functions like char-numeric?.
-
-(define (real-character chr)
- (if (< chr 256)
- (integer->char chr)
- #\nul))
-
-;;; Return the control modified version of a character. This is not
-;;; just setting a modifier bit, because ASCII conrol characters must be
-;;; handled as such, and in elisp C-? is the delete character for
-;;; historical reasons. Otherwise, we set bit 26.
-
-(define (add-control chr)
- (let ((real (real-character chr)))
- (if (char-alphabetic? real)
- (- (char->integer (char-upcase real)) (char->integer #\@))
- (case real
- ((#\?) 127)
- ((#\@) 0)
- (else (set-char-bit chr 26))))))
-
-;;; Parse a charcode given in some base, basically octal or hexadecimal
-;;; are needed. A requested number of digits can be given (#f means it
-;;; does not matter and arbitrary many are allowed), and additionally
-;;; early return allowed (if fewer valid digits are found). These
-;;; options are all we need to handle the \u, \U, \x and \ddd (octal
-;;; digits) escape sequences.
-
-(define (charcode-escape port base digits early-return)
- (let iterate ((result 0)
- (procdigs 0))
- (if (and digits (>= procdigs digits))
- result
- (let* ((cur (read-char port))
- (value (cond
- ((char-numeric? cur)
- (- (char->integer cur) (char->integer #\0)))
- ((char-alphabetic? cur)
- (let ((code (- (char->integer (char-upcase cur))
- (char->integer #\A))))
- (if (< code 0)
- #f
- (+ code 10))))
- (else #f)))
- (valid (and value (< value base))))
- (if (not valid)
- (if (or (not digits) early-return)
- (begin
- (unread-char cur port)
- result)
- (lexer-error port
- "invalid digit in escape-code"
- base
- cur))
- (iterate (+ (* result base) value) (1+ procdigs)))))))
-
-;;; Read a character and process escape-sequences when necessary. The
-;;; special in-string argument defines if this character is part of a
-;;; string literal or a single character literal, the difference being
-;;; that in strings the meta modifier sets bit 7, while it is bit 27 for
-;;; characters.
-
-(define basic-escape-codes
- '((#\a . 7)
- (#\b . 8)
- (#\t . 9)
- (#\n . 10)
- (#\v . 11)
- (#\f . 12)
- (#\r . 13)
- (#\e . 27)
- (#\s . 32)
- (#\d . 127)))
-
-(define (get-character port in-string)
- (let ((meta-bits `((#\A . 22)
- (#\s . 23)
- (#\H . 24)
- (#\S . 25)
- (#\M . ,(if in-string 7 27))))
- (cur (read-char port)))
- (if (char=? cur #\\)
- ;; Handle an escape-sequence.
- (let* ((escaped (read-char port))
- (esc-code (assq-ref basic-escape-codes escaped))
- (meta (assq-ref meta-bits escaped)))
- (cond
- ;; Meta-check must be before esc-code check because \s- must
- ;; be recognized as the super-meta modifier if a - follows.
- ;; If not, it will be caught as \s -> space escape code.
- ((and meta (is-char? (peek-char port) #\-))
- (if (not (char=? (read-char port) #\-))
- (error "expected - after control sequence"))
- (set-char-bit (get-character port in-string) meta))
- ;; One of the basic control character escape names?
- (esc-code esc-code)
- ;; Handle \ddd octal code if it is one.
- ((and (char>=? escaped #\0) (char<? escaped #\8))
- (begin
- (unread-char escaped port)
- (charcode-escape port 8 3 #t)))
- ;; Check for some escape-codes directly or otherwise use the
- ;; escaped character literally.
- (else
- (case escaped
- ((#\^) (add-control (get-character port in-string)))
- ((#\C)
- (if (is-char? (peek-char port) #\-)
- (begin
- (if (not (char=? (read-char port) #\-))
- (error "expected - after control sequence"))
- (add-control (get-character port in-string)))
- escaped))
- ((#\x) (charcode-escape port 16 #f #t))
- ((#\u) (charcode-escape port 16 4 #f))
- ((#\U) (charcode-escape port 16 8 #f))
- (else (char->integer escaped))))))
- ;; No escape-sequence, just the literal character. But remember
- ;; to get the code instead!
- (char->integer cur))))
-
-;;; Read a symbol or number from a port until something follows that
-;;; marks the start of a new token (like whitespace or parentheses).
-;;; The data read is returned as a string for further conversion to the
-;;; correct type, but we also return what this is
-;;; (integer/float/symbol). If any escaped character is found, it must
-;;; be a symbol. Otherwise we at the end check the result-string
-;;; against regular expressions to determine if it is possibly an
-;;; integer or a float.
-
-(define integer-regex (make-regexp "^[+-]?[0-9]+\\.?$"))
-
-(define float-regex
- (make-regexp
- "^[+-]?([0-9]+\\.?[0-9]*|[0-9]*\\.?[0-9]+)(e[+-]?[0-9]+)?$"))
-
-;;; A dot is also allowed literally, only a single dort alone is parsed
-;;; as the 'dot' terminal for dotted lists.
-
-(define no-escape-punctuation (string->char-set "-+=*/_~!@$%^&:<>{}?."))
-
-(define (get-symbol-or-number port)
- (let iterate ((result-chars '())
- (had-escape #f))
- (let* ((c (read-char port))
- (finish (lambda ()
- (let ((result (list->string
- (reverse result-chars))))
- (values
- (cond
- ((and (not had-escape)
- (regexp-exec integer-regex result))
- 'integer)
- ((and (not had-escape)
- (regexp-exec float-regex result))
- 'float)
- (else 'symbol))
- result))))
- (need-no-escape? (lambda (c)
- (or (char-numeric? c)
- (char-alphabetic? c)
- (char-set-contains?
- no-escape-punctuation
- c)))))
- (cond
- ((eof-object? c) (finish))
- ((need-no-escape? c) (iterate (cons c result-chars) had-escape))
- ((char=? c #\\) (iterate (cons (read-char port) result-chars) #t))
- (else
- (unread-char c port)
- (finish))))))
-
-;;; Parse a circular structure marker without the leading # (which was
-;;; already read and recognized), that is, a number as identifier and
-;;; then either = or #.
-
-(define (get-circular-marker port)
- (call-with-values
- (lambda ()
- (let iterate ((result 0))
- (let ((cur (read-char port)))
- (if (char-numeric? cur)
- (let ((val (- (char->integer cur) (char->integer #\0))))
- (iterate (+ (* result 10) val)))
- (values result cur)))))
- (lambda (id type)
- (case type
- ((#\#) `(circular-ref . ,id))
- ((#\=) `(circular-def . ,id))
- (else (lexer-error port
- "invalid circular marker character"
- type))))))
-
-;;; Main lexer routine, which is given a port and does look for the next
-;;; token.
-
-(define (lex port)
- (let ((return (let ((file (if (file-port? port)
- (port-filename port)
- #f))
- (line (1+ (port-line port)))
- (column (1+ (port-column port))))
- (lambda (token value)
- (let ((obj (cons token value)))
- (set-source-property! obj 'filename file)
- (set-source-property! obj 'line line)
- (set-source-property! obj 'column column)
- obj))))
- ;; Read afterwards so the source-properties are correct above
- ;; and actually point to the very character to be read.
- (c (read-char port)))
- (cond
- ;; End of input must be specially marked to the parser.
- ((eof-object? c) (return 'eof c))
- ;; Whitespace, just skip it.
- ((char-whitespace? c) (lex port))
- ;; The dot is only the one for dotted lists if followed by
- ;; whitespace. Otherwise it is considered part of a number of
- ;; symbol.
- ((and (char=? c #\.)
- (char-whitespace? (peek-char port)))
- (return 'dot #f))
- ;; Continue checking for literal character values.
- (else
- (case c
- ;; A line comment, skip until end-of-line is found.
- ((#\;)
- (let iterate ()
- (let ((cur (read-char port)))
- (if (or (eof-object? cur) (char=? cur #\newline))
- (lex port)
- (iterate)))))
- ;; A character literal.
- ((#\?)
- (return 'character (get-character port #f)))
- ;; A literal string. This is mainly a sequence of characters
- ;; just as in the character literals, the only difference is
- ;; that escaped newline and space are to be completely ignored
- ;; and that meta-escapes set bit 7 rather than bit 27.
- ((#\")
- (let iterate ((result-chars '()))
- (let ((cur (read-char port)))
- (case cur
- ((#\")
- (return 'string (list->string (reverse result-chars))))
- ((#\\)
- (let ((escaped (read-char port)))
- (case escaped
- ((#\newline #\space)
- (iterate result-chars))
- (else
- (unread-char escaped port)
- (unread-char cur port)
- (iterate
- (cons (integer->char (get-character port #t))
- result-chars))))))
- (else (iterate (cons cur result-chars)))))))
- ((#\#)
- (let ((c (read-char port)))
- (case c
- ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
- (unread-char c port)
- (let ((mark (get-circular-marker port)))
- (return (car mark) (cdr mark))))
- ((#\')
- (return 'function #f)))))
- ;; Parentheses and other special-meaning single characters.
- ((#\() (return 'paren-open #f))
- ((#\)) (return 'paren-close #f))
- ((#\[) (return 'square-open #f))
- ((#\]) (return 'square-close #f))
- ((#\') (return 'quote #f))
- ((#\`) (return 'backquote #f))
- ;; Unquote and unquote-splicing.
- ((#\,)
- (if (is-char? (peek-char port) #\@)
- (if (not (char=? (read-char port) #\@))
- (error "expected @ in unquote-splicing")
- (return 'unquote-splicing #f))
- (return 'unquote #f)))
- ;; Remaining are numbers and symbols. Process input until next
- ;; whitespace is found, and see if it looks like a number
- ;; (float/integer) or symbol and return accordingly.
- (else
- (unread-char c port)
- (call-with-values
- (lambda () (get-symbol-or-number port))
- (lambda (type str)
- (case type
- ((symbol)
- ;; str could be empty if the first character is already
- ;; something not allowed in a symbol (and not escaped)!
- ;; Take care about that, it is an error because that
- ;; character should have been handled elsewhere or is
- ;; invalid in the input.
- (if (zero? (string-length str))
- (begin
- ;; Take it out so the REPL might not get into an
- ;; infinite loop with further reading attempts.
- (read-char port)
- (error "invalid character in input" c))
- (return 'symbol (string->symbol str))))
- ((integer)
- ;; In elisp, something like "1." is an integer, while
- ;; string->number returns an inexact real. Thus we need
- ;; a conversion here, but it should always result in an
- ;; integer!
- (return
- 'integer
- (let ((num (inexact->exact (string->number str))))
- (if (not (integer? num))
- (error "expected integer" str num))
- num)))
- ((float)
- (return 'float (let ((num (string->number str)))
- (if (exact? num)
- (error "expected inexact float"
- str
- num))
- num)))
- (else (error "wrong number/symbol type" type)))))))))))
-
-;;; Build a lexer thunk for a port. This is the exported routine which
-;;; can be used to create a lexer for the parser to use.
-
-(define (get-lexer port)
- (lambda () (lex port)))
-
-;;; Build a special lexer that will only read enough for one expression
-;;; and then always return end-of-input. If we find one of the quotation
-;;; stuff, one more expression is needed in any case.
-
-(define (get-lexer/1 port)
- (let ((lex (get-lexer port))
- (finished #f)
- (paren-level 0))
- (lambda ()
- (if finished
- (cons 'eof ((@ (ice-9 binary-ports) eof-object)))
- (let ((next (lex))
- (quotation #f))
- (case (car next)
- ((paren-open square-open)
- (set! paren-level (1+ paren-level)))
- ((paren-close square-close)
- (set! paren-level (1- paren-level)))
- ((quote backquote unquote unquote-splicing circular-def)
- (set! quotation #t)))
- (if (and (not quotation) (<= paren-level 0))
- (set! finished #t))
- next)))))
-;;; Guile Emacs Lisp
-
-;;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language elisp parser)
- #\use-module (language elisp lexer)
- #\export (read-elisp))
-
-;;; The parser (reader) for elisp expressions.
-;;;
-;;; It is hand-written (just as the lexer is) instead of using some
-;;; parser generator because this allows easier transfer of source
-;;; properties from the lexer ((text parse-lalr) seems not to allow
-;;; access to the original lexer token-pair) and is easy enough anyways.
-
-;;; Report a parse error. The first argument is some current lexer
-;;; token where source information is available should it be useful.
-
-(define (parse-error token msg . args)
- (apply error msg args))
-
-;;; For parsing circular structures, we keep track of definitions in a
-;;; hash-map that maps the id's to their values. When defining a new
-;;; id, though, we immediatly fill the slot with a promise before
-;;; parsing and setting the real value, because it must already be
-;;; available at that time in case of a circular reference. The promise
-;;; refers to a local variable that will be set when the real value is
-;;; available through a closure. After parsing the expression is
-;;; completed, we work through it again and force all promises we find.
-;;; The definitions themselves are stored in a fluid and their scope is
-;;; one call to read-elisp (but not only the currently parsed
-;;; expression!).
-
-(define circular-definitions (make-fluid))
-
-(define (make-circular-definitions)
- (make-hash-table))
-
-(define (circular-ref token)
- (if (not (eq? (car token) 'circular-ref))
- (error "invalid token for circular-ref" token))
- (let* ((id (cdr token))
- (value (hashq-ref (fluid-ref circular-definitions) id)))
- (if value
- value
- (parse-error token "undefined circular reference" id))))
-
-;;; Returned is a closure that, when invoked, will set the final value.
-;;; This means both the variable the promise will return and the
-;;; hash-table slot so we don't generate promises any longer.
-
-(define (circular-define! token)
- (if (not (eq? (car token) 'circular-def))
- (error "invalid token for circular-define!" token))
- (let ((value #f)
- (table (fluid-ref circular-definitions))
- (id (cdr token)))
- (hashq-set! table id (delay value))
- (lambda (real-value)
- (set! value real-value)
- (hashq-set! table id real-value))))
-
-;;; Work through a parsed data structure and force the promises there.
-;;; After a promise is forced, the resulting value must not be recursed
-;;; on; this may lead to infinite recursion with a circular structure,
-;;; and additionally this value was already processed when it was
-;;; defined. All deep data structures that can be parsed must be
-;;; handled here!
-
-(define (force-promises! data)
- (cond
- ((pair? data)
- (begin
- (if (promise? (car data))
- (set-car! data (force (car data)))
- (force-promises! (car data)))
- (if (promise? (cdr data))
- (set-cdr! data (force (cdr data)))
- (force-promises! (cdr data)))))
- ((vector? data)
- (let ((len (vector-length data)))
- (let iterate ((i 0))
- (if (< i len)
- (let ((el (vector-ref data i)))
- (if (promise? el)
- (vector-set! data i (force el))
- (force-promises! el))
- (iterate (1+ i)))))))
- ;; Else nothing needs to be done.
- ))
-
-;;; We need peek-functionality for the next lexer token, this is done
-;;; with some single token look-ahead storage. This is handled by a
-;;; closure which allows getting or peeking the next token. When one
-;;; expression is fully parsed, we don't want a look-ahead stored here
-;;; because it would miss from future parsing. This is verified by the
-;;; finish action.
-
-(define (make-lexer-buffer lex)
- (let ((look-ahead #f))
- (lambda (action)
- (if (eq? action 'finish)
- (if look-ahead
- (error "lexer-buffer is not empty when finished")
- #f)
- (begin
- (if (not look-ahead)
- (set! look-ahead (lex)))
- (case action
- ((peek) look-ahead)
- ((get)
- (let ((result look-ahead))
- (set! look-ahead #f)
- result))
- (else (error "invalid lexer-buffer action" action))))))))
-
-;;; Get the contents of a list, where the opening parentheses has
-;;; already been found. The same code is used for vectors and lists,
-;;; where lists allow the dotted tail syntax and vectors not;
-;;; additionally, the closing parenthesis must of course match. The
-;;; implementation here is not tail-recursive, but I think it is clearer
-;;; and simpler this way.
-
-(define (get-list lex allow-dot close-square)
- (let* ((next (lex 'peek))
- (type (car next)))
- (cond
- ((eq? type (if close-square 'square-close 'paren-close))
- (begin
- (if (not (eq? (car (lex 'get)) type))
- (error "got different token than peeked"))
- '()))
- ((and allow-dot (eq? type 'dot))
- (begin
- (if (not (eq? (car (lex 'get)) type))
- (error "got different token than peeked"))
- (let ((tail (get-list lex #f close-square)))
- (if (not (= (length tail) 1))
- (parse-error next
- "expected exactly one element after dot"))
- (car tail))))
- (else
- ;; Do both parses in exactly this sequence!
- (let* ((head (get-expression lex))
- (tail (get-list lex allow-dot close-square)))
- (cons head tail))))))
-
-;;; Parse a single expression from a lexer-buffer. This is the main
-;;; routine in our recursive-descent parser.
-
-(define quotation-symbols '((quote . quote)
- (backquote . #\`)
- (unquote . #\,)
- (unquote-splicing . #\,\@)))
-
-(define (get-expression lex)
- (let* ((token (lex 'get))
- (type (car token))
- (return (lambda (result)
- (if (pair? result)
- (set-source-properties!
- result
- (source-properties token)))
- result)))
- (case type
- ((eof)
- (parse-error token "end of file during parsing"))
- ((integer float symbol character string)
- (return (cdr token)))
- ((function)
- (return `(function ,(get-expression lex))))
- ((quote backquote unquote unquote-splicing)
- (return (list (assq-ref quotation-symbols type)
- (get-expression lex))))
- ((paren-open)
- (return (get-list lex #t #f)))
- ((square-open)
- (return (list->vector (get-list lex #f #t))))
- ((circular-ref)
- (circular-ref token))
- ((circular-def)
- ;; The order of definitions is important!
- (let* ((setter (circular-define! token))
- (expr (get-expression lex)))
- (setter expr)
- (force-promises! expr)
- expr))
- (else
- (parse-error token "expected expression, got" token)))))
-
-;;; Define the reader function based on this; build a lexer, a
-;;; lexer-buffer, and then parse a single expression to return. We also
-;;; define a circular-definitions data structure to use.
-
-(define (read-elisp port)
- (with-fluids ((circular-definitions (make-circular-definitions)))
- (let* ((lexer (get-lexer port))
- (lexbuf (make-lexer-buffer lexer))
- (next (lexbuf 'peek)))
- (if (eq? (car next) 'eof)
- (cdr next)
- (let ((result (get-expression lexbuf)))
- (lexbuf 'finish)
- result)))))
-;;; Guile Emacs Lisp
-
-;;; Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language elisp runtime)
- #\export (nil-value
- t-value
- value-slot-module
- function-slot-module
- elisp-bool
- ensure-fluid!
- reference-variable
- set-variable!
- runtime-error
- macro-error)
- #\export-syntax (built-in-func built-in-macro defspecial prim))
-
-;;; This module provides runtime support for the Elisp front-end.
-
-;;; Values for t and nil. (FIXME remove this abstraction)
-
-(define nil-value #nil)
-
-(define t-value #t)
-
-;;; Modules for the binding slots.
-;;; Note: Naming those value-slot and/or function-slot clashes with the
-;;; submodules of these names!
-
-(define value-slot-module '(language elisp runtime value-slot))
-
-(define function-slot-module '(language elisp runtime function-slot))
-
-;;; Report an error during macro compilation, that means some special
-;;; compilation (syntax) error; or report a simple runtime-error from a
-;;; built-in function.
-
-(define (macro-error msg . args)
- (apply error msg args))
-
-(define runtime-error macro-error)
-
-;;; Convert a scheme boolean to Elisp.
-
-(define (elisp-bool b)
- (if b
- t-value
- nil-value))
-
-;;; Routines for access to elisp dynamically bound symbols. This is
-;;; used for runtime access using functions like symbol-value or set,
-;;; where the symbol accessed might not be known at compile-time. These
-;;; always access the dynamic binding and can not be used for the
-;;; lexical!
-
-(define (ensure-fluid! module sym)
- (let ((intf (resolve-interface module))
- (resolved (resolve-module module)))
- (if (not (module-defined? intf sym))
- (let ((fluid (make-unbound-fluid)))
- (module-define! resolved sym fluid)
- (module-export! resolved `(,sym))))))
-
-(define (reference-variable module sym)
- (let ((resolved (resolve-module module)))
- (cond
- ((equal? module function-slot-module)
- (module-ref resolved sym))
- (else
- (ensure-fluid! module sym)
- (fluid-ref (module-ref resolved sym))))))
-
-(define (set-variable! module sym value)
- (let ((intf (resolve-interface module))
- (resolved (resolve-module module)))
- (cond
- ((equal? module function-slot-module)
- (cond
- ((module-defined? intf sym)
- (module-set! resolved sym value))
- (else
- (module-define! resolved sym value)
- (module-export! resolved `(,sym)))))
- (else
- (ensure-fluid! module sym)
- (fluid-set! (module-ref resolved sym) value))))
- value)
-
-;;; Define a predefined function or predefined macro for use in the
-;;; function-slot and macro-slot modules, respectively.
-
-(define-syntax built-in-func
- (syntax-rules ()
- ((_ name value)
- (begin
- (define-public name value)))))
-
-(define (make-id template-id . data)
- (let ((append-symbols
- (lambda (symbols)
- (string->symbol
- (apply string-append (map symbol->string symbols))))))
- (datum->syntax template-id
- (append-symbols
- (map (lambda (datum)
- ((if (identifier? datum)
- syntax->datum
- identity)
- datum))
- data)))))
-
-(define-syntax built-in-macro
- (lambda (x)
- (syntax-case x ()
- ((_ name value)
- (with-syntax ((scheme-name (make-id #'name 'macro- #'name)))
- #'(begin
- (define-public scheme-name
- (make-fluid (cons 'macro value)))))))))
-
-(define-syntax defspecial
- (lambda (x)
- (syntax-case x ()
- ((_ name args body ...)
- (with-syntax ((scheme-name (make-id #'name 'compile- #'name)))
- #'(begin
- (define scheme-name
- (make-fluid
- (cons 'special-operator
- (lambda args body ...))))))))))
-
-;;; Call a guile-primitive that may be rebound for elisp and thus needs
-;;; absolute addressing.
-
-(define-syntax prim
- (syntax-rules ()
- ((_ sym args ...)
- ((@ (guile) sym) args ...))))
-;;; Guile Emacs Lisp
-
-;;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (language elisp runtime function-slot)
- #\use-module (language elisp runtime subrs)
- #\use-module ((language elisp runtime macros)
- #\select
- ((macro-lambda . lambda)
- (macro-prog1 . prog1)
- (macro-prog2 . prog2)
- (macro-when . when)
- (macro-unless . unless)
- (macro-cond . cond)
- (macro-and . and)
- (macro-or . or)
- (macro-dotimes . dotimes)
- (macro-dolist . dolist)
- (macro-catch . catch)
- (macro-unwind-protect . unwind-protect)
- (macro-pop . pop)
- (macro-push . push)))
- #\use-module ((language elisp compile-tree-il)
- #\select
- ((compile-progn . progn)
- (compile-if . if)
- (compile-defconst . defconst)
- (compile-defvar . defvar)
- (compile-setq . setq)
- (compile-let . let)
- (compile-lexical-let . lexical-let)
- (compile-flet . flet)
- (compile-let* . let*)
- (compile-lexical-let* . lexical-let*)
- (compile-flet* . flet*)
- (compile-with-always-lexical . with-always-lexical)
- (compile-guile-ref . guile-ref)
- (compile-guile-primitive . guile-primitive)
- (compile-while . while)
- (compile-function . function)
- (compile-defun . defun)
- (compile-defmacro . defmacro)
- (#{compile-\`} . #\`)
- (compile-quote . quote)))
- #\duplicates (last)
- ;; special operators
- #\re-export (progn
- if
- defconst
- defvar
- setq
- let
- lexical-let
- flet
- let*
- lexical-let*
- flet*
- with-always-lexical
- guile-ref
- guile-primitive
- while
- function
- defun
- defmacro
- #\`
- quote)
- ;; macros
- #\re-export (lambda
- prog1
- prog2
- when
- unless
- cond
- and
- or
- dotimes
- dolist
- catch
- unwind-protect
- pop
- push)
- ;; functions
- #\re-export (eq
- equal
- floatp
- integerp
- numberp
- wholenump
- zerop
- =
- /=
- <
- <=
- >
- >=
- max
- min
- abs
- float
- 1+
- 1-
- +
- -
- *
- %
- ffloor
- fceiling
- ftruncate
- fround
- consp
- atomp
- listp
- nlistp
- null
- car
- cdr
- car-safe
- cdr-safe
- nth
- nthcdr
- length
- cons
- list
- make-list
- append
- reverse
- copy-tree
- number-sequence
- setcar
- setcdr
- symbol-value
- symbol-function
- set
- fset
- makunbound
- fmakunbound
- boundp
- fboundp
- apply
- funcall
- throw
- not
- eval
- load))
-;;; Guile Emacs Lisp
-
-;;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language elisp runtime macros)
- #\use-module (language elisp runtime))
-
-;;; This module contains the macro definitions of elisp symbols. In
-;;; contrast to the other runtime modules, those are used directly
-;;; during compilation, of course, so not really in runtime. But I
-;;; think it fits well to the others here.
-
-(built-in-macro lambda
- (lambda cdr
- `(function (lambda ,@cdr))))
-
-;;; The prog1 and prog2 constructs can easily be defined as macros using
-;;; progn and some lexical-let's to save the intermediate value to
-;;; return at the end.
-
-(built-in-macro prog1
- (lambda (form1 . rest)
- (let ((temp (gensym)))
- `(lexical-let ((,temp ,form1))
- ,@rest
- ,temp))))
-
-(built-in-macro prog2
- (lambda (form1 form2 . rest)
- `(progn ,form1 (prog1 ,form2 ,@rest))))
-
-;;; Define the conditionals when and unless as macros.
-
-(built-in-macro when
- (lambda (condition . thens)
- `(if ,condition (progn ,@thens) nil)))
-
-(built-in-macro unless
- (lambda (condition . elses)
- `(if ,condition nil (progn ,@elses))))
-
-;;; Impement the cond form as nested if's. A special case is a
-;;; (condition) subform, in which case we need to return the condition
-;;; itself if it is true and thus save it in a local variable before
-;;; testing it.
-
-(built-in-macro cond
- (lambda (. clauses)
- (let iterate ((tail clauses))
- (if (null? tail)
- 'nil
- (let ((cur (car tail))
- (rest (iterate (cdr tail))))
- (prim cond
- ((prim or (not (list? cur)) (null? cur))
- (macro-error "invalid clause in cond" cur))
- ((null? (cdr cur))
- (let ((var (gensym)))
- `(lexical-let ((,var ,(car cur)))
- (if ,var
- ,var
- ,rest))))
- (else
- `(if ,(car cur)
- (progn ,@(cdr cur))
- ,rest))))))))
-
-;;; The `and' and `or' forms can also be easily defined with macros.
-
-(built-in-macro and
- (case-lambda
- (() 't)
- ((x) x)
- ((x . args)
- (let iterate ((x x) (tail args))
- (if (null? tail)
- x
- `(if ,x
- ,(iterate (car tail) (cdr tail))
- nil))))))
-
-(built-in-macro or
- (case-lambda
- (() 'nil)
- ((x) x)
- ((x . args)
- (let iterate ((x x) (tail args))
- (if (null? tail)
- x
- (let ((var (gensym)))
- `(lexical-let ((,var ,x))
- (if ,var
- ,var
- ,(iterate (car tail) (cdr tail))))))))))
-
-;;; Define the dotimes and dolist iteration macros.
-
-(built-in-macro dotimes
- (lambda (args . body)
- (if (prim or
- (not (list? args))
- (< (length args) 2)
- (> (length args) 3))
- (macro-error "invalid dotimes arguments" args)
- (let ((var (car args))
- (count (cadr args)))
- (if (not (symbol? var))
- (macro-error "expected symbol as dotimes variable"))
- `(let ((,var 0))
- (while ((guile-primitive <) ,var ,count)
- ,@body
- (setq ,var ((guile-primitive 1+) ,var)))
- ,@(if (= (length args) 3)
- (list (caddr args))
- '()))))))
-
-(built-in-macro dolist
- (lambda (args . body)
- (if (prim or
- (not (list? args))
- (< (length args) 2)
- (> (length args) 3))
- (macro-error "invalid dolist arguments" args)
- (let ((var (car args))
- (iter-list (cadr args))
- (tailvar (gensym)))
- (if (not (symbol? var))
- (macro-error "expected symbol as dolist variable")
- `(let (,var)
- (lexical-let ((,tailvar ,iter-list))
- (while ((guile-primitive not)
- ((guile-primitive null?) ,tailvar))
- (setq ,var ((guile-primitive car) ,tailvar))
- ,@body
- (setq ,tailvar ((guile-primitive cdr) ,tailvar)))
- ,@(if (= (length args) 3)
- (list (caddr args))
- '()))))))))
-
-;;; Exception handling. unwind-protect and catch are implemented as
-;;; macros (throw is a built-in function).
-
-;;; catch and throw can mainly be implemented directly using Guile's
-;;; primitives for exceptions, the only difficulty is that the keys used
-;;; within Guile must be symbols, while elisp allows any value and
-;;; checks for matches using eq (eq?). We handle this by using always #t
-;;; as key for the Guile primitives and check for matches inside the
-;;; handler; if the elisp keys are not eq?, we rethrow the exception.
-
-(built-in-macro catch
- (lambda (tag . body)
- (if (null? body)
- (macro-error "catch with empty body"))
- (let ((tagsym (gensym)))
- `(lexical-let ((,tagsym ,tag))
- ((guile-primitive catch)
- #t
- (lambda () ,@body)
- ,(let* ((dummy-key (gensym))
- (elisp-key (gensym))
- (value (gensym))
- (arglist `(,dummy-key ,elisp-key ,value)))
- `(with-always-lexical
- ,arglist
- (lambda ,arglist
- (if (eq ,elisp-key ,tagsym)
- ,value
- ((guile-primitive throw) ,dummy-key ,elisp-key
- ,value))))))))))
-
-;;; unwind-protect is just some weaker construct as dynamic-wind, so
-;;; straight-forward to implement.
-
-(built-in-macro unwind-protect
- (lambda (body . clean-ups)
- (if (null? clean-ups)
- (macro-error "unwind-protect without cleanup code"))
- `((guile-primitive dynamic-wind)
- (lambda () nil)
- (lambda () ,body)
- (lambda () ,@clean-ups))))
-
-;;; Pop off the first element from a list or push one to it.
-
-(built-in-macro pop
- (lambda (list-name)
- `(prog1 (car ,list-name)
- (setq ,list-name (cdr ,list-name)))))
-
-(built-in-macro push
- (lambda (new-el list-name)
- `(setq ,list-name (cons ,new-el ,list-name))))
-;;; Guile Emacs Lisp
-
-;;; Copyright (C) 2009 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or modify
-;;; it under the terms of the GNU Lesser General Public License as
-;;; published by the Free Software Foundation; either version 3 of the
-;;; License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful, but
-;;; WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-;;; 02110-1301 USA
-
-;;; Code:
-
-(define-module (language elisp runtime subrs)
- #\use-module (language elisp runtime)
- #\use-module (system base compile))
-
-;;; This module contains the function-slots of elisp symbols. Elisp
-;;; built-in functions are implemented as predefined function bindings
-;;; here.
-
-;;; Equivalence and equalness predicates.
-
-(built-in-func eq
- (lambda (a b)
- (elisp-bool (eq? a b))))
-
-(built-in-func equal
- (lambda (a b)
- (elisp-bool (equal? a b))))
-
-;;; Number predicates.
-
-(built-in-func floatp
- (lambda (num)
- (elisp-bool (and (real? num)
- (or (inexact? num)
- (prim not (integer? num)))))))
-
-(built-in-func integerp
- (lambda (num)
- (elisp-bool (and (exact? num)
- (integer? num)))))
-
-(built-in-func numberp
- (lambda (num)
- (elisp-bool (real? num))))
-
-(built-in-func wholenump
- (lambda (num)
- (elisp-bool (and (exact? num)
- (integer? num)
- (prim >= num 0)))))
-
-(built-in-func zerop
- (lambda (num)
- (elisp-bool (prim = num 0))))
-
-;;; Number comparisons.
-
-(built-in-func =
- (lambda (num1 num2)
- (elisp-bool (prim = num1 num2))))
-
-(built-in-func /=
- (lambda (num1 num2)
- (elisp-bool (prim not (prim = num1 num2)))))
-
-(built-in-func <
- (lambda (num1 num2)
- (elisp-bool (prim < num1 num2))))
-
-(built-in-func <=
- (lambda (num1 num2)
- (elisp-bool (prim <= num1 num2))))
-
-(built-in-func >
- (lambda (num1 num2)
- (elisp-bool (prim > num1 num2))))
-
-(built-in-func >=
- (lambda (num1 num2)
- (elisp-bool (prim >= num1 num2))))
-
-(built-in-func max
- (lambda (. nums)
- (prim apply (@ (guile) max) nums)))
-
-(built-in-func min
- (lambda (. nums)
- (prim apply (@ (guile) min) nums)))
-
-(built-in-func abs
- (@ (guile) abs))
-
-;;; Number conversion.
-
-(built-in-func float
- (lambda (num)
- (if (exact? num)
- (exact->inexact num)
- num)))
-
-;;; TODO: truncate, floor, ceiling, round.
-
-;;; Arithmetic functions.
-
-(built-in-func 1+ (@ (guile) 1+))
-
-(built-in-func 1- (@ (guile) 1-))
-
-(built-in-func + (@ (guile) +))
-
-(built-in-func - (@ (guile) -))
-
-(built-in-func * (@ (guile) *))
-
-(built-in-func % (@ (guile) modulo))
-
-;;; TODO: / with correct integer/real behaviour, mod (for floating-piont
-;;; values).
-
-;;; Floating-point rounding operations.
-
-(built-in-func ffloor (@ (guile) floor))
-
-(built-in-func fceiling (@ (guile) ceiling))
-
-(built-in-func ftruncate (@ (guile) truncate))
-
-(built-in-func fround (@ (guile) round))
-
-;;; List predicates.
-
-(built-in-func consp
- (lambda (el)
- (elisp-bool (pair? el))))
-
-(built-in-func atomp
- (lambda (el)
- (elisp-bool (prim not (pair? el)))))
-
-(built-in-func listp
- (lambda (el)
- (elisp-bool (or (pair? el) (null? el)))))
-
-(built-in-func nlistp
- (lambda (el)
- (elisp-bool (and (prim not (pair? el))
- (prim not (null? el))))))
-
-(built-in-func null
- (lambda (el)
- (elisp-bool (null? el))))
-
-;;; Accessing list elements.
-
-(built-in-func car
- (lambda (el)
- (if (null? el)
- nil-value
- (prim car el))))
-
-(built-in-func cdr
- (lambda (el)
- (if (null? el)
- nil-value
- (prim cdr el))))
-
-(built-in-func car-safe
- (lambda (el)
- (if (pair? el)
- (prim car el)
- nil-value)))
-
-(built-in-func cdr-safe
- (lambda (el)
- (if (pair? el)
- (prim cdr el)
- nil-value)))
-
-(built-in-func nth
- (lambda (n lst)
- (if (negative? n)
- (prim car lst)
- (let iterate ((i n)
- (tail lst))
- (cond
- ((null? tail) nil-value)
- ((zero? i) (prim car tail))
- (else (iterate (prim 1- i) (prim cdr tail))))))))
-
-(built-in-func nthcdr
- (lambda (n lst)
- (if (negative? n)
- lst
- (let iterate ((i n)
- (tail lst))
- (cond
- ((null? tail) nil-value)
- ((zero? i) tail)
- (else (iterate (prim 1- i) (prim cdr tail))))))))
-
-(built-in-func length (@ (guile) length))
-
-;;; Building lists.
-
-(built-in-func cons (@ (guile) cons))
-
-(built-in-func list (@ (guile) list))
-
-(built-in-func make-list
- (lambda (len obj)
- (prim make-list len obj)))
-
-(built-in-func append (@ (guile) append))
-
-(built-in-func reverse (@ (guile) reverse))
-
-(built-in-func copy-tree (@ (guile) copy-tree))
-
-(built-in-func number-sequence
- (lambda (from . rest)
- (if (prim > (prim length rest) 2)
- (runtime-error "too many arguments for number-sequence"
- (prim cdddr rest))
- (if (null? rest)
- `(,from)
- (let ((to (prim car rest))
- (sep (if (or (null? (prim cdr rest))
- (eq? nil-value (prim cadr rest)))
- 1
- (prim cadr rest))))
- (cond
- ((or (eq? nil-value to) (prim = to from)) `(,from))
- ((and (zero? sep) (prim not (prim = from to)))
- (runtime-error "infinite list in number-sequence"))
- ((prim < (prim * to sep) (prim * from sep)) '())
- (else
- (let iterate ((i (prim +
- from
- (prim *
- sep
- (prim quotient
- (prim abs
- (prim -
- to
- from))
- (prim abs sep)))))
- (result '()))
- (if (prim = i from)
- (prim cons i result)
- (iterate (prim - i sep)
- (prim cons i result)))))))))))
-
-;;; Changing lists.
-
-(built-in-func setcar
- (lambda (cell val)
- (if (and (null? cell) (null? val))
- #nil
- (prim set-car! cell val))
- val))
-
-(built-in-func setcdr
- (lambda (cell val)
- (if (and (null? cell) (null? val))
- #nil
- (prim set-cdr! cell val))
- val))
-
-;;; Accessing symbol bindings for symbols known only at runtime.
-
-(built-in-func symbol-value
- (lambda (sym)
- (reference-variable value-slot-module sym)))
-
-(built-in-func symbol-function
- (lambda (sym)
- (reference-variable function-slot-module sym)))
-
-(built-in-func set
- (lambda (sym value)
- (set-variable! value-slot-module sym value)))
-
-(built-in-func fset
- (lambda (sym value)
- (set-variable! function-slot-module sym value)))
-
-(built-in-func makunbound
- (lambda (sym)
- (if (module-bound? (resolve-interface value-slot-module) sym)
- (let ((var (module-variable (resolve-module value-slot-module)
- sym)))
- (if (and (variable-bound? var) (fluid? (variable-ref var)))
- (fluid-unset! (variable-ref var))
- (variable-unset! var))))
- sym))
-
-(built-in-func fmakunbound
- (lambda (sym)
- (if (module-bound? (resolve-interface function-slot-module) sym)
- (let ((var (module-variable
- (resolve-module function-slot-module)
- sym)))
- (if (and (variable-bound? var) (fluid? (variable-ref var)))
- (fluid-unset! (variable-ref var))
- (variable-unset! var))))
- sym))
-
-(built-in-func boundp
- (lambda (sym)
- (elisp-bool
- (and
- (module-bound? (resolve-interface value-slot-module) sym)
- (let ((var (module-variable (resolve-module value-slot-module)
- sym)))
- (and (variable-bound? var)
- (if (fluid? (variable-ref var))
- (fluid-bound? (variable-ref var))
- #t)))))))
-
-(built-in-func fboundp
- (lambda (sym)
- (elisp-bool
- (and
- (module-bound? (resolve-interface function-slot-module) sym)
- (let* ((var (module-variable (resolve-module function-slot-module)
- sym)))
- (and (variable-bound? var)
- (if (fluid? (variable-ref var))
- (fluid-bound? (variable-ref var))
- #t)))))))
-
-;;; Function calls. These must take care of special cases, like using
-;;; symbols or raw lambda-lists as functions!
-
-(built-in-func apply
- (lambda (func . args)
- (let ((real-func (cond
- ((symbol? func)
- (reference-variable function-slot-module func))
- ((list? func)
- (if (and (prim not (null? func))
- (eq? (prim car func) 'lambda))
- (compile func #\from 'elisp #\to 'value)
- (runtime-error "list is not a function"
- func)))
- (else func))))
- (prim apply (@ (guile) apply) real-func args))))
-
-(built-in-func funcall
- (lambda (func . args)
- (apply func args)))
-
-;;; Throw can be implemented as built-in function.
-
-(built-in-func throw
- (lambda (tag value)
- (prim throw 'elisp-exception tag value)))
-
-;;; Miscellaneous.
-
-(built-in-func not
- (lambda (x)
- (if x nil-value t-value)))
-
-(built-in-func eval
- (lambda (form)
- (compile form #\from 'elisp #\to 'value)))
-
-(built-in-func load
- (lambda* (file)
- (compile-file file #\from 'elisp #\to 'value)
- #t))
-;;; Guile Emacs Lisp
-
-;;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language elisp runtime value-slot))
-
-;;; This module contains the value-slots of elisp symbols.
-;;; Guile Emac Lisp
-
-;; Copyright (C) 2001, 2009, 2010 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language elisp spec)
- #\use-module (language elisp compile-tree-il)
- #\use-module (language elisp parser)
- #\use-module (system base language)
- #\export (elisp))
-
-(define-language elisp
- #\title "Emacs Lisp"
- #\reader (lambda (port env) (read-elisp port))
- #\printer write
- #\compilers `((tree-il . ,compile-tree-il)))
-;;; Guile Low Intermediate Language
-
-;; Copyright (C) 2001, 2009, 2010 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language glil)
- #\use-module (system base syntax)
- #\use-module (system base pmatch)
- #\use-module ((srfi srfi-1) #\select (fold))
- #\export
- (<glil-program> make-glil-program glil-program?
- glil-program-meta glil-program-body
-
- <glil-std-prelude> make-glil-std-prelude glil-std-prelude?
- glil-std-prelude-nreq glil-std-prelude-nlocs glil-std-prelude-else-label
-
- <glil-opt-prelude> make-glil-opt-prelude glil-opt-prelude?
- glil-opt-prelude-nreq glil-opt-prelude-nopt glil-opt-prelude-rest
- glil-opt-prelude-nlocs glil-opt-prelude-else-label
-
- <glil-kw-prelude> make-glil-kw-prelude glil-kw-prelude?
- glil-kw-prelude-nreq glil-kw-prelude-nopt glil-kw-prelude-kw
- glil-kw-prelude-allow-other-keys? glil-kw-prelude-rest
- glil-kw-prelude-nlocs glil-kw-prelude-else-label
-
- <glil-bind> make-glil-bind glil-bind?
- glil-bind-vars
-
- <glil-mv-bind> make-glil-mv-bind glil-mv-bind?
- glil-mv-bind-vars glil-mv-bind-rest
-
- <glil-unbind> make-glil-unbind glil-unbind?
-
- <glil-source> make-glil-source glil-source?
- glil-source-props
-
- <glil-void> make-glil-void glil-void?
-
- <glil-const> make-glil-const glil-const?
- glil-const-obj
-
- <glil-lexical> make-glil-lexical glil-lexical?
- glil-lexical-local? glil-lexical-boxed? glil-lexical-op glil-lexical-index
-
- <glil-toplevel> make-glil-toplevel glil-toplevel?
- glil-toplevel-op glil-toplevel-name
-
- <glil-module> make-glil-module glil-module?
- glil-module-op glil-module-mod glil-module-name glil-module-public?
-
- <glil-label> make-glil-label glil-label?
- glil-label-label
-
- <glil-branch> make-glil-branch glil-branch?
- glil-branch-inst glil-branch-label
-
- <glil-call> make-glil-call glil-call?
- glil-call-inst glil-call-nargs
-
- <glil-mv-call> make-glil-mv-call glil-mv-call?
- glil-mv-call-nargs glil-mv-call-ra
-
- <glil-prompt> make-glil-prompt glil-prompt? glil-prompt-label glil-prompt-escape-only?
-
- parse-glil unparse-glil))
-
-(define (print-glil x port)
- (format port "#<glil ~s>" (unparse-glil x)))
-
-(define-type (<glil> #\printer print-glil)
- ;; Meta operations
- (<glil-program> meta body)
- (<glil-std-prelude> nreq nlocs else-label)
- (<glil-opt-prelude> nreq nopt rest nlocs else-label)
- (<glil-kw-prelude> nreq nopt rest kw allow-other-keys? nlocs else-label)
- (<glil-bind> vars)
- (<glil-mv-bind> vars rest)
- (<glil-unbind>)
- (<glil-source> props)
- ;; Objects
- (<glil-void>)
- (<glil-const> obj)
- ;; Variables
- (<glil-lexical> local? boxed? op index)
- (<glil-toplevel> op name)
- (<glil-module> op mod name public?)
- ;; Controls
- (<glil-label> label)
- (<glil-branch> inst label)
- (<glil-call> inst nargs)
- (<glil-mv-call> nargs ra)
- (<glil-prompt> label escape-only?))
-
-
-
-(define (parse-glil x)
- (pmatch x
- ((program ,meta . ,body)
- (make-glil-program meta (map parse-glil body)))
- ((std-prelude ,nreq ,nlocs ,else-label)
- (make-glil-std-prelude nreq nlocs else-label))
- ((opt-prelude ,nreq ,nopt ,rest ,nlocs ,else-label)
- (make-glil-opt-prelude nreq nopt rest nlocs else-label))
- ((kw-prelude ,nreq ,nopt ,rest ,kw ,allow-other-keys? ,nlocs ,else-label)
- (make-glil-kw-prelude nreq nopt rest kw allow-other-keys? nlocs else-label))
- ((bind . ,vars) (make-glil-bind vars))
- ((mv-bind ,vars ,rest) (make-glil-mv-bind vars rest))
- ((unbind) (make-glil-unbind))
- ((source ,props) (make-glil-source props))
- ((void) (make-glil-void))
- ((const ,obj) (make-glil-const obj))
- ((lexical ,local? ,boxed? ,op ,index) (make-glil-lexical local? boxed? op index))
- ((toplevel ,op ,name) (make-glil-toplevel op name))
- ((module public ,op ,mod ,name) (make-glil-module op mod name #t))
- ((module private ,op ,mod ,name) (make-glil-module op mod name #f))
- ((label ,label) (make-glil-label label))
- ((branch ,inst ,label) (make-glil-branch inst label))
- ((call ,inst ,nargs) (make-glil-call inst nargs))
- ((mv-call ,nargs ,ra) (make-glil-mv-call nargs ra))
- ((prompt ,label ,escape-only?)
- (make-glil-prompt label escape-only?))
- (else (error "invalid glil" x))))
-
-(define (unparse-glil glil)
- (record-case glil
- ;; meta
- ((<glil-program> meta body)
- `(program ,meta ,@(map unparse-glil body)))
- ((<glil-std-prelude> nreq nlocs else-label)
- `(std-prelude ,nreq ,nlocs ,else-label))
- ((<glil-opt-prelude> nreq nopt rest nlocs else-label)
- `(opt-prelude ,nreq ,nopt ,rest ,nlocs ,else-label))
- ((<glil-kw-prelude> nreq nopt rest kw allow-other-keys? nlocs else-label)
- `(kw-prelude ,nreq ,nopt ,rest ,kw ,allow-other-keys? ,nlocs ,else-label))
- ((<glil-bind> vars) `(bind ,@vars))
- ((<glil-mv-bind> vars rest) `(mv-bind ,vars ,rest))
- ((<glil-unbind>) `(unbind))
- ((<glil-source> props) `(source ,props))
- ;; constants
- ((<glil-void>) `(void))
- ((<glil-const> obj) `(const ,obj))
- ;; variables
- ((<glil-lexical> local? boxed? op index)
- `(lexical ,local? ,boxed? ,op ,index))
- ((<glil-toplevel> op name)
- `(toplevel ,op ,name))
- ((<glil-module> op mod name public?)
- `(module ,(if public? 'public 'private) ,op ,mod ,name))
- ;; controls
- ((<glil-label> label) `(label ,label))
- ((<glil-branch> inst label) `(branch ,inst ,label))
- ((<glil-call> inst nargs) `(call ,inst ,nargs))
- ((<glil-mv-call> nargs ra) `(mv-call ,nargs ,ra))
- ((<glil-prompt> label escape-only?)
- `(prompt ,label escape-only?))))
-;;; Guile VM assembler
-
-;; Copyright (C) 2001, 2009, 2010, 2011, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language glil compile-assembly)
- #\use-module (system base syntax)
- #\use-module (system base pmatch)
- #\use-module (language glil)
- #\use-module (language assembly)
- #\use-module (system vm instruction)
- #\use-module ((system vm program) #\select (make-binding))
- #\use-module (ice-9 receive)
- #\use-module (ice-9 vlist)
- #\use-module ((srfi srfi-1) #\select (fold))
- #\use-module (rnrs bytevectors)
- #\export (compile-assembly))
-
-;; Traversal helpers
-;;
-(define (vhash-fold-right2 proc vhash s0 s1)
- (let lp ((i (vlist-length vhash)) (s0 s0) (s1 s1))
- (if (zero? i)
- (values s0 s1)
- (receive (s0 s1) (let ((pair (vlist-ref vhash (1- i))))
- (proc (car pair) (cdr pair) s0 s1))
- (lp (1- i) s0 s1)))))
-
-(define (fold2 proc ls s0 s1)
- (let lp ((ls ls) (s0 s0) (s1 s1))
- (if (null? ls)
- (values s0 s1)
- (receive (s0 s1) (proc (car ls) s0 s1)
- (lp (cdr ls) s0 s1)))))
-
-(define (vector-fold2 proc vect s0 s1)
- (let ((len (vector-length vect)))
- (let lp ((i 0) (s0 s0) (s1 s1))
- (if (< i len)
- (receive (s0 s1) (proc (vector-ref vect i) s0 s1)
- (lp (1+ i) s0 s1))
- (values s0 s1)))))
-
-;; Variable cache cells go in the object table, and serialize as their
-;; keys. The reason we wrap the keys in these records is so they don't
-;; compare as `equal?' to other objects in the object table.
-;;
-;; `key' is either a symbol or the list (MODNAME SYM PUBLIC?)
-
-(define-record <variable-cache-cell> key)
-
-(define (limn-sources sources)
- (let lp ((in sources) (out '()) (filename #f))
- (if (null? in)
- (reverse! out)
- (let ((addr (caar in))
- (new-filename (assq-ref (cdar in ) 'filename))
- (line (assq-ref (cdar in) 'line))
- (column (assq-ref (cdar in) 'column)))
- (cond
- ((not (equal? new-filename filename))
- (lp (cdr in)
- `((,addr . (,line . ,column))
- (filename . ,new-filename)
- . ,out)
- new-filename))
- ((or (null? out) (not (equal? (cdar out) `(,line . ,column))))
- (lp (cdr in)
- `((,addr . (,line . ,column))
- . ,out)
- filename))
- (else
- (lp (cdr in) out filename)))))))
-
-
-;; Avoid going through the compiler so as to avoid adding to the
-;; constant store.
-(define (make-meta bindings sources arities tail)
- (let ((body `(,@(dump-object `(,bindings ,sources ,arities ,@tail) 0)
- (return))))
- `(load-program ()
- ,(addr+ 0 body)
- #f
- ,@body)))
-
-;; If this is true, the object doesn't need to go in a constant table.
-;;
-(define (immediate? x)
- (object->assembly x))
-
-;; This tests for a proper scheme list whose last cdr is '(), not #nil.
-;;
-(define (scheme-list? x)
- (and (list? x)
- (or (eq? x '())
- (let ((p (last-pair x)))
- (and (pair? p)
- (eq? (cdr p) '()))))))
-
-;; Note: in all of these procedures that build up constant tables, the
-;; first (zeroth) index is reserved. At runtime it is replaced with the
-;; procedure's module. Hence all of this 1+ length business.
-
-;; Build up a vhash of constant -> index, allowing us to build up a
-;; constant table for a whole compilation unit.
-;;
-(define (build-constant-store x)
- (define (add-to-store store x)
- (define (add-to-end store x)
- (vhash-cons x (1+ (vlist-length store)) store))
- (cond
- ((vhash-assoc x store)
- ;; Already in the store.
- store)
- ((immediate? x)
- ;; Immediates don't need to go in the constant table.
- store)
- ((or (number? x)
- (string? x)
- (symbol? x)
- (keyword? x))
- ;; Atoms.
- (add-to-end store x))
- ((variable-cache-cell? x)
- ;; Variable cache cells (see below).
- (add-to-end (add-to-store store (variable-cache-cell-key x))
- x))
- ((list? x)
- ;; Add the elements to the store, then the list itself. We could
- ;; try hashing the cdrs as well, but that seems a bit overkill, and
- ;; this way we do compress the bytecode a bit by allowing the use of
- ;; the `list' opcode.
- (let ((store (fold (lambda (x store)
- (add-to-store store x))
- store
- x)))
- (add-to-end store x)))
- ((pair? x)
- ;; Non-lists get caching on both fields.
- (let ((store (add-to-store (add-to-store store (car x))
- (cdr x))))
- (add-to-end store x)))
- ((and (vector? x)
- (equal? (array-shape x) (list (list 0 (1- (vector-length x))))))
- ;; Likewise, add the elements to the store, then the vector itself.
- ;; Important for the vectors produced by the psyntax expansion
- ;; process.
- (let ((store (fold (lambda (x store)
- (add-to-store store x))
- store
- (vector->list x))))
- (add-to-end store x)))
- ((array? x)
- ;; Naive assumption that if folks are using arrays, that perhaps
- ;; there's not much more duplication.
- (add-to-end store x))
- (else
- (error "build-constant-store: unrecognized object" x))))
-
- (let walk ((x x) (store vlist-null))
- (record-case x
- ((<glil-program> meta body)
- (fold walk store body))
- ((<glil-const> obj)
- (add-to-store store obj))
- ((<glil-kw-prelude> kw)
- (add-to-store store kw))
- ((<glil-toplevel> op name)
- ;; We don't add toplevel variable cache cells to the global
- ;; constant table, because they are sensitive to changes in
- ;; modules as the toplevel expressions are evaluated. So we just
- ;; add the name.
- (add-to-store store name))
- ((<glil-module> op mod name public?)
- ;; However, it is fine add module variable cache cells to the
- ;; global table, as their bindings are not dependent on the
- ;; current module.
- (add-to-store store
- (make-variable-cache-cell (list mod name public?))))
- (else store))))
-
-;; Analyze one <glil-program> to determine its object table. Produces a
-;; vhash of constant to index.
-;;
-(define (build-object-table x)
- (define (add store x)
- (if (vhash-assoc x store)
- store
- (vhash-cons x (1+ (vlist-length store)) store)))
- (record-case x
- ((<glil-program> meta body)
- (fold (lambda (x table)
- (record-case x
- ((<glil-program> meta body)
- ;; Add the GLIL itself to the table.
- (add table x))
- ((<glil-const> obj)
- (if (immediate? obj)
- table
- (add table obj)))
- ((<glil-kw-prelude> kw)
- (add table kw))
- ((<glil-toplevel> op name)
- (add table (make-variable-cache-cell name)))
- ((<glil-module> op mod name public?)
- (add table (make-variable-cache-cell (list mod name public?))))
- (else table)))
- vlist-null
- body))))
-
-;; A functional stack of names of live variables.
-(define (make-open-binding name boxed? index)
- (list name boxed? index))
-(define (make-closed-binding open-binding start end)
- (make-binding (car open-binding) (cadr open-binding)
- (caddr open-binding) start end))
-(define (open-binding bindings vars start)
- (cons
- (acons start
- (map
- (lambda (v)
- (pmatch v
- ((,name ,boxed? ,i)
- (make-open-binding name boxed? i))
- (else (error "unknown binding type" v))))
- vars)
- (car bindings))
- (cdr bindings)))
-(define (close-binding bindings end)
- (pmatch bindings
- ((((,start . ,closing) . ,open) . ,closed)
- (cons open
- (fold (lambda (o tail)
- ;; the cons is for dsu sort
- (acons start (make-closed-binding o start end)
- tail))
- closed
- closing)))
- (else (error "broken bindings" bindings))))
-(define (close-all-bindings bindings end)
- (if (null? (car bindings))
- (map cdr
- (stable-sort (reverse (cdr bindings))
- (lambda (x y) (< (car x) (car y)))))
- (close-all-bindings (close-binding bindings end) end)))
-
-
-;; A functional arities thingamajiggy.
-;; arities := ((ip nreq [[nopt] [[rest] [kw]]]]) ...)
-(define (open-arity addr nreq nopt rest kw arities)
- (cons
- (cond
- (kw (list addr nreq nopt rest kw))
- (rest (list addr nreq nopt rest))
- (nopt (list addr nreq nopt))
- (nreq (list addr nreq))
- (else (list addr)))
- arities))
-(define (close-arity addr arities)
- (pmatch arities
- (() '())
- (((,start . ,tail) . ,rest)
- `((,start ,addr . ,tail) . ,rest))
- (else (error "bad arities" arities))))
-(define (begin-arity end start nreq nopt rest kw arities)
- (open-arity start nreq nopt rest kw (close-arity end arities)))
-
-(define (compile-assembly glil)
- (let* ((all-constants (build-constant-store glil))
- (prog (compile-program glil all-constants))
- (len (byte-length prog)))
- ;; The top objcode thunk. We're going to wrap this thunk in
- ;; a thunk -- yo dawgs -- with the goal being to lift all
- ;; constants up to the top level. The store forms a DAG, so
- ;; we can actually build up later elements in terms of
- ;; earlier ones.
- ;;
- (cond
- ((vlist-null? all-constants)
- ;; No constants: just emit the inner thunk.
- prog)
- (else
- ;; We have an object store, so write it out, attach it
- ;; to the inner thunk, and tail call.
- (receive (tablecode addr) (dump-constants all-constants)
- (let ((prog (align-program prog addr)))
- ;; Outer thunk.
- `(load-program ()
- ,(+ (addr+ addr prog)
- 2 ; for (tail-call 0)
- )
- #f
- ;; Load the table, build the inner
- ;; thunk, then tail call.
- ,@tablecode
- ,@prog
- (tail-call 0))))))))
-
-(define (compile-program glil constants)
- (record-case glil
- ((<glil-program> meta body)
- (let lp ((body body) (code '()) (bindings '(())) (source-alist '())
- (label-alist '()) (arities '()) (addr 0))
- (cond
- ((null? body)
- (let ((code (fold append '() code))
- (bindings (close-all-bindings bindings addr))
- (sources (limn-sources (reverse! source-alist)))
- (labels (reverse label-alist))
- (arities (reverse (close-arity addr arities)))
- (len addr))
- (let* ((meta (make-meta bindings sources arities meta))
- (meta-pad (if meta (modulo (- 8 (modulo len 8)) 8) 0)))
- `(load-program ,labels
- ,(+ len meta-pad)
- ,meta
- ,@code
- ,@(if meta
- (make-list meta-pad '(nop))
- '())))))
- (else
- (receive (subcode bindings source-alist label-alist arities)
- (glil->assembly (car body) bindings
- source-alist label-alist
- constants arities addr)
- (lp (cdr body) (cons subcode code)
- bindings source-alist label-alist arities
- (addr+ addr subcode)))))))))
-
-(define (compile-objtable constants table addr)
- (define (load-constant idx)
- (if (< idx 256)
- (values `((object-ref ,idx))
- 2)
- (values `((long-object-ref
- ,(quotient idx 256) ,(modulo idx 256)))
- 3)))
- (cond
- ((vlist-null? table)
- ;; Empty table; just return #f.
- (values '((make-false))
- (1+ addr)))
- (else
- (call-with-values
- (lambda ()
- (vhash-fold-right2
- (lambda (obj idx codes addr)
- (cond
- ((vhash-assoc obj constants)
- => (lambda (pair)
- (receive (load len) (load-constant (cdr pair))
- (values (cons load codes)
- (+ addr len)))))
- ((variable-cache-cell? obj)
- (cond
- ((vhash-assoc (variable-cache-cell-key obj) constants)
- => (lambda (pair)
- (receive (load len) (load-constant (cdr pair))
- (values (cons load codes)
- (+ addr len)))))
- (else (error "vcache cell key not in table" obj))))
- ((glil-program? obj)
- ;; Programs are not cached in the global constants
- ;; table because when a program is loaded, its module
- ;; is bound, and we want to do that only after any
- ;; preceding effectful statements.
- (let* ((table (build-object-table obj))
- (prog (compile-program obj table)))
- (receive (tablecode addr)
- (compile-objtable constants table addr)
- (let ((prog (align-program prog addr)))
- (values (cons `(,@tablecode ,@prog)
- codes)
- (addr+ addr prog))))))
- (else
- (error "unrecognized constant" obj))))
- table
- '(((make-false))) (1+ addr)))
- (lambda (elts addr)
- (let ((len (1+ (vlist-length table))))
- (values
- (fold append
- `((vector ,(quotient len 256) ,(modulo len 256)))
- elts)
- (+ addr 3))))))))
-
-(define (glil->assembly glil bindings source-alist label-alist
- constants arities addr)
- (define (emit-code x)
- (values x bindings source-alist label-alist arities))
- (define (emit-object-ref i)
- (values (if (< i 256)
- `((object-ref ,i))
- `((long-object-ref ,(quotient i 256) ,(modulo i 256))))
- bindings source-alist label-alist arities))
- (define (emit-code/arity x nreq nopt rest kw)
- (values x bindings source-alist label-alist
- (begin-arity addr (addr+ addr x) nreq nopt rest kw arities)))
-
- (record-case glil
- ((<glil-program> meta body)
- (cond
- ((vhash-assoc glil constants)
- ;; We are cached in someone's objtable; just emit a load.
- => (lambda (pair)
- (emit-object-ref (cdr pair))))
- (else
- ;; Otherwise, build an objtable for the program, compile it, and
- ;; emit a load-program.
- (let* ((table (build-object-table glil))
- (prog (compile-program glil table)))
- (receive (tablecode addr) (compile-objtable constants table addr)
- (emit-code `(,@tablecode ,@(align-program prog addr))))))))
-
- ((<glil-std-prelude> nreq nlocs else-label)
- (emit-code/arity
- (if (and (< nreq 8) (< nlocs (+ nreq 32)) (not else-label))
- `((assert-nargs-ee/locals ,(logior nreq (ash (- nlocs nreq) 3))))
- `(,(if else-label
- `(br-if-nargs-ne ,(quotient nreq 256)
- ,(modulo nreq 256)
- ,else-label)
- `(assert-nargs-ee ,(quotient nreq 256)
- ,(modulo nreq 256)))
- (reserve-locals ,(quotient nlocs 256)
- ,(modulo nlocs 256))))
- nreq #f #f #f))
-
- ((<glil-opt-prelude> nreq nopt rest nlocs else-label)
- (let ((bind-required
- (if else-label
- `((br-if-nargs-lt ,(quotient nreq 256)
- ,(modulo nreq 256)
- ,else-label))
- `((assert-nargs-ge ,(quotient nreq 256)
- ,(modulo nreq 256)))))
- (bind-optionals
- (if (zero? nopt)
- '()
- `((bind-optionals ,(quotient (+ nopt nreq) 256)
- ,(modulo (+ nreq nopt) 256)))))
- (bind-rest
- (cond
- (rest
- `((push-rest ,(quotient (+ nreq nopt) 256)
- ,(modulo (+ nreq nopt) 256))))
- (else
- (if else-label
- `((br-if-nargs-gt ,(quotient (+ nreq nopt) 256)
- ,(modulo (+ nreq nopt) 256)
- ,else-label))
- `((assert-nargs-ee ,(quotient (+ nreq nopt) 256)
- ,(modulo (+ nreq nopt) 256))))))))
- (emit-code/arity
- `(,@bind-required
- ,@bind-optionals
- ,@bind-rest
- (reserve-locals ,(quotient nlocs 256)
- ,(modulo nlocs 256)))
- nreq nopt rest #f)))
-
- ((<glil-kw-prelude> nreq nopt rest kw allow-other-keys? nlocs else-label)
- (let* ((kw-idx (or (and=> (vhash-assoc kw constants) cdr)
- (error "kw not in objtable")))
- (bind-required
- (if else-label
- `((br-if-nargs-lt ,(quotient nreq 256)
- ,(modulo nreq 256)
- ,else-label))
- `((assert-nargs-ge ,(quotient nreq 256)
- ,(modulo nreq 256)))))
- (ntotal (apply max (+ nreq nopt) (map 1+ (map cdr kw))))
- (bind-optionals-and-shuffle
- `((,(if (and else-label (not rest))
- 'bind-optionals/shuffle-or-br
- 'bind-optionals/shuffle)
- ,(quotient nreq 256)
- ,(modulo nreq 256)
- ,(quotient (+ nreq nopt) 256)
- ,(modulo (+ nreq nopt) 256)
- ,(quotient ntotal 256)
- ,(modulo ntotal 256)
- ,@(if (and else-label (not rest))
- `(,else-label)
- '()))))
- (bind-kw
- ;; when this code gets called, all optionals are filled
- ;; in, space has been made for kwargs, and the kwargs
- ;; themselves have been shuffled above the slots for all
- ;; req/opt/kwargs locals.
- `((bind-kwargs
- ,(quotient kw-idx 256)
- ,(modulo kw-idx 256)
- ,(quotient ntotal 256)
- ,(modulo ntotal 256)
- ,(logior (if rest 2 0)
- (if allow-other-keys? 1 0)))))
- (bind-rest
- (if rest
- `((bind-rest ,(quotient ntotal 256)
- ,(modulo ntotal 256)
- ,(quotient rest 256)
- ,(modulo rest 256)))
- '())))
-
- (let ((code `(,@bind-required
- ,@bind-optionals-and-shuffle
- ,@bind-kw
- ,@bind-rest
- (reserve-locals ,(quotient nlocs 256)
- ,(modulo nlocs 256)))))
- (values code bindings source-alist label-alist
- (begin-arity addr (addr+ addr code) nreq nopt rest
- (and kw (cons allow-other-keys? kw))
- arities)))))
-
- ((<glil-bind> vars)
- (values '()
- (open-binding bindings vars addr)
- source-alist
- label-alist
- arities))
-
- ((<glil-mv-bind> vars rest)
- (if (integer? vars)
- (values `((truncate-values ,vars ,(if rest 1 0)))
- bindings
- source-alist
- label-alist
- arities)
- (values `((truncate-values ,(length vars) ,(if rest 1 0)))
- (open-binding bindings vars addr)
- source-alist
- label-alist
- arities)))
-
- ((<glil-unbind>)
- (values '()
- (close-binding bindings addr)
- source-alist
- label-alist
- arities))
-
- ((<glil-source> props)
- (values '()
- bindings
- (acons addr props source-alist)
- label-alist
- arities))
-
- ((<glil-void>)
- (emit-code '((void))))
-
- ((<glil-const> obj)
- (cond
- ((object->assembly obj)
- => (lambda (code)
- (emit-code (list code))))
- ((vhash-assoc obj constants)
- => (lambda (pair)
- (emit-object-ref (cdr pair))))
- (else (error "const not in table" obj))))
-
- ((<glil-lexical> local? boxed? op index)
- (emit-code
- (if local?
- (if (< index 256)
- (case op
- ((ref) (if boxed?
- `((local-boxed-ref ,index))
- `((local-ref ,index))))
- ((set) (if boxed?
- `((local-boxed-set ,index))
- `((local-set ,index))))
- ((box) `((box ,index)))
- ((empty-box) `((empty-box ,index)))
- ((fix) `((fix-closure 0 ,index)))
- ((bound?) (if boxed?
- `((local-ref ,index)
- (variable-bound?))
- `((local-bound? ,index))))
- (else (error "what" op)))
- (let ((a (quotient index 256))
- (b (modulo index 256)))
- (case op
- ((ref)
- (if boxed?
- `((long-local-ref ,a ,b)
- (variable-ref))
- `((long-local-ref ,a ,b))))
- ((set)
- (if boxed?
- `((long-local-ref ,a ,b)
- (variable-set))
- `((long-local-set ,a ,b))))
- ((box)
- `((make-variable)
- (variable-set)
- (long-local-set ,a ,b)))
- ((empty-box)
- `((make-variable)
- (long-local-set ,a ,b)))
- ((fix)
- `((fix-closure ,a ,b)))
- ((bound?)
- (if boxed?
- `((long-local-ref ,a ,b)
- (variable-bound?))
- `((long-local-bound? ,a ,b))))
- (else (error "what" op)))))
- `((,(case op
- ((ref) (if boxed? 'free-boxed-ref 'free-ref))
- ((set) (if boxed? 'free-boxed-set (error "what." glil)))
- (else (error "what" op)))
- ,index)))))
-
- ((<glil-toplevel> op name)
- (case op
- ((ref set)
- (cond
- ((and=> (vhash-assoc (make-variable-cache-cell name) constants)
- cdr)
- => (lambda (i)
- (emit-code (if (< i 256)
- `((,(case op
- ((ref) 'toplevel-ref)
- ((set) 'toplevel-set))
- ,i))
- `((,(case op
- ((ref) 'long-toplevel-ref)
- ((set) 'long-toplevel-set))
- ,(quotient i 256)
- ,(modulo i 256)))))))
- (else
- (let ((i (or (and=> (vhash-assoc name constants) cdr)
- (error "toplevel name not in objtable" name))))
- (emit-code `(,(if (< i 256)
- `(object-ref ,i)
- `(long-object-ref ,(quotient i 256)
- ,(modulo i 256)))
- (link-now)
- ,(case op
- ((ref) '(variable-ref))
- ((set) '(variable-set)))))))))
- ((define)
- (let ((i (or (and=> (vhash-assoc name constants) cdr)
- (error "toplevel name not in objtable" name))))
- (emit-code `(,(if (< i 256)
- `(object-ref ,i)
- `(long-object-ref ,(quotient i 256)
- ,(modulo i 256)))
- (define)))))
- (else
- (error "unknown toplevel var kind" op name))))
-
- ((<glil-module> op mod name public?)
- (let ((key (list mod name public?)))
- (case op
- ((ref set)
- (let ((i (or (and=> (vhash-assoc (make-variable-cache-cell key)
- constants) cdr)
- (error "module vcache not in objtable" key))))
- (emit-code (if (< i 256)
- `((,(case op
- ((ref) 'toplevel-ref)
- ((set) 'toplevel-set))
- ,i))
- `((,(case op
- ((ref) 'long-toplevel-ref)
- ((set) 'long-toplevel-set))
- ,(quotient i 256)
- ,(modulo i 256)))))))
- (else
- (error "unknown module var kind" op key)))))
-
- ((<glil-label> label)
- (let ((code (align-block addr)))
- (values code
- bindings
- source-alist
- (acons label (addr+ addr code) label-alist)
- arities)))
-
- ((<glil-branch> inst label)
- (emit-code `((,inst ,label))))
-
- ;; nargs is number of stack args to insn. probably should rename.
- ((<glil-call> inst nargs)
- (if (not (instruction? inst))
- (error "Unknown instruction:" inst))
- (let ((pops (instruction-pops inst)))
- (cond ((< pops 0)
- (case (instruction-length inst)
- ((1) (emit-code `((,inst ,nargs))))
- ((2) (emit-code `((,inst ,(quotient nargs 256)
- ,(modulo nargs 256)))))
- (else (error "Unknown length for variable-arg instruction:"
- inst (instruction-length inst)))))
- ((= pops nargs)
- (emit-code `((,inst))))
- (else
- (error "Wrong number of stack arguments to instruction:" inst nargs)))))
-
- ((<glil-mv-call> nargs ra)
- (emit-code `((mv-call ,nargs ,ra))))
-
- ((<glil-prompt> label escape-only?)
- (emit-code `((prompt ,(if escape-only? 1 0) ,label))))))
-
-(define (dump-object x addr)
- (define (too-long x)
- (error (string-append x " too long")))
-
- (cond
- ((object->assembly x) => list)
- ((variable-cache-cell? x) (dump-object (variable-cache-cell-key x) addr))
- ((number? x)
- `((load-number ,(number->string x))))
- ((string? x)
- (case (string-bytes-per-char x)
- ((1) `((load-string ,x)))
- ((4) (align-code `(load-wide-string ,x) addr 4 4))
- (else (error "bad string bytes per char" x))))
- ((symbol? x)
- (let ((str (symbol->string x)))
- (case (string-bytes-per-char str)
- ((1) `((load-symbol ,str)))
- ((4) `(,@(dump-object str addr)
- (make-symbol)))
- (else (error "bad string bytes per char" str)))))
- ((keyword? x)
- `(,@(dump-object (keyword->symbol x) addr)
- (make-keyword)))
- ((scheme-list? x)
- (let ((tail (let ((len (length x)))
- (if (>= len 65536) (too-long "list"))
- `((list ,(quotient len 256) ,(modulo len 256))))))
- (let dump-objects ((objects x) (codes '()) (addr addr))
- (if (null? objects)
- (fold append tail codes)
- (let ((code (dump-object (car objects) addr)))
- (dump-objects (cdr objects) (cons code codes)
- (addr+ addr code)))))))
- ((pair? x)
- (let ((kar (dump-object (car x) addr)))
- `(,@kar
- ,@(dump-object (cdr x) (addr+ addr kar))
- (cons))))
- ((and (vector? x)
- (equal? (array-shape x) (list (list 0 (1- (vector-length x))))))
- (let* ((len (vector-length x))
- (tail (if (>= len 65536)
- (too-long "vector")
- `((vector ,(quotient len 256) ,(modulo len 256))))))
- (let dump-objects ((i 0) (codes '()) (addr addr))
- (if (>= i len)
- (fold append tail codes)
- (let ((code (dump-object (vector-ref x i) addr)))
- (dump-objects (1+ i) (cons code codes)
- (addr+ addr code)))))))
- ((and (array? x) (symbol? (array-type x)))
- (let* ((type (dump-object (array-type x) addr))
- (shape (dump-object (array-shape x) (addr+ addr type))))
- `(,@type
- ,@shape
- ,@(align-code
- `(load-array ,(uniform-array->bytevector x))
- (addr+ (addr+ addr type) shape)
- 8
- 4))))
- ((array? x)
- ;; an array of generic scheme values
- (let* ((contents (array-contents x))
- (len (vector-length contents)))
- (let dump-objects ((i 0) (codes '()) (addr addr))
- (if (< i len)
- (let ((code (dump-object (vector-ref contents i) addr)))
- (dump-objects (1+ i) (cons code codes)
- (addr+ addr code)))
- (fold append
- `(,@(dump-object (array-shape x) addr)
- (make-array ,(quotient (ash len -16) 256)
- ,(logand #xff (ash len -8))
- ,(logand #xff len)))
- codes)))))
- (else
- (error "dump-object: unrecognized object" x))))
-
-(define (dump-constants constants)
- (define (ref-or-dump x i addr)
- (let ((pair (vhash-assoc x constants)))
- (if (and pair (< (cdr pair) i))
- (let ((idx (cdr pair)))
- (if (< idx 256)
- (values `((object-ref ,idx))
- (+ addr 2))
- (values `((long-object-ref ,(quotient idx 256)
- ,(modulo idx 256)))
- (+ addr 3))))
- (dump1 x i addr))))
- (define (dump1 x i addr)
- (cond
- ((object->assembly x)
- => (lambda (code)
- (values (list code)
- (+ (byte-length code) addr))))
- ((or (number? x)
- (string? x)
- (symbol? x)
- (keyword? x))
- ;; Atoms.
- (let ((code (dump-object x addr)))
- (values code (addr+ addr code))))
- ((variable-cache-cell? x)
- (dump1 (variable-cache-cell-key x) i addr))
- ((scheme-list? x)
- (receive (codes addr)
- (fold2 (lambda (x codes addr)
- (receive (subcode addr) (ref-or-dump x i addr)
- (values (cons subcode codes) addr)))
- x '() addr)
- (values (fold append
- (let ((len (length x)))
- `((list ,(quotient len 256) ,(modulo len 256))))
- codes)
- (+ addr 3))))
- ((pair? x)
- (receive (car-code addr) (ref-or-dump (car x) i addr)
- (receive (cdr-code addr) (ref-or-dump (cdr x) i addr)
- (values `(,@car-code ,@cdr-code (cons))
- (1+ addr)))))
- ((and (vector? x)
- (<= (vector-length x) #xffff)
- (equal? (array-shape x) (list (list 0 (1- (vector-length x))))))
- (receive (codes addr)
- (vector-fold2 (lambda (x codes addr)
- (receive (subcode addr) (ref-or-dump x i addr)
- (values (cons subcode codes) addr)))
- x '() addr)
- (values (fold append
- (let ((len (vector-length x)))
- `((vector ,(quotient len 256) ,(modulo len 256))))
- codes)
- (+ addr 3))))
- ((and (array? x) (symbol? (array-type x)))
- (receive (type addr) (ref-or-dump (array-type x) i addr)
- (receive (shape addr) (ref-or-dump (array-shape x) i addr)
- (let ((bv (align-code `(load-array ,(uniform-array->bytevector x))
- addr 8 4)))
- (values `(,@type ,@shape ,@bv)
- (addr+ addr bv))))))
- ((array? x)
- (let ((contents (array-contents x)))
- (receive (codes addr)
- (vector-fold2 (lambda (x codes addr)
- (receive (subcode addr) (ref-or-dump x i addr)
- (values (cons subcode codes) addr)))
- contents '() addr)
- (receive (shape addr) (ref-or-dump (array-shape x) i addr)
- (values (fold append
- (let ((len (vector-length contents)))
- `(,@shape
- (make-array ,(quotient (ash len -16) 256)
- ,(logand #xff (ash len -8))
- ,(logand #xff len))))
- codes)
- (+ addr 4))))))
- (else
- (error "write-table: unrecognized object" x))))
-
- (receive (codes addr)
- (vhash-fold-right2 (lambda (obj idx code addr)
- ;; The vector is on the stack. Dup it, push
- ;; the index, push the val, then vector-set.
- (let ((pre `((dup)
- ,(object->assembly idx))))
- (receive (valcode addr) (dump1 obj idx
- (addr+ addr pre))
- (values (cons* '((vector-set))
- valcode
- pre
- code)
- (1+ addr)))))
- constants
- '(((assert-nargs-ee/locals 1)
- ;; Push the vector.
- (local-ref 0)))
- 4)
- (let* ((len (1+ (vlist-length constants)))
- (pre-prog-addr (+ 2 ; reserve-locals
- len 3 ; empty vector
- 2 ; local-set
- 1 ; new-frame
- 2 ; local-ref
- ))
- (prog (align-program
- `(load-program ()
- ,(+ addr 1)
- #f
- ;; The `return' will be at the tail of the
- ;; program. The vector is already pushed
- ;; on the stack.
- . ,(fold append '((return)) codes))
- pre-prog-addr)))
- (values `(;; Reserve storage for the vector.
- (assert-nargs-ee/locals ,(logior 0 (ash 1 3)))
- ;; Push the vector, and store it in slot 0.
- ,@(make-list len '(make-false))
- (vector ,(quotient len 256) ,(modulo len 256))
- (local-set 0)
- ;; Now we open the call frame.
- ;;
- (new-frame)
- ;; Now build a thunk to init the constants. It will
- ;; have the unfinished constant table both as its
- ;; argument and as its objtable. The former allows it
- ;; to update the objtable, with vector-set!, and the
- ;; latter allows init code to refer to previously set
- ;; values.
- ;;
- ;; Grab the vector, to be the objtable.
- (local-ref 0)
- ;; Now the load-program, properly aligned. Pops the vector.
- ,@prog
- ;; Grab the vector, as an argument this time.
- (local-ref 0)
- ;; Call the init thunk with the vector as an arg.
- (call 1)
- ;; The thunk also returns the vector. Leave it on the
- ;; stack for compile-assembly to use.
- )
- ;; The byte length of the init code, which we can
- ;; determine without folding over the code again.
- (+ (addr+ pre-prog-addr prog) ; aligned program
- 2 ; local-ref
- 2 ; call
- )))))
-;;; Guile Lowlevel Intermediate Language
-
-;; Copyright (C) 2001, 2009, 2010, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language glil spec)
- #\use-module (system base language)
- #\use-module (language glil)
- #\use-module (language glil compile-assembly)
- #\export (glil))
-
-(define (write-glil exp . port)
- (apply write (unparse-glil exp) port))
-
-(define (compile-asm x e opts)
- (values (compile-assembly x) e e))
-
-(define-language glil
- #\title "Guile Lowlevel Intermediate Language (GLIL)"
- #\reader (lambda (port env) (read port))
- #\printer write-glil
- #\parser parse-glil
- #\compilers `((assembly . ,compile-asm))
- #\for-humans? #f
- )
-;;; Guile Virtual Machine Object Code
-
-;; Copyright (C) 2001 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language objcode)
- #\export (encode-length decode-length))
-
-
-;;;
-;;; Variable-length interface
-;;;
-
-;; NOTE: decoded in vm_fetch_length in vm.c as well.
-
-(define (encode-length len)
- (cond ((< len 254) (u8vector len))
- ((< len (* 256 256))
- (u8vector 254 (quotient len 256) (modulo len 256)))
- ((< len most-positive-fixnum)
- (u8vector 255
- (quotient len (* 256 256 256))
- (modulo (quotient len (* 256 256)) 256)
- (modulo (quotient len 256) 256)
- (modulo len 256)))
- (else (error "Too long code length:" len))))
-
-(define (decode-length pop)
- (let ((x (pop)))
- (cond ((< x 254) x)
- ((= x 254) (+ (ash x 8) (pop)))
- (else
- (let* ((b2 (pop))
- (b3 (pop))
- (b4 (pop)))
- (+ (ash x 24) (ash b2 16) (ash b3 8) b4))))))
-;;; Guile Lowlevel Intermediate Language
-
-;; Copyright (C) 2001, 2009, 2010, 2011, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language objcode spec)
- #\use-module (system base language)
- #\use-module (system vm objcode)
- #\use-module (system vm program)
- #\export (objcode))
-
-(define (objcode->value x e opts)
- (let ((thunk (make-program x #f #f)))
- (if (eq? e (current-module))
- ;; save a cons in this case
- (values (thunk) e e)
- (save-module-excursion
- (lambda ()
- (set-current-module e)
- (values (thunk) e e))))))
-
-;; since locals are allocated on the stack and can have limited scope,
-;; in many cases we use one local for more than one lexical variable. so
-;; the returned locals set is a list, where element N of the list is
-;; itself a list of bindings for local variable N.
-(define (collapse-locals locs)
- (let lp ((ret '()) (locs locs))
- (if (null? locs)
- (map cdr (sort! ret
- (lambda (x y) (< (car x) (car y)))))
- (let ((b (car locs)))
- (cond
- ((assv-ref ret (binding:index b))
- => (lambda (bindings)
- (append! bindings (list b))
- (lp ret (cdr locs))))
- (else
- (lp (acons (binding:index b) (list b) ret)
- (cdr locs))))))))
-
-(define (decompile-value x env opts)
- (cond
- ((program? x)
- (let ((objs (program-objects x))
- (meta (program-meta x))
- (free-vars (program-free-variables x))
- (binds (program-bindings x))
- (srcs (program-sources x)))
- (let ((blocs (and binds (collapse-locals binds))))
- (values (program-objcode x)
- `((objects . ,objs)
- (meta . ,(and meta (meta)))
- (free-vars . ,free-vars)
- (blocs . ,blocs)
- (sources . ,srcs))))))
- ((objcode? x)
- (values x #f))
- (else
- (error "Object for disassembly not a program or objcode" x))))
-
-(define-language objcode
- #\title "Guile Object Code"
- #\reader #f
- #\printer write-objcode
- #\compilers `((value . ,objcode->value))
- #\decompilers `((value . ,decompile-value))
- #\for-humans? #f
- )
-;;; Guile Scheme specification
-
-;; Copyright (C) 2001, 2009, 2010 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language scheme compile-tree-il)
- #\use-module (language tree-il)
- #\export (compile-tree-il))
-
-;;; environment := MODULE
-
-(define (compile-tree-il x e opts)
- (save-module-excursion
- (lambda ()
- (set-current-module e)
- (let* ((x (macroexpand x 'c '(compile load eval)))
- (cenv (current-module)))
- (values x cenv cenv)))))
-;;; Guile VM code converters
-
-;; Copyright (C) 2001, 2009, 2012, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language scheme decompile-tree-il)
- #\use-module (language tree-il)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-26)
- #\use-module (ice-9 receive)
- #\use-module (ice-9 vlist)
- #\use-module (ice-9 match)
- #\use-module (system base syntax)
- #\export (decompile-tree-il))
-
-(define (decompile-tree-il e env opts)
- (apply do-decompile e env opts))
-
-(define* (do-decompile e env
- #\key
- (use-derived-syntax? #t)
- (avoid-lambda? #t)
- (use-case? #t)
- (strip-numeric-suffixes? #f)
- #\allow-other-keys)
-
- (receive (output-name-table occurrence-count-table)
- (choose-output-names e use-derived-syntax? strip-numeric-suffixes?)
-
- (define (output-name s) (hashq-ref output-name-table s))
- (define (occurrence-count s) (hashq-ref occurrence-count-table s))
-
- (define (const x) (lambda (_) x))
- (define (atom? x) (not (or (pair? x) (vector? x))))
-
- (define (build-void) '(if #f #f))
-
- (define (build-begin es)
- (match es
- (() (build-void))
- ((e) e)
- (_ `(begin ,@es))))
-
- (define (build-lambda-body e)
- (match e
- (('let () body ...) body)
- (('begin es ...) es)
- (_ (list e))))
-
- (define (build-begin-body e)
- (match e
- (('begin es ...) es)
- (_ (list e))))
-
- (define (build-define name e)
- (match e
- ((? (const avoid-lambda?)
- ('lambda formals body ...))
- `(define (,name ,@formals) ,@body))
- ((? (const avoid-lambda?)
- ('lambda* formals body ...))
- `(define* (,name ,@formals) ,@body))
- (_ `(define ,name ,e))))
-
- (define (build-let names vals body)
- (match `(let ,(map list names vals)
- ,@(build-lambda-body body))
- ((_ () e) e)
- ((_ (b) ('let* (bs ...) body ...))
- `(let* (,b ,@bs) ,@body))
- ((? (const use-derived-syntax?)
- (_ (b1) ('let (b2) body ...)))
- `(let* (,b1 ,b2) ,@body))
- (e e)))
-
- (define (build-letrec in-order? names vals body)
- (match `(,(if in-order? 'letrec* 'letrec)
- ,(map list names vals)
- ,@(build-lambda-body body))
- ((_ () e) e)
- ((_ () body ...) `(let () ,@body))
- ((_ ((name ('lambda (formals ...) body ...)))
- (name args ...))
- (=> failure)
- (if (= (length formals) (length args))
- `(let ,name ,(map list formals args) ,@body)
- (failure)))
- ((? (const avoid-lambda?)
- ('letrec* _ body ...))
- `(let ()
- ,@(map build-define names vals)
- ,@body))
- (e e)))
-
- (define (build-if test consequent alternate)
- (match alternate
- (('if #f _) `(if ,test ,consequent))
- (_ `(if ,test ,consequent ,alternate))))
-
- (define (build-and xs)
- (match xs
- (() #t)
- ((x) x)
- (_ `(and ,@xs))))
-
- (define (build-or xs)
- (match xs
- (() #f)
- ((x) x)
- (_ `(or ,@xs))))
-
- (define (case-test-var test)
- (match test
- (('memv (? atom? v) ('quote (datums ...)))
- v)
- (('eqv? (? atom? v) ('quote datum))
- v)
- (_ #f)))
-
- (define (test->datums v test)
- (match (cons v test)
- ((v 'memv v ('quote (xs ...)))
- xs)
- ((v 'eqv? v ('quote x))
- (list x))
- (_ #f)))
-
- (define (build-else-tail e)
- (match e
- (('if #f _) '())
- (('and xs ... x) `((,(build-and xs) ,@(build-begin-body x))
- (else #f)))
- (_ `((else ,@(build-begin-body e))))))
-
- (define (build-cond-else-tail e)
- (match e
- (('cond clauses ...) clauses)
- (_ (build-else-tail e))))
-
- (define (build-case-else-tail v e)
- (match (cons v e)
- ((v 'case v clauses ...)
- clauses)
- ((v 'if ('memv v ('quote (xs ...))) consequent . alternate*)
- `((,xs ,@(build-begin-body consequent))
- ,@(build-case-else-tail v (build-begin alternate*))))
- ((v 'if ('eqv? v ('quote x)) consequent . alternate*)
- `(((,x) ,@(build-begin-body consequent))
- ,@(build-case-else-tail v (build-begin alternate*))))
- (_ (build-else-tail e))))
-
- (define (clauses+tail clauses)
- (match clauses
- ((cs ... (and c ('else . _))) (values cs (list c)))
- (_ (values clauses '()))))
-
- (define (build-cond tests consequents alternate)
- (case (length tests)
- ((0) alternate)
- ((1) (build-if (car tests) (car consequents) alternate))
- (else `(cond ,@(map (lambda (test consequent)
- `(,test ,@(build-begin-body consequent)))
- tests consequents)
- ,@(build-cond-else-tail alternate)))))
-
- (define (build-cond-or-case tests consequents alternate)
- (if (not use-case?)
- (build-cond tests consequents alternate)
- (let* ((v (and (not (null? tests))
- (case-test-var (car tests))))
- (datum-lists (take-while identity
- (map (cut test->datums v <>)
- tests)))
- (n (length datum-lists))
- (tail (build-case-else-tail v (build-cond
- (drop tests n)
- (drop consequents n)
- alternate))))
- (receive (clauses tail) (clauses+tail tail)
- (let ((n (+ n (length clauses)))
- (datum-lists (append datum-lists
- (map car clauses)))
- (consequents (append consequents
- (map build-begin
- (map cdr clauses)))))
- (if (< n 2)
- (build-cond tests consequents alternate)
- `(case ,v
- ,@(map cons datum-lists (map build-begin-body
- (take consequents n)))
- ,@tail)))))))
-
- (define (recurse e)
-
- (define (recurse-body e)
- (build-lambda-body (recurse e)))
-
- (record-case e
- ((<void>)
- (build-void))
-
- ((<const> exp)
- (if (and (self-evaluating? exp) (not (vector? exp)))
- exp
- `(quote ,exp)))
-
- ((<sequence> exps)
- (build-begin (map recurse exps)))
-
- ((<application> proc args)
- (match `(,(recurse proc) ,@(map recurse args))
- ((('lambda (formals ...) body ...) args ...)
- (=> failure)
- (if (= (length formals) (length args))
- (build-let formals args (build-begin body))
- (failure)))
- (e e)))
-
- ((<primitive-ref> name)
- name)
-
- ((<lexical-ref> gensym)
- (output-name gensym))
-
- ((<lexical-set> gensym exp)
- `(set! ,(output-name gensym) ,(recurse exp)))
-
- ((<module-ref> mod name public?)
- `(,(if public? '@ '@@) ,mod ,name))
-
- ((<module-set> mod name public? exp)
- `(set! (,(if public? '@ '@@) ,mod ,name) ,(recurse exp)))
-
- ((<toplevel-ref> name)
- name)
-
- ((<toplevel-set> name exp)
- `(set! ,name ,(recurse exp)))
-
- ((<toplevel-define> name exp)
- (build-define name (recurse exp)))
-
- ((<lambda> meta body)
- (if body
- (let ((body (recurse body))
- (doc (assq-ref meta 'documentation)))
- (if (not doc)
- body
- (match body
- (('lambda formals body ...)
- `(lambda ,formals ,doc ,@body))
- (('lambda* formals body ...)
- `(lambda* ,formals ,doc ,@body))
- (('case-lambda (formals body ...) clauses ...)
- `(case-lambda (,formals ,doc ,@body) ,@clauses))
- (('case-lambda* (formals body ...) clauses ...)
- `(case-lambda* (,formals ,doc ,@body) ,@clauses))
- (e e))))
- '(case-lambda)))
-
- ((<lambda-case> req opt rest kw inits gensyms body alternate)
- (let ((names (map output-name gensyms)))
- (cond
- ((and (not opt) (not kw) (not alternate))
- `(lambda ,(if rest (apply cons* names) names)
- ,@(recurse-body body)))
- ((and (not opt) (not kw))
- (let ((alt-expansion (recurse alternate))
- (formals (if rest (apply cons* names) names)))
- (case (car alt-expansion)
- ((lambda)
- `(case-lambda (,formals ,@(recurse-body body))
- ,(cdr alt-expansion)))
- ((lambda*)
- `(case-lambda* (,formals ,@(recurse-body body))
- ,(cdr alt-expansion)))
- ((case-lambda)
- `(case-lambda (,formals ,@(recurse-body body))
- ,@(cdr alt-expansion)))
- ((case-lambda*)
- `(case-lambda* (,formals ,@(recurse-body body))
- ,@(cdr alt-expansion))))))
- (else
- (let* ((alt-expansion (and alternate (recurse alternate)))
- (nreq (length req))
- (nopt (if opt (length opt) 0))
- (restargs (if rest (list-ref names (+ nreq nopt)) '()))
- (reqargs (list-head names nreq))
- (optargs (if opt
- `(#\optional
- ,@(map list
- (list-head (list-tail names nreq) nopt)
- (map recurse
- (list-head inits nopt))))
- '()))
- (kwargs (if kw
- `(#\key
- ,@(map list
- (map output-name (map caddr (cdr kw)))
- (map recurse
- (list-tail inits nopt))
- (map car (cdr kw)))
- ,@(if (car kw)
- '(#\allow-other-keys)
- '()))
- '()))
- (formals `(,@reqargs ,@optargs ,@kwargs . ,restargs)))
- (if (not alt-expansion)
- `(lambda* ,formals ,@(recurse-body body))
- (case (car alt-expansion)
- ((lambda lambda*)
- `(case-lambda* (,formals ,@(recurse-body body))
- ,(cdr alt-expansion)))
- ((case-lambda case-lambda*)
- `(case-lambda* (,formals ,@(recurse-body body))
- ,@(cdr alt-expansion))))))))))
-
- ((<conditional> test consequent alternate)
- (define (simplify-test e)
- (match e
- (('if ('eqv? (? atom? v) ('quote a)) #t ('eqv? v ('quote b)))
- `(memv ,v '(,a ,b)))
- (('if ('eqv? (? atom? v) ('quote a)) #t ('memv v ('quote (bs ...))))
- `(memv ,v '(,a ,@bs)))
- (('case (? atom? v)
- ((datum) #t) ...
- ('else ('eqv? v ('quote last-datum))))
- `(memv ,v '(,@datum ,last-datum)))
- (_ e)))
- (match `(if ,(simplify-test (recurse test))
- ,(recurse consequent)
- ,@(if (void? alternate) '()
- (list (recurse alternate))))
- (('if test ('if ('and xs ...) consequent))
- (build-if (build-and (cons test xs))
- consequent
- (build-void)))
- ((? (const use-derived-syntax?)
- ('if test1 ('if test2 consequent)))
- (build-if (build-and (list test1 test2))
- consequent
- (build-void)))
- (('if (? atom? x) x ('or ys ...))
- (build-or (cons x ys)))
- ((? (const use-derived-syntax?)
- ('if (? atom? x) x y))
- (build-or (list x y)))
- (('if test consequent)
- `(if ,test ,consequent))
- (('if test ('and xs ...) #f)
- (build-and (cons test xs)))
- ((? (const use-derived-syntax?)
- ('if test consequent #f))
- (build-and (list test consequent)))
- ((? (const use-derived-syntax?)
- ('if test1 consequent1
- ('if test2 consequent2 . alternate*)))
- (build-cond-or-case (list test1 test2)
- (list consequent1 consequent2)
- (build-begin alternate*)))
- (('if test consequent ('cond clauses ...))
- `(cond (,test ,@(build-begin-body consequent))
- ,@clauses))
- (('if ('memv (? atom? v) ('quote (xs ...))) consequent
- ('case v clauses ...))
- `(case ,v (,xs ,@(build-begin-body consequent))
- ,@clauses))
- (('if ('eqv? (? atom? v) ('quote x)) consequent
- ('case v clauses ...))
- `(case ,v ((,x) ,@(build-begin-body consequent))
- ,@clauses))
- (e e)))
-
- ((<let> gensyms vals body)
- (match (build-let (map output-name gensyms)
- (map recurse vals)
- (recurse body))
- (('let ((v e)) ('or v xs ...))
- (=> failure)
- (if (and (not (null? gensyms))
- (= 3 (occurrence-count (car gensyms))))
- `(or ,e ,@xs)
- (failure)))
- (('let ((v e)) ('case v clauses ...))
- (=> failure)
- (if (and (not (null? gensyms))
- ;; FIXME: This fails if any of the 'memv's were
- ;; optimized into multiple 'eqv?'s, because the
- ;; occurrence count will be higher than we expect.
- (= (occurrence-count (car gensyms))
- (1+ (length (clauses+tail clauses)))))
- `(case ,e ,@clauses)
- (failure)))
- (e e)))
-
- ((<letrec> in-order? gensyms vals body)
- (build-letrec in-order?
- (map output-name gensyms)
- (map recurse vals)
- (recurse body)))
-
- ((<fix> gensyms vals body)
- ;; not a typo, we really do translate back to letrec. use letrec* since it
- ;; doesn't matter, and the naive letrec* transformation does not require an
- ;; inner let.
- (build-letrec #t
- (map output-name gensyms)
- (map recurse vals)
- (recurse body)))
-
- ((<let-values> exp body)
- `(call-with-values (lambda () ,@(recurse-body exp))
- ,(recurse (make-lambda #f '() body))))
-
- ((<dynwind> body winder unwinder)
- `(dynamic-wind ,(recurse winder)
- (lambda () ,@(recurse-body body))
- ,(recurse unwinder)))
-
- ((<dynlet> fluids vals body)
- `(with-fluids ,(map list
- (map recurse fluids)
- (map recurse vals))
- ,@(recurse-body body)))
-
- ((<dynref> fluid)
- `(fluid-ref ,(recurse fluid)))
-
- ((<dynset> fluid exp)
- `(fluid-set! ,(recurse fluid) ,(recurse exp)))
-
- ((<prompt> tag body handler)
- `(call-with-prompt
- ,(recurse tag)
- (lambda () ,@(recurse-body body))
- ,(recurse handler)))
-
-
- ((<abort> tag args tail)
- `(apply abort ,(recurse tag) ,@(map recurse args)
- ,(recurse tail)))))
- (values (recurse e) env)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Algorithm for choosing better variable names
-;; ============================================
-;;
-;; First we perform an analysis pass, collecting the following
-;; information:
-;;
-;; * For each gensym: how many occurrences will occur in the output?
-;;
-;; * For each gensym A: which gensyms does A conflict with? Gensym A
-;; and gensym B conflict if they have the same base name (usually the
-;; same as the source name, but see below), and if giving them the
-;; same name would cause a bad variable reference due to unintentional
-;; variable capture.
-;;
-;; The occurrence counter is indexed by gensym and is global (within each
-;; invocation of the algorithm), implemented using a hash table. We also
-;; keep a global mapping from gensym to source name as provided by the
-;; binding construct (we prefer not to trust the source names in the
-;; lexical ref or set).
-;;
-;; As we recurse down into lexical binding forms, we keep track of a
-;; mapping from base name to an ordered list of bindings, innermost
-;; first. When we encounter a variable occurrence, we increment the
-;; counter, look up the base name (preferring not to trust the 'name' in
-;; the lexical ref or set), and then look up the bindings currently in
-;; effect for that base name. Hopefully our gensym will be the first
-;; (innermost) binding. If not, we register a conflict between the
-;; referenced gensym and the other bound gensyms with the same base name
-;; that shadow the binding we want. These are simply the gensyms on the
-;; binding list that come before our gensym.
-;;
-;; Top-level bindings are treated specially. Whenever top-level
-;; references are found, they conflict with every lexical binding
-;; currently in effect with the same base name. They are guaranteed to
-;; be assigned to their source names. For purposes of recording
-;; conflicts (which are normally keyed on gensyms) top-level identifiers
-;; are assigned a pseudo-gensym that is an interned pair of the form
-;; (top-level . <name>). This allows them to be compared using 'eq?'
-;; like other gensyms.
-;;
-;; The base name is normally just the source name. However, if the
-;; source name has a suffix of the form "-N" (where N is a positive
-;; integer without leading zeroes), then we strip that suffix (multiple
-;; times if necessary) to form the base name. We must do this because
-;; we add suffixes of that form in order to resolve conflicts, and we
-;; must ensure that only identifiers with the same base name can
-;; possibly conflict with each other.
-;;
-;; XXX FIXME: Currently, primitives are treated exactly like top-level
-;; bindings. This handles conflicting lexical bindings properly, but
-;; does _not_ handle the case where top-level bindings conflict with the
-;; needed primitives.
-;;
-;; Also note that this requires that 'choose-output-names' be kept in
-;; sync with 'tree-il->scheme'. Primitives that are introduced by
-;; 'tree-il->scheme' must be anticipated by 'choose-output-name'.
-;;
-;; We also ensure that lexically-bound identifiers found in operator
-;; position will never be assigned one of the standard primitive names.
-;; This is needed because 'tree-il->scheme' recognizes primitive names
-;; in operator position and assumes that they have the standard
-;; bindings.
-;;
-;;
-;; How we assign an output name to each gensym
-;; ===========================================
-;;
-;; We process the gensyms in order of decreasing occurrence count, with
-;; each gensym choosing the best output name possible, as long as it
-;; isn't the same name as any of the previously-chosen output names of
-;; conflicting gensyms.
-;;
-
-
-;;
-;; 'choose-output-names' analyzes the top-level form e, chooses good
-;; variable names that are as close as possible to the source names,
-;; and returns two values:
-;;
-;; * a hash table mapping gensym to output name
-;; * a hash table mapping gensym to number of occurrences
-;;
-(define choose-output-names
- (let ()
- (define primitive?
- ;; This is a list of primitives that 'tree-il->scheme' assumes
- ;; will have the standard bindings when found in operator
- ;; position.
- (let* ((primitives '(if quote @ @@ set! define define*
- begin let let* letrec letrec*
- and or cond case
- lambda lambda* case-lambda case-lambda*
- apply call-with-values dynamic-wind
- with-fluids fluid-ref fluid-set!
- call-with-prompt abort memv eqv?))
- (table (make-hash-table (length primitives))))
- (for-each (cut hashq-set! table <> #t) primitives)
- (lambda (name) (hashq-ref table name))))
-
- ;; Repeatedly strip suffix of the form "-N", where N is a string
- ;; that could be produced by number->string given a positive
- ;; integer. In other words, the first digit of N may not be 0.
- (define compute-base-name
- (let ((digits (string->char-set "0123456789")))
- (define (base-name-string str)
- (let ((i (string-skip-right str digits)))
- (if (and i (< (1+ i) (string-length str))
- (eq? #\- (string-ref str i))
- (not (eq? #\0 (string-ref str (1+ i)))))
- (base-name-string (substring str 0 i))
- str)))
- (lambda (sym)
- (string->symbol (base-name-string (symbol->string sym))))))
-
- ;; choose-output-names
- (lambda (e use-derived-syntax? strip-numeric-suffixes?)
-
- (define lexical-gensyms '())
-
- (define top-level-intern!
- (let ((table (make-hash-table)))
- (lambda (name)
- (let ((h (hashq-create-handle! table name #f)))
- (or (cdr h) (begin (set-cdr! h (cons 'top-level name))
- (cdr h)))))))
- (define (top-level? s) (pair? s))
- (define (top-level-name s) (cdr s))
-
- (define occurrence-count-table (make-hash-table))
- (define (occurrence-count s) (or (hashq-ref occurrence-count-table s) 0))
- (define (increment-occurrence-count! s)
- (let ((h (hashq-create-handle! occurrence-count-table s 0)))
- (if (zero? (cdr h))
- (set! lexical-gensyms (cons s lexical-gensyms)))
- (set-cdr! h (1+ (cdr h)))))
-
- (define base-name
- (let ((table (make-hash-table)))
- (lambda (name)
- (let ((h (hashq-create-handle! table name #f)))
- (or (cdr h) (begin (set-cdr! h (compute-base-name name))
- (cdr h)))))))
-
- (define source-name-table (make-hash-table))
- (define (set-source-name! s name)
- (if (not (top-level? s))
- (let ((name (if strip-numeric-suffixes?
- (base-name name)
- name)))
- (hashq-set! source-name-table s name))))
- (define (source-name s)
- (if (top-level? s)
- (top-level-name s)
- (hashq-ref source-name-table s)))
-
- (define conflict-table (make-hash-table))
- (define (conflicts s) (or (hashq-ref conflict-table s) '()))
- (define (add-conflict! a b)
- (define (add! a b)
- (if (not (top-level? a))
- (let ((h (hashq-create-handle! conflict-table a '())))
- (if (not (memq b (cdr h)))
- (set-cdr! h (cons b (cdr h)))))))
- (add! a b)
- (add! b a))
-
- (let recurse-with-bindings ((e e) (bindings vlist-null))
- (let recurse ((e e))
-
- ;; We call this whenever we encounter a top-level ref or set
- (define (top-level name)
- (let ((bname (base-name name)))
- (let ((s (top-level-intern! name))
- (conflicts (vhash-foldq* cons '() bname bindings)))
- (for-each (cut add-conflict! s <>) conflicts))))
-
- ;; We call this whenever we encounter a primitive reference.
- ;; We must also call it for every primitive that might be
- ;; inserted by 'tree-il->scheme'. It is okay to call this
- ;; even when 'tree-il->scheme' will not insert the named
- ;; primitive; the worst that will happen is for a lexical
- ;; variable of the same name to be renamed unnecessarily.
- (define (primitive name) (top-level name))
-
- ;; We call this whenever we encounter a lexical ref or set.
- (define (lexical s)
- (increment-occurrence-count! s)
- (let ((conflicts
- (take-while
- (lambda (s*) (not (eq? s s*)))
- (reverse! (vhash-foldq* cons
- '()
- (base-name (source-name s))
- bindings)))))
- (for-each (cut add-conflict! s <>) conflicts)))
-
- (record-case e
- ((<void>) (primitive 'if)) ; (if #f #f)
- ((<const>) (primitive 'quote))
-
- ((<application> proc args)
- (if (lexical-ref? proc)
- (let* ((gensym (lexical-ref-gensym proc))
- (name (source-name gensym)))
- ;; If the operator position contains a bare variable
- ;; reference with the same source name as a standard
- ;; primitive, we must ensure that it will be given a
- ;; different name, so that 'tree-il->scheme' will not
- ;; misinterpret the resulting expression.
- (if (primitive? name)
- (add-conflict! gensym (top-level-intern! name)))))
- (recurse proc)
- (for-each recurse args))
-
- ((<primitive-ref> name) (primitive name))
-
- ((<lexical-ref> gensym) (lexical gensym))
- ((<lexical-set> gensym exp)
- (primitive 'set!) (lexical gensym) (recurse exp))
-
- ((<module-ref> public?) (primitive (if public? '@ '@@)))
- ((<module-set> public? exp)
- (primitive 'set!) (primitive (if public? '@ '@@)) (recurse exp))
-
- ((<toplevel-ref> name) (top-level name))
- ((<toplevel-set> name exp)
- (primitive 'set!) (top-level name) (recurse exp))
- ((<toplevel-define> name exp) (top-level name) (recurse exp))
-
- ((<conditional> test consequent alternate)
- (cond (use-derived-syntax?
- (primitive 'and) (primitive 'or)
- (primitive 'cond) (primitive 'case)
- (primitive 'else) (primitive '=>)))
- (primitive 'if)
- (recurse test) (recurse consequent) (recurse alternate))
-
- ((<sequence> exps) (primitive 'begin) (for-each recurse exps))
- ((<lambda> body)
- (if body (recurse body) (primitive 'case-lambda)))
-
- ((<lambda-case> req opt rest kw inits gensyms body alternate)
- (primitive 'lambda)
- (cond ((or opt kw alternate)
- (primitive 'lambda*)
- (primitive 'case-lambda)
- (primitive 'case-lambda*)))
- (primitive 'let)
- (if use-derived-syntax? (primitive 'let*))
- (let* ((names (append req (or opt '()) (if rest (list rest) '())
- (map cadr (if kw (cdr kw) '()))))
- (base-names (map base-name names))
- (body-bindings
- (fold vhash-consq bindings base-names gensyms)))
- (for-each increment-occurrence-count! gensyms)
- (for-each set-source-name! gensyms names)
- (for-each recurse inits)
- (recurse-with-bindings body body-bindings)
- (if alternate (recurse alternate))))
-
- ((<let> names gensyms vals body)
- (primitive 'let)
- (cond (use-derived-syntax? (primitive 'let*) (primitive 'or)))
- (for-each increment-occurrence-count! gensyms)
- (for-each set-source-name! gensyms names)
- (for-each recurse vals)
- (recurse-with-bindings
- body (fold vhash-consq bindings (map base-name names) gensyms)))
-
- ((<letrec> in-order? names gensyms vals body)
- (primitive 'let)
- (cond (use-derived-syntax? (primitive 'let*) (primitive 'or)))
- (primitive (if in-order? 'letrec* 'letrec))
- (for-each increment-occurrence-count! gensyms)
- (for-each set-source-name! gensyms names)
- (let* ((base-names (map base-name names))
- (bindings (fold vhash-consq bindings base-names gensyms)))
- (for-each (cut recurse-with-bindings <> bindings) vals)
- (recurse-with-bindings body bindings)))
-
- ((<fix> names gensyms vals body)
- (primitive 'let)
- (primitive 'letrec*)
- (cond (use-derived-syntax? (primitive 'let*) (primitive 'or)))
- (for-each increment-occurrence-count! gensyms)
- (for-each set-source-name! gensyms names)
- (let* ((base-names (map base-name names))
- (bindings (fold vhash-consq bindings base-names gensyms)))
- (for-each (cut recurse-with-bindings <> bindings) vals)
- (recurse-with-bindings body bindings)))
-
- ((<let-values> exp body)
- (primitive 'call-with-values)
- (recurse exp) (recurse body))
-
- ((<dynwind> winder body unwinder)
- (primitive 'dynamic-wind)
- (recurse winder) (recurse body) (recurse unwinder))
-
- ((<dynlet> fluids vals body)
- (primitive 'with-fluids)
- (for-each recurse fluids)
- (for-each recurse vals)
- (recurse body))
-
- ((<dynref> fluid) (primitive 'fluid-ref) (recurse fluid))
- ((<dynset> fluid exp)
- (primitive 'fluid-set!) (recurse fluid) (recurse exp))
-
- ((<prompt> tag body handler)
- (primitive 'call-with-prompt)
- (primitive 'lambda)
- (recurse tag) (recurse body) (recurse handler))
-
- ((<abort> tag args tail)
- (primitive 'apply)
- (primitive 'abort)
- (recurse tag) (for-each recurse args) (recurse tail)))))
-
- (let ()
- (define output-name-table (make-hash-table))
- (define (set-output-name! s name)
- (hashq-set! output-name-table s name))
- (define (output-name s)
- (if (top-level? s)
- (top-level-name s)
- (hashq-ref output-name-table s)))
-
- (define sorted-lexical-gensyms
- (sort-list lexical-gensyms
- (lambda (a b) (> (occurrence-count a)
- (occurrence-count b)))))
-
- (for-each (lambda (s)
- (set-output-name!
- s
- (let ((the-conflicts (conflicts s))
- (the-source-name (source-name s)))
- (define (not-yet-taken? name)
- (not (any (lambda (s*)
- (and=> (output-name s*)
- (cut eq? name <>)))
- the-conflicts)))
- (if (not-yet-taken? the-source-name)
- the-source-name
- (let ((prefix (string-append
- (symbol->string the-source-name)
- "-")))
- (let loop ((i 1) (name the-source-name))
- (if (not-yet-taken? name)
- name
- (loop (+ i 1)
- (string->symbol
- (string-append
- prefix
- (number->string i)))))))))))
- sorted-lexical-gensyms)
- (values output-name-table occurrence-count-table)))))
-;;; Guile Scheme specification
-
-;; Copyright (C) 2001, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language scheme spec)
- #\use-module (system base compile)
- #\use-module (system base language)
- #\use-module (language scheme compile-tree-il)
- #\use-module (language scheme decompile-tree-il)
- #\export (scheme))
-
-;;;
-;;; Language definition
-;;;
-
-(define-language scheme
- #\title "Scheme"
- #\reader (lambda (port env)
- ;; Use the binding of current-reader from the environment.
- ;; FIXME: Handle `read-options' as well?
- ((or (and=> (and=> (module-variable env 'current-reader)
- variable-ref)
- fluid-ref)
- read)
- port))
-
- #\compilers `((tree-il . ,compile-tree-il))
- #\decompilers `((tree-il . ,decompile-tree-il))
- #\evaluator (lambda (x module) (primitive-eval x))
- #\printer write
- #\make-default-environment
- (lambda ()
- ;; Ideally we'd duplicate the whole module hierarchy so that `set!',
- ;; `fluid-set!', etc. don't have any effect in the current environment.
- (let ((m (make-fresh-user-module)))
- ;; Provide a separate `current-reader' fluid so that
- ;; compile-time changes to `current-reader' are
- ;; limited to the current compilation unit.
- (module-define! m 'current-reader (make-fluid))
-
- ;; Default to `simple-format', as is the case until
- ;; (ice-9 format) is loaded. This allows
- ;; compile-time warnings to be emitted when using
- ;; unsupported options.
- (module-set! m 'format simple-format)
-
- m)))
-;;;; Copyright (C) 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-(define-module (language tree-il)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-11)
- #\use-module (system base pmatch)
- #\use-module (system base syntax)
- #\export (tree-il-src
-
- <void> void? make-void void-src
- <const> const? make-const const-src const-exp
- <primitive-ref> primitive-ref? make-primitive-ref primitive-ref-src primitive-ref-name
- <lexical-ref> lexical-ref? make-lexical-ref lexical-ref-src lexical-ref-name lexical-ref-gensym
- <lexical-set> lexical-set? make-lexical-set lexical-set-src lexical-set-name lexical-set-gensym lexical-set-exp
- <module-ref> module-ref? make-module-ref module-ref-src module-ref-mod module-ref-name module-ref-public?
- <module-set> module-set? make-module-set module-set-src module-set-mod module-set-name module-set-public? module-set-exp
- <toplevel-ref> toplevel-ref? make-toplevel-ref toplevel-ref-src toplevel-ref-name
- <toplevel-set> toplevel-set? make-toplevel-set toplevel-set-src toplevel-set-name toplevel-set-exp
- <toplevel-define> toplevel-define? make-toplevel-define toplevel-define-src toplevel-define-name toplevel-define-exp
- <conditional> conditional? make-conditional conditional-src conditional-test conditional-consequent conditional-alternate
- <application> application? make-application application-src application-proc application-args
- <sequence> sequence? make-sequence sequence-src sequence-exps
- <lambda> lambda? make-lambda lambda-src lambda-meta lambda-body
- <lambda-case> lambda-case? make-lambda-case lambda-case-src
- lambda-case-req lambda-case-opt lambda-case-rest lambda-case-kw
- lambda-case-inits lambda-case-gensyms
- lambda-case-body lambda-case-alternate
- <let> let? make-let let-src let-names let-gensyms let-vals let-body
- <letrec> letrec? make-letrec letrec-src letrec-in-order? letrec-names letrec-gensyms letrec-vals letrec-body
- <fix> fix? make-fix fix-src fix-names fix-gensyms fix-vals fix-body
- <let-values> let-values? make-let-values let-values-src let-values-exp let-values-body
- <dynwind> dynwind? make-dynwind dynwind-src dynwind-winder dynwind-body dynwind-unwinder
- <dynlet> dynlet? make-dynlet dynlet-src dynlet-fluids dynlet-vals dynlet-body
- <dynref> dynref? make-dynref dynref-src dynref-fluid
- <dynset> dynset? make-dynset dynset-src dynset-fluid dynset-exp
- <prompt> prompt? make-prompt prompt-src prompt-tag prompt-body prompt-handler
- <abort> abort? make-abort abort-src abort-tag abort-args abort-tail
-
- parse-tree-il
- unparse-tree-il
- tree-il->scheme
-
- tree-il-fold
- make-tree-il-folder
- post-order!
- pre-order!
-
- tree-il=?
- tree-il-hash))
-
-(define (print-tree-il exp port)
- (format port "#<tree-il ~S>" (unparse-tree-il exp)))
-
-(define-syntax borrow-core-vtables
- (lambda (x)
- (syntax-case x ()
- ((_)
- (let lp ((n 0) (out '()))
- (if (< n (vector-length %expanded-vtables))
- (lp (1+ n)
- (let* ((vtable (vector-ref %expanded-vtables n))
- (stem (struct-ref vtable (+ vtable-offset-user 0)))
- (fields (struct-ref vtable (+ vtable-offset-user 2)))
- (sfields (map
- (lambda (f) (datum->syntax x f))
- fields))
- (type (datum->syntax x (symbol-append '< stem '>)))
- (ctor (datum->syntax x (symbol-append 'make- stem)))
- (pred (datum->syntax x (symbol-append stem '?))))
- (let lp ((n 0) (fields fields)
- (out (cons*
- #`(define (#,ctor #,@sfields)
- (make-struct #,type 0 #,@sfields))
- #`(define (#,pred x)
- (and (struct? x)
- (eq? (struct-vtable x) #,type)))
- #`(struct-set! #,type vtable-index-printer
- print-tree-il)
- #`(define #,type
- (vector-ref %expanded-vtables #,n))
- out)))
- (if (null? fields)
- out
- (lp (1+ n)
- (cdr fields)
- (let ((acc (datum->syntax
- x (symbol-append stem '- (car fields)))))
- (cons #`(define #,acc
- (make-procedure-with-setter
- (lambda (x) (struct-ref x #,n))
- (lambda (x v) (struct-set! x #,n v))))
- out)))))))
- #`(begin #,@(reverse out))))))))
-
-(borrow-core-vtables)
-
- ;; (<void>)
- ;; (<const> exp)
- ;; (<primitive-ref> name)
- ;; (<lexical-ref> name gensym)
- ;; (<lexical-set> name gensym exp)
- ;; (<module-ref> mod name public?)
- ;; (<module-set> mod name public? exp)
- ;; (<toplevel-ref> name)
- ;; (<toplevel-set> name exp)
- ;; (<toplevel-define> name exp)
- ;; (<conditional> test consequent alternate)
- ;; (<application> proc args)
- ;; (<sequence> exps)
- ;; (<lambda> meta body)
- ;; (<lambda-case> req opt rest kw inits gensyms body alternate)
- ;; (<let> names gensyms vals body)
- ;; (<letrec> in-order? names gensyms vals body)
- ;; (<dynlet> fluids vals body)
-
-(define-type (<tree-il> #\common-slots (src) #\printer print-tree-il)
- (<fix> names gensyms vals body)
- (<let-values> exp body)
- (<dynwind> winder body unwinder)
- (<dynref> fluid)
- (<dynset> fluid exp)
- (<prompt> tag body handler)
- (<abort> tag args tail))
-
-
-
-(define (location x)
- (and (pair? x)
- (let ((props (source-properties x)))
- (and (pair? props) props))))
-
-(define (parse-tree-il exp)
- (let ((loc (location exp))
- (retrans (lambda (x) (parse-tree-il x))))
- (pmatch exp
- ((void)
- (make-void loc))
-
- ((apply ,proc . ,args)
- (make-application loc (retrans proc) (map retrans args)))
-
- ((if ,test ,consequent ,alternate)
- (make-conditional loc (retrans test) (retrans consequent) (retrans alternate)))
-
- ((primitive ,name) (guard (symbol? name))
- (make-primitive-ref loc name))
-
- ((lexical ,name) (guard (symbol? name))
- (make-lexical-ref loc name name))
-
- ((lexical ,name ,sym) (guard (symbol? name) (symbol? sym))
- (make-lexical-ref loc name sym))
-
- ((set! (lexical ,name) ,exp) (guard (symbol? name))
- (make-lexical-set loc name name (retrans exp)))
-
- ((set! (lexical ,name ,sym) ,exp) (guard (symbol? name) (symbol? sym))
- (make-lexical-set loc name sym (retrans exp)))
-
- ((@ ,mod ,name) (guard (and-map symbol? mod) (symbol? name))
- (make-module-ref loc mod name #t))
-
- ((set! (@ ,mod ,name) ,exp) (guard (and-map symbol? mod) (symbol? name))
- (make-module-set loc mod name #t (retrans exp)))
-
- ((@@ ,mod ,name) (guard (and-map symbol? mod) (symbol? name))
- (make-module-ref loc mod name #f))
-
- ((set! (@@ ,mod ,name) ,exp) (guard (and-map symbol? mod) (symbol? name))
- (make-module-set loc mod name #f (retrans exp)))
-
- ((toplevel ,name) (guard (symbol? name))
- (make-toplevel-ref loc name))
-
- ((set! (toplevel ,name) ,exp) (guard (symbol? name))
- (make-toplevel-set loc name (retrans exp)))
-
- ((define ,name ,exp) (guard (symbol? name))
- (make-toplevel-define loc name (retrans exp)))
-
- ((lambda ,meta ,body)
- (make-lambda loc meta (retrans body)))
-
- ((lambda-case ((,req ,opt ,rest ,kw ,inits ,gensyms) ,body) ,alternate)
- (make-lambda-case loc req opt rest kw
- (map retrans inits) gensyms
- (retrans body)
- (and=> alternate retrans)))
-
- ((lambda-case ((,req ,opt ,rest ,kw ,inits ,gensyms) ,body))
- (make-lambda-case loc req opt rest kw
- (map retrans inits) gensyms
- (retrans body)
- #f))
-
- ((const ,exp)
- (make-const loc exp))
-
- ((begin . ,exps)
- (make-sequence loc (map retrans exps)))
-
- ((let ,names ,gensyms ,vals ,body)
- (make-let loc names gensyms (map retrans vals) (retrans body)))
-
- ((letrec ,names ,gensyms ,vals ,body)
- (make-letrec loc #f names gensyms (map retrans vals) (retrans body)))
-
- ((letrec* ,names ,gensyms ,vals ,body)
- (make-letrec loc #t names gensyms (map retrans vals) (retrans body)))
-
- ((fix ,names ,gensyms ,vals ,body)
- (make-fix loc names gensyms (map retrans vals) (retrans body)))
-
- ((let-values ,exp ,body)
- (make-let-values loc (retrans exp) (retrans body)))
-
- ((dynwind ,winder ,body ,unwinder)
- (make-dynwind loc (retrans winder) (retrans body) (retrans unwinder)))
-
- ((dynlet ,fluids ,vals ,body)
- (make-dynlet loc (map retrans fluids) (map retrans vals) (retrans body)))
-
- ((dynref ,fluid)
- (make-dynref loc (retrans fluid)))
-
- ((dynset ,fluid ,exp)
- (make-dynset loc (retrans fluid) (retrans exp)))
-
- ((prompt ,tag ,body ,handler)
- (make-prompt loc (retrans tag) (retrans body) (retrans handler)))
-
- ((abort ,tag ,args ,tail)
- (make-abort loc (retrans tag) (map retrans args) (retrans tail)))
-
- (else
- (error "unrecognized tree-il" exp)))))
-
-(define (unparse-tree-il tree-il)
- (record-case tree-il
- ((<void>)
- '(void))
-
- ((<application> proc args)
- `(apply ,(unparse-tree-il proc) ,@(map unparse-tree-il args)))
-
- ((<conditional> test consequent alternate)
- `(if ,(unparse-tree-il test) ,(unparse-tree-il consequent) ,(unparse-tree-il alternate)))
-
- ((<primitive-ref> name)
- `(primitive ,name))
-
- ((<lexical-ref> name gensym)
- `(lexical ,name ,gensym))
-
- ((<lexical-set> name gensym exp)
- `(set! (lexical ,name ,gensym) ,(unparse-tree-il exp)))
-
- ((<module-ref> mod name public?)
- `(,(if public? '@ '@@) ,mod ,name))
-
- ((<module-set> mod name public? exp)
- `(set! (,(if public? '@ '@@) ,mod ,name) ,(unparse-tree-il exp)))
-
- ((<toplevel-ref> name)
- `(toplevel ,name))
-
- ((<toplevel-set> name exp)
- `(set! (toplevel ,name) ,(unparse-tree-il exp)))
-
- ((<toplevel-define> name exp)
- `(define ,name ,(unparse-tree-il exp)))
-
- ((<lambda> meta body)
- (if body
- `(lambda ,meta ,(unparse-tree-il body))
- `(lambda ,meta (lambda-case))))
-
- ((<lambda-case> req opt rest kw inits gensyms body alternate)
- `(lambda-case ((,req ,opt ,rest ,kw ,(map unparse-tree-il inits) ,gensyms)
- ,(unparse-tree-il body))
- . ,(if alternate (list (unparse-tree-il alternate)) '())))
-
- ((<const> exp)
- `(const ,exp))
-
- ((<sequence> exps)
- `(begin ,@(map unparse-tree-il exps)))
-
- ((<let> names gensyms vals body)
- `(let ,names ,gensyms ,(map unparse-tree-il vals) ,(unparse-tree-il body)))
-
- ((<letrec> in-order? names gensyms vals body)
- `(,(if in-order? 'letrec* 'letrec) ,names ,gensyms
- ,(map unparse-tree-il vals) ,(unparse-tree-il body)))
-
- ((<fix> names gensyms vals body)
- `(fix ,names ,gensyms ,(map unparse-tree-il vals) ,(unparse-tree-il body)))
-
- ((<let-values> exp body)
- `(let-values ,(unparse-tree-il exp) ,(unparse-tree-il body)))
-
- ((<dynwind> winder body unwinder)
- `(dynwind ,(unparse-tree-il winder) ,(unparse-tree-il body)
- ,(unparse-tree-il unwinder)))
-
- ((<dynlet> fluids vals body)
- `(dynlet ,(map unparse-tree-il fluids) ,(map unparse-tree-il vals)
- ,(unparse-tree-il body)))
-
- ((<dynref> fluid)
- `(dynref ,(unparse-tree-il fluid)))
-
- ((<dynset> fluid exp)
- `(dynset ,(unparse-tree-il fluid) ,(unparse-tree-il exp)))
-
- ((<prompt> tag body handler)
- `(prompt ,(unparse-tree-il tag) ,(unparse-tree-il body) ,(unparse-tree-il handler)))
-
- ((<abort> tag args tail)
- `(abort ,(unparse-tree-il tag) ,(map unparse-tree-il args)
- ,(unparse-tree-il tail)))))
-
-(define* (tree-il->scheme e #\optional (env #f) (opts '()))
- (values ((@ (language scheme decompile-tree-il)
- decompile-tree-il)
- e env opts)))
-
-
-(define (tree-il-fold leaf down up seed tree)
- "Traverse TREE, calling LEAF on each leaf encountered, DOWN upon descent
-into a sub-tree, and UP when leaving a sub-tree. Each of these procedures is
-invoked as `(PROC TREE SEED)', where TREE is the sub-tree or leaf considered
-and SEED is the current result, intially seeded with SEED.
-
-This is an implementation of `foldts' as described by Andy Wingo in
-``Applications of fold to XML transformation''."
- (let loop ((tree tree)
- (result seed))
- (if (or (null? tree) (pair? tree))
- (fold loop result tree)
- (record-case tree
- ((<lexical-set> exp)
- (up tree (loop exp (down tree result))))
- ((<module-set> exp)
- (up tree (loop exp (down tree result))))
- ((<toplevel-set> exp)
- (up tree (loop exp (down tree result))))
- ((<toplevel-define> exp)
- (up tree (loop exp (down tree result))))
- ((<conditional> test consequent alternate)
- (up tree (loop alternate
- (loop consequent
- (loop test (down tree result))))))
- ((<application> proc args)
- (up tree (loop (cons proc args) (down tree result))))
- ((<sequence> exps)
- (up tree (loop exps (down tree result))))
- ((<lambda> body)
- (let ((result (down tree result)))
- (up tree
- (if body
- (loop body result)
- result))))
- ((<lambda-case> inits body alternate)
- (up tree (if alternate
- (loop alternate
- (loop body (loop inits (down tree result))))
- (loop body (loop inits (down tree result))))))
- ((<let> vals body)
- (up tree (loop body
- (loop vals
- (down tree result)))))
- ((<letrec> vals body)
- (up tree (loop body
- (loop vals
- (down tree result)))))
- ((<fix> vals body)
- (up tree (loop body
- (loop vals
- (down tree result)))))
- ((<let-values> exp body)
- (up tree (loop body (loop exp (down tree result)))))
- ((<dynwind> body winder unwinder)
- (up tree (loop unwinder
- (loop winder
- (loop body (down tree result))))))
- ((<dynlet> fluids vals body)
- (up tree (loop body
- (loop vals
- (loop fluids (down tree result))))))
- ((<dynref> fluid)
- (up tree (loop fluid (down tree result))))
- ((<dynset> fluid exp)
- (up tree (loop exp (loop fluid (down tree result)))))
- ((<prompt> tag body handler)
- (up tree
- (loop tag (loop body (loop handler
- (down tree result))))))
- ((<abort> tag args tail)
- (up tree (loop tail (loop args (loop tag (down tree result))))))
- (else
- (leaf tree result))))))
-
-
-(define-syntax-rule (make-tree-il-folder seed ...)
- (lambda (tree down up seed ...)
- (define (fold-values proc exps seed ...)
- (if (null? exps)
- (values seed ...)
- (let-values (((seed ...) (proc (car exps) seed ...)))
- (fold-values proc (cdr exps) seed ...))))
- (let foldts ((tree tree) (seed seed) ...)
- (let*-values
- (((seed ...) (down tree seed ...))
- ((seed ...)
- (record-case tree
- ((<lexical-set> exp)
- (foldts exp seed ...))
- ((<module-set> exp)
- (foldts exp seed ...))
- ((<toplevel-set> exp)
- (foldts exp seed ...))
- ((<toplevel-define> exp)
- (foldts exp seed ...))
- ((<conditional> test consequent alternate)
- (let*-values (((seed ...) (foldts test seed ...))
- ((seed ...) (foldts consequent seed ...)))
- (foldts alternate seed ...)))
- ((<application> proc args)
- (let-values (((seed ...) (foldts proc seed ...)))
- (fold-values foldts args seed ...)))
- ((<sequence> exps)
- (fold-values foldts exps seed ...))
- ((<lambda> body)
- (if body
- (foldts body seed ...)
- (values seed ...)))
- ((<lambda-case> inits body alternate)
- (let-values (((seed ...) (fold-values foldts inits seed ...)))
- (if alternate
- (let-values (((seed ...) (foldts body seed ...)))
- (foldts alternate seed ...))
- (foldts body seed ...))))
- ((<let> vals body)
- (let*-values (((seed ...) (fold-values foldts vals seed ...)))
- (foldts body seed ...)))
- ((<letrec> vals body)
- (let*-values (((seed ...) (fold-values foldts vals seed ...)))
- (foldts body seed ...)))
- ((<fix> vals body)
- (let*-values (((seed ...) (fold-values foldts vals seed ...)))
- (foldts body seed ...)))
- ((<let-values> exp body)
- (let*-values (((seed ...) (foldts exp seed ...)))
- (foldts body seed ...)))
- ((<dynwind> body winder unwinder)
- (let*-values (((seed ...) (foldts body seed ...))
- ((seed ...) (foldts winder seed ...)))
- (foldts unwinder seed ...)))
- ((<dynlet> fluids vals body)
- (let*-values (((seed ...) (fold-values foldts fluids seed ...))
- ((seed ...) (fold-values foldts vals seed ...)))
- (foldts body seed ...)))
- ((<dynref> fluid)
- (foldts fluid seed ...))
- ((<dynset> fluid exp)
- (let*-values (((seed ...) (foldts fluid seed ...)))
- (foldts exp seed ...)))
- ((<prompt> tag body handler)
- (let*-values (((seed ...) (foldts tag seed ...))
- ((seed ...) (foldts body seed ...)))
- (foldts handler seed ...)))
- ((<abort> tag args tail)
- (let*-values (((seed ...) (foldts tag seed ...))
- ((seed ...) (fold-values foldts args seed ...)))
- (foldts tail seed ...)))
- (else
- (values seed ...)))))
- (up tree seed ...)))))
-
-(define (post-order! f x)
- (let lp ((x x))
- (record-case x
- ((<application> proc args)
- (set! (application-proc x) (lp proc))
- (set! (application-args x) (map lp args)))
-
- ((<conditional> test consequent alternate)
- (set! (conditional-test x) (lp test))
- (set! (conditional-consequent x) (lp consequent))
- (set! (conditional-alternate x) (lp alternate)))
-
- ((<lexical-set> name gensym exp)
- (set! (lexical-set-exp x) (lp exp)))
-
- ((<module-set> mod name public? exp)
- (set! (module-set-exp x) (lp exp)))
-
- ((<toplevel-set> name exp)
- (set! (toplevel-set-exp x) (lp exp)))
-
- ((<toplevel-define> name exp)
- (set! (toplevel-define-exp x) (lp exp)))
-
- ((<lambda> body)
- (if body
- (set! (lambda-body x) (lp body))))
-
- ((<lambda-case> inits body alternate)
- (set! inits (map lp inits))
- (set! (lambda-case-body x) (lp body))
- (if alternate
- (set! (lambda-case-alternate x) (lp alternate))))
-
- ((<sequence> exps)
- (set! (sequence-exps x) (map lp exps)))
-
- ((<let> gensyms vals body)
- (set! (let-vals x) (map lp vals))
- (set! (let-body x) (lp body)))
-
- ((<letrec> gensyms vals body)
- (set! (letrec-vals x) (map lp vals))
- (set! (letrec-body x) (lp body)))
-
- ((<fix> gensyms vals body)
- (set! (fix-vals x) (map lp vals))
- (set! (fix-body x) (lp body)))
-
- ((<let-values> exp body)
- (set! (let-values-exp x) (lp exp))
- (set! (let-values-body x) (lp body)))
-
- ((<dynwind> body winder unwinder)
- (set! (dynwind-body x) (lp body))
- (set! (dynwind-winder x) (lp winder))
- (set! (dynwind-unwinder x) (lp unwinder)))
-
- ((<dynlet> fluids vals body)
- (set! (dynlet-fluids x) (map lp fluids))
- (set! (dynlet-vals x) (map lp vals))
- (set! (dynlet-body x) (lp body)))
-
- ((<dynref> fluid)
- (set! (dynref-fluid x) (lp fluid)))
-
- ((<dynset> fluid exp)
- (set! (dynset-fluid x) (lp fluid))
- (set! (dynset-exp x) (lp exp)))
-
- ((<prompt> tag body handler)
- (set! (prompt-tag x) (lp tag))
- (set! (prompt-body x) (lp body))
- (set! (prompt-handler x) (lp handler)))
-
- ((<abort> tag args tail)
- (set! (abort-tag x) (lp tag))
- (set! (abort-args x) (map lp args))
- (set! (abort-tail x) (lp tail)))
-
- (else #f))
-
- (or (f x) x)))
-
-(define (pre-order! f x)
- (let lp ((x x))
- (let ((x (or (f x) x)))
- (record-case x
- ((<application> proc args)
- (set! (application-proc x) (lp proc))
- (set! (application-args x) (map lp args)))
-
- ((<conditional> test consequent alternate)
- (set! (conditional-test x) (lp test))
- (set! (conditional-consequent x) (lp consequent))
- (set! (conditional-alternate x) (lp alternate)))
-
- ((<lexical-set> exp)
- (set! (lexical-set-exp x) (lp exp)))
-
- ((<module-set> exp)
- (set! (module-set-exp x) (lp exp)))
-
- ((<toplevel-set> exp)
- (set! (toplevel-set-exp x) (lp exp)))
-
- ((<toplevel-define> exp)
- (set! (toplevel-define-exp x) (lp exp)))
-
- ((<lambda> body)
- (if body
- (set! (lambda-body x) (lp body))))
-
- ((<lambda-case> inits body alternate)
- (set! inits (map lp inits))
- (set! (lambda-case-body x) (lp body))
- (if alternate (set! (lambda-case-alternate x) (lp alternate))))
-
- ((<sequence> exps)
- (set! (sequence-exps x) (map lp exps)))
-
- ((<let> vals body)
- (set! (let-vals x) (map lp vals))
- (set! (let-body x) (lp body)))
-
- ((<letrec> vals body)
- (set! (letrec-vals x) (map lp vals))
- (set! (letrec-body x) (lp body)))
-
- ((<fix> vals body)
- (set! (fix-vals x) (map lp vals))
- (set! (fix-body x) (lp body)))
-
- ((<let-values> exp body)
- (set! (let-values-exp x) (lp exp))
- (set! (let-values-body x) (lp body)))
-
- ((<dynwind> body winder unwinder)
- (set! (dynwind-body x) (lp body))
- (set! (dynwind-winder x) (lp winder))
- (set! (dynwind-unwinder x) (lp unwinder)))
-
- ((<dynlet> fluids vals body)
- (set! (dynlet-fluids x) (map lp fluids))
- (set! (dynlet-vals x) (map lp vals))
- (set! (dynlet-body x) (lp body)))
-
- ((<dynref> fluid)
- (set! (dynref-fluid x) (lp fluid)))
-
- ((<dynset> fluid exp)
- (set! (dynset-fluid x) (lp fluid))
- (set! (dynset-exp x) (lp exp)))
-
- ((<prompt> tag body handler)
- (set! (prompt-tag x) (lp tag))
- (set! (prompt-body x) (lp body))
- (set! (prompt-handler x) (lp handler)))
-
- ((<abort> tag args tail)
- (set! (abort-tag x) (lp tag))
- (set! (abort-args x) (map lp args))
- (set! (abort-tail x) (lp tail)))
-
- (else #f))
- x)))
-
-;; FIXME: We should have a better primitive than this.
-(define (struct-nfields x)
- (/ (string-length (symbol->string (struct-layout x))) 2))
-
-(define (tree-il=? a b)
- (cond
- ((struct? a)
- (and (struct? b)
- (eq? (struct-vtable a) (struct-vtable b))
- ;; Assume that all structs are tree-il, so we skip over the
- ;; src slot.
- (let lp ((n (1- (struct-nfields a))))
- (or (zero? n)
- (and (tree-il=? (struct-ref a n) (struct-ref b n))
- (lp (1- n)))))))
- ((pair? a)
- (and (pair? b)
- (tree-il=? (car a) (car b))
- (tree-il=? (cdr a) (cdr b))))
- (else
- (equal? a b))))
-
-(define-syntax hash-bits
- (make-variable-transformer
- (lambda (x)
- (syntax-case x ()
- (var
- (identifier? #'var)
- (logcount most-positive-fixnum))))))
-
-(define (tree-il-hash exp)
- (let ((hash-depth 4)
- (hash-width 3))
- (define (hash-exp exp depth)
- (define (rotate x bits)
- (logior (ash x (- bits))
- (ash (logand x (1- (ash 1 bits))) (- hash-bits bits))))
- (define (mix h1 h2)
- (logxor h1 (rotate h2 8)))
- (define (hash-struct s)
- (let ((len (struct-nfields s))
- (h (hashq (struct-vtable s) most-positive-fixnum)))
- (if (zero? depth)
- h
- (let lp ((i (max (- len hash-width) 1)) (h h))
- (if (< i len)
- (lp (1+ i) (mix (hash-exp (struct-ref s i) (1+ depth)) h))
- h)))))
- (define (hash-list l)
- (let ((h (hashq 'list most-positive-fixnum)))
- (if (zero? depth)
- h
- (let lp ((l l) (width 0) (h h))
- (if (< width hash-width)
- (lp (cdr l) (1+ width)
- (mix (hash-exp (car l) (1+ depth)) h))
- h)))))
- (cond
- ((struct? exp) (hash-struct exp))
- ((list? exp) (hash-list exp))
- (else (hash exp most-positive-fixnum))))
-
- (hash-exp exp 0)))
-;;; TREE-IL -> GLIL compiler
-
-;; Copyright (C) 2001, 2008, 2009, 2010, 2011, 2012,
-;; 2014 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language tree-il analyze)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-9)
- #\use-module (srfi srfi-11)
- #\use-module (srfi srfi-26)
- #\use-module (ice-9 vlist)
- #\use-module (ice-9 match)
- #\use-module (system base syntax)
- #\use-module (system base message)
- #\use-module (system vm program)
- #\use-module (language tree-il)
- #\use-module (system base pmatch)
- #\export (analyze-lexicals
- analyze-tree
- unused-variable-analysis
- unused-toplevel-analysis
- unbound-variable-analysis
- arity-analysis
- format-analysis))
-
-;; Allocation is the process of assigning storage locations for lexical
-;; variables. A lexical variable has a distinct "address", or storage
-;; location, for each procedure in which it is referenced.
-;;
-;; A variable is "local", i.e., allocated on the stack, if it is
-;; referenced from within the procedure that defined it. Otherwise it is
-;; a "closure" variable. For example:
-;;
-;; (lambda (a) a) ; a will be local
-;; `a' is local to the procedure.
-;;
-;; (lambda (a) (lambda () a))
-;; `a' is local to the outer procedure, but a closure variable with
-;; respect to the inner procedure.
-;;
-;; If a variable is ever assigned, it needs to be heap-allocated
-;; ("boxed"). This is so that closures and continuations capture the
-;; variable's identity, not just one of the values it may have over the
-;; course of program execution. If the variable is never assigned, there
-;; is no distinction between value and identity, so closing over its
-;; identity (whether through closures or continuations) can make a copy
-;; of its value instead.
-;;
-;; Local variables are stored on the stack within a procedure's call
-;; frame. Their index into the stack is determined from their linear
-;; postion within a procedure's binding path:
-;; (let (0 1)
-;; (let (2 3) ...)
-;; (let (2) ...))
-;; (let (2 3 4) ...))
-;; etc.
-;;
-;; This algorithm has the problem that variables are only allocated
-;; indices at the end of the binding path. If variables bound early in
-;; the path are not used in later portions of the path, their indices
-;; will not be recycled. This problem is particularly egregious in the
-;; expansion of `or':
-;;
-;; (or x y z)
-;; -> (let ((a x)) (if a a (let ((b y)) (if b b z))))
-;;
-;; As you can see, the `a' binding is only used in the ephemeral
-;; `consequent' clause of the first `if', but its index would be
-;; reserved for the whole of the `or' expansion. So we have a hack for
-;; this specific case. A proper solution would be some sort of liveness
-;; analysis, and not our linear allocation algorithm.
-;;
-;; Closure variables are captured when a closure is created, and stored in a
-;; vector inline to the closure object itself. Each closure variable has a
-;; unique index into that vector.
-;;
-;; There is one more complication. Procedures bound by <fix> may, in
-;; some cases, be rendered inline to their parent procedure. That is to
-;; say,
-;;
-;; (letrec ((lp (lambda () (lp)))) (lp))
-;; => (fix ((lp (lambda () (lp)))) (lp))
-;; => goto FIX-BODY; LP: goto LP; FIX-BODY: goto LP;
-;; ^ jump over the loop ^ the fixpoint lp ^ starting off the loop
-;;
-;; The upshot is that we don't have to allocate any space for the `lp'
-;; closure at all, as it can be rendered inline as a loop. So there is
-;; another kind of allocation, "label allocation", in which the
-;; procedure is simply a label, placed at the start of the lambda body.
-;; The label is the gensym under which the lambda expression is bound.
-;;
-;; The analyzer checks to see that the label is called with the correct
-;; number of arguments. Calls to labels compile to rename + goto.
-;; Lambda, the ultimate goto!
-;;
-;;
-;; The return value of `analyze-lexicals' is a hash table, the
-;; "allocation".
-;;
-;; The allocation maps gensyms -- recall that each lexically bound
-;; variable has a unique gensym -- to storage locations ("addresses").
-;; Since one gensym may have many storage locations, if it is referenced
-;; in many procedures, it is a two-level map.
-;;
-;; The allocation also stored information on how many local variables
-;; need to be allocated for each procedure, lexicals that have been
-;; translated into labels, and information on what free variables to
-;; capture from its lexical parent procedure.
-;;
-;; In addition, we have a conflation: while we're traversing the code,
-;; recording information to pass to the compiler, we take the
-;; opportunity to generate labels for each lambda-case clause, so that
-;; generated code can skip argument checks at runtime if they match at
-;; compile-time.
-;;
-;; Also, while we're a-traversing and an-allocating, we check prompt
-;; handlers to see if the "continuation" argument is used. If not, we
-;; mark the prompt as being "escape-only". This allows us to implement
-;; `catch' and `throw' using `prompt' and `control', but without causing
-;; a continuation to be reified. Heh heh.
-;;
-;; That is:
-;;
-;; sym -> {lambda -> address}
-;; lambda -> (labels . free-locs)
-;; lambda-case -> (gensym . nlocs)
-;; prompt -> escape-only?
-;;
-;; address ::= (local? boxed? . index)
-;; labels ::= ((sym . lambda) ...)
-;; free-locs ::= ((sym0 . address0) (sym1 . address1) ...)
-;; free variable addresses are relative to parent proc.
-
-(define (make-hashq k v)
- (let ((res (make-hash-table)))
- (hashq-set! res k v)
- res))
-
-(define (analyze-lexicals x)
- ;; bound-vars: lambda -> (sym ...)
- ;; all identifiers bound within a lambda
- (define bound-vars (make-hash-table))
- ;; free-vars: lambda -> (sym ...)
- ;; all identifiers referenced in a lambda, but not bound
- ;; NB, this includes identifiers referenced by contained lambdas
- (define free-vars (make-hash-table))
- ;; assigned: sym -> #t
- ;; variables that are assigned
- (define assigned (make-hash-table))
- ;; refcounts: sym -> count
- ;; allows us to detect the or-expansion in O(1) time
- (define refcounts (make-hash-table))
- ;; labels: sym -> lambda
- ;; for determining if fixed-point procedures can be rendered as
- ;; labels.
- (define labels (make-hash-table))
-
- ;; returns variables referenced in expr
- (define (analyze! x proc labels-in-proc tail? tail-call-args)
- (define (step y) (analyze! y proc '() #f #f))
- (define (step-tail y) (analyze! y proc labels-in-proc tail? #f))
- (define (step-tail-call y args) (analyze! y proc labels-in-proc #f
- (and tail? args)))
- (define (recur/labels x new-proc labels)
- (analyze! x new-proc (append labels labels-in-proc) #t #f))
- (define (recur x new-proc) (analyze! x new-proc '() tail? #f))
- (record-case x
- ((<application> proc args)
- (apply lset-union eq? (step-tail-call proc args)
- (map step args)))
-
- ((<conditional> test consequent alternate)
- (lset-union eq? (step test) (step-tail consequent) (step-tail alternate)))
-
- ((<lexical-ref> gensym)
- (hashq-set! refcounts gensym (1+ (hashq-ref refcounts gensym 0)))
- (if (not (and tail-call-args
- (memq gensym labels-in-proc)
- (let ((p (hashq-ref labels gensym)))
- (and p
- (let lp ((c (lambda-body p)))
- (and c (lambda-case? c)
- (or
- ;; for now prohibit optional &
- ;; keyword arguments; can relax this
- ;; restriction later
- (and (= (length (lambda-case-req c))
- (length tail-call-args))
- (not (lambda-case-opt c))
- (not (lambda-case-kw c))
- (not (lambda-case-rest c)))
- (lp (lambda-case-alternate c)))))))))
- (hashq-set! labels gensym #f))
- (list gensym))
-
- ((<lexical-set> gensym exp)
- (hashq-set! assigned gensym #t)
- (hashq-set! labels gensym #f)
- (lset-adjoin eq? (step exp) gensym))
-
- ((<module-set> exp)
- (step exp))
-
- ((<toplevel-set> exp)
- (step exp))
-
- ((<toplevel-define> exp)
- (step exp))
-
- ((<sequence> exps)
- (let lp ((exps exps) (ret '()))
- (cond ((null? exps) '())
- ((null? (cdr exps))
- (lset-union eq? ret (step-tail (car exps))))
- (else
- (lp (cdr exps) (lset-union eq? ret (step (car exps))))))))
-
- ((<lambda> body)
- ;; order is important here
- (hashq-set! bound-vars x '())
- (let ((free (recur body x)))
- (hashq-set! bound-vars x (reverse! (hashq-ref bound-vars x)))
- (hashq-set! free-vars x free)
- free))
-
- ((<lambda-case> opt kw inits gensyms body alternate)
- (hashq-set! bound-vars proc
- (append (reverse gensyms) (hashq-ref bound-vars proc)))
- (lset-union
- eq?
- (lset-difference eq?
- (lset-union eq?
- (apply lset-union eq? (map step inits))
- (step-tail body))
- gensyms)
- (if alternate (step-tail alternate) '())))
-
- ((<let> gensyms vals body)
- (hashq-set! bound-vars proc
- (append (reverse gensyms) (hashq-ref bound-vars proc)))
- (lset-difference eq?
- (apply lset-union eq? (step-tail body) (map step vals))
- gensyms))
-
- ((<letrec> gensyms vals body)
- (hashq-set! bound-vars proc
- (append (reverse gensyms) (hashq-ref bound-vars proc)))
- (for-each (lambda (sym) (hashq-set! assigned sym #t)) gensyms)
- (lset-difference eq?
- (apply lset-union eq? (step-tail body) (map step vals))
- gensyms))
-
- ((<fix> gensyms vals body)
- ;; Try to allocate these procedures as labels.
- (for-each (lambda (sym val) (hashq-set! labels sym val))
- gensyms vals)
- (hashq-set! bound-vars proc
- (append (reverse gensyms) (hashq-ref bound-vars proc)))
- ;; Step into subexpressions.
- (let* ((var-refs
- (map
- ;; Since we're trying to label-allocate the lambda,
- ;; pretend it's not a closure, and just recurse into its
- ;; body directly. (Otherwise, recursing on a closure
- ;; that references one of the fix's bound vars would
- ;; prevent label allocation.)
- (lambda (x)
- (record-case x
- ((<lambda> body)
- ;; just like the closure case, except here we use
- ;; recur/labels instead of recur
- (hashq-set! bound-vars x '())
- (let ((free (recur/labels body x gensyms)))
- (hashq-set! bound-vars x (reverse! (hashq-ref bound-vars x)))
- (hashq-set! free-vars x free)
- free))))
- vals))
- (vars-with-refs (map cons gensyms var-refs))
- (body-refs (recur/labels body proc gensyms)))
- (define (delabel-dependents! sym)
- (let ((refs (assq-ref vars-with-refs sym)))
- (if refs
- (for-each (lambda (sym)
- (if (hashq-ref labels sym)
- (begin
- (hashq-set! labels sym #f)
- (delabel-dependents! sym))))
- refs))))
- ;; Stepping into the lambdas and the body might have made some
- ;; procedures not label-allocatable -- which might have
- ;; knock-on effects. For example:
- ;; (fix ((a (lambda () (b)))
- ;; (b (lambda () a)))
- ;; (a))
- ;; As far as `a' is concerned, both `a' and `b' are
- ;; label-allocatable. But `b' references `a' not in a proc-tail
- ;; position, which makes `a' not label-allocatable. The
- ;; knock-on effect is that, when back-propagating this
- ;; information to `a', `b' will also become not
- ;; label-allocatable, as it is referenced within `a', which is
- ;; allocated as a closure. This is a transitive relationship.
- (for-each (lambda (sym)
- (if (not (hashq-ref labels sym))
- (delabel-dependents! sym)))
- gensyms)
- ;; Now lift bound variables with label-allocated lambdas to the
- ;; parent procedure.
- (for-each
- (lambda (sym val)
- (if (hashq-ref labels sym)
- ;; Remove traces of the label-bound lambda. The free
- ;; vars will propagate up via the return val.
- (begin
- (hashq-set! bound-vars proc
- (append (hashq-ref bound-vars val)
- (hashq-ref bound-vars proc)))
- (hashq-remove! bound-vars val)
- (hashq-remove! free-vars val))))
- gensyms vals)
- (lset-difference eq?
- (apply lset-union eq? body-refs var-refs)
- gensyms)))
-
- ((<let-values> exp body)
- (lset-union eq? (step exp) (step body)))
-
- ((<dynwind> body winder unwinder)
- (lset-union eq? (step body) (step winder) (step unwinder)))
-
- ((<dynlet> fluids vals body)
- (apply lset-union eq? (step body) (map step (append fluids vals))))
-
- ((<dynref> fluid)
- (step fluid))
-
- ((<dynset> fluid exp)
- (lset-union eq? (step fluid) (step exp)))
-
- ((<prompt> tag body handler)
- (lset-union eq? (step tag) (step body) (step-tail handler)))
-
- ((<abort> tag args tail)
- (apply lset-union eq? (step tag) (step tail) (map step args)))
-
- (else '())))
-
- ;; allocation: sym -> {lambda -> address}
- ;; lambda -> (labels . free-locs)
- ;; lambda-case -> (gensym . nlocs)
- (define allocation (make-hash-table))
-
- (define (allocate! x proc n)
- (define (recur y) (allocate! y proc n))
- (record-case x
- ((<application> proc args)
- (apply max (recur proc) (map recur args)))
-
- ((<conditional> test consequent alternate)
- (max (recur test) (recur consequent) (recur alternate)))
-
- ((<lexical-set> exp)
- (recur exp))
-
- ((<module-set> exp)
- (recur exp))
-
- ((<toplevel-set> exp)
- (recur exp))
-
- ((<toplevel-define> exp)
- (recur exp))
-
- ((<sequence> exps)
- (apply max (map recur exps)))
-
- ((<lambda> body)
- ;; allocate closure vars in order
- (let lp ((c (hashq-ref free-vars x)) (n 0))
- (if (pair? c)
- (begin
- (hashq-set! (hashq-ref allocation (car c))
- x
- `(#f ,(hashq-ref assigned (car c)) . ,n))
- (lp (cdr c) (1+ n)))))
-
- (let ((nlocs (allocate! body x 0))
- (free-addresses
- (map (lambda (v)
- (hashq-ref (hashq-ref allocation v) proc))
- (hashq-ref free-vars x)))
- (labels (filter cdr
- (map (lambda (sym)
- (cons sym (hashq-ref labels sym)))
- (hashq-ref bound-vars x)))))
- ;; set procedure allocations
- (hashq-set! allocation x (cons labels free-addresses)))
- n)
-
- ((<lambda-case> opt kw inits gensyms body alternate)
- (max
- (let lp ((gensyms gensyms) (n n))
- (if (null? gensyms)
- (let ((nlocs (apply
- max
- (allocate! body proc n)
- ;; inits not logically at the end, but they
- ;; are the list...
- (map (lambda (x) (allocate! x proc n)) inits))))
- ;; label and nlocs for the case
- (hashq-set! allocation x (cons (gensym ":LCASE") nlocs))
- nlocs)
- (begin
- (hashq-set! allocation (car gensyms)
- (make-hashq
- proc `(#t ,(hashq-ref assigned (car gensyms)) . ,n)))
- (lp (cdr gensyms) (1+ n)))))
- (if alternate (allocate! alternate proc n) n)))
-
- ((<let> gensyms vals body)
- (let ((nmax (apply max (map recur vals))))
- (cond
- ;; the `or' hack
- ((and (conditional? body)
- (= (length gensyms) 1)
- (let ((v (car gensyms)))
- (and (not (hashq-ref assigned v))
- (= (hashq-ref refcounts v 0) 2)
- (lexical-ref? (conditional-test body))
- (eq? (lexical-ref-gensym (conditional-test body)) v)
- (lexical-ref? (conditional-consequent body))
- (eq? (lexical-ref-gensym (conditional-consequent body)) v))))
- (hashq-set! allocation (car gensyms)
- (make-hashq proc `(#t #f . ,n)))
- ;; the 1+ for this var
- (max nmax (1+ n) (allocate! (conditional-alternate body) proc n)))
- (else
- (let lp ((gensyms gensyms) (n n))
- (if (null? gensyms)
- (max nmax (allocate! body proc n))
- (let ((v (car gensyms)))
- (hashq-set!
- allocation v
- (make-hashq proc
- `(#t ,(hashq-ref assigned v) . ,n)))
- (lp (cdr gensyms) (1+ n)))))))))
-
- ((<letrec> gensyms vals body)
- (let lp ((gensyms gensyms) (n n))
- (if (null? gensyms)
- (let ((nmax (apply max
- (map (lambda (x)
- (allocate! x proc n))
- vals))))
- (max nmax (allocate! body proc n)))
- (let ((v (car gensyms)))
- (hashq-set!
- allocation v
- (make-hashq proc
- `(#t ,(hashq-ref assigned v) . ,n)))
- (lp (cdr gensyms) (1+ n))))))
-
- ((<fix> gensyms vals body)
- (let lp ((in gensyms) (n n))
- (if (null? in)
- (let lp ((gensyms gensyms) (vals vals) (nmax n))
- (cond
- ((null? gensyms)
- (max nmax (allocate! body proc n)))
- ((hashq-ref labels (car gensyms))
- ;; allocate lambda body inline to proc
- (lp (cdr gensyms)
- (cdr vals)
- (record-case (car vals)
- ((<lambda> body)
- (max nmax (allocate! body proc n))))))
- (else
- ;; allocate closure
- (lp (cdr gensyms)
- (cdr vals)
- (max nmax (allocate! (car vals) proc n))))))
-
- (let ((v (car in)))
- (cond
- ((hashq-ref assigned v)
- (error "fixpoint procedures may not be assigned" x))
- ((hashq-ref labels v)
- ;; no binding, it's a label
- (lp (cdr in) n))
- (else
- ;; allocate closure binding
- (hashq-set! allocation v (make-hashq proc `(#t #f . ,n)))
- (lp (cdr in) (1+ n))))))))
-
- ((<let-values> exp body)
- (max (recur exp) (recur body)))
-
- ((<dynwind> body winder unwinder)
- (max (recur body) (recur winder) (recur unwinder)))
-
- ((<dynlet> fluids vals body)
- (apply max (recur body) (map recur (append fluids vals))))
-
- ((<dynref> fluid)
- (recur fluid))
-
- ((<dynset> fluid exp)
- (max (recur fluid) (recur exp)))
-
- ((<prompt> tag body handler)
- (let ((cont-var (and (lambda-case? handler)
- (pair? (lambda-case-gensyms handler))
- (car (lambda-case-gensyms handler)))))
- (hashq-set! allocation x
- (and cont-var (zero? (hashq-ref refcounts cont-var 0))))
- (max (recur tag) (recur body) (recur handler))))
-
- ((<abort> tag args tail)
- (apply max (recur tag) (recur tail) (map recur args)))
-
- (else n)))
-
- (analyze! x #f '() #t #f)
- (allocate! x #f 0)
-
- allocation)
-
-
-;;;
-;;; Tree analyses for warnings.
-;;;
-
-(define-record-type <tree-analysis>
- (make-tree-analysis leaf down up post init)
- tree-analysis?
- (leaf tree-analysis-leaf) ;; (lambda (x result env locs) ...)
- (down tree-analysis-down) ;; (lambda (x result env locs) ...)
- (up tree-analysis-up) ;; (lambda (x result env locs) ...)
- (post tree-analysis-post) ;; (lambda (result env) ...)
- (init tree-analysis-init)) ;; arbitrary value
-
-(define (analyze-tree analyses tree env)
- "Run all tree analyses listed in ANALYSES on TREE for ENV, using
-`tree-il-fold'. Return TREE. The leaf/down/up procedures of each analysis are
-passed a ``location stack', which is the stack of `tree-il-src' values for each
-parent tree (a list); it can be used to approximate source location when
-accurate information is missing from a given `tree-il' element."
-
- (define (traverse proc update-locs)
- ;; Return a tree traversing procedure that returns a list of analysis
- ;; results prepended by the location stack.
- (lambda (x results)
- (let ((locs (update-locs x (car results))))
- (cons locs ;; the location stack
- (map (lambda (analysis result)
- ((proc analysis) x result env locs))
- analyses
- (cdr results))))))
-
- ;; Keeping/extending/shrinking the location stack.
- (define (keep-locs x locs) locs)
- (define (extend-locs x locs) (cons (tree-il-src x) locs))
- (define (shrink-locs x locs) (cdr locs))
-
- (let ((results
- (tree-il-fold (traverse tree-analysis-leaf keep-locs)
- (traverse tree-analysis-down extend-locs)
- (traverse tree-analysis-up shrink-locs)
- (cons '() ;; empty location stack
- (map tree-analysis-init analyses))
- tree)))
-
- (for-each (lambda (analysis result)
- ((tree-analysis-post analysis) result env))
- analyses
- (cdr results)))
-
- tree)
-
-
-;;;
-;;; Unused variable analysis.
-;;;
-
-;; <binding-info> records are used during tree traversals in
-;; `unused-variable-analysis'. They contain a list of the local vars
-;; currently in scope, and a list of locals vars that have been referenced.
-(define-record-type <binding-info>
- (make-binding-info vars refs)
- binding-info?
- (vars binding-info-vars) ;; ((GENSYM NAME LOCATION) ...)
- (refs binding-info-refs)) ;; (GENSYM ...)
-
-(define (gensym? sym)
- ;; Return #t if SYM is (likely) a generated symbol.
- (string-any #\space (symbol->string sym)))
-
-(define unused-variable-analysis
- ;; Report unused variables in the given tree.
- (make-tree-analysis
- (lambda (x info env locs)
- ;; X is a leaf: extend INFO's refs accordingly.
- (let ((refs (binding-info-refs info))
- (vars (binding-info-vars info)))
- (record-case x
- ((<lexical-ref> gensym)
- (make-binding-info vars (vhash-consq gensym #t refs)))
- (else info))))
-
- (lambda (x info env locs)
- ;; Going down into X: extend INFO's variable list
- ;; accordingly.
- (let ((refs (binding-info-refs info))
- (vars (binding-info-vars info))
- (src (tree-il-src x)))
- (define (extend inner-vars inner-names)
- (fold (lambda (var name vars)
- (vhash-consq var (list name src) vars))
- vars
- inner-vars
- inner-names))
-
- (record-case x
- ((<lexical-set> gensym)
- (make-binding-info vars (vhash-consq gensym #t refs)))
- ((<lambda-case> req opt inits rest kw gensyms)
- (let ((names `(,@req
- ,@(or opt '())
- ,@(if rest (list rest) '())
- ,@(if kw (map cadr (cdr kw)) '()))))
- (make-binding-info (extend gensyms names) refs)))
- ((<let> gensyms names)
- (make-binding-info (extend gensyms names) refs))
- ((<letrec> gensyms names)
- (make-binding-info (extend gensyms names) refs))
- ((<fix> gensyms names)
- (make-binding-info (extend gensyms names) refs))
- (else info))))
-
- (lambda (x info env locs)
- ;; Leaving X's scope: shrink INFO's variable list
- ;; accordingly and reported unused nested variables.
- (let ((refs (binding-info-refs info))
- (vars (binding-info-vars info)))
- (define (shrink inner-vars refs)
- (vlist-for-each
- (lambda (var)
- (let ((gensym (car var)))
- ;; Don't report lambda parameters as unused.
- (if (and (memq gensym inner-vars)
- (not (vhash-assq gensym refs))
- (not (lambda-case? x)))
- (let ((name (cadr var))
- ;; We can get approximate source location by going up
- ;; the LOCS location stack.
- (loc (or (caddr var)
- (find pair? locs))))
- (if (and (not (gensym? name))
- (not (eq? name '_)))
- (warning 'unused-variable loc name))))))
- vars)
- (vlist-drop vars (length inner-vars)))
-
- ;; For simplicity, we leave REFS untouched, i.e., with
- ;; names of variables that are now going out of scope.
- ;; It doesn't hurt as these are unique names, it just
- ;; makes REFS unnecessarily fat.
- (record-case x
- ((<lambda-case> gensyms)
- (make-binding-info (shrink gensyms refs) refs))
- ((<let> gensyms)
- (make-binding-info (shrink gensyms refs) refs))
- ((<letrec> gensyms)
- (make-binding-info (shrink gensyms refs) refs))
- ((<fix> gensyms)
- (make-binding-info (shrink gensyms refs) refs))
- (else info))))
-
- (lambda (result env) #t)
- (make-binding-info vlist-null vlist-null)))
-
-
-;;;
-;;; Unused top-level variable analysis.
-;;;
-
-;; <reference-graph> record top-level definitions that are made, references to
-;; top-level definitions and their context (the top-level definition in which
-;; the reference appears), as well as the current context (the top-level
-;; definition we're currently in). The second part (`refs' below) is
-;; effectively a graph from which we can determine unused top-level definitions.
-(define-record-type <reference-graph>
- (make-reference-graph refs defs toplevel-context)
- reference-graph?
- (defs reference-graph-defs) ;; ((NAME . LOC) ...)
- (refs reference-graph-refs) ;; ((REF-CONTEXT REF ...) ...)
- (toplevel-context reference-graph-toplevel-context)) ;; NAME | #f
-
-(define (graph-reachable-nodes root refs reachable)
- ;; Add to REACHABLE the nodes reachable from ROOT in graph REFS. REFS is a
- ;; vhash mapping nodes to the list of their children: for instance,
- ;; ((A -> (B C)) (B -> (A)) (C -> ())) corresponds to
- ;;
- ;; ,-------.
- ;; v |
- ;; A ----> B
- ;; |
- ;; v
- ;; C
- ;;
- ;; REACHABLE is a vhash of nodes known to be otherwise reachable.
-
- (let loop ((root root)
- (path vlist-null)
- (result reachable))
- (if (or (vhash-assq root path)
- (vhash-assq root result))
- result
- (let* ((children (or (and=> (vhash-assq root refs) cdr) '()))
- (path (vhash-consq root #t path))
- (result (fold (lambda (kid result)
- (loop kid path result))
- result
- children)))
- (fold (lambda (kid result)
- (vhash-consq kid #t result))
- result
- children)))))
-
-(define (graph-reachable-nodes* roots refs)
- ;; Return the list of nodes in REFS reachable from the nodes listed in ROOTS.
- (vlist-fold (lambda (root+true result)
- (let* ((root (car root+true))
- (reachable (graph-reachable-nodes root refs result)))
- (vhash-consq root #t reachable)))
- vlist-null
- roots))
-
-(define (partition* pred vhash)
- ;; Partition VHASH according to PRED. Return the two resulting vhashes.
- (let ((result
- (vlist-fold (lambda (k+v result)
- (let ((k (car k+v))
- (v (cdr k+v))
- (r1 (car result))
- (r2 (cdr result)))
- (if (pred k)
- (cons (vhash-consq k v r1) r2)
- (cons r1 (vhash-consq k v r2)))))
- (cons vlist-null vlist-null)
- vhash)))
- (values (car result) (cdr result))))
-
-(define unused-toplevel-analysis
- ;; Report unused top-level definitions that are not exported.
- (let ((add-ref-from-context
- (lambda (graph name)
- ;; Add an edge CTX -> NAME in GRAPH.
- (let* ((refs (reference-graph-refs graph))
- (defs (reference-graph-defs graph))
- (ctx (reference-graph-toplevel-context graph))
- (ctx-refs (or (and=> (vhash-assq ctx refs) cdr) '())))
- (make-reference-graph (vhash-consq ctx (cons name ctx-refs) refs)
- defs ctx)))))
- (define (macro-variable? name env)
- (and (module? env)
- (let ((var (module-variable env name)))
- (and var (variable-bound? var)
- (macro? (variable-ref var))))))
-
- (make-tree-analysis
- (lambda (x graph env locs)
- ;; X is a leaf.
- (let ((ctx (reference-graph-toplevel-context graph)))
- (record-case x
- ((<toplevel-ref> name src)
- (add-ref-from-context graph name))
- (else graph))))
-
- (lambda (x graph env locs)
- ;; Going down into X.
- (let ((ctx (reference-graph-toplevel-context graph))
- (refs (reference-graph-refs graph))
- (defs (reference-graph-defs graph)))
- (record-case x
- ((<toplevel-define> name src)
- (let ((refs refs)
- (defs (vhash-consq name (or src (find pair? locs))
- defs)))
- (make-reference-graph refs defs name)))
- ((<toplevel-set> name src)
- (add-ref-from-context graph name))
- (else graph))))
-
- (lambda (x graph env locs)
- ;; Leaving X's scope.
- (record-case x
- ((<toplevel-define>)
- (let ((refs (reference-graph-refs graph))
- (defs (reference-graph-defs graph)))
- (make-reference-graph refs defs #f)))
- (else graph)))
-
- (lambda (graph env)
- ;; Process the resulting reference graph: determine all private definitions
- ;; not reachable from any public definition. Macros
- ;; (syntax-transformers), which are globally bound, never considered
- ;; unused since we can't tell whether a macro is actually used; in
- ;; addition, macros are considered roots of the graph since they may use
- ;; private bindings. FIXME: The `make-syntax-transformer' calls don't
- ;; contain any literal `toplevel-ref' of the global bindings they use so
- ;; this strategy fails.
- (define (exported? name)
- (if (module? env)
- (module-variable (module-public-interface env) name)
- #t))
-
- (let-values (((public-defs private-defs)
- (partition* (lambda (name)
- (or (exported? name)
- (macro-variable? name env)))
- (reference-graph-defs graph))))
- (let* ((roots (vhash-consq #f #t public-defs))
- (refs (reference-graph-refs graph))
- (reachable (graph-reachable-nodes* roots refs))
- (unused (vlist-filter (lambda (name+src)
- (not (vhash-assq (car name+src)
- reachable)))
- private-defs)))
- (vlist-for-each (lambda (name+loc)
- (let ((name (car name+loc))
- (loc (cdr name+loc)))
- (if (not (gensym? name))
- (warning 'unused-toplevel loc name))))
- unused))))
-
- (make-reference-graph vlist-null vlist-null #f))))
-
-
-;;;
-;;; Unbound variable analysis.
-;;;
-
-;; <toplevel-info> records are used during tree traversal in search of
-;; possibly unbound variable. They contain a list of references to
-;; potentially unbound top-level variables, and a list of the top-level
-;; defines that have been encountered.
-(define-record-type <toplevel-info>
- (make-toplevel-info refs defs)
- toplevel-info?
- (refs toplevel-info-refs) ;; ((VARIABLE-NAME . LOCATION) ...)
- (defs toplevel-info-defs)) ;; (VARIABLE-NAME ...)
-
-(define (goops-toplevel-definition proc args env)
- ;; If application of PROC to ARGS is a GOOPS top-level definition, return
- ;; the name of the variable being defined; otherwise return #f. This
- ;; assumes knowledge of the current implementation of `define-class' et al.
- (define (toplevel-define-arg args)
- (match args
- ((($ <const> _ (and (? symbol?) exp)) _)
- exp)
- (_ #f)))
-
- (match proc
- (($ <module-ref> _ '(oop goops) 'toplevel-define! #f)
- (toplevel-define-arg args))
- (($ <toplevel-ref> _ 'toplevel-define!)
- ;; This may be the result of expanding one of the GOOPS macros within
- ;; `oop/goops.scm'.
- (and (eq? env (resolve-module '(oop goops)))
- (toplevel-define-arg args)))
- (_ #f)))
-
-(define unbound-variable-analysis
- ;; Report possibly unbound variables in the given tree.
- (make-tree-analysis
- (lambda (x info env locs)
- ;; X is a leaf: extend INFO's refs accordingly.
- (let ((refs (toplevel-info-refs info))
- (defs (toplevel-info-defs info)))
- (define (bound? name)
- (or (and (module? env)
- (module-variable env name))
- (vhash-assq name defs)))
-
- (record-case x
- ((<toplevel-ref> name src)
- (if (bound? name)
- info
- (let ((src (or src (find pair? locs))))
- (make-toplevel-info (vhash-consq name src refs)
- defs))))
- (else info))))
-
- (lambda (x info env locs)
- ;; Going down into X.
- (let* ((refs (toplevel-info-refs info))
- (defs (toplevel-info-defs info))
- (src (tree-il-src x)))
- (define (bound? name)
- (or (and (module? env)
- (module-variable env name))
- (vhash-assq name defs)))
-
- (record-case x
- ((<toplevel-set> name src)
- (if (bound? name)
- (make-toplevel-info refs defs)
- (let ((src (find pair? locs)))
- (make-toplevel-info (vhash-consq name src refs)
- defs))))
- ((<toplevel-define> name)
- (make-toplevel-info (vhash-delq name refs)
- (vhash-consq name #t defs)))
-
- ((<application> proc args)
- ;; Check for a dynamic top-level definition, as is
- ;; done by code expanded from GOOPS macros.
- (let ((name (goops-toplevel-definition proc args
- env)))
- (if (symbol? name)
- (make-toplevel-info (vhash-delq name refs)
- (vhash-consq name #t defs))
- (make-toplevel-info refs defs))))
- (else
- (make-toplevel-info refs defs)))))
-
- (lambda (x info env locs)
- ;; Leaving X's scope.
- info)
-
- (lambda (toplevel env)
- ;; Post-process the result.
- (vlist-for-each (lambda (name+loc)
- (let ((name (car name+loc))
- (loc (cdr name+loc)))
- (warning 'unbound-variable loc name)))
- (vlist-reverse (toplevel-info-refs toplevel))))
-
- (make-toplevel-info vlist-null vlist-null)))
-
-
-;;;
-;;; Arity analysis.
-;;;
-
-;; <arity-info> records contain information about lexical definitions of
-;; procedures currently in scope, top-level procedure definitions that have
-;; been encountered, and calls to top-level procedures that have been
-;; encountered.
-(define-record-type <arity-info>
- (make-arity-info toplevel-calls lexical-lambdas toplevel-lambdas)
- arity-info?
- (toplevel-calls toplevel-procedure-calls) ;; ((NAME . APPLICATION) ...)
- (lexical-lambdas lexical-lambdas) ;; ((GENSYM . DEFINITION) ...)
- (toplevel-lambdas toplevel-lambdas)) ;; ((NAME . DEFINITION) ...)
-
-(define (validate-arity proc application lexical?)
- ;; Validate the argument count of APPLICATION, a tree-il application of
- ;; PROC, emitting a warning in case of argument count mismatch.
-
- (define (filter-keyword-args keywords allow-other-keys? args)
- ;; Filter keyword arguments from ARGS and return the resulting list.
- ;; KEYWORDS is the list of allowed keywords, and ALLOW-OTHER-KEYS?
- ;; specified whethere keywords not listed in KEYWORDS are allowed.
- (let loop ((args args)
- (result '()))
- (if (null? args)
- (reverse result)
- (let ((arg (car args)))
- (if (and (const? arg)
- (or (memq (const-exp arg) keywords)
- (and allow-other-keys?
- (keyword? (const-exp arg)))))
- (loop (if (pair? (cdr args))
- (cddr args)
- '())
- result)
- (loop (cdr args)
- (cons arg result)))))))
-
- (define (arities proc)
- ;; Return the arities of PROC, which can be either a tree-il or a
- ;; procedure.
- (define (len x)
- (or (and (or (null? x) (pair? x))
- (length x))
- 0))
- (cond ((program? proc)
- (values (procedure-name proc)
- (map (lambda (a)
- (list (arity:nreq a) (arity:nopt a) (arity:rest? a)
- (map car (arity:kw a))
- (arity:allow-other-keys? a)))
- (program-arities proc))))
- ((procedure? proc)
- (if (struct? proc)
- ;; An applicable struct.
- (arities (struct-ref proc 0))
- ;; An applicable smob.
- (let ((arity (procedure-minimum-arity proc)))
- (values (procedure-name proc)
- (list (list (car arity) (cadr arity) (caddr arity)
- #f #f))))))
- (else
- (let loop ((name #f)
- (proc proc)
- (arities '()))
- (if (not proc)
- (values name (reverse arities))
- (record-case proc
- ((<lambda-case> req opt rest kw alternate)
- (loop name alternate
- (cons (list (len req) (len opt) rest
- (and (pair? kw) (map car (cdr kw)))
- (and (pair? kw) (car kw)))
- arities)))
- ((<lambda> meta body)
- (loop (assoc-ref meta 'name) body arities))
- (else
- (values #f #f))))))))
-
- (let ((args (application-args application))
- (src (tree-il-src application)))
- (call-with-values (lambda () (arities proc))
- (lambda (name arities)
- (define matches?
- (find (lambda (arity)
- (pmatch arity
- ((,req ,opt ,rest? ,kw ,aok?)
- (let ((args (if (pair? kw)
- (filter-keyword-args kw aok? args)
- args)))
- (if (and req opt)
- (let ((count (length args)))
- (and (>= count req)
- (or rest?
- (<= count (+ req opt)))))
- #t)))
- (else #t)))
- arities))
-
- (if (not matches?)
- (warning 'arity-mismatch src
- (or name (with-output-to-string (lambda () (write proc))))
- lexical?)))))
- #t)
-
-(define arity-analysis
- ;; Report arity mismatches in the given tree.
- (make-tree-analysis
- (lambda (x info env locs)
- ;; X is a leaf.
- info)
- (lambda (x info env locs)
- ;; Down into X.
- (define (extend lexical-name val info)
- ;; If VAL is a lambda, add NAME to the lexical-lambdas of INFO.
- (let ((toplevel-calls (toplevel-procedure-calls info))
- (lexical-lambdas (lexical-lambdas info))
- (toplevel-lambdas (toplevel-lambdas info)))
- (record-case val
- ((<lambda> body)
- (make-arity-info toplevel-calls
- (vhash-consq lexical-name val
- lexical-lambdas)
- toplevel-lambdas))
- ((<lexical-ref> gensym)
- ;; lexical alias
- (let ((val* (vhash-assq gensym lexical-lambdas)))
- (if (pair? val*)
- (extend lexical-name (cdr val*) info)
- info)))
- ((<toplevel-ref> name)
- ;; top-level alias
- (make-arity-info toplevel-calls
- (vhash-consq lexical-name val
- lexical-lambdas)
- toplevel-lambdas))
- (else info))))
-
- (let ((toplevel-calls (toplevel-procedure-calls info))
- (lexical-lambdas (lexical-lambdas info))
- (toplevel-lambdas (toplevel-lambdas info)))
-
- (record-case x
- ((<toplevel-define> name exp)
- (record-case exp
- ((<lambda> body)
- (make-arity-info toplevel-calls
- lexical-lambdas
- (vhash-consq name exp toplevel-lambdas)))
- ((<toplevel-ref> name)
- ;; alias for another toplevel
- (let ((proc (vhash-assq name toplevel-lambdas)))
- (make-arity-info toplevel-calls
- lexical-lambdas
- (vhash-consq (toplevel-define-name x)
- (if (pair? proc)
- (cdr proc)
- exp)
- toplevel-lambdas))))
- (else info)))
- ((<let> gensyms vals)
- (fold extend info gensyms vals))
- ((<letrec> gensyms vals)
- (fold extend info gensyms vals))
- ((<fix> gensyms vals)
- (fold extend info gensyms vals))
-
- ((<application> proc args src)
- (record-case proc
- ((<lambda> body)
- (validate-arity proc x #t)
- info)
- ((<toplevel-ref> name)
- (make-arity-info (vhash-consq name x toplevel-calls)
- lexical-lambdas
- toplevel-lambdas))
- ((<lexical-ref> gensym)
- (let ((proc (vhash-assq gensym lexical-lambdas)))
- (if (pair? proc)
- (record-case (cdr proc)
- ((<toplevel-ref> name)
- ;; alias to toplevel
- (make-arity-info (vhash-consq name x toplevel-calls)
- lexical-lambdas
- toplevel-lambdas))
- (else
- (validate-arity (cdr proc) x #t)
- info))
-
- ;; If GENSYM wasn't found, it may be because it's an
- ;; argument of the procedure being compiled.
- info)))
- (else info)))
- (else info))))
-
- (lambda (x info env locs)
- ;; Up from X.
- (define (shrink name val info)
- ;; Remove NAME from the lexical-lambdas of INFO.
- (let ((toplevel-calls (toplevel-procedure-calls info))
- (lexical-lambdas (lexical-lambdas info))
- (toplevel-lambdas (toplevel-lambdas info)))
- (make-arity-info toplevel-calls
- (if (vhash-assq name lexical-lambdas)
- (vlist-tail lexical-lambdas)
- lexical-lambdas)
- toplevel-lambdas)))
-
- (let ((toplevel-calls (toplevel-procedure-calls info))
- (lexical-lambdas (lexical-lambdas info))
- (toplevel-lambdas (toplevel-lambdas info)))
- (record-case x
- ((<let> gensyms vals)
- (fold shrink info gensyms vals))
- ((<letrec> gensyms vals)
- (fold shrink info gensyms vals))
- ((<fix> gensyms vals)
- (fold shrink info gensyms vals))
-
- (else info))))
-
- (lambda (result env)
- ;; Post-processing: check all top-level procedure calls that have been
- ;; encountered.
- (let ((toplevel-calls (toplevel-procedure-calls result))
- (toplevel-lambdas (toplevel-lambdas result)))
- (vlist-for-each
- (lambda (name+application)
- (let* ((name (car name+application))
- (application (cdr name+application))
- (proc
- (or (and=> (vhash-assq name toplevel-lambdas) cdr)
- (and (module? env)
- (false-if-exception
- (module-ref env name)))))
- (proc*
- ;; handle toplevel aliases
- (if (toplevel-ref? proc)
- (let ((name (toplevel-ref-name proc)))
- (and (module? env)
- (false-if-exception
- (module-ref env name))))
- proc)))
- (cond ((lambda? proc*)
- (validate-arity proc* application #t))
- ((procedure? proc*)
- (validate-arity proc* application #f)))))
- toplevel-calls)))
-
- (make-arity-info vlist-null vlist-null vlist-null)))
-
-
-;;;
-;;; `format' argument analysis.
-;;;
-
-(define &syntax-error
- ;; The `throw' key for syntax errors.
- (gensym "format-string-syntax-error"))
-
-(define (format-string-argument-count fmt)
- ;; Return the minimum and maxium number of arguments that should
- ;; follow format string FMT (or, ahem, a good estimate thereof) or
- ;; `any' if the format string can be followed by any number of
- ;; arguments.
-
- (define (drop-group chars end)
- ;; Drop characters from CHARS until "~END" is encountered.
- (let loop ((chars chars)
- (tilde? #f))
- (if (null? chars)
- (throw &syntax-error 'unterminated-iteration)
- (if tilde?
- (if (eq? (car chars) end)
- (cdr chars)
- (loop (cdr chars) #f))
- (if (eq? (car chars) #\~)
- (loop (cdr chars) #t)
- (loop (cdr chars) #f))))))
-
- (define (digit? char)
- ;; Return true if CHAR is a digit, #f otherwise.
- (memq char '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)))
-
- (define (previous-number chars)
- ;; Return the previous series of digits found in CHARS.
- (let ((numbers (take-while digit? chars)))
- (and (not (null? numbers))
- (string->number (list->string (reverse numbers))))))
-
- (let loop ((chars (string->list fmt))
- (state 'literal)
- (params '())
- (conditions '())
- (end-group #f)
- (min-count 0)
- (max-count 0))
- (if (null? chars)
- (if end-group
- (throw &syntax-error 'unterminated-conditional)
- (values min-count max-count))
- (case state
- ((tilde)
- (case (car chars)
- ((#\~ #\% #\& #\t #\T #\_ #\newline #\( #\) #\! #\| #\/ #\q #\Q)
- (loop (cdr chars) 'literal '()
- conditions end-group
- min-count max-count))
- ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\, #\: #\@ #\+ #\- #\#)
- (loop (cdr chars)
- 'tilde (cons (car chars) params)
- conditions end-group
- min-count max-count))
- ((#\v #\V) (loop (cdr chars)
- 'tilde (cons (car chars) params)
- conditions end-group
- (+ 1 min-count)
- (+ 1 max-count)))
- ((#\p #\P) (let* ((colon? (memq #\: params))
- (min-count (if colon?
- (max 1 min-count)
- (+ 1 min-count))))
- (loop (cdr chars) 'literal '()
- conditions end-group
- min-count
- (if colon?
- (max max-count min-count)
- (+ 1 max-count)))))
- ((#\[)
- (loop chars 'literal '() '()
- (let ((selector (previous-number params))
- (at? (memq #\@ params)))
- (lambda (chars conds)
- ;; end of group
- (let ((mins (map car conds))
- (maxs (map cdr conds))
- (sel? (and selector
- (< selector (length conds)))))
- (if (and (every number? mins)
- (every number? maxs))
- (loop chars 'literal '() conditions end-group
- (+ min-count
- (if sel?
- (car (list-ref conds selector))
- (+ (if at? 0 1)
- (if (null? mins)
- 0
- (apply min mins)))))
- (+ max-count
- (if sel?
- (cdr (list-ref conds selector))
- (+ (if at? 0 1)
- (if (null? maxs)
- 0
- (apply max maxs))))))
- (values 'any 'any))))) ;; XXX: approximation
- 0 0))
- ((#\;)
- (if end-group
- (loop (cdr chars) 'literal '()
- (cons (cons min-count max-count) conditions)
- end-group
- 0 0)
- (throw &syntax-error 'unexpected-semicolon)))
- ((#\])
- (if end-group
- (end-group (cdr chars)
- (reverse (cons (cons min-count max-count)
- conditions)))
- (throw &syntax-error 'unexpected-conditional-termination)))
- ((#\{) (if (memq #\@ params)
- (values min-count 'any)
- (loop (drop-group (cdr chars) #\})
- 'literal '()
- conditions end-group
- (+ 1 min-count) (+ 1 max-count))))
- ((#\*) (if (memq #\@ params)
- (values 'any 'any) ;; it's unclear what to do here
- (loop (cdr chars)
- 'literal '()
- conditions end-group
- (+ (or (previous-number params) 1)
- min-count)
- (+ (or (previous-number params) 1)
- max-count))))
- ((#\? #\k #\K)
- ;; We don't have enough info to determine the exact number
- ;; of args, but we could determine a lower bound (TODO).
- (values 'any 'any))
- ((#\^)
- (values min-count 'any))
- ((#\h #\H)
- (let ((argc (if (memq #\: params) 2 1)))
- (loop (cdr chars) 'literal '()
- conditions end-group
- (+ argc min-count)
- (+ argc max-count))))
- ((#\')
- (if (null? (cdr chars))
- (throw &syntax-error 'unexpected-termination)
- (loop (cddr chars) 'tilde (cons (cadr chars) params)
- conditions end-group min-count max-count)))
- (else (loop (cdr chars) 'literal '()
- conditions end-group
- (+ 1 min-count) (+ 1 max-count)))))
- ((literal)
- (case (car chars)
- ((#\~) (loop (cdr chars) 'tilde '()
- conditions end-group
- min-count max-count))
- (else (loop (cdr chars) 'literal '()
- conditions end-group
- min-count max-count))))
- (else (error "computer bought the farm" state))))))
-
-(define (proc-ref? exp proc special-name env)
- "Return #t when EXP designates procedure PROC in ENV. As a last
-resort, return #t when EXP refers to the global variable SPECIAL-NAME."
-
- (define special?
- (cut eq? <> special-name))
-
- (match exp
- (($ <toplevel-ref> _ (? special?))
- ;; Allow top-levels like: (define _ (cut gettext <> "my-domain")).
- #t)
- (($ <toplevel-ref> _ name)
- (let ((var (module-variable env name)))
- (and var (variable-bound? var)
- (eq? (variable-ref var) proc))))
- (($ <module-ref> _ _ (? special?))
- #t)
- (($ <module-ref> _ module name public?)
- (let* ((mod (if public?
- (false-if-exception (resolve-interface module))
- (resolve-module module #\ensure #f)))
- (var (and mod (module-variable mod name))))
- (and var (variable-bound? var) (eq? (variable-ref var) proc))))
- (($ <lexical-ref> _ (? special?))
- #t)
- (_ #f)))
-
-(define gettext? (cut proc-ref? <> gettext '_ <>))
-(define ngettext? (cut proc-ref? <> ngettext 'N_ <>))
-
-(define (const-fmt x env)
- ;; Return the literal format string for X, or #f.
- (match x
- (($ <const> _ (? string? exp))
- exp)
- (($ <application> _ (? (cut gettext? <> env))
- (($ <const> _ (? string? fmt))))
- ;; Gettexted literals, like `(_ "foo")'.
- fmt)
- (($ <application> _ (? (cut ngettext? <> env))
- (($ <const> _ (? string? fmt)) ($ <const> _ (? string?)) _ \.\.1))
- ;; Plural gettextized literals, like `(N_ "singular" "plural" n)'.
-
- ;; TODO: Check whether the singular and plural strings have the
- ;; same format escapes.
- fmt)
- (_ #f)))
-
-(define format-analysis
- ;; Report arity mismatches in the given tree.
- (make-tree-analysis
- (lambda (x _ env locs)
- ;; X is a leaf.
- #t)
-
- (lambda (x _ env locs)
- ;; Down into X.
- (define (check-format-args args loc)
- (pmatch args
- ((,port ,fmt . ,rest)
- (guard (const-fmt fmt env))
- (if (and (const? port)
- (not (boolean? (const-exp port))))
- (warning 'format loc 'wrong-port (const-exp port)))
- (let ((fmt (const-fmt fmt env))
- (count (length rest)))
- (catch &syntax-error
- (lambda ()
- (let-values (((min max)
- (format-string-argument-count fmt)))
- (and min max
- (or (and (or (eq? min 'any) (>= count min))
- (or (eq? max 'any) (<= count max)))
- (warning 'format loc 'wrong-format-arg-count
- fmt min max count)))))
- (lambda (_ key)
- (warning 'format loc 'syntax-error key fmt)))))
- ((,port ,fmt . ,rest)
- (if (and (const? port)
- (not (boolean? (const-exp port))))
- (warning 'format loc 'wrong-port (const-exp port)))
-
- (match fmt
- (($ <const> loc* (? (negate string?) fmt))
- (warning 'format (or loc* loc) 'wrong-format-string fmt))
-
- ;; Warn on non-literal format strings, unless they refer to
- ;; a lexical variable named "fmt".
- (($ <lexical-ref> _ fmt)
- #t)
- ((? (negate const?))
- (warning 'format loc 'non-literal-format-string))))
- (else
- (warning 'format loc 'wrong-num-args (length args)))))
-
- (define (check-simple-format-args args loc)
- ;; Check the arguments to the `simple-format' procedure, which is
- ;; less capable than that of (ice-9 format).
-
- (define allowed-chars
- '(#\A #\S #\a #\s #\~ #\%))
-
- (define (format-chars fmt)
- (let loop ((chars (string->list fmt))
- (result '()))
- (match chars
- (()
- (reverse result))
- ((#\~ opt rest ...)
- (loop rest (cons opt result)))
- ((_ rest ...)
- (loop rest result)))))
-
- (match args
- ((port ($ <const> _ (? string? fmt)) _ ...)
- (let ((opts (format-chars fmt)))
- (or (every (cut memq <> allowed-chars) opts)
- (begin
- (warning 'format loc 'simple-format fmt
- (find (negate (cut memq <> allowed-chars)) opts))
- #f))))
- ((port (= (cut const-fmt <> env) (? string? fmt)) args ...)
- (check-simple-format-args `(,port ,(make-const loc fmt) ,args) loc))
- (_ #t)))
-
- (define (resolve-toplevel name)
- (and (module? env)
- (false-if-exception (module-ref env name))))
-
- (match x
- (($ <application> src ($ <toplevel-ref> _ name) args)
- (let ((proc (resolve-toplevel name)))
- (if (or (and (eq? proc (@ (guile) simple-format))
- (check-simple-format-args args
- (or src (find pair? locs))))
- (eq? proc (@ (ice-9 format) format)))
- (check-format-args args (or src (find pair? locs))))))
- (($ <application> src ($ <module-ref> _ '(ice-9 format) 'format) args)
- (check-format-args args (or src (find pair? locs))))
- (($ <application> src ($ <module-ref> _ '(guile)
- (or 'format 'simple-format))
- args)
- (and (check-simple-format-args args
- (or src (find pair? locs)))
- (check-format-args args (or src (find pair? locs)))))
- (_ #t))
- #t)
-
- (lambda (x _ env locs)
- ;; Up from X.
- #t)
-
- (lambda (_ env)
- ;; Post-processing.
- #t)
-
- #t))
-;;; Tree-il canonicalizer
-
-;; Copyright (C) 2011, 2012, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language tree-il canonicalize)
- #\use-module (language tree-il)
- #\use-module (ice-9 match)
- #\use-module (srfi srfi-1)
- #\export (canonicalize!))
-
-(define (tree-il-any proc exp)
- (tree-il-fold (lambda (exp res)
- (or res (proc exp)))
- (lambda (exp res)
- (or res (proc exp)))
- (lambda (exp res) res)
- #f exp))
-
-(define (canonicalize! x)
- (post-order!
- (lambda (x)
- (match x
- (($ <sequence> src (tail))
- tail)
- (($ <sequence> src exps)
- (and (any sequence? exps)
- (make-sequence src
- (append-map (lambda (x)
- (if (sequence? x)
- (sequence-exps x)
- (list x)))
- exps))))
- (($ <let> src () () () body)
- body)
- (($ <letrec> src _ () () () body)
- body)
- (($ <fix> src () () () body)
- body)
- (($ <dynlet> src () () body)
- body)
- (($ <lambda> src meta #f)
- ;; Give a body to case-lambda with no clauses.
- (make-lambda
- src meta
- (make-lambda-case
- #f '() #f #f #f '() '()
- (make-application
- #f
- (make-primitive-ref #f 'throw)
- (list (make-const #f 'wrong-number-of-args)
- (make-const #f #f)
- (make-const #f "Wrong number of arguments")
- (make-const #f '())
- (make-const #f #f)))
- #f)))
- (($ <prompt> src tag body handler)
- (define (escape-only? handler)
- (match handler
- (($ <lambda-case> _ (_ . _) _ _ _ _ (cont . _) body #f)
- (not (tree-il-any (lambda (x)
- (and (lexical-ref? x)
- (eq? (lexical-ref-gensym x) cont)))
- body)))
- (else #f)))
- (define (thunk-application? x)
- (match x
- (($ <application> _
- ($ <lambda> _ _ ($ <lambda-case> _ () #f #f #f))
- ()) #t)
- (_ #f)))
- (define (make-thunk-application body)
- (define thunk
- (make-lambda #f '()
- (make-lambda-case #f '() #f #f #f '() '() body #f)))
- (make-application #f thunk '()))
-
- ;; This code has a nasty job to do: to ensure that either the
- ;; handler is escape-only, or the body is the application of a
- ;; thunk. Sad but true.
- (if (or (escape-only? handler)
- (thunk-application? body))
- #f
- (make-prompt src tag (make-thunk-application body) handler)))
- (_ #f)))
- x))
-;;; TREE-IL -> GLIL compiler
-
-;; Copyright (C) 2001,2008,2009,2010,2011,2012,2013,2014 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language tree-il compile-glil)
- #\use-module (system base syntax)
- #\use-module (system base pmatch)
- #\use-module (system base message)
- #\use-module (ice-9 receive)
- #\use-module (language glil)
- #\use-module (system vm instruction)
- #\use-module (language tree-il)
- #\use-module (language tree-il optimize)
- #\use-module (language tree-il canonicalize)
- #\use-module (language tree-il analyze)
- #\use-module ((srfi srfi-1) #\select (filter-map))
- #\export (compile-glil))
-
-;; allocation:
-;; sym -> {lambda -> address}
-;; lambda -> (labels . free-locs)
-;; lambda-case -> (gensym . nlocs)
-;;
-;; address ::= (local? boxed? . index)
-;; labels ::= ((sym . lambda) ...)
-;; free-locs ::= ((sym0 . address0) (sym1 . address1) ...)
-;; free variable addresses are relative to parent proc.
-
-(define *comp-module* (make-fluid))
-
-(define %warning-passes
- `((unused-variable . ,unused-variable-analysis)
- (unused-toplevel . ,unused-toplevel-analysis)
- (unbound-variable . ,unbound-variable-analysis)
- (arity-mismatch . ,arity-analysis)
- (format . ,format-analysis)))
-
-(define (compile-glil x e opts)
- (define warnings
- (or (and=> (memq #\warnings opts) cadr)
- '()))
-
- ;; Go through the warning passes.
- (let ((analyses (filter-map (lambda (kind)
- (assoc-ref %warning-passes kind))
- warnings)))
- (analyze-tree analyses x e))
-
- (let* ((x (make-lambda (tree-il-src x) '()
- (make-lambda-case #f '() #f #f #f '() '() x #f)))
- (x (optimize! x e opts))
- (x (canonicalize! x))
- (allocation (analyze-lexicals x)))
-
- (with-fluids ((*comp-module* e))
- (values (flatten-lambda x #f allocation)
- e
- e))))
-
-
-
-(define *primcall-ops* (make-hash-table))
-(for-each
- (lambda (x) (hash-set! *primcall-ops* (car x) (cdr x)))
- '(((eq? . 2) . eq?)
- ((eqv? . 2) . eqv?)
- ((equal? . 2) . equal?)
- ((= . 2) . ee?)
- ((< . 2) . lt?)
- ((> . 2) . gt?)
- ((<= . 2) . le?)
- ((>= . 2) . ge?)
- ((+ . 2) . add)
- ((- . 2) . sub)
- ((1+ . 1) . add1)
- ((1- . 1) . sub1)
- ((* . 2) . mul)
- ((/ . 2) . div)
- ((quotient . 2) . quo)
- ((remainder . 2) . rem)
- ((modulo . 2) . mod)
- ((ash . 2) . ash)
- ((logand . 2) . logand)
- ((logior . 2) . logior)
- ((logxor . 2) . logxor)
- ((not . 1) . not)
- ((pair? . 1) . pair?)
- ((cons . 2) . cons)
- ((car . 1) . car)
- ((cdr . 1) . cdr)
- ((set-car! . 2) . set-car!)
- ((set-cdr! . 2) . set-cdr!)
- ((null? . 1) . null?)
- ((list? . 1) . list?)
- ((symbol? . 1) . symbol?)
- ((vector? . 1) . vector?)
- (list . list)
- (vector . vector)
- ((class-of . 1) . class-of)
- ((vector-ref . 2) . vector-ref)
- ((vector-set! . 3) . vector-set)
- ((variable-ref . 1) . variable-ref)
- ;; nb, *not* variable-set! -- the args are switched
- ((variable-bound? . 1) . variable-bound?)
- ((struct? . 1) . struct?)
- ((struct-vtable . 1) . struct-vtable)
- ((struct-ref . 2) . struct-ref)
- ((struct-set! . 3) . struct-set)
- (make-struct/no-tail . make-struct)
-
- ;; hack for javascript
- ((return . 1) . return)
- ;; hack for lua
- (return/values . return/values)
-
- ((bytevector-u8-ref . 2) . bv-u8-ref)
- ((bytevector-u8-set! . 3) . bv-u8-set)
- ((bytevector-s8-ref . 2) . bv-s8-ref)
- ((bytevector-s8-set! . 3) . bv-s8-set)
-
- ((bytevector-u16-ref . 3) . bv-u16-ref)
- ((bytevector-u16-set! . 4) . bv-u16-set)
- ((bytevector-u16-native-ref . 2) . bv-u16-native-ref)
- ((bytevector-u16-native-set! . 3) . bv-u16-native-set)
- ((bytevector-s16-ref . 3) . bv-s16-ref)
- ((bytevector-s16-set! . 4) . bv-s16-set)
- ((bytevector-s16-native-ref . 2) . bv-s16-native-ref)
- ((bytevector-s16-native-set! . 3) . bv-s16-native-set)
-
- ((bytevector-u32-ref . 3) . bv-u32-ref)
- ((bytevector-u32-set! . 4) . bv-u32-set)
- ((bytevector-u32-native-ref . 2) . bv-u32-native-ref)
- ((bytevector-u32-native-set! . 3) . bv-u32-native-set)
- ((bytevector-s32-ref . 3) . bv-s32-ref)
- ((bytevector-s32-set! . 4) . bv-s32-set)
- ((bytevector-s32-native-ref . 2) . bv-s32-native-ref)
- ((bytevector-s32-native-set! . 3) . bv-s32-native-set)
-
- ((bytevector-u64-ref . 3) . bv-u64-ref)
- ((bytevector-u64-set! . 4) . bv-u64-set)
- ((bytevector-u64-native-ref . 2) . bv-u64-native-ref)
- ((bytevector-u64-native-set! . 3) . bv-u64-native-set)
- ((bytevector-s64-ref . 3) . bv-s64-ref)
- ((bytevector-s64-set! . 4) . bv-s64-set)
- ((bytevector-s64-native-ref . 2) . bv-s64-native-ref)
- ((bytevector-s64-native-set! . 3) . bv-s64-native-set)
-
- ((bytevector-ieee-single-ref . 3) . bv-f32-ref)
- ((bytevector-ieee-single-set! . 4) . bv-f32-set)
- ((bytevector-ieee-single-native-ref . 2) . bv-f32-native-ref)
- ((bytevector-ieee-single-native-set! . 3) . bv-f32-native-set)
- ((bytevector-ieee-double-ref . 3) . bv-f64-ref)
- ((bytevector-ieee-double-set! . 4) . bv-f64-set)
- ((bytevector-ieee-double-native-ref . 2) . bv-f64-native-ref)
- ((bytevector-ieee-double-native-set! . 3) . bv-f64-native-set)))
-
-
-
-
-(define (make-label) (gensym ":L"))
-
-(define (vars->bind-list ids vars allocation proc)
- (map (lambda (id v)
- (pmatch (hashq-ref (hashq-ref allocation v) proc)
- ((#t ,boxed? . ,n)
- (list id boxed? n))
- (,x (error "bad var list element" id v x))))
- ids
- vars))
-
-(define (emit-bindings src ids vars allocation proc emit-code)
- (emit-code src (make-glil-bind
- (vars->bind-list ids vars allocation proc))))
-
-(define (with-output-to-code proc)
- (let ((out '()))
- (define (emit-code src x)
- (set! out (cons x out))
- (if src
- (set! out (cons (make-glil-source src) out))))
- (proc emit-code)
- (reverse out)))
-
-(define (flatten-lambda x self-label allocation)
- (record-case x
- ((<lambda> src meta body)
- (make-glil-program
- meta
- (with-output-to-code
- (lambda (emit-code)
- ;; write source info for proc
- (if src (emit-code #f (make-glil-source src)))
- ;; compile the body, yo
- (flatten-lambda-case body allocation x self-label
- (car (hashq-ref allocation x))
- emit-code)))))))
-
-(define (flatten-lambda-case lcase allocation self self-label fix-labels
- emit-code)
- (define (emit-label label)
- (emit-code #f (make-glil-label label)))
- (define (emit-branch src inst label)
- (emit-code src (make-glil-branch inst label)))
-
- ;; RA: "return address"; #f unless we're in a non-tail fix with labels
- ;; MVRA: "multiple-values return address"; #f unless we're in a let-values
- (let comp ((x lcase) (context 'tail) (RA #f) (MVRA #f))
- (define (comp-tail tree) (comp tree context RA MVRA))
- (define (comp-push tree) (comp tree 'push #f #f))
- (define (comp-drop tree) (comp tree 'drop #f #f))
- (define (comp-vals tree MVRA) (comp tree 'vals #f MVRA))
- (define (comp-fix tree RA) (comp tree context RA MVRA))
-
- ;; A couple of helpers. Note that if we are in tail context, we
- ;; won't have an RA.
- (define (maybe-emit-return)
- (if RA
- (emit-branch #f 'br RA)
- (if (eq? context 'tail)
- (emit-code #f (make-glil-call 'return 1)))))
-
- ;; After lexical binding forms in non-tail context, call this
- ;; function to clear stack slots, allowing their previous values to
- ;; be collected.
- (define (clear-stack-slots context syms)
- (case context
- ((push drop)
- (for-each (lambda (v)
- (and=>
- ;; Can be #f if the var is labels-allocated.
- (hashq-ref allocation v)
- (lambda (h)
- (pmatch (hashq-ref h self)
- ((#t _ . ,n)
- (emit-code #f (make-glil-void))
- (emit-code #f (make-glil-lexical #t #f 'set n)))
- (,loc (error "bad let var allocation" x loc))))))
- syms))))
-
- (record-case x
- ((<void>)
- (case context
- ((push vals tail)
- (emit-code #f (make-glil-void))))
- (maybe-emit-return))
-
- ((<const> src exp)
- (case context
- ((push vals tail)
- (emit-code src (make-glil-const exp))))
- (maybe-emit-return))
-
- ;; FIXME: should represent sequence as exps tail
- ((<sequence> exps)
- (let lp ((exps exps))
- (if (null? (cdr exps))
- (comp-tail (car exps))
- (begin
- (comp-drop (car exps))
- (lp (cdr exps))))))
-
- ((<application> src proc args)
- ;; FIXME: need a better pattern-matcher here
- (cond
- ((and (primitive-ref? proc)
- (eq? (primitive-ref-name proc) '@apply)
- (>= (length args) 1))
- (let ((proc (car args))
- (args (cdr args)))
- (cond
- ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'values)
- (not (eq? context 'push)) (not (eq? context 'vals)))
- ;; tail: (lambda () (apply values '(1 2)))
- ;; drop: (lambda () (apply values '(1 2)) 3)
- ;; push: (lambda () (list (apply values '(10 12)) 1))
- (case context
- ((drop) (for-each comp-drop args) (maybe-emit-return))
- ((tail)
- (for-each comp-push args)
- (emit-code src (make-glil-call 'return/values* (length args))))))
-
- (else
- (case context
- ((tail)
- (comp-push proc)
- (for-each comp-push args)
- (emit-code src (make-glil-call 'tail-apply (1+ (length args)))))
- ((push)
- (emit-code src (make-glil-call 'new-frame 0))
- (comp-push proc)
- (for-each comp-push args)
- (emit-code src (make-glil-call 'apply (1+ (length args))))
- (maybe-emit-return))
- ((vals)
- (comp-vals
- (make-application src (make-primitive-ref #f 'apply)
- (cons proc args))
- MVRA)
- (maybe-emit-return))
- ((drop)
- ;; Well, shit. The proc might return any number of
- ;; values (including 0), since it's in a drop context,
- ;; yet apply does not create a MV continuation. So we
- ;; mv-call out to our trampoline instead.
- (comp-drop
- (make-application src (make-primitive-ref #f 'apply)
- (cons proc args)))
- (maybe-emit-return)))))))
-
- ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'values))
- ;; tail: (lambda () (values '(1 2)))
- ;; drop: (lambda () (values '(1 2)) 3)
- ;; push: (lambda () (list (values '(10 12)) 1))
- ;; vals: (let-values (((a b ...) (values 1 2 ...))) ...)
- (case context
- ((drop) (for-each comp-drop args) (maybe-emit-return))
- ((push)
- (case (length args)
- ((0)
- ;; FIXME: This is surely an error. We need to add a
- ;; values-mismatch warning pass.
- (emit-code src (make-glil-call 'new-frame 0))
- (comp-push proc)
- (emit-code src (make-glil-call 'call 0))
- (maybe-emit-return))
- (else
- ;; Taking advantage of unspecified order of evaluation of
- ;; arguments.
- (for-each comp-drop (cdr args))
- (comp-push (car args))
- (maybe-emit-return))))
- ((vals)
- (for-each comp-push args)
- (emit-code #f (make-glil-const (length args)))
- (emit-branch src 'br MVRA))
- ((tail)
- (for-each comp-push args)
- (emit-code src (let ((len (length args)))
- (if (= len 1)
- (make-glil-call 'return 1)
- (make-glil-call 'return/values len)))))))
-
- ((and (primitive-ref? proc)
- (eq? (primitive-ref-name proc) '@call-with-values)
- (= (length args) 2))
- ;; CONSUMER
- ;; PRODUCER
- ;; (mv-call MV)
- ;; ([tail]-call 1)
- ;; goto POST
- ;; MV: [tail-]call/nargs
- ;; POST: (maybe-drop)
- (case context
- ((vals)
- ;; Fall back.
- (comp-vals
- (make-application src (make-primitive-ref #f 'call-with-values)
- args)
- MVRA)
- (maybe-emit-return))
- (else
- (let ((MV (make-label)) (POST (make-label))
- (producer (car args)) (consumer (cadr args)))
- (if (not (eq? context 'tail))
- (emit-code src (make-glil-call 'new-frame 0)))
- (comp-push consumer)
- (emit-code src (make-glil-call 'new-frame 0))
- (comp-push producer)
- (emit-code src (make-glil-mv-call 0 MV))
- (case context
- ((tail) (emit-code src (make-glil-call 'tail-call 1)))
- (else (emit-code src (make-glil-call 'call 1))
- (emit-branch #f 'br POST)))
- (emit-label MV)
- (case context
- ((tail) (emit-code src (make-glil-call 'tail-call/nargs 0)))
- (else (emit-code src (make-glil-call 'call/nargs 0))
- (emit-label POST)
- (if (eq? context 'drop)
- (emit-code #f (make-glil-call 'drop 1)))
- (maybe-emit-return)))))))
-
- ((and (primitive-ref? proc)
- (eq? (primitive-ref-name proc) '@call-with-current-continuation)
- (= (length args) 1))
- (case context
- ((tail)
- (comp-push (car args))
- (emit-code src (make-glil-call 'tail-call/cc 1)))
- ((vals)
- (comp-vals
- (make-application
- src (make-primitive-ref #f 'call-with-current-continuation)
- args)
- MVRA)
- (maybe-emit-return))
- ((push)
- (comp-push (car args))
- (emit-code src (make-glil-call 'call/cc 1))
- (maybe-emit-return))
- ((drop)
- ;; Crap. Just like `apply' in drop context.
- (comp-drop
- (make-application
- src (make-primitive-ref #f 'call-with-current-continuation)
- args))
- (maybe-emit-return))))
-
- ;; A hack for variable-set, the opcode for which takes its args
- ;; reversed, relative to the variable-set! function
- ((and (primitive-ref? proc)
- (eq? (primitive-ref-name proc) 'variable-set!)
- (= (length args) 2))
- (comp-push (cadr args))
- (comp-push (car args))
- (emit-code src (make-glil-call 'variable-set 2))
- (case context
- ((tail push vals) (emit-code #f (make-glil-void))))
- (maybe-emit-return))
-
- ((and (primitive-ref? proc)
- (or (hash-ref *primcall-ops*
- (cons (primitive-ref-name proc) (length args)))
- (hash-ref *primcall-ops* (primitive-ref-name proc))))
- => (lambda (op)
- (for-each comp-push args)
- (emit-code src (make-glil-call op (length args)))
- (case (instruction-pushes op)
- ((0)
- (case context
- ((tail push vals) (emit-code #f (make-glil-void))))
- (maybe-emit-return))
- ((1)
- (case context
- ((drop) (emit-code #f (make-glil-call 'drop 1))))
- (maybe-emit-return))
- ((-1)
- ;; A control instruction, like return/values. Here we
- ;; just have to hope that the author of the tree-il
- ;; knew what they were doing.
- *unspecified*)
- (else
- (error "bad primitive op: too many pushes"
- op (instruction-pushes op))))))
-
- ;; call to the same lambda-case in tail position
- ((and (lexical-ref? proc)
- self-label (eq? (lexical-ref-gensym proc) self-label)
- (eq? context 'tail)
- (not (lambda-case-kw lcase))
- (not (lambda-case-rest lcase))
- (= (length args)
- (+ (length (lambda-case-req lcase))
- (or (and=> (lambda-case-opt lcase) length) 0))))
- (for-each comp-push args)
- (for-each (lambda (sym)
- (pmatch (hashq-ref (hashq-ref allocation sym) self)
- ((#t #f . ,index) ; unboxed
- (emit-code #f (make-glil-lexical #t #f 'set index)))
- ((#t #t . ,index) ; boxed
- ;; new box
- (emit-code #f (make-glil-lexical #t #t 'box index)))
- (,x (error "bad lambda-case arg allocation" x))))
- (reverse (lambda-case-gensyms lcase)))
- (emit-branch src 'br (car (hashq-ref allocation lcase))))
-
- ;; lambda, the ultimate goto
- ((and (lexical-ref? proc)
- (assq (lexical-ref-gensym proc) fix-labels))
- ;; like the self-tail-call case, though we can handle "drop"
- ;; contexts too. first, evaluate new values, pushing them on
- ;; the stack
- (for-each comp-push args)
- ;; find the specific case, rename args, and goto the case label
- (let lp ((lcase (lambda-body
- (assq-ref fix-labels (lexical-ref-gensym proc)))))
- (cond
- ((and (lambda-case? lcase)
- (not (lambda-case-kw lcase))
- (not (lambda-case-opt lcase))
- (not (lambda-case-rest lcase))
- (= (length args) (length (lambda-case-req lcase))))
- ;; we have a case that matches the args; rename variables
- ;; and goto the case label
- (for-each (lambda (sym)
- (pmatch (hashq-ref (hashq-ref allocation sym) self)
- ((#t #f . ,index) ; unboxed
- (emit-code #f (make-glil-lexical #t #f 'set index)))
- ((#t #t . ,index) ; boxed
- (emit-code #f (make-glil-lexical #t #t 'box index)))
- (,x (error "bad lambda-case arg allocation" x))))
- (reverse (lambda-case-gensyms lcase)))
- (emit-branch src 'br (car (hashq-ref allocation lcase))))
- ((lambda-case? lcase)
- ;; no match, try next case
- (lp (lambda-case-alternate lcase)))
- (else
- ;; no cases left. we can't really handle this currently.
- ;; ideally we would push on a new frame, then do a "local
- ;; call" -- which doesn't require consing up a program
- ;; object. but for now error, as this sort of case should
- ;; preclude label allocation.
- (error "couldn't find matching case for label call" x)))))
-
- (else
- (if (not (eq? context 'tail))
- (emit-code src (make-glil-call 'new-frame 0)))
- (comp-push proc)
- (for-each comp-push args)
- (let ((len (length args)))
- (case context
- ((tail) (if (<= len #xff)
- (emit-code src (make-glil-call 'tail-call len))
- (begin
- (comp-push (make-const #f len))
- (emit-code src (make-glil-call 'tail-call/nargs 0)))))
- ((push) (if (<= len #xff)
- (emit-code src (make-glil-call 'call len))
- (begin
- (comp-push (make-const #f len))
- (emit-code src (make-glil-call 'call/nargs 0))))
- (maybe-emit-return))
- ;; FIXME: mv-call doesn't have a /nargs variant, so it is
- ;; limited to 255 args. Can work around it with a
- ;; trampoline and tail-call/nargs, but it's not so nice.
- ((vals) (emit-code src (make-glil-mv-call len MVRA))
- (maybe-emit-return))
- ((drop) (let ((MV (make-label)) (POST (make-label)))
- (emit-code src (make-glil-mv-call len MV))
- (emit-code #f (make-glil-call 'drop 1))
- (emit-branch #f 'br (or RA POST))
- (emit-label MV)
- (emit-code #f (make-glil-mv-bind 0 #f))
- (if RA
- (emit-branch #f 'br RA)
- (emit-label POST)))))))))
-
- ((<conditional> src test consequent alternate)
- ;; TEST
- ;; (br-if-not L1)
- ;; consequent
- ;; (br L2)
- ;; L1: alternate
- ;; L2:
- (let ((L1 (make-label)) (L2 (make-label)))
- ;; need a pattern matcher
- (record-case test
- ((<application> proc args)
- (record-case proc
- ((<primitive-ref> name)
- (let ((len (length args)))
- (cond
-
- ((and (eq? name 'eq?) (= len 2))
- (comp-push (car args))
- (comp-push (cadr args))
- (emit-branch src 'br-if-not-eq L1))
-
- ((and (eq? name 'null?) (= len 1))
- (comp-push (car args))
- (emit-branch src 'br-if-not-null L1))
-
- ((and (eq? name 'not) (= len 1))
- (let ((app (car args)))
- (record-case app
- ((<application> proc args)
- (let ((len (length args)))
- (record-case proc
- ((<primitive-ref> name)
- (cond
-
- ((and (eq? name 'eq?) (= len 2))
- (comp-push (car args))
- (comp-push (cadr args))
- (emit-branch src 'br-if-eq L1))
-
- ((and (eq? name 'null?) (= len 1))
- (comp-push (car args))
- (emit-branch src 'br-if-null L1))
-
- (else
- (comp-push app)
- (emit-branch src 'br-if L1))))
- (else
- (comp-push app)
- (emit-branch src 'br-if L1)))))
- (else
- (comp-push app)
- (emit-branch src 'br-if L1)))))
-
- (else
- (comp-push test)
- (emit-branch src 'br-if-not L1)))))
- (else
- (comp-push test)
- (emit-branch src 'br-if-not L1))))
- (else
- (comp-push test)
- (emit-branch src 'br-if-not L1)))
-
- (comp-tail consequent)
- ;; if there is an RA, comp-tail will cause a jump to it -- just
- ;; have to clean up here if there is no RA.
- (if (and (not RA) (not (eq? context 'tail)))
- (emit-branch #f 'br L2))
- (emit-label L1)
- (comp-tail alternate)
- (if (and (not RA) (not (eq? context 'tail)))
- (emit-label L2))))
-
- ((<primitive-ref> src name)
- (cond
- ((eq? (module-variable (fluid-ref *comp-module*) name)
- (module-variable the-root-module name))
- (case context
- ((tail push vals)
- (emit-code src (make-glil-toplevel 'ref name))))
- (maybe-emit-return))
- ((module-variable the-root-module name)
- (case context
- ((tail push vals)
- (emit-code src (make-glil-module 'ref '(guile) name #f))))
- (maybe-emit-return))
- (else
- (case context
- ((tail push vals)
- (emit-code src (make-glil-module
- 'ref (module-name (fluid-ref *comp-module*)) name #f))))
- (maybe-emit-return))))
-
- ((<lexical-ref> src gensym)
- (case context
- ((push vals tail)
- (pmatch (hashq-ref (hashq-ref allocation gensym) self)
- ((,local? ,boxed? . ,index)
- (emit-code src (make-glil-lexical local? boxed? 'ref index)))
- (,loc
- (error "bad lexical allocation" x loc)))))
- (maybe-emit-return))
-
- ((<lexical-set> src gensym exp)
- (comp-push exp)
- (pmatch (hashq-ref (hashq-ref allocation gensym) self)
- ((,local? ,boxed? . ,index)
- (emit-code src (make-glil-lexical local? boxed? 'set index)))
- (,loc
- (error "bad lexical allocation" x loc)))
- (case context
- ((tail push vals)
- (emit-code #f (make-glil-void))))
- (maybe-emit-return))
-
- ((<module-ref> src mod name public?)
- (emit-code src (make-glil-module 'ref mod name public?))
- (case context
- ((drop) (emit-code #f (make-glil-call 'drop 1))))
- (maybe-emit-return))
-
- ((<module-set> src mod name public? exp)
- (comp-push exp)
- (emit-code src (make-glil-module 'set mod name public?))
- (case context
- ((tail push vals)
- (emit-code #f (make-glil-void))))
- (maybe-emit-return))
-
- ((<toplevel-ref> src name)
- (emit-code src (make-glil-toplevel 'ref name))
- (case context
- ((drop) (emit-code #f (make-glil-call 'drop 1))))
- (maybe-emit-return))
-
- ((<toplevel-set> src name exp)
- (comp-push exp)
- (emit-code src (make-glil-toplevel 'set name))
- (case context
- ((tail push vals)
- (emit-code #f (make-glil-void))))
- (maybe-emit-return))
-
- ((<toplevel-define> src name exp)
- (comp-push exp)
- (emit-code src (make-glil-toplevel 'define name))
- (case context
- ((tail push vals)
- (emit-code #f (make-glil-void))))
- (maybe-emit-return))
-
- ((<lambda>)
- (let ((free-locs (cdr (hashq-ref allocation x))))
- (case context
- ((push vals tail)
- (emit-code #f (flatten-lambda x #f allocation))
- (if (not (null? free-locs))
- (begin
- (for-each
- (lambda (loc)
- (pmatch loc
- ((,local? ,boxed? . ,n)
- (emit-code #f (make-glil-lexical local? #f 'ref n)))
- (else (error "bad lambda free var allocation" x loc))))
- free-locs)
- (emit-code #f (make-glil-call 'make-closure
- (length free-locs))))))))
- (maybe-emit-return))
-
- ((<lambda-case> src req opt rest kw inits gensyms alternate body)
- ;; o/~ feature on top of feature o/~
- ;; req := (name ...)
- ;; opt := (name ...) | #f
- ;; rest := name | #f
- ;; kw: (allow-other-keys? (keyword name var) ...) | #f
- ;; gensyms: (sym ...)
- ;; init: tree-il in context of gensyms
- ;; gensyms map to named arguments in the following order:
- ;; required, optional (positional), rest, keyword.
- (let* ((nreq (length req))
- (nopt (if opt (length opt) 0))
- (rest-idx (and rest (+ nreq nopt)))
- (opt-names (or opt '()))
- (allow-other-keys? (if kw (car kw) #f))
- (kw-indices (map (lambda (x)
- (pmatch x
- ((,key ,name ,var)
- (cons key (list-index gensyms var)))
- (else (error "bad kwarg" x))))
- (if kw (cdr kw) '())))
- (nargs (apply max (+ nreq nopt (if rest 1 0))
- (map 1+ (map cdr kw-indices))))
- (nlocs (cdr (hashq-ref allocation x)))
- (alternate-label (and alternate (make-label))))
- (or (= nargs
- (length gensyms)
- (+ nreq (length inits) (if rest 1 0)))
- (error "lambda-case gensyms don't correspond to args"
- req opt rest kw inits gensyms nreq nopt kw-indices nargs))
- ;; the prelude, to check args & reset the stack pointer,
- ;; allowing room for locals
- (emit-code
- src
- (cond
- (kw
- (make-glil-kw-prelude nreq nopt rest-idx kw-indices
- allow-other-keys? nlocs alternate-label))
- ((or rest opt)
- (make-glil-opt-prelude nreq nopt rest-idx nlocs alternate-label))
- (#t
- (make-glil-std-prelude nreq nlocs alternate-label))))
- ;; box args if necessary
- (for-each
- (lambda (v)
- (pmatch (hashq-ref (hashq-ref allocation v) self)
- ((#t #t . ,n)
- (emit-code #f (make-glil-lexical #t #f 'ref n))
- (emit-code #f (make-glil-lexical #t #t 'box n)))))
- gensyms)
- ;; write bindings info
- (if (not (null? gensyms))
- (emit-bindings
- #f
- (let lp ((kw (if kw (cdr kw) '()))
- (names (append (reverse opt-names) (reverse req)))
- (gensyms (list-tail gensyms (+ nreq nopt
- (if rest 1 0)))))
- (pmatch kw
- (()
- ;; fixme: check that gensyms is empty
- (reverse (if rest (cons rest names) names)))
- (((,key ,name ,var) . ,kw)
- (if (memq var gensyms)
- (lp kw (cons name names) (delq var gensyms))
- (lp kw names gensyms)))
- (,kw (error "bad keywords, yo" kw))))
- gensyms allocation self emit-code))
- ;; init optional/kw args
- (let lp ((inits inits) (n nreq) (gensyms (list-tail gensyms nreq)))
- (cond
- ((null? inits)) ; done
- ((and rest-idx (= n rest-idx))
- (lp inits (1+ n) (cdr gensyms)))
- (#t
- (pmatch (hashq-ref (hashq-ref allocation (car gensyms)) self)
- ((#t ,boxed? . ,n*) (guard (= n* n))
- (let ((L (make-label)))
- (emit-code #f (make-glil-lexical #t boxed? 'bound? n))
- (emit-code #f (make-glil-branch 'br-if L))
- (comp-push (car inits))
- (emit-code #f (make-glil-lexical #t boxed? 'set n))
- (emit-label L)
- (lp (cdr inits) (1+ n) (cdr gensyms))))
- (#t (error "bad arg allocation" (car gensyms) inits))))))
- ;; post-prelude case label for label calls
- (emit-label (car (hashq-ref allocation x)))
- (comp-tail body)
- (if (not (null? gensyms))
- (emit-code #f (make-glil-unbind)))
- (if alternate-label
- (begin
- (emit-label alternate-label)
- (flatten-lambda-case alternate allocation self self-label
- fix-labels emit-code)))))
-
- ((<let> src names gensyms vals body)
- (for-each comp-push vals)
- (emit-bindings src names gensyms allocation self emit-code)
- (for-each (lambda (v)
- (pmatch (hashq-ref (hashq-ref allocation v) self)
- ((#t #f . ,n)
- (emit-code src (make-glil-lexical #t #f 'set n)))
- ((#t #t . ,n)
- (emit-code src (make-glil-lexical #t #t 'box n)))
- (,loc (error "bad let var allocation" x loc))))
- (reverse gensyms))
- (comp-tail body)
- (clear-stack-slots context gensyms)
- (emit-code #f (make-glil-unbind)))
-
- ((<letrec> src in-order? names gensyms vals body)
- ;; First prepare heap storage slots.
- (for-each (lambda (v)
- (pmatch (hashq-ref (hashq-ref allocation v) self)
- ((#t #t . ,n)
- (emit-code src (make-glil-lexical #t #t 'empty-box n)))
- (,loc (error "bad letrec var allocation" x loc))))
- gensyms)
- ;; Even though the slots are empty, the bindings are valid.
- (emit-bindings src names gensyms allocation self emit-code)
- (cond
- (in-order?
- ;; For letrec*, bind values in order.
- (for-each (lambda (name v val)
- (pmatch (hashq-ref (hashq-ref allocation v) self)
- ((#t #t . ,n)
- (comp-push val)
- (emit-code src (make-glil-lexical #t #t 'set n)))
- (,loc (error "bad letrec var allocation" x loc))))
- names gensyms vals))
- (else
- ;; But for letrec, eval all values, then bind.
- (for-each comp-push vals)
- (for-each (lambda (v)
- (pmatch (hashq-ref (hashq-ref allocation v) self)
- ((#t #t . ,n)
- (emit-code src (make-glil-lexical #t #t 'set n)))
- (,loc (error "bad letrec var allocation" x loc))))
- (reverse gensyms))))
- (comp-tail body)
- (clear-stack-slots context gensyms)
- (emit-code #f (make-glil-unbind)))
-
- ((<fix> src names gensyms vals body)
- ;; The ideal here is to just render the lambda bodies inline, and
- ;; wire the code together with gotos. We can do that if
- ;; analyze-lexicals has determined that a given var has "label"
- ;; allocation -- which is the case if it is in `fix-labels'.
- ;;
- ;; But even for closures that we can't inline, we can do some
- ;; tricks to avoid heap-allocation for the binding itself. Since
- ;; we know the vals are lambdas, we can set them to their local
- ;; var slots first, then capture their bindings, mutating them in
- ;; place.
- (let ((new-RA (if (or (eq? context 'tail) RA) #f (make-label))))
- (for-each
- (lambda (x v)
- (cond
- ((hashq-ref allocation x)
- ;; allocating a closure
- (emit-code #f (flatten-lambda x v allocation))
- (let ((free-locs (cdr (hashq-ref allocation x))))
- (if (not (null? free-locs))
- ;; Need to make-closure first, so we have a fresh closure on
- ;; the heap, but with a temporary free values.
- (begin
- (for-each (lambda (loc)
- (emit-code #f (make-glil-const #f)))
- free-locs)
- (emit-code #f (make-glil-call 'make-closure
- (length free-locs))))))
- (pmatch (hashq-ref (hashq-ref allocation v) self)
- ((#t #f . ,n)
- (emit-code src (make-glil-lexical #t #f 'set n)))
- (,loc (error "bad fix var allocation" x loc))))
- (else
- ;; labels allocation: emit label & body, but jump over it
- (let ((POST (make-label)))
- (emit-branch #f 'br POST)
- (let lp ((lcase (lambda-body x)))
- (if lcase
- (record-case lcase
- ((<lambda-case> src req gensyms body alternate)
- (emit-label (car (hashq-ref allocation lcase)))
- ;; FIXME: opt & kw args in the bindings
- (emit-bindings #f req gensyms allocation self emit-code)
- (if src
- (emit-code #f (make-glil-source src)))
- (comp-fix body (or RA new-RA))
- (emit-code #f (make-glil-unbind))
- (lp alternate)))
- (emit-label POST)))))))
- vals
- gensyms)
- ;; Emit bindings metadata for closures
- (let ((binds (let lp ((out '()) (gensyms gensyms) (names names))
- (cond ((null? gensyms) (reverse! out))
- ((assq (car gensyms) fix-labels)
- (lp out (cdr gensyms) (cdr names)))
- (else
- (lp (acons (car gensyms) (car names) out)
- (cdr gensyms) (cdr names)))))))
- (emit-bindings src (map cdr binds) (map car binds)
- allocation self emit-code))
- ;; Now go back and fix up the bindings for closures.
- (for-each
- (lambda (x v)
- (let ((free-locs (if (hashq-ref allocation x)
- (cdr (hashq-ref allocation x))
- ;; can hit this latter case for labels allocation
- '())))
- (if (not (null? free-locs))
- (begin
- (for-each
- (lambda (loc)
- (pmatch loc
- ((,local? ,boxed? . ,n)
- (emit-code #f (make-glil-lexical local? #f 'ref n)))
- (else (error "bad free var allocation" x loc))))
- free-locs)
- (pmatch (hashq-ref (hashq-ref allocation v) self)
- ((#t #f . ,n)
- (emit-code #f (make-glil-lexical #t #f 'fix n)))
- (,loc (error "bad fix var allocation" x loc)))))))
- vals
- gensyms)
- (comp-tail body)
- (if new-RA
- (emit-label new-RA))
- (clear-stack-slots context gensyms)
- (emit-code #f (make-glil-unbind))))
-
- ((<let-values> src exp body)
- (record-case body
- ((<lambda-case> req opt kw rest gensyms body alternate)
- (if (or opt kw alternate)
- (error "unexpected lambda-case in let-values" x))
- (let ((MV (make-label)))
- (comp-vals exp MV)
- (emit-code #f (make-glil-const 1))
- (emit-label MV)
- (emit-code src (make-glil-mv-bind
- (vars->bind-list
- (append req (if rest (list rest) '()))
- gensyms allocation self)
- (and rest #t)))
- (for-each (lambda (v)
- (pmatch (hashq-ref (hashq-ref allocation v) self)
- ((#t #f . ,n)
- (emit-code src (make-glil-lexical #t #f 'set n)))
- ((#t #t . ,n)
- (emit-code src (make-glil-lexical #t #t 'box n)))
- (,loc (error "bad let-values var allocation" x loc))))
- (reverse gensyms))
- (comp-tail body)
- (clear-stack-slots context gensyms)
- (emit-code #f (make-glil-unbind))))))
-
- ;; much trickier than i thought this would be, at first, due to the need
- ;; to have body's return value(s) on the stack while the unwinder runs,
- ;; then proceed with returning or dropping or what-have-you, interacting
- ;; with RA and MVRA. What have you, I say.
- ((<dynwind> src body winder unwinder)
- (comp-push winder)
- (comp-push unwinder)
- (comp-drop (make-application src winder '()))
- (emit-code #f (make-glil-call 'wind 2))
-
- (case context
- ((tail)
- (let ((MV (make-label)))
- (comp-vals body MV)
- ;; one value: unwind...
- (emit-code #f (make-glil-call 'unwind 0))
- (comp-drop (make-application src unwinder '()))
- ;; ...and return the val
- (emit-code #f (make-glil-call 'return 1))
-
- (emit-label MV)
- ;; multiple values: unwind...
- (emit-code #f (make-glil-call 'unwind 0))
- (comp-drop (make-application src unwinder '()))
- ;; and return the values.
- (emit-code #f (make-glil-call 'return/nvalues 1))))
-
- ((push)
- ;; we only want one value. so ask for one value
- (comp-push body)
- ;; and unwind, leaving the val on the stack
- (emit-code #f (make-glil-call 'unwind 0))
- (comp-drop (make-application src unwinder '())))
-
- ((vals)
- (let ((MV (make-label)))
- (comp-vals body MV)
- ;; one value: push 1 and fall through to MV case
- (emit-code #f (make-glil-const 1))
-
- (emit-label MV)
- ;; multiple values: unwind...
- (emit-code #f (make-glil-call 'unwind 0))
- (comp-drop (make-application src unwinder '()))
- ;; and goto the MVRA.
- (emit-branch #f 'br MVRA)))
-
- ((drop)
- ;; compile body, discarding values. then unwind...
- (comp-drop body)
- (emit-code #f (make-glil-call 'unwind 0))
- (comp-drop (make-application src unwinder '()))
- ;; and fall through, or goto RA if there is one.
- (if RA
- (emit-branch #f 'br RA)))))
-
- ((<dynlet> src fluids vals body)
- (for-each comp-push fluids)
- (for-each comp-push vals)
- (emit-code #f (make-glil-call 'wind-fluids (length fluids)))
-
- (case context
- ((tail)
- (let ((MV (make-label)))
- ;; NB: in tail case, it is possible to preserve asymptotic tail
- ;; recursion, via merging unwind-fluids structures -- but we'd need
- ;; to compile in the body twice (once in tail context, assuming the
- ;; caller unwinds, and once with this trampoline thing, unwinding
- ;; ourselves).
- (comp-vals body MV)
- ;; one value: unwind and return
- (emit-code #f (make-glil-call 'unwind-fluids 0))
- (emit-code #f (make-glil-call 'return 1))
-
- (emit-label MV)
- ;; multiple values: unwind and return values
- (emit-code #f (make-glil-call 'unwind-fluids 0))
- (emit-code #f (make-glil-call 'return/nvalues 1))))
-
- ((push)
- (comp-push body)
- (emit-code #f (make-glil-call 'unwind-fluids 0)))
-
- ((vals)
- (let ((MV (make-label)))
- (comp-vals body MV)
- ;; one value: push 1 and fall through to MV case
- (emit-code #f (make-glil-const 1))
-
- (emit-label MV)
- ;; multiple values: unwind and goto MVRA
- (emit-code #f (make-glil-call 'unwind-fluids 0))
- (emit-branch #f 'br MVRA)))
-
- ((drop)
- ;; compile body, discarding values. then unwind...
- (comp-drop body)
- (emit-code #f (make-glil-call 'unwind-fluids 0))
- ;; and fall through, or goto RA if there is one.
- (if RA
- (emit-branch #f 'br RA)))))
-
- ((<dynref> src fluid)
- (case context
- ((drop)
- (comp-drop fluid))
- ((push vals tail)
- (comp-push fluid)
- (emit-code #f (make-glil-call 'fluid-ref 1))))
- (maybe-emit-return))
-
- ((<dynset> src fluid exp)
- (comp-push fluid)
- (comp-push exp)
- (emit-code #f (make-glil-call 'fluid-set 2))
- (case context
- ((push vals tail)
- (emit-code #f (make-glil-void))))
- (maybe-emit-return))
-
- ;; What's the deal here? The deal is that we are compiling the start of a
- ;; delimited continuation. We try to avoid heap allocation in the normal
- ;; case; so the body is an expression, not a thunk, and we try to render
- ;; the handler inline. Also we did some analysis, in analyze.scm, so that
- ;; if the continuation isn't referenced, we don't reify it. This makes it
- ;; possible to implement catch and throw with delimited continuations,
- ;; without any overhead.
- ((<prompt> src tag body handler)
- (let ((H (make-label))
- (POST (make-label))
- (escape-only? (hashq-ref allocation x)))
- ;; First, set up the prompt.
- (comp-push tag)
- (emit-code src (make-glil-prompt H escape-only?))
-
- ;; Then we compile the body, with its normal return path, unwinding
- ;; before proceeding.
- (case context
- ((tail)
- (let ((MV (make-label)))
- (comp-vals body MV)
- ;; one value: unwind and return
- (emit-code #f (make-glil-call 'unwind 0))
- (emit-code #f (make-glil-call 'return 1))
- ;; multiple values: unwind and return
- (emit-label MV)
- (emit-code #f (make-glil-call 'unwind 0))
- (emit-code #f (make-glil-call 'return/nvalues 1))))
-
- ((push)
- ;; we only want one value. so ask for one value, unwind, and jump to
- ;; post
- (comp-push body)
- (emit-code #f (make-glil-call 'unwind 0))
- (emit-branch #f 'br (or RA POST)))
-
- ((vals)
- (let ((MV (make-label)))
- (comp-vals body MV)
- ;; one value: push 1 and fall through to MV case
- (emit-code #f (make-glil-const 1))
- ;; multiple values: unwind and goto MVRA
- (emit-label MV)
- (emit-code #f (make-glil-call 'unwind 0))
- (emit-branch #f 'br MVRA)))
-
- ((drop)
- ;; compile body, discarding values, then unwind & fall through.
- (comp-drop body)
- (emit-code #f (make-glil-call 'unwind 0))
- (emit-branch #f 'br (or RA POST))))
-
- (emit-label H)
- ;; Now the handler. The stack is now made up of the continuation, and
- ;; then the args to the continuation (pushed separately), and then the
- ;; number of args, including the continuation.
- (record-case handler
- ((<lambda-case> req opt kw rest gensyms body alternate)
- (if (or opt kw alternate)
- (error "unexpected lambda-case in prompt" x))
- (emit-code src (make-glil-mv-bind
- (vars->bind-list
- (append req (if rest (list rest) '()))
- gensyms allocation self)
- (and rest #t)))
- (for-each (lambda (v)
- (pmatch (hashq-ref (hashq-ref allocation v) self)
- ((#t #f . ,n)
- (emit-code src (make-glil-lexical #t #f 'set n)))
- ((#t #t . ,n)
- (emit-code src (make-glil-lexical #t #t 'box n)))
- (,loc
- (error "bad prompt handler arg allocation" x loc))))
- (reverse gensyms))
- (comp-tail body)
- (emit-code #f (make-glil-unbind))))
-
- (if (and (not RA)
- (or (eq? context 'push) (eq? context 'drop)))
- (emit-label POST))))
-
- ((<abort> src tag args tail)
- (comp-push tag)
- (for-each comp-push args)
- (comp-push tail)
- (emit-code src (make-glil-call 'abort (length args)))
- ;; so, the abort can actually return. if it does, the values will be on
- ;; the stack, then the MV marker, just as in an MV context.
- (case context
- ((tail)
- ;; Return values.
- (emit-code #f (make-glil-call 'return/nvalues 1)))
- ((drop)
- ;; Drop all values and goto RA, or otherwise fall through.
- (emit-code #f (make-glil-mv-bind 0 #f))
- (if RA (emit-branch #f 'br RA)))
- ((push)
- ;; Truncate to one value.
- (emit-code #f (make-glil-mv-bind 1 #f)))
- ((vals)
- ;; Go to MVRA.
- (emit-branch #f 'br MVRA)))))))
-;;; Common Subexpression Elimination (CSE) on Tree-IL
-
-;; Copyright (C) 2011, 2012, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (language tree-il cse)
- #\use-module (language tree-il)
- #\use-module (language tree-il primitives)
- #\use-module (language tree-il effects)
- #\use-module (ice-9 vlist)
- #\use-module (ice-9 match)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-9)
- #\use-module (srfi srfi-11)
- #\use-module (srfi srfi-26)
- #\export (cse))
-
-;;;
-;;; This pass eliminates common subexpressions in Tree-IL. It works
-;;; best locally -- within a function -- so it is meant to be run after
-;;; partial evaluation, which usually inlines functions and so opens up
-;;; a bigger space for CSE to work.
-;;;
-;;; The algorithm traverses the tree of expressions, returning two
-;;; values: the newly rebuilt tree, and a "database". The database is
-;;; the set of expressions that will have been evaluated as part of
-;;; evaluating an expression. For example, in:
-;;;
-;;; (1- (+ (if a b c) (* x y)))
-;;;
-;;; We can say that when it comes time to evaluate (1- <>), that the
-;;; subexpressions +, x, y, and (* x y) must have been evaluated in
-;;; values context. We know that a was evaluated in test context, but
-;;; we don't know if it was true or false.
-;;;
-;;; The expressions in the database /dominate/ any subsequent
-;;; expression: FOO dominates BAR if evaluation of BAR implies that any
-;;; effects associated with FOO have already occured.
-;;;
-;;; When adding expressions to the database, we record the context in
-;;; which they are evaluated. We treat expressions in test context
-;;; specially: the presence of such an expression indicates that the
-;;; expression is true. In this way we can elide duplicate predicates.
-;;;
-;;; Duplicate predicates are not common in code that users write, but
-;;; can occur quite frequently in macro-generated code.
-;;;
-;;; For example:
-;;;
-;;; (and (foo? x) (foo-bar x))
-;;; => (if (and (struct? x) (eq? (struct-vtable x) <foo>))
-;;; (if (and (struct? x) (eq? (struct-vtable x) <foo>))
-;;; (struct-ref x 1)
-;;; (throw 'not-a-foo))
-;;; #f))
-;;; => (if (and (struct? x) (eq? (struct-vtable x) <foo>))
-;;; (struct-ref x 1)
-;;; #f)
-;;;
-;;; A conditional bailout in effect context also has the effect of
-;;; adding predicates to the database:
-;;;
-;;; (begin (foo-bar x) (foo-baz x))
-;;; => (begin
-;;; (if (and (struct? x) (eq? (struct-vtable x) <foo>))
-;;; (struct-ref x 1)
-;;; (throw 'not-a-foo))
-;;; (if (and (struct? x) (eq? (struct-vtable x) <foo>))
-;;; (struct-ref x 2)
-;;; (throw 'not-a-foo)))
-;;; => (begin
-;;; (if (and (struct? x) (eq? (struct-vtable x) <foo>))
-;;; (struct-ref x 1)
-;;; (throw 'not-a-foo))
-;;; (struct-ref x 2))
-;;;
-;;; When removing code, we have to ensure that the semantics of the
-;;; source program and the residual program are the same. It's easy to
-;;; ensure that they have the same value, because those manipulations
-;;; are just algebraic, but the tricky thing is to ensure that the
-;;; expressions exhibit the same ordering of effects. For that, we use
-;;; the effects analysis of (language tree-il effects). We only
-;;; eliminate code if the duplicate code commutes with all of the
-;;; dominators on the path from the duplicate to the original.
-;;;
-;;; The implementation uses vhashes as the fundamental data structure.
-;;; This can be seen as a form of global value numbering. This
-;;; algorithm currently spends most of its time in vhash-assoc. I'm not
-;;; sure whether that is due to our bad hash function in Guile 2.0, an
-;;; inefficiency in vhashes, or what. Overall though the complexity
-;;; should be linear, or N log N -- whatever vhash-assoc's complexity
-;;; is. Walking the dominators is nonlinear, but that only happens when
-;;; we've actually found a common subexpression so that should be OK.
-;;;
-
-;; Logging helpers, as in peval.
-;;
-(define-syntax *logging* (identifier-syntax #f))
-;; (define %logging #f)
-;; (define-syntax *logging* (identifier-syntax %logging))
-(define-syntax log
- (syntax-rules (quote)
- ((log 'event arg ...)
- (if (and *logging*
- (or (eq? *logging* #t)
- (memq 'event *logging*)))
- (log* 'event arg ...)))))
-(define (log* event . args)
- (let ((pp (module-ref (resolve-interface '(ice-9 pretty-print))
- 'pretty-print)))
- (pp `(log ,event . ,args))
- (newline)
- (values)))
-
-;; A pre-pass on the source program to determine the set of assigned
-;; lexicals.
-;;
-(define* (build-assigned-var-table exp #\optional (table vlist-null))
- (tree-il-fold
- (lambda (exp res)
- res)
- (lambda (exp res)
- (match exp
- (($ <lexical-set> src name gensym exp)
- (vhash-consq gensym #t res))
- (_ res)))
- (lambda (exp res) res)
- table exp))
-
-(define (boolean-valued-primitive? primitive)
- (or (negate-primitive primitive)
- (eq? primitive 'not)
- (let ((chars (symbol->string primitive)))
- (eqv? (string-ref chars (1- (string-length chars)))
- #\?))))
-
-(define (boolean-valued-expression? x ctx)
- (match x
- (($ <application> _
- ($ <primitive-ref> _ (? boolean-valued-primitive?))) #t)
- (($ <const> _ (? boolean?)) #t)
- (_ (eq? ctx 'test))))
-
-(define (singly-valued-expression? x ctx)
- (match x
- (($ <const>) #t)
- (($ <lexical-ref>) #t)
- (($ <void>) #t)
- (($ <lexical-ref>) #t)
- (($ <primitive-ref>) #t)
- (($ <module-ref>) #t)
- (($ <toplevel-ref>) #t)
- (($ <application> _
- ($ <primitive-ref> _ (? singly-valued-primitive?))) #t)
- (($ <application> _ ($ <primitive-ref> _ 'values) (val)) #t)
- (($ <lambda>) #t)
- (_ (eq? ctx 'value))))
-
-(define* (cse exp)
- "Eliminate common subexpressions in EXP."
-
- (define assigned-lexical?
- (let ((table (build-assigned-var-table exp)))
- (lambda (sym)
- (vhash-assq sym table))))
-
- (define %compute-effects
- (make-effects-analyzer assigned-lexical?))
-
- (define (negate exp ctx)
- (match exp
- (($ <const> src x)
- (make-const src (not x)))
- (($ <void> src)
- (make-const src #f))
- (($ <conditional> src test consequent alternate)
- (make-conditional src test (negate consequent ctx) (negate alternate ctx)))
- (($ <application> _ ($ <primitive-ref> _ 'not)
- ((and x (? (cut boolean-valued-expression? <> ctx)))))
- x)
- (($ <application> src
- ($ <primitive-ref> _ (and pred (? negate-primitive)))
- args)
- (make-application src
- (make-primitive-ref #f (negate-primitive pred))
- args))
- (_
- (make-application #f (make-primitive-ref #f 'not) (list exp)))))
-
-
- (define (hasher n)
- (lambda (x size) (modulo n size)))
-
- (define (add-to-db exp effects ctx db)
- (let ((v (vector exp effects ctx))
- (h (tree-il-hash exp)))
- (vhash-cons v h db (hasher h))))
-
- (define (control-flow-boundary db)
- (let ((h (hashq 'lambda most-positive-fixnum)))
- (vhash-cons 'lambda h db (hasher h))))
-
- (define (find-dominating-expression exp effects ctx db)
- (define (entry-matches? v1 v2)
- (match (if (vector? v1) v1 v2)
- (#(exp* effects* ctx*)
- (and (tree-il=? exp exp*)
- (or (not ctx) (eq? ctx* ctx))))
- (_ #f)))
-
- (let ((len (vlist-length db))
- (h (tree-il-hash exp)))
- (and (vhash-assoc #t db entry-matches? (hasher h))
- (let lp ((n 0))
- (and (< n len)
- (match (vlist-ref db n)
- (('lambda . h*)
- ;; We assume that lambdas can escape and thus be
- ;; called from anywhere. Thus code inside a lambda
- ;; only has a dominating expression if it does not
- ;; depend on any effects.
- (and (not (depends-on-effects? effects &all-effects))
- (lp (1+ n))))
- ((#(exp* effects* ctx*) . h*)
- (log 'walk (unparse-tree-il exp) effects
- (unparse-tree-il exp*) effects* ctx*)
- (or (and (= h h*)
- (or (not ctx) (eq? ctx ctx*))
- (tree-il=? exp exp*))
- (and (effects-commute? effects effects*)
- (lp (1+ n)))))))))))
-
- ;; Return #t if EXP is dominated by an instance of itself. In that
- ;; case, we can exclude *type-check* effects, because the first
- ;; expression already caused them if needed.
- (define (has-dominating-effect? exp effects db)
- (or (constant? effects)
- (and
- (effect-free?
- (exclude-effects effects
- (logior &zero-values
- &allocation
- &type-check)))
- (find-dominating-expression exp effects #f db))))
-
- (define (find-dominating-test exp effects db)
- (and
- (effect-free?
- (exclude-effects effects (logior &allocation
- &type-check)))
- (match exp
- (($ <const> src val)
- (if (boolean? val)
- exp
- (make-const src (not (not val)))))
- ;; For (not FOO), try to prove FOO, then negate the result.
- (($ <application> src ($ <primitive-ref> _ 'not) (exp*))
- (match (find-dominating-test exp* effects db)
- (($ <const> _ val)
- (log 'inferring exp (not val))
- (make-const src (not val)))
- (_
- #f)))
- (_
- (cond
- ((find-dominating-expression exp effects 'test db)
- ;; We have an EXP fact, so we infer #t.
- (log 'inferring exp #t)
- (make-const (tree-il-src exp) #t))
- ((find-dominating-expression (negate exp 'test) effects 'test db)
- ;; We have a (not EXP) fact, so we infer #f.
- (log 'inferring exp #f)
- (make-const (tree-il-src exp) #f))
- (else
- ;; Otherwise we don't know.
- #f))))))
-
- (define (add-to-env exp name sym db env)
- (let* ((v (vector exp name sym (vlist-length db)))
- (h (tree-il-hash exp)))
- (vhash-cons v h env (hasher h))))
-
- (define (augment-env env names syms exps db)
- (if (null? names)
- env
- (let ((name (car names)) (sym (car syms)) (exp (car exps)))
- (augment-env (if (or (assigned-lexical? sym)
- (lexical-ref? exp))
- env
- (add-to-env exp name sym db env))
- (cdr names) (cdr syms) (cdr exps) db))))
-
- (define (find-dominating-lexical exp effects env db)
- (define (entry-matches? v1 v2)
- (match (if (vector? v1) v1 v2)
- (#(exp* name sym db)
- (tree-il=? exp exp*))
- (_ #f)))
-
- (define (unroll db base n)
- (or (zero? n)
- (match (vlist-ref db base)
- (('lambda . h*)
- ;; See note in find-dominating-expression.
- (and (not (depends-on-effects? effects &all-effects))
- (unroll db (1+ base) (1- n))))
- ((#(exp* effects* ctx*) . h*)
- (and (effects-commute? effects effects*)
- (unroll db (1+ base) (1- n)))))))
-
- (let ((h (tree-il-hash exp)))
- (and (effect-free? (exclude-effects effects &type-check))
- (vhash-assoc exp env entry-matches? (hasher h))
- (let ((env-len (vlist-length env))
- (db-len (vlist-length db)))
- (let lp ((n 0) (m 0))
- (and (< n env-len)
- (match (vlist-ref env n)
- ((#(exp* name sym db-len*) . h*)
- (let ((niter (- (- db-len db-len*) m)))
- (and (unroll db m niter)
- (if (and (= h h*) (tree-il=? exp* exp))
- (make-lexical-ref (tree-il-src exp) name sym)
- (lp (1+ n) (- db-len db-len*)))))))))))))
-
- (define (lookup-lexical sym env)
- (let ((env-len (vlist-length env)))
- (let lp ((n 0))
- (and (< n env-len)
- (match (vlist-ref env n)
- ((#(exp _ sym* _) . _)
- (if (eq? sym sym*)
- exp
- (lp (1+ n)))))))))
-
- (define (intersection db+ db-)
- (vhash-fold-right
- (lambda (k h out)
- (if (vhash-assoc k db- equal? (hasher h))
- (vhash-cons k h out (hasher h))
- out))
- vlist-null
- db+))
-
- (define (concat db1 db2)
- (vhash-fold-right (lambda (k h tail)
- (vhash-cons k h tail (hasher h)))
- db2 db1))
-
- (let visit ((exp exp)
- (db vlist-null) ; dominating expressions: #(exp effects ctx) -> hash
- (env vlist-null) ; named expressions: #(exp name sym db) -> hash
- (ctx 'values)) ; test, effect, value, or values
-
- (define (parallel-visit exps db env ctx)
- (let lp ((in exps) (out '()) (db* vlist-null))
- (if (pair? in)
- (call-with-values (lambda () (visit (car in) db env ctx))
- (lambda (x db**)
- (lp (cdr in) (cons x out) (concat db** db*))))
- (values (reverse out) db*))))
-
- (define (compute-effects exp)
- (%compute-effects exp (lambda (sym) (lookup-lexical sym env))))
-
- (define (bailout? exp)
- (causes-effects? (compute-effects exp) &definite-bailout))
-
- (define (return exp db*)
- (let ((effects (compute-effects exp)))
- (cond
- ((and (eq? ctx 'effect)
- (not (lambda-case? exp))
- (or (effect-free?
- (exclude-effects effects
- (logior &zero-values
- &allocation)))
- (has-dominating-effect? exp effects db)))
- (cond
- ((void? exp)
- (values exp db*))
- (else
- (log 'elide ctx (unparse-tree-il exp))
- (values (make-void #f) db*))))
- ((and (boolean-valued-expression? exp ctx)
- (find-dominating-test exp effects db))
- => (lambda (exp)
- (log 'propagate-test ctx (unparse-tree-il exp))
- (values exp db*)))
- ((and (singly-valued-expression? exp ctx)
- (find-dominating-lexical exp effects env db))
- => (lambda (exp)
- (log 'propagate-value ctx (unparse-tree-il exp))
- (values exp db*)))
- ((and (constant? effects) (memq ctx '(value values)))
- ;; Adds nothing to the db.
- (values exp db*))
- (else
- (log 'return ctx effects (unparse-tree-il exp) db*)
- (values exp
- (add-to-db exp effects ctx db*))))))
-
- (log 'visit ctx (unparse-tree-il exp) db env)
-
- (match exp
- (($ <const>)
- (return exp vlist-null))
- (($ <void>)
- (return exp vlist-null))
- (($ <lexical-ref> _ _ gensym)
- (return exp vlist-null))
- (($ <lexical-set> src name gensym exp)
- (let*-values (((exp db*) (visit exp db env 'value)))
- (return (make-lexical-set src name gensym exp)
- db*)))
- (($ <let> src names gensyms vals body)
- (let*-values (((vals db*) (parallel-visit vals db env 'value))
- ((body db**) (visit body (concat db* db)
- (augment-env env names gensyms vals db)
- ctx)))
- (return (make-let src names gensyms vals body)
- (concat db** db*))))
- (($ <letrec> src in-order? names gensyms vals body)
- (let*-values (((vals db*) (parallel-visit vals db env 'value))
- ((body db**) (visit body (concat db* db)
- (augment-env env names gensyms vals db)
- ctx)))
- (return (make-letrec src in-order? names gensyms vals body)
- (concat db** db*))))
- (($ <fix> src names gensyms vals body)
- (let*-values (((vals db*) (parallel-visit vals db env 'value))
- ((body db**) (visit body (concat db* db) env ctx)))
- (return (make-fix src names gensyms vals body)
- (concat db** db*))))
- (($ <let-values> src producer consumer)
- (let*-values (((producer db*) (visit producer db env 'values))
- ((consumer db**) (visit consumer (concat db* db) env ctx)))
- (return (make-let-values src producer consumer)
- (concat db** db*))))
- (($ <dynwind> src winder body unwinder)
- (let*-values (((pre db*) (visit winder db env 'value))
- ((body db**) (visit body (concat db* db) env ctx))
- ((post db***) (visit unwinder db env 'value)))
- (return (make-dynwind src pre body post)
- (concat db* (concat db** db***)))))
- (($ <dynlet> src fluids vals body)
- (let*-values (((fluids db*) (parallel-visit fluids db env 'value))
- ((vals db**) (parallel-visit vals db env 'value))
- ((body db***) (visit body (concat db** (concat db* db))
- env ctx)))
- (return (make-dynlet src fluids vals body)
- (concat db*** (concat db** db*)))))
- (($ <dynref> src fluid)
- (let*-values (((fluid db*) (visit fluid db env 'value)))
- (return (make-dynref src fluid)
- db*)))
- (($ <dynset> src fluid exp)
- (let*-values (((fluid db*) (visit fluid db env 'value))
- ((exp db**) (visit exp db env 'value)))
- (return (make-dynset src fluid exp)
- (concat db** db*))))
- (($ <toplevel-ref>)
- (return exp vlist-null))
- (($ <module-ref>)
- (return exp vlist-null))
- (($ <module-set> src mod name public? exp)
- (let*-values (((exp db*) (visit exp db env 'value)))
- (return (make-module-set src mod name public? exp)
- db*)))
- (($ <toplevel-define> src name exp)
- (let*-values (((exp db*) (visit exp db env 'value)))
- (return (make-toplevel-define src name exp)
- db*)))
- (($ <toplevel-set> src name exp)
- (let*-values (((exp db*) (visit exp db env 'value)))
- (return (make-toplevel-set src name exp)
- db*)))
- (($ <primitive-ref>)
- (return exp vlist-null))
- (($ <conditional> src test consequent alternate)
- (let*-values
- (((test db+) (visit test db env 'test))
- ((converse db-) (visit (negate test 'test) db env 'test))
- ((consequent db++) (visit consequent (concat db+ db) env ctx))
- ((alternate db--) (visit alternate (concat db- db) env ctx)))
- (match (make-conditional src test consequent alternate)
- (($ <conditional> _ ($ <const> _ exp))
- (if exp
- (return consequent (concat db++ db+))
- (return alternate (concat db-- db-))))
- ;; (if FOO A A) => (begin FOO A)
- (($ <conditional> src _
- ($ <const> _ a) ($ <const> _ (? (cut equal? a <>))))
- (visit (make-sequence #f (list test (make-const #f a)))
- db env ctx))
- ;; (if FOO #t #f) => FOO for boolean-valued FOO.
- (($ <conditional> src
- (? (cut boolean-valued-expression? <> ctx))
- ($ <const> _ #t) ($ <const> _ #f))
- (return test db+))
- ;; (if FOO #f #t) => (not FOO)
- (($ <conditional> src _ ($ <const> _ #f) ($ <const> _ #t))
- (visit (negate test ctx) db env ctx))
-
- ;; Allow "and"-like conditions to accumulate in test context.
- ((and c ($ <conditional> _ _ _ ($ <const> _ #f)))
- (return c (if (eq? ctx 'test) (concat db++ db+) vlist-null)))
- ((and c ($ <conditional> _ _ ($ <const> _ #f) _))
- (return c (if (eq? ctx 'test) (concat db-- db-) vlist-null)))
-
- ;; Conditional bailouts turn expressions into predicates.
- ((and c ($ <conditional> _ _ _ (? bailout?)))
- (return c (concat db++ db+)))
- ((and c ($ <conditional> _ _ (? bailout?) _))
- (return c (concat db-- db-)))
-
- (c
- (return c (intersection (concat db++ db+) (concat db-- db-)))))))
- (($ <application> src proc args)
- (let*-values (((proc db*) (visit proc db env 'value))
- ((args db**) (parallel-visit args db env 'value)))
- (return (make-application src proc args)
- (concat db** db*))))
- (($ <lambda> src meta body)
- (let*-values (((body _) (if body
- (visit body (control-flow-boundary db)
- env 'values)
- (values #f #f))))
- (return (make-lambda src meta body)
- vlist-null)))
- (($ <lambda-case> src req opt rest kw inits gensyms body alt)
- (let*-values (((inits _) (parallel-visit inits db env 'value))
- ((body db*) (visit body db env ctx))
- ((alt _) (if alt
- (visit alt db env ctx)
- (values #f #f))))
- (return (make-lambda-case src req opt rest kw inits gensyms body alt)
- (if alt vlist-null db*))))
- (($ <sequence> src exps)
- (let lp ((in exps) (out '()) (db* vlist-null))
- (match in
- ((last)
- (let*-values (((last db**) (visit last (concat db* db) env ctx)))
- (if (null? out)
- (return last (concat db** db*))
- (return (make-sequence src (reverse (cons last out)))
- (concat db** db*)))))
- ((head . rest)
- (let*-values (((head db**) (visit head (concat db* db) env 'effect)))
- (cond
- ((sequence? head)
- (lp (append (sequence-exps head) rest) out db*))
- ((void? head)
- (lp rest out db*))
- (else
- (lp rest (cons head out) (concat db** db*)))))))))
- (($ <prompt> src tag body handler)
- (let*-values (((tag db*) (visit tag db env 'value))
- ((body _) (visit body (concat db* db) env 'values))
- ((handler _) (visit handler (concat db* db) env ctx)))
- (return (make-prompt src tag body handler)
- db*)))
- (($ <abort> src tag args tail)
- (let*-values (((tag db*) (visit tag db env 'value))
- ((args db**) (parallel-visit args db env 'value))
- ((tail db***) (visit tail db env 'value)))
- (return (make-abort src tag args tail)
- (concat db* (concat db** db***))))))))
-;;; Tree-IL verifier
-
-;; Copyright (C) 2011, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (language tree-il debug)
- #\use-module (language tree-il)
- #\use-module (ice-9 match)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-26)
- #\export (verify-tree-il))
-
-(define (verify-tree-il exp)
- (define seen-gensyms (make-hash-table))
- (define (add sym env)
- (if (hashq-ref seen-gensyms sym)
- (error "duplicate gensym" sym)
- (begin
- (hashq-set! seen-gensyms sym #t)
- (cons sym env))))
- (define (add-env new env)
- (if (null? new)
- env
- (add-env (cdr new) (add (car new) env))))
-
- (let visit ((exp exp)
- (env '()))
- (match exp
- (($ <lambda-case> src req opt rest kw inits gensyms body alt)
- (cond
- ((not (and (list? req) (and-map symbol? req)))
- (error "bad required args (should be list of symbols)" exp))
- ((and opt (not (and (list? opt) (and-map symbol? opt))))
- (error "bad optionals (should be #f or list of symbols)" exp))
- ((and rest (not (symbol? rest)))
- (error "bad required args (should be #f or symbol)" exp))
- ((and kw (not (match kw
- ((aok . kwlist)
- (and (list? kwlist)
- (and-map
- (lambda (x)
- (match x
- (((? keyword?) (? symbol?) (? symbol? sym))
- (memq sym gensyms))
- (_ #f)))
- kwlist)))
- (_ #f))))
- (error "bad keywords (should be #f or (aok (kw name sym) ...))" exp))
- ((not (and (list? gensyms) (and-map symbol? gensyms)))
- (error "bad gensyms (should be list of symbols)" exp))
- ((not (and (list? gensyms) (and-map symbol? gensyms)))
- (error "bad gensyms (should be list of symbols)" exp))
- ((not (= (length gensyms)
- (+ (length req)
- (if opt (length opt) 0)
- ;; FIXME: technically possible for kw gensyms to
- ;; alias other gensyms
- (if rest 1 0)
- (if kw (1- (length kw)) 0))))
- (error "unexpected gensyms length" exp))
- (else
- (let lp ((env (add-env (take gensyms (length req)) env))
- (nopt (if opt (length opt) 0))
- (inits inits)
- (tail (drop gensyms (length req))))
- (if (zero? nopt)
- (let lp ((env (if rest (add (car tail) env) env))
- (inits inits)
- (tail (if rest (cdr tail) tail)))
- (if (pair? inits)
- (begin
- (visit (car inits) env)
- (lp (add (car tail) env) (cdr inits)
- (cdr tail)))
- (visit body env)))
- (begin
- (visit (car inits) env)
- (lp (add (car tail) env)
- (1- nopt)
- (cdr inits)
- (cdr tail)))))
- (if alt (visit alt env)))))
- (($ <lexical-ref> src name gensym)
- (cond
- ((not (symbol? name))
- (error "name should be a symbol" name))
- ((not (hashq-ref seen-gensyms gensym))
- (error "unbound lexical" exp))
- ((not (memq gensym env))
- (error "displaced lexical" exp))))
- (($ <lexical-set> src name gensym exp)
- (cond
- ((not (symbol? name))
- (error "name should be a symbol" name))
- ((not (hashq-ref seen-gensyms gensym))
- (error "unbound lexical" exp))
- ((not (memq gensym env))
- (error "displaced lexical" exp))
- (else
- (visit exp env))))
- (($ <lambda> src meta body)
- (cond
- ((and meta (not (and (list? meta) (and-map pair? meta))))
- (error "meta should be alist" meta))
- ((and body (not (lambda-case? body)))
- (error "lambda body should be lambda-case" exp))
- (else
- (if body
- (visit body env)))))
- (($ <let> src names gensyms vals body)
- (cond
- ((not (and (list? names) (and-map symbol? names)))
- (error "names should be list of syms" exp))
- ((not (and (list? gensyms) (and-map symbol? gensyms)))
- (error "gensyms should be list of syms" exp))
- ((not (list? vals))
- (error "vals should be list" exp))
- ((not (= (length names) (length gensyms) (length vals)))
- (error "names, syms, vals should be same length" exp))
- (else
- (for-each (cut visit <> env) vals)
- (visit body (add-env gensyms env)))))
- (($ <letrec> src in-order? names gensyms vals body)
- (cond
- ((not (and (list? names) (and-map symbol? names)))
- (error "names should be list of syms" exp))
- ((not (and (list? gensyms) (and-map symbol? gensyms)))
- (error "gensyms should be list of syms" exp))
- ((not (list? vals))
- (error "vals should be list" exp))
- ((not (= (length names) (length gensyms) (length vals)))
- (error "names, syms, vals should be same length" exp))
- (else
- (let ((env (add-env gensyms env)))
- (for-each (cut visit <> env) vals)
- (visit body env)))))
- (($ <fix> src names gensyms vals body)
- (cond
- ((not (and (list? names) (and-map symbol? names)))
- (error "names should be list of syms" exp))
- ((not (and (list? gensyms) (and-map symbol? gensyms)))
- (error "gensyms should be list of syms" exp))
- ((not (list? vals))
- (error "vals should be list" exp))
- ((not (= (length names) (length gensyms) (length vals)))
- (error "names, syms, vals should be same length" exp))
- (else
- (let ((env (add-env gensyms env)))
- (for-each (cut visit <> env) vals)
- (visit body env)))))
- (($ <let-values> src exp body)
- (cond
- ((not (lambda-case? body))
- (error "let-values body should be lambda-case" exp))
- (else
- (visit exp env)
- (visit body env))))
- (($ <const> src val) #t)
- (($ <void> src) #t)
- (($ <toplevel-ref> src name)
- (cond
- ((not (symbol? name))
- (error "name should be a symbol" name))))
- (($ <module-ref> src mod name public?)
- (cond
- ((not (and (list? mod) (and-map symbol? mod)))
- (error "module name should be list of symbols" exp))
- ((not (symbol? name))
- (error "name should be symbol" exp))))
- (($ <primitive-ref> src name)
- (cond
- ((not (symbol? name))
- (error "name should be symbol" exp))))
- (($ <toplevel-set> src name exp)
- (cond
- ((not (symbol? name))
- (error "name should be a symbol" name))
- (else
- (visit exp env))))
- (($ <toplevel-define> src name exp)
- (cond
- ((not (symbol? name))
- (error "name should be a symbol" name))
- (else
- (visit exp env))))
- (($ <module-set> src mod name public? exp)
- (cond
- ((not (and (list? mod) (and-map symbol? mod)))
- (error "module name should be list of symbols" exp))
- ((not (symbol? name))
- (error "name should be symbol" exp))
- (else
- (visit exp env))))
- (($ <dynlet> src fluids vals body)
- (cond
- ((not (list? fluids))
- (error "fluids should be list" exp))
- ((not (list? vals))
- (error "vals should be list" exp))
- ((not (= (length fluids) (length vals)))
- (error "mismatch in fluids/vals" exp))
- (else
- (for-each (cut visit <> env) fluids)
- (for-each (cut visit <> env) vals)
- (visit body env))))
- (($ <dynwind> src winder body unwinder)
- (visit winder env)
- (visit body env)
- (visit unwinder env))
- (($ <dynref> src fluid)
- (visit fluid env))
- (($ <dynset> src fluid exp)
- (visit fluid env)
- (visit exp env))
- (($ <conditional> src condition subsequent alternate)
- (visit condition env)
- (visit subsequent env)
- (visit alternate env))
- (($ <application> src proc args)
- (cond
- ((not (list? args))
- (error "expected list of args" args))
- (else
- (visit proc env)
- (for-each (cut visit <> env) args))))
- (($ <sequence> src exps)
- (cond
- ((not (list? exps))
- (error "expected list of exps" exp))
- ((null? exps)
- (error "expected more than one exp" exp))
- (else
- (for-each (cut visit <> env) exps))))
- (($ <prompt> src tag body handler)
- (visit tag env)
- (visit body env)
- (visit handler env))
- (($ <abort> src tag args tail)
- (visit tag env)
- (for-each (cut visit <> env) args)
- (visit tail env))
- (_
- (error "unexpected tree-il" exp)))
- (let ((src (tree-il-src exp)))
- (if (and src (not (and (list? src) (and-map pair? src)
- (and-map symbol? (map car src)))))
- (error "bad src"))
- ;; Return it, why not.
- exp)))
-;;; Effects analysis on Tree-IL
-
-;; Copyright (C) 2011, 2012, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (language tree-il effects)
- #\use-module (language tree-il)
- #\use-module (language tree-il primitives)
- #\use-module (ice-9 match)
- #\export (make-effects-analyzer
- &mutable-lexical
- &toplevel
- &fluid
- &definite-bailout
- &possible-bailout
- &zero-values
- &allocation
- &mutable-data
- &type-check
- &all-effects
- effects-commute?
- exclude-effects
- effect-free?
- constant?
- depends-on-effects?
- causes-effects?))
-
-;;;
-;;; Hey, it's some effects analysis! If you invoke
-;;; `make-effects-analyzer', you get a procedure that computes the set
-;;; of effects that an expression depends on and causes. This
-;;; information is useful when writing algorithms that move code around,
-;;; while preserving the semantics of an input program.
-;;;
-;;; The effects set is represented by a bitfield, as a fixnum. The set
-;;; of possible effects is modelled rather coarsely. For example, a
-;;; toplevel reference to FOO is modelled as depending on the &toplevel
-;;; effect, and causing a &type-check effect. If any intervening code
-;;; sets any toplevel variable, that will block motion of FOO.
-;;;
-;;; For each effect, two bits are reserved: one to indicate that an
-;;; expression depends on the effect, and the other to indicate that an
-;;; expression causes the effect.
-;;;
-
-(define-syntax define-effects
- (lambda (x)
- (syntax-case x ()
- ((_ all name ...)
- (with-syntax (((n ...) (iota (length #'(name ...)))))
- #'(begin
- (define-syntax name (identifier-syntax (ash 1 (* n 2))))
- ...
- (define-syntax all (identifier-syntax (logior name ...)))))))))
-
-;; Here we define the effects, indicating the meaning of the effect.
-;;
-;; Effects that are described in a "depends on" sense can also be used
-;; in the "causes" sense.
-;;
-;; Effects that are described as causing an effect are not usually used
-;; in a "depends-on" sense. Although the "depends-on" sense is used
-;; when checking for the existence of the "causes" effect, the effects
-;; analyzer will not associate the "depends-on" sense of these effects
-;; with any expression.
-;;
-(define-effects &all-effects
- ;; Indicates that an expression depends on the value of a mutable
- ;; lexical variable.
- &mutable-lexical
-
- ;; Indicates that an expression depends on the value of a toplevel
- ;; variable.
- &toplevel
-
- ;; Indicates that an expression depends on the value of a fluid
- ;; variable.
- &fluid
-
- ;; Indicates that an expression definitely causes a non-local,
- ;; non-resumable exit -- a bailout. Only used in the "changes" sense.
- &definite-bailout
-
- ;; Indicates that an expression may cause a bailout.
- &possible-bailout
-
- ;; Indicates than an expression may return zero values -- a "causes"
- ;; effect.
- &zero-values
-
- ;; Indicates that an expression may return a fresh object -- a
- ;; "causes" effect.
- &allocation
-
- ;; Indicates that an expression depends on the value of a mutable data
- ;; structure.
- &mutable-data
-
- ;; Indicates that an expression may cause a type check. A type check,
- ;; for the purposes of this analysis, is the possibility of throwing
- ;; an exception the first time an expression is evaluated. If the
- ;; expression did not cause an exception to be thrown, users can
- ;; assume that evaluating the expression again will not cause an
- ;; exception to be thrown.
- ;;
- ;; For example, (+ x y) might throw if X or Y are not numbers. But if
- ;; it doesn't throw, it should be safe to elide a dominated, common
- ;; subexpression (+ x y).
- &type-check)
-
-(define-syntax &no-effects (identifier-syntax 0))
-
-;; Definite bailout is an oddball effect. Since it indicates that an
-;; expression definitely causes bailout, it's not in the set of effects
-;; of a call to an unknown procedure. At the same time, it's also
-;; special in that a definite bailout in a subexpression doesn't always
-;; cause an outer expression to include &definite-bailout in its
-;; effects. For that reason we have to treat it specially.
-;;
-(define-syntax &all-effects-but-bailout
- (identifier-syntax
- (logand &all-effects (lognot &definite-bailout))))
-
-(define-inlinable (cause effect)
- (ash effect 1))
-
-(define-inlinable (&depends-on a)
- (logand a &all-effects))
-(define-inlinable (&causes a)
- (logand a (cause &all-effects)))
-
-(define (exclude-effects effects exclude)
- (logand effects (lognot (cause exclude))))
-(define (effect-free? effects)
- (zero? (&causes effects)))
-(define (constant? effects)
- (zero? effects))
-
-(define-inlinable (depends-on-effects? x effects)
- (not (zero? (logand (&depends-on x) effects))))
-(define-inlinable (causes-effects? x effects)
- (not (zero? (logand (&causes x) (cause effects)))))
-
-(define-inlinable (effects-commute? a b)
- (and (not (causes-effects? a (&depends-on b)))
- (not (causes-effects? b (&depends-on a)))))
-
-(define (make-effects-analyzer assigned-lexical?)
- "Returns a procedure of type EXP -> EFFECTS that analyzes the effects
-of an expression."
-
- (let ((cache (make-hash-table)))
- (define* (compute-effects exp #\optional (lookup (lambda (x) #f)))
- (define (compute-effects exp)
- (or (hashq-ref cache exp)
- (let ((effects (visit exp)))
- (hashq-set! cache exp effects)
- effects)))
-
- (define (accumulate-effects exps)
- (let lp ((exps exps) (out &no-effects))
- (if (null? exps)
- out
- (lp (cdr exps) (logior out (compute-effects (car exps)))))))
-
- (define (visit exp)
- (match exp
- (($ <const>)
- &no-effects)
- (($ <void>)
- &no-effects)
- (($ <lexical-ref> _ _ gensym)
- (if (assigned-lexical? gensym)
- &mutable-lexical
- &no-effects))
- (($ <lexical-set> _ name gensym exp)
- (logior (cause &mutable-lexical)
- (compute-effects exp)))
- (($ <let> _ names gensyms vals body)
- (logior (if (or-map assigned-lexical? gensyms)
- (cause &allocation)
- &no-effects)
- (accumulate-effects vals)
- (compute-effects body)))
- (($ <letrec> _ in-order? names gensyms vals body)
- (logior (if (or-map assigned-lexical? gensyms)
- (cause &allocation)
- &no-effects)
- (accumulate-effects vals)
- (compute-effects body)))
- (($ <fix> _ names gensyms vals body)
- (logior (if (or-map assigned-lexical? gensyms)
- (cause &allocation)
- &no-effects)
- (accumulate-effects vals)
- (compute-effects body)))
- (($ <let-values> _ producer consumer)
- (logior (compute-effects producer)
- (compute-effects consumer)
- (cause &type-check)))
- (($ <dynwind> _ winder body unwinder)
- (logior (compute-effects winder)
- (compute-effects body)
- (compute-effects unwinder)))
- (($ <dynlet> _ fluids vals body)
- (logior (accumulate-effects fluids)
- (accumulate-effects vals)
- (cause &type-check)
- (cause &fluid)
- (compute-effects body)))
- (($ <dynref> _ fluid)
- (logior (compute-effects fluid)
- (cause &type-check)
- &fluid))
- (($ <dynset> _ fluid exp)
- (logior (compute-effects fluid)
- (compute-effects exp)
- (cause &type-check)
- (cause &fluid)))
- (($ <toplevel-ref>)
- (logior &toplevel
- (cause &type-check)))
- (($ <module-ref>)
- (logior &toplevel
- (cause &type-check)))
- (($ <module-set> _ mod name public? exp)
- (logior (cause &toplevel)
- (cause &type-check)
- (compute-effects exp)))
- (($ <toplevel-define> _ name exp)
- (logior (cause &toplevel)
- (compute-effects exp)))
- (($ <toplevel-set> _ name exp)
- (logior (cause &toplevel)
- (compute-effects exp)))
- (($ <primitive-ref>)
- &no-effects)
- (($ <conditional> _ test consequent alternate)
- (let ((tfx (compute-effects test))
- (cfx (compute-effects consequent))
- (afx (compute-effects alternate)))
- (if (causes-effects? (logior tfx (logand afx cfx))
- &definite-bailout)
- (logior tfx cfx afx)
- (exclude-effects (logior tfx cfx afx)
- &definite-bailout))))
-
- ;; Zero values.
- (($ <application> _ ($ <primitive-ref> _ 'values) ())
- (cause &zero-values))
-
- ;; Effect-free primitives.
- (($ <application> _
- ($ <primitive-ref> _ (or 'values 'eq? 'eqv? 'equal?))
- args)
- (accumulate-effects args))
-
- (($ <application> _
- ($ <primitive-ref> _ (or 'not 'pair? 'null? 'list? 'symbol?
- 'vector? 'struct? 'string? 'number?
- 'char?))
- (arg))
- (compute-effects arg))
-
- ;; Primitives that allocate memory.
- (($ <application> _ ($ <primitive-ref> _ 'cons) (x y))
- (logior (compute-effects x) (compute-effects y)
- (cause &allocation)))
-
- (($ <application> _ ($ <primitive-ref> _ (or 'list 'vector)) args)
- (logior (accumulate-effects args) (cause &allocation)))
-
- (($ <application> _ ($ <primitive-ref> _ 'make-prompt-tag) ())
- (cause &allocation))
-
- (($ <application> _ ($ <primitive-ref> _ 'make-prompt-tag) (arg))
- (logior (compute-effects arg) (cause &allocation)))
-
- ;; Primitives that are normally effect-free, but which might
- ;; cause type checks, allocate memory, or access mutable
- ;; memory. FIXME: expand, to be more precise.
- (($ <application> _
- ($ <primitive-ref> _ (and name
- (? effect-free-primitive?)))
- args)
- (logior (accumulate-effects args)
- (cause &type-check)
- (if (constructor-primitive? name)
- (cause &allocation)
- (if (accessor-primitive? name)
- &mutable-data
- &no-effects))))
-
- ;; Lambda applications might throw wrong-number-of-args.
- (($ <application> _ ($ <lambda> _ _ body) args)
- (logior (accumulate-effects args)
- (match body
- (($ <lambda-case> _ req #f #f #f () syms body #f)
- (logior (compute-effects body)
- (if (= (length req) (length args))
- 0
- (cause &type-check))))
- (($ <lambda-case>)
- (logior (compute-effects body)
- (cause &type-check)))
- (#f
- ;; Calling a case-lambda with no clauses
- ;; definitely causes bailout.
- (logior (cause &definite-bailout)
- (cause &possible-bailout))))))
-
- ;; Bailout primitives.
- (($ <application> src ($ <primitive-ref> _ (? bailout-primitive? name))
- args)
- (logior (accumulate-effects args)
- (cause &definite-bailout)
- (cause &possible-bailout)))
-
- ;; A call to a lexically bound procedure, perhaps labels
- ;; allocated.
- (($ <application> _ (and proc ($ <lexical-ref> _ _ sym)) args)
- (cond
- ((lookup sym)
- => (lambda (proc)
- (compute-effects (make-application #f proc args))))
- (else
- (logior &all-effects-but-bailout
- (cause &all-effects-but-bailout)))))
-
- ;; A call to an unknown procedure can do anything.
- (($ <application> _ proc args)
- (logior &all-effects-but-bailout
- (cause &all-effects-but-bailout)))
-
- (($ <lambda> _ meta body)
- &no-effects)
- (($ <lambda-case> _ req opt rest kw inits gensyms body alt)
- (logior (exclude-effects (accumulate-effects inits)
- &definite-bailout)
- (if (or-map assigned-lexical? gensyms)
- (cause &allocation)
- &no-effects)
- (compute-effects body)
- (if alt (compute-effects alt) &no-effects)))
-
- (($ <sequence> _ exps)
- (let lp ((exps exps) (effects &no-effects))
- (match exps
- ((tail)
- (logior (compute-effects tail)
- ;; Returning zero values to a for-effect continuation is
- ;; not observable.
- (exclude-effects effects (cause &zero-values))))
- ((head . tail)
- (lp tail (logior (compute-effects head) effects))))))
-
- (($ <prompt> _ tag body handler)
- (logior (compute-effects tag)
- (compute-effects body)
- (compute-effects handler)))
-
- (($ <abort> _ tag args tail)
- (logior &all-effects-but-bailout
- (cause &all-effects-but-bailout)))))
-
- (compute-effects exp))
-
- compute-effects))
-;;; transformation of letrec into simpler forms
-
-;; Copyright (C) 2009, 2010, 2011, 2012, 2016 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (language tree-il fix-letrec)
- #\use-module (system base syntax)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-11)
- #\use-module (language tree-il)
- #\use-module (language tree-il effects)
- #\export (fix-letrec!))
-
-;; For a detailed discussion, see "Fixing Letrec: A Faithful Yet
-;; Efficient Implementation of Scheme's Recursive Binding Construct", by
-;; Oscar Waddell, Dipanwita Sarkar, and R. Kent Dybvig.
-
-(define fix-fold
- (make-tree-il-folder unref ref set simple lambda complex))
-
-(define (simple-expression? x bound-vars simple-primcall?)
- (record-case x
- ((<void>) #t)
- ((<const>) #t)
- ((<lexical-ref> gensym)
- (not (memq gensym bound-vars)))
- ((<conditional> test consequent alternate)
- (and (simple-expression? test bound-vars simple-primcall?)
- (simple-expression? consequent bound-vars simple-primcall?)
- (simple-expression? alternate bound-vars simple-primcall?)))
- ((<sequence> exps)
- (and-map (lambda (x) (simple-expression? x bound-vars simple-primcall?))
- exps))
- ((<application> proc args)
- (and (primitive-ref? proc)
- (simple-primcall? x)
- (and-map (lambda (x)
- (simple-expression? x bound-vars simple-primcall?))
- args)))
- (else #f)))
-
-(define (partition-vars x)
- (let-values
- (((unref ref set simple lambda* complex)
- (fix-fold x
- (lambda (x unref ref set simple lambda* complex)
- (record-case x
- ((<lexical-ref> gensym)
- (values (delq gensym unref)
- (lset-adjoin eq? ref gensym)
- set
- simple
- lambda*
- complex))
- ((<lexical-set> gensym)
- (values unref
- ref
- (lset-adjoin eq? set gensym)
- simple
- lambda*
- complex))
- ((<letrec> gensyms)
- (values (append gensyms unref)
- ref
- set
- simple
- lambda*
- complex))
- ((<let> gensyms)
- (values (append gensyms unref)
- ref
- set
- simple
- lambda*
- complex))
- (else
- (values unref ref set simple lambda* complex))))
- (lambda (x unref ref set simple lambda* complex)
- (record-case x
- ((<letrec> in-order? (orig-gensyms gensyms) vals)
- (define compute-effects
- (make-effects-analyzer (lambda (x) (memq x set))))
- (define (effect-free-primcall? x)
- (let ((effects (compute-effects x)))
- (effect-free?
- (exclude-effects effects (logior &allocation
- &type-check)))))
- (define (effect+exception-free-primcall? x)
- (let ((effects (compute-effects x)))
- (effect-free?
- (exclude-effects effects &allocation))))
- (let lp ((gensyms orig-gensyms) (vals vals)
- (s '()) (l '()) (c '()))
- (cond
- ((null? gensyms)
- ;; Unreferenced complex vars are still
- ;; complex for letrec*. We need to update
- ;; our algorithm to "Fixing letrec reloaded"
- ;; to fix this.
- (values (if in-order?
- (lset-difference eq? unref c)
- unref)
- ref
- set
- (append s simple)
- (append l lambda*)
- (append c complex)))
- ((memq (car gensyms) unref)
- ;; See above note about unref and letrec*.
- (if (and in-order?
- (not (lambda? (car vals)))
- (not (simple-expression?
- (car vals) orig-gensyms
- effect+exception-free-primcall?)))
- (lp (cdr gensyms) (cdr vals)
- s l (cons (car gensyms) c))
- (lp (cdr gensyms) (cdr vals)
- s l c)))
- ((memq (car gensyms) set)
- (lp (cdr gensyms) (cdr vals)
- s l (cons (car gensyms) c)))
- ((lambda? (car vals))
- (lp (cdr gensyms) (cdr vals)
- s (cons (car gensyms) l) c))
- ((simple-expression?
- (car vals) orig-gensyms
- (if in-order?
- effect+exception-free-primcall?
- effect-free-primcall?))
- ;; For letrec*, we can't consider e.g. `car' to be
- ;; "simple", as it could raise an exception. Hence
- ;; effect+exception-free-primitive? above.
- (lp (cdr gensyms) (cdr vals)
- (cons (car gensyms) s) l c))
- (else
- (lp (cdr gensyms) (cdr vals)
- s l (cons (car gensyms) c))))))
- ((<let> (orig-gensyms gensyms) vals)
- ;; The point is to compile let-bound lambdas as
- ;; efficiently as we do letrec-bound lambdas, so
- ;; we use the same algorithm for analyzing the
- ;; gensyms. There is no problem recursing into the
- ;; bindings after the let, because all variables
- ;; have been renamed.
- (let lp ((gensyms orig-gensyms) (vals vals)
- (s '()) (l '()) (c '()))
- (cond
- ((null? gensyms)
- (values unref
- ref
- set
- (append s simple)
- (append l lambda*)
- (append c complex)))
- ((memq (car gensyms) unref)
- (lp (cdr gensyms) (cdr vals)
- s l c))
- ((memq (car gensyms) set)
- (lp (cdr gensyms) (cdr vals)
- s l (cons (car gensyms) c)))
- ((and (lambda? (car vals))
- (not (memq (car gensyms) set)))
- (lp (cdr gensyms) (cdr vals)
- s (cons (car gensyms) l) c))
- ;; There is no difference between simple and
- ;; complex, for the purposes of let. Just lump
- ;; them all into complex.
- (else
- (lp (cdr gensyms) (cdr vals)
- s l (cons (car gensyms) c))))))
- (else
- (values unref ref set simple lambda* complex))))
- '()
- '()
- '()
- '()
- '()
- '())))
- (values unref simple lambda* complex)))
-
-(define (make-sequence* src exps)
- (let lp ((in exps) (out '()))
- (if (null? (cdr in))
- (if (null? out)
- (car in)
- (make-sequence src (reverse (cons (car in) out))))
- (let ((head (car in)))
- (record-case head
- ((<lambda>) (lp (cdr in) out))
- ((<const>) (lp (cdr in) out))
- ((<lexical-ref>) (lp (cdr in) out))
- ((<void>) (lp (cdr in) out))
- (else (lp (cdr in) (cons head out))))))))
-
-(define (fix-letrec! x)
- (let-values (((unref simple lambda* complex) (partition-vars x)))
- (post-order!
- (lambda (x)
- (record-case x
-
- ;; Sets to unreferenced variables may be replaced by their
- ;; expression, called for effect.
- ((<lexical-set> gensym exp)
- (if (memq gensym unref)
- (make-sequence* #f (list exp (make-void #f)))
- x))
-
- ((<letrec> src in-order? names gensyms vals body)
- (let ((binds (map list gensyms names vals)))
- ;; The bindings returned by this function need to appear in the same
- ;; order that they appear in the letrec.
- (define (lookup set)
- (let lp ((binds binds))
- (cond
- ((null? binds) '())
- ((memq (caar binds) set)
- (cons (car binds) (lp (cdr binds))))
- (else (lp (cdr binds))))))
- (let ((u (lookup unref))
- (s (lookup simple))
- (l (lookup lambda*))
- (c (lookup complex)))
- ;; Bind "simple" bindings, and locations for complex
- ;; bindings.
- (make-let
- src
- (append (map cadr s) (map cadr c))
- (append (map car s) (map car c))
- (append (map caddr s) (map (lambda (x) (make-void #f)) c))
- ;; Bind lambdas using the fixpoint operator.
- (make-fix
- src (map cadr l) (map car l) (map caddr l)
- (make-sequence*
- src
- (append
- ;; The right-hand-sides of the unreferenced
- ;; bindings, for effect.
- (map caddr u)
- (cond
- ((null? c)
- ;; No complex bindings, just emit the body.
- (list body))
- (in-order?
- ;; For letrec*, assign complex bindings in order, then the
- ;; body.
- (append
- (map (lambda (c)
- (make-lexical-set #f (cadr c) (car c)
- (caddr c)))
- c)
- (list body)))
- (else
- ;; Otherwise for plain letrec, evaluate the "complex"
- ;; bindings, in a `let' to indicate that order doesn't
- ;; matter, and bind to their variables.
- (list
- (let ((tmps (map (lambda (x)
- (module-gensym "fixlr"))
- c)))
- (make-let
- #f (map cadr c) tmps (map caddr c)
- (make-sequence
- #f
- (map (lambda (x tmp)
- (make-lexical-set
- #f (cadr x) (car x)
- (make-lexical-ref #f (cadr x) tmp)))
- c tmps))))
- body))))))))))
-
- ((<let> src names gensyms vals body)
- (let ((binds (map list gensyms names vals)))
- (define (lookup set)
- (map (lambda (v) (assq v binds))
- (lset-intersection eq? gensyms set)))
- (let ((u (lookup unref))
- (l (lookup lambda*))
- (c (lookup complex)))
- (make-sequence*
- src
- (append
- ;; unreferenced bindings, called for effect.
- (map caddr u)
- (list
- ;; unassigned lambdas use fix.
- (make-fix src (map cadr l) (map car l) (map caddr l)
- ;; and the "complex" bindings.
- (make-let src (map cadr c) (map car c) (map caddr c)
- body))))))))
-
- (else x)))
- x)))
-
-;;; Local Variables:
-;;; eval: (put 'record-case 'scheme-indent-function 1)
-;;; End:
-;;; a simple inliner
-
-;; Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (language tree-il inline)
- #\export (inline!))
-
-(define (inline! x)
- (issue-deprecation-warning
- "`inline!' is deprecated. Use (language tree-il peval) instead.")
- x)
-;;; Tree-il optimizer
-
-;; Copyright (C) 2009, 2011, 2012 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language tree-il optimize)
- #\use-module (language tree-il)
- #\use-module (language tree-il primitives)
- #\use-module (language tree-il peval)
- #\use-module (language tree-il cse)
- #\use-module (language tree-il fix-letrec)
- #\use-module (language tree-il debug)
- #\use-module (ice-9 match)
- #\export (optimize!))
-
-(define (optimize! x env opts)
- (let ((peval (match (memq #\partial-eval? opts)
- ((#\partial-eval? #f _ ...)
- ;; Disable partial evaluation.
- (lambda (x e) x))
- (_ peval)))
- (cse (match (memq #\cse? opts)
- ((#\cse? #f _ ...)
- ;; Disable CSE.
- (lambda (x) x))
- (_ cse))))
- (fix-letrec!
- (verify-tree-il
- (cse
- (verify-tree-il
- (peval (expand-primitives! (resolve-primitives! x env))
- env)))))))
-;;; Tree-IL partial evaluator
-
-;; Copyright (C) 2011-2014 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (language tree-il peval)
- #\use-module (language tree-il)
- #\use-module (language tree-il primitives)
- #\use-module (language tree-il effects)
- #\use-module (ice-9 vlist)
- #\use-module (ice-9 match)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-9)
- #\use-module (srfi srfi-11)
- #\use-module (srfi srfi-26)
- #\use-module (ice-9 control)
- #\export (peval))
-
-;;;
-;;; Partial evaluation is Guile's most important source-to-source
-;;; optimization pass. It performs copy propagation, dead code
-;;; elimination, inlining, and constant folding, all while preserving
-;;; the order of effects in the residual program.
-;;;
-;;; For more on partial evaluation, see William Cook’s excellent
-;;; tutorial on partial evaluation at DSL 2011, called “Build your own
-;;; partial evaluator in 90 minutes”[0].
-;;;
-;;; Our implementation of this algorithm was heavily influenced by
-;;; Waddell and Dybvig's paper, "Fast and Effective Procedure Inlining",
-;;; IU CS Dept. TR 484.
-;;;
-;;; [0] http://www.cs.utexas.edu/~wcook/tutorial/.
-;;;
-
-;; First, some helpers.
-;;
-(define-syntax *logging* (identifier-syntax #f))
-
-;; For efficiency we define *logging* to inline to #f, so that the call
-;; to log* gets optimized out. If you want to log, uncomment these
-;; lines:
-;;
-;; (define %logging #f)
-;; (define-syntax *logging* (identifier-syntax %logging))
-;;
-;; Then you can change %logging at runtime.
-
-(define-syntax log
- (syntax-rules (quote)
- ((log 'event arg ...)
- (if (and *logging*
- (or (eq? *logging* #t)
- (memq 'event *logging*)))
- (log* 'event arg ...)))))
-
-(define (log* event . args)
- (let ((pp (module-ref (resolve-interface '(ice-9 pretty-print))
- 'pretty-print)))
- (pp `(log ,event . ,args))
- (newline)
- (values)))
-
-(define (tree-il-any proc exp)
- (let/ec k
- (tree-il-fold (lambda (exp res)
- (let ((res (proc exp)))
- (if res (k res) #f)))
- (lambda (exp res)
- (let ((res (proc exp)))
- (if res (k res) #f)))
- (lambda (exp res) #f)
- #f exp)))
-
-(define (vlist-any proc vlist)
- (let ((len (vlist-length vlist)))
- (let lp ((i 0))
- (and (< i len)
- (or (proc (vlist-ref vlist i))
- (lp (1+ i)))))))
-
-(define (singly-valued-expression? exp)
- (match exp
- (($ <const>) #t)
- (($ <lexical-ref>) #t)
- (($ <void>) #t)
- (($ <lexical-ref>) #t)
- (($ <primitive-ref>) #t)
- (($ <module-ref>) #t)
- (($ <toplevel-ref>) #t)
- (($ <application> _
- ($ <primitive-ref> _ (? singly-valued-primitive?))) #t)
- (($ <application> _ ($ <primitive-ref> _ 'values) (val)) #t)
- (($ <lambda>) #t)
- (else #f)))
-
-(define (truncate-values x)
- "Discard all but the first value of X."
- (if (singly-valued-expression? x)
- x
- (make-application (tree-il-src x)
- (make-primitive-ref #f 'values)
- (list x))))
-
-;; Peval will do a one-pass analysis on the source program to determine
-;; the set of assigned lexicals, and to identify unreferenced and
-;; singly-referenced lexicals.
-;;
-(define-record-type <var>
- (make-var name gensym refcount set?)
- var?
- (name var-name)
- (gensym var-gensym)
- (refcount var-refcount set-var-refcount!)
- (set? var-set? set-var-set?!))
-
-(define* (build-var-table exp #\optional (table vlist-null))
- (tree-il-fold
- (lambda (exp res)
- (match exp
- (($ <lexical-ref> src name gensym)
- (let ((var (cdr (vhash-assq gensym res))))
- (set-var-refcount! var (1+ (var-refcount var)))
- res))
- (_ res)))
- (lambda (exp res)
- (match exp
- (($ <lambda-case> src req opt rest kw init gensyms body alt)
- (fold (lambda (name sym res)
- (vhash-consq sym (make-var name sym 0 #f) res))
- res
- (append req (or opt '()) (if rest (list rest) '())
- (match kw
- ((aok? (kw name sym) ...) name)
- (_ '())))
- gensyms))
- (($ <let> src names gensyms vals body)
- (fold (lambda (name sym res)
- (vhash-consq sym (make-var name sym 0 #f) res))
- res names gensyms))
- (($ <letrec> src in-order? names gensyms vals body)
- (fold (lambda (name sym res)
- (vhash-consq sym (make-var name sym 0 #f) res))
- res names gensyms))
- (($ <fix> src names gensyms vals body)
- (fold (lambda (name sym res)
- (vhash-consq sym (make-var name sym 0 #f) res))
- res names gensyms))
- (($ <lexical-set> src name gensym exp)
- (set-var-set?! (cdr (vhash-assq gensym res)) #t)
- res)
- (_ res)))
- (lambda (exp res) res)
- table exp))
-
-;; Counters are data structures used to limit the effort that peval
-;; spends on particular inlining attempts. Each call site in the source
-;; program is allocated some amount of effort. If peval exceeds the
-;; effort counter while attempting to inline a call site, it aborts the
-;; inlining attempt and residualizes a call instead.
-;;
-;; As there is a fixed number of call sites, that makes `peval' O(N) in
-;; the number of call sites in the source program.
-;;
-;; Counters should limit the size of the residual program as well, but
-;; currently this is not implemented.
-;;
-;; At the top level, before seeing any peval call, there is no counter,
-;; because inlining will terminate as there is no recursion. When peval
-;; sees a call at the top level, it will make a new counter, allocating
-;; it some amount of effort and size.
-;;
-;; This top-level effort counter effectively "prints money". Within a
-;; toplevel counter, no more effort is printed ex nihilo; for a nested
-;; inlining attempt to proceed, effort must be transferred from the
-;; toplevel counter to the nested counter.
-;;
-;; Via `data' and `prev', counters form a linked list, terminating in a
-;; toplevel counter. In practice `data' will be the a pointer to the
-;; source expression of the procedure being inlined.
-;;
-;; In this way peval can detect a recursive inlining attempt, by walking
-;; back on the `prev' links looking for matching `data'. Recursive
-;; counters receive a more limited effort allocation, as we don't want
-;; to spend all of the effort for a toplevel inlining site on loops.
-;; Also, recursive counters don't need a prompt at each inlining site:
-;; either the call chain folds entirely, or it will be residualized at
-;; its original call.
-;;
-(define-record-type <counter>
- (%make-counter effort size continuation recursive? data prev)
- counter?
- (effort effort-counter)
- (size size-counter)
- (continuation counter-continuation)
- (recursive? counter-recursive? set-counter-recursive?!)
- (data counter-data)
- (prev counter-prev))
-
-(define (abort-counter c)
- ((counter-continuation c)))
-
-(define (record-effort! c)
- (let ((e (effort-counter c)))
- (if (zero? (variable-ref e))
- (abort-counter c)
- (variable-set! e (1- (variable-ref e))))))
-
-(define (record-size! c)
- (let ((s (size-counter c)))
- (if (zero? (variable-ref s))
- (abort-counter c)
- (variable-set! s (1- (variable-ref s))))))
-
-(define (find-counter data counter)
- (and counter
- (if (eq? data (counter-data counter))
- counter
- (find-counter data (counter-prev counter)))))
-
-(define* (transfer! from to #\optional
- (effort (variable-ref (effort-counter from)))
- (size (variable-ref (size-counter from))))
- (define (transfer-counter! from-v to-v amount)
- (let* ((from-balance (variable-ref from-v))
- (to-balance (variable-ref to-v))
- (amount (min amount from-balance)))
- (variable-set! from-v (- from-balance amount))
- (variable-set! to-v (+ to-balance amount))))
-
- (transfer-counter! (effort-counter from) (effort-counter to) effort)
- (transfer-counter! (size-counter from) (size-counter to) size))
-
-(define (make-top-counter effort-limit size-limit continuation data)
- (%make-counter (make-variable effort-limit)
- (make-variable size-limit)
- continuation
- #t
- data
- #f))
-
-(define (make-nested-counter continuation data current)
- (let ((c (%make-counter (make-variable 0)
- (make-variable 0)
- continuation
- #f
- data
- current)))
- (transfer! current c)
- c))
-
-(define (make-recursive-counter effort-limit size-limit orig current)
- (let ((c (%make-counter (make-variable 0)
- (make-variable 0)
- (counter-continuation orig)
- #t
- (counter-data orig)
- current)))
- (transfer! current c effort-limit size-limit)
- c))
-
-;; Operand structures allow bindings to be processed lazily instead of
-;; eagerly. By doing so, hopefully we can get process them in a way
-;; appropriate to their use contexts. Operands also prevent values from
-;; being visited multiple times, wasting effort.
-;;
-;; TODO: Record value size in operand structure?
-;;
-(define-record-type <operand>
- (%make-operand var sym visit source visit-count use-count
- copyable? residual-value constant-value alias)
- operand?
- (var operand-var)
- (sym operand-sym)
- (visit %operand-visit)
- (source operand-source)
- (visit-count operand-visit-count set-operand-visit-count!)
- (use-count operand-use-count set-operand-use-count!)
- (copyable? operand-copyable? set-operand-copyable?!)
- (residual-value operand-residual-value %set-operand-residual-value!)
- (constant-value operand-constant-value set-operand-constant-value!)
- (alias operand-alias set-operand-alias!))
-
-(define* (make-operand var sym #\optional source visit alias)
- ;; Bind SYM to VAR, with value SOURCE. Unassigned bound operands are
- ;; considered copyable until we prove otherwise. If we have a source
- ;; expression, truncate it to one value. Copy propagation does not
- ;; work on multiply-valued expressions.
- (let ((source (and=> source truncate-values)))
- (%make-operand var sym visit source 0 0
- (and source (not (var-set? var))) #f #f
- (and (not (var-set? var)) alias))))
-
-(define* (make-bound-operands vars syms sources visit #\optional aliases)
- (if aliases
- (map (lambda (name sym source alias)
- (make-operand name sym source visit alias))
- vars syms sources aliases)
- (map (lambda (name sym source)
- (make-operand name sym source visit #f))
- vars syms sources)))
-
-(define (make-unbound-operands vars syms)
- (map make-operand vars syms))
-
-(define (set-operand-residual-value! op val)
- (%set-operand-residual-value!
- op
- (match val
- (($ <application> src ($ <primitive-ref> _ 'values) (first))
- ;; The continuation of a residualized binding does not need the
- ;; introduced `values' node, so undo the effects of truncation.
- first)
- (else
- val))))
-
-(define* (visit-operand op counter ctx #\optional effort-limit size-limit)
- ;; Peval is O(N) in call sites of the source program. However,
- ;; visiting an operand can introduce new call sites. If we visit an
- ;; operand outside a counter -- i.e., outside an inlining attempt --
- ;; this can lead to divergence. So, if we are visiting an operand to
- ;; try to copy it, and there is no counter, make a new one.
- ;;
- ;; This will only happen at most as many times as there are lexical
- ;; references in the source program.
- (and (zero? (operand-visit-count op))
- (dynamic-wind
- (lambda ()
- (set-operand-visit-count! op (1+ (operand-visit-count op))))
- (lambda ()
- (and (operand-source op)
- (if (or counter (and (not effort-limit) (not size-limit)))
- ((%operand-visit op) (operand-source op) counter ctx)
- (let/ec k
- (define (abort)
- ;; If we abort when visiting the value in a
- ;; fresh context, we won't succeed in any future
- ;; attempt, so don't try to copy it again.
- (set-operand-copyable?! op #f)
- (k #f))
- ((%operand-visit op)
- (operand-source op)
- (make-top-counter effort-limit size-limit abort op)
- ctx)))))
- (lambda ()
- (set-operand-visit-count! op (1- (operand-visit-count op)))))))
-
-;; A helper for constant folding.
-;;
-(define (types-check? primitive-name args)
- (case primitive-name
- ((values) #t)
- ((not pair? null? list? symbol? vector? struct?)
- (= (length args) 1))
- ((eq? eqv? equal?)
- (= (length args) 2))
- ;; FIXME: add more cases?
- (else #f)))
-
-(define* (peval exp #\optional (cenv (current-module)) (env vlist-null)
- #\key
- (operator-size-limit 40)
- (operand-size-limit 20)
- (value-size-limit 10)
- (effort-limit 500)
- (recursive-effort-limit 100))
- "Partially evaluate EXP in compilation environment CENV, with
-top-level bindings from ENV and return the resulting expression."
-
- ;; This is a simple partial evaluator. It effectively performs
- ;; constant folding, copy propagation, dead code elimination, and
- ;; inlining.
-
- ;; TODO:
- ;;
- ;; Propagate copies across toplevel bindings, if we can prove the
- ;; bindings to be immutable.
- ;;
- ;; Specialize lambda expressions with invariant arguments.
-
- (define local-toplevel-env
- ;; The top-level environment of the module being compiled.
- (match exp
- (($ <toplevel-define> _ name)
- (vhash-consq name #t env))
- (($ <sequence> _ exps)
- (fold (lambda (x r)
- (match x
- (($ <toplevel-define> _ name)
- (vhash-consq name #t r))
- (_ r)))
- env
- exps))
- (_ env)))
-
- (define (local-toplevel? name)
- (vhash-assq name local-toplevel-env))
-
- ;; gensym -> <var>
- ;; renamed-term -> original-term
- ;;
- (define store (build-var-table exp))
-
- (define (record-new-temporary! name sym refcount)
- (set! store (vhash-consq sym (make-var name sym refcount #f) store)))
-
- (define (lookup-var sym)
- (let ((v (vhash-assq sym store)))
- (if v (cdr v) (error "unbound var" sym (vlist->list store)))))
-
- (define (fresh-gensyms vars)
- (map (lambda (var)
- (let ((new (gensym (string-append (symbol->string (var-name var))
- " "))))
- (set! store (vhash-consq new var store))
- new))
- vars))
-
- (define (fresh-temporaries ls)
- (map (lambda (elt)
- (let ((new (gensym "tmp ")))
- (record-new-temporary! 'tmp new 1)
- new))
- ls))
-
- (define (assigned-lexical? sym)
- (var-set? (lookup-var sym)))
-
- (define (lexical-refcount sym)
- (var-refcount (lookup-var sym)))
-
- ;; ORIG has been alpha-renamed to NEW. Analyze NEW and record a link
- ;; from it to ORIG.
- ;;
- (define (record-source-expression! orig new)
- (set! store (vhash-consq new (source-expression orig) store))
- new)
-
- ;; Find the source expression corresponding to NEW. Used to detect
- ;; recursive inlining attempts.
- ;;
- (define (source-expression new)
- (let ((x (vhash-assq new store)))
- (if x (cdr x) new)))
-
- (define (record-operand-use op)
- (set-operand-use-count! op (1+ (operand-use-count op))))
-
- (define (unrecord-operand-uses op n)
- (let ((count (- (operand-use-count op) n)))
- (when (zero? count)
- (set-operand-residual-value! op #f))
- (set-operand-use-count! op count)))
-
- (define* (residualize-lexical op #\optional ctx val)
- (log 'residualize op)
- (record-operand-use op)
- (if (memq ctx '(value values))
- (set-operand-residual-value! op val))
- (make-lexical-ref #f (var-name (operand-var op)) (operand-sym op)))
-
- (define (fold-constants src name args ctx)
- (define (apply-primitive name args)
- ;; todo: further optimize commutative primitives
- (catch #t
- (lambda ()
- (call-with-values
- (lambda ()
- (case name
- ((eq? eqv?)
- ;; Constants will be deduplicated later, but eq?
- ;; folding can happen now. Anticipate the
- ;; deduplication by using equal? instead of eq?.
- ;; Same for eqv?.
- (apply equal? args))
- (else
- (apply (module-ref the-scm-module name) args))))
- (lambda results
- (values #t results))))
- (lambda _
- (values #f '()))))
-
- (define (make-values src values)
- (match values
- ((single) single) ; 1 value
- ((_ ...) ; 0, or 2 or more values
- (make-application src (make-primitive-ref src 'values)
- values))))
- (define (residualize-call)
- (make-application src (make-primitive-ref #f name) args))
- (cond
- ((every const? args)
- (let-values (((success? values)
- (apply-primitive name (map const-exp args))))
- (log 'fold success? values name args)
- (if success?
- (case ctx
- ((effect) (make-void src))
- ((test)
- ;; Values truncation: only take the first
- ;; value.
- (if (pair? values)
- (make-const src (car values))
- (make-values src '())))
- (else
- (make-values src (map (cut make-const src <>) values))))
- (residualize-call))))
- ((and (eq? ctx 'effect) (types-check? name args))
- (make-void #f))
- (else
- (residualize-call))))
-
- (define (inline-values src exp nmin nmax consumer)
- (let loop ((exp exp))
- (match exp
- ;; Some expression types are always singly-valued.
- ((or ($ <const>)
- ($ <void>)
- ($ <lambda>)
- ($ <lexical-ref>)
- ($ <toplevel-ref>)
- ($ <module-ref>)
- ($ <primitive-ref>)
- ($ <dynref>)
- ($ <lexical-set>) ; FIXME: these set! expressions
- ($ <toplevel-set>) ; could return zero values in
- ($ <toplevel-define>) ; the future
- ($ <module-set>) ;
- ($ <dynset>) ;
- ($ <application> src
- ($ <primitive-ref> _ (? singly-valued-primitive?))))
- (and (<= nmin 1) (or (not nmax) (>= nmax 1))
- (make-application src (make-lambda #f '() consumer) (list exp))))
-
- ;; Statically-known number of values.
- (($ <application> src ($ <primitive-ref> _ 'values) vals)
- (and (<= nmin (length vals)) (or (not nmax) (>= nmax (length vals)))
- (make-application src (make-lambda #f '() consumer) vals)))
-
- ;; Not going to copy code into both branches.
- (($ <conditional>) #f)
-
- ;; Bail on other applications.
- (($ <application>) #f)
-
- ;; Bail on prompt and abort.
- (($ <prompt>) #f)
- (($ <abort>) #f)
-
- ;; Propagate to tail positions.
- (($ <let> src names gensyms vals body)
- (let ((body (loop body)))
- (and body
- (make-let src names gensyms vals body))))
- (($ <letrec> src in-order? names gensyms vals body)
- (let ((body (loop body)))
- (and body
- (make-letrec src in-order? names gensyms vals body))))
- (($ <fix> src names gensyms vals body)
- (let ((body (loop body)))
- (and body
- (make-fix src names gensyms vals body))))
- (($ <let-values> src exp
- ($ <lambda-case> src2 req opt rest kw inits gensyms body #f))
- (let ((body (loop body)))
- (and body
- (make-let-values src exp
- (make-lambda-case src2 req opt rest kw
- inits gensyms body #f)))))
- (($ <dynwind> src winder body unwinder)
- (let ((body (loop body)))
- (and body
- (make-dynwind src winder body unwinder))))
- (($ <dynlet> src fluids vals body)
- (let ((body (loop body)))
- (and body
- (make-dynlet src fluids vals body))))
- (($ <sequence> src exps)
- (match exps
- ((head ... tail)
- (let ((tail (loop tail)))
- (and tail
- (make-sequence src (append head (list tail)))))))))))
-
- (define compute-effects
- (make-effects-analyzer assigned-lexical?))
-
- (define (constant-expression? x)
- ;; Return true if X is constant, for the purposes of copying or
- ;; elision---i.e., if it is known to have no effects, does not
- ;; allocate storage for a mutable object, and does not access
- ;; mutable data (like `car' or toplevel references).
- (constant? (compute-effects x)))
-
- (define (prune-bindings ops in-order? body counter ctx build-result)
- ;; This helper handles both `let' and `letrec'/`fix'. In the latter
- ;; cases we need to make sure that if referenced binding A needs
- ;; as-yet-unreferenced binding B, that B is processed for value.
- ;; Likewise if C, when processed for effect, needs otherwise
- ;; unreferenced D, then D needs to be processed for value too.
- ;;
- (define (referenced? op)
- ;; When we visit lambdas in operator context, we just copy them,
- ;; as we will process their body later. However this does have
- ;; the problem that any free var referenced by the lambda is not
- ;; marked as needing residualization. Here we hack around this
- ;; and treat all bindings as referenced if we are in operator
- ;; context.
- (or (eq? ctx 'operator)
- (not (zero? (operand-use-count op)))))
-
- ;; values := (op ...)
- ;; effects := (op ...)
- (define (residualize values effects)
- ;; Note, values and effects are reversed.
- (cond
- (in-order?
- (let ((values (filter operand-residual-value ops)))
- (if (null? values)
- body
- (build-result (map (compose var-name operand-var) values)
- (map operand-sym values)
- (map operand-residual-value values)
- body))))
- (else
- (let ((body
- (if (null? effects)
- body
- (let ((effect-vals (map operand-residual-value effects)))
- (make-sequence #f (reverse (cons body effect-vals)))))))
- (if (null? values)
- body
- (let ((values (reverse values)))
- (build-result (map (compose var-name operand-var) values)
- (map operand-sym values)
- (map operand-residual-value values)
- body)))))))
-
- ;; old := (bool ...)
- ;; values := (op ...)
- ;; effects := ((op . value) ...)
- (let prune ((old (map referenced? ops)) (values '()) (effects '()))
- (let lp ((ops* ops) (values values) (effects effects))
- (cond
- ((null? ops*)
- (let ((new (map referenced? ops)))
- (if (not (equal? new old))
- (prune new values '())
- (residualize values
- (map (lambda (op val)
- (set-operand-residual-value! op val)
- op)
- (map car effects) (map cdr effects))))))
- (else
- (let ((op (car ops*)))
- (cond
- ((memq op values)
- (lp (cdr ops*) values effects))
- ((operand-residual-value op)
- (lp (cdr ops*) (cons op values) effects))
- ((referenced? op)
- (set-operand-residual-value! op (visit-operand op counter 'value))
- (lp (cdr ops*) (cons op values) effects))
- (else
- (lp (cdr ops*)
- values
- (let ((effect (visit-operand op counter 'effect)))
- (if (void? effect)
- effects
- (acons op effect effects))))))))))))
-
- (define (small-expression? x limit)
- (let/ec k
- (tree-il-fold
- (lambda (x res) ; leaf
- (1+ res))
- (lambda (x res) ; down
- (1+ res))
- (lambda (x res) ; up
- (if (< res limit)
- res
- (k #f)))
- 0 x)
- #t))
-
- (define (extend-env sym op env)
- (vhash-consq (operand-sym op) op (vhash-consq sym op env)))
-
- (let loop ((exp exp)
- (env vlist-null) ; vhash of gensym -> <operand>
- (counter #f) ; inlined call stack
- (ctx 'values)) ; effect, value, values, test, operator, or call
- (define (lookup var)
- (cond
- ((vhash-assq var env) => cdr)
- (else (error "unbound var" var))))
-
- ;; Find a value referenced a specific number of times. This is a hack
- ;; that's used for propagating fresh data structures like rest lists and
- ;; prompt tags. Usually we wouldn't copy consed data, but we can do so in
- ;; some special cases like `apply' or prompts if we can account
- ;; for all of its uses.
- ;;
- ;; You don't want to use this in general because it introduces a slight
- ;; nonlinearity by running peval again (though with a small effort and size
- ;; counter).
- ;;
- (define (find-definition x n-aliases)
- (cond
- ((lexical-ref? x)
- (cond
- ((lookup (lexical-ref-gensym x))
- => (lambda (op)
- (if (var-set? (operand-var op))
- (values #f #f)
- (let ((y (or (operand-residual-value op)
- (visit-operand op counter 'value 10 10)
- (operand-source op))))
- (cond
- ((and (lexical-ref? y)
- (= (lexical-refcount (lexical-ref-gensym x)) 1))
- ;; X is a simple alias for Y. Recurse, regardless of
- ;; the number of aliases we were expecting.
- (find-definition y n-aliases))
- ((= (lexical-refcount (lexical-ref-gensym x)) n-aliases)
- ;; We found a definition that is aliased the right
- ;; number of times. We still recurse in case it is a
- ;; lexical.
- (values (find-definition y 1)
- op))
- (else
- ;; We can't account for our aliases.
- (values #f #f)))))))
- (else
- ;; A formal parameter. Can't say anything about that.
- (values #f #f))))
- ((= n-aliases 1)
- ;; Not a lexical: success, but only if we are looking for an
- ;; unaliased value.
- (values x #f))
- (else (values #f #f))))
-
- (define (visit exp ctx)
- (loop exp env counter ctx))
-
- (define (for-value exp) (visit exp 'value))
- (define (for-values exp) (visit exp 'values))
- (define (for-test exp) (visit exp 'test))
- (define (for-effect exp) (visit exp 'effect))
- (define (for-call exp) (visit exp 'call))
- (define (for-tail exp) (visit exp ctx))
-
- (if counter
- (record-effort! counter))
-
- (log 'visit ctx (and=> counter effort-counter)
- (unparse-tree-il exp))
-
- (match exp
- (($ <const>)
- (case ctx
- ((effect) (make-void #f))
- (else exp)))
- (($ <void>)
- (case ctx
- ((test) (make-const #f #t))
- (else exp)))
- (($ <lexical-ref> _ _ gensym)
- (log 'begin-copy gensym)
- (let lp ((op (lookup gensym)))
- (cond
- ((eq? ctx 'effect)
- (log 'lexical-for-effect gensym)
- (make-void #f))
- ((operand-alias op)
- ;; This is an unassigned operand that simply aliases some
- ;; other operand. Recurse to avoid residualizing the leaf
- ;; binding.
- => lp)
- ((eq? ctx 'call)
- ;; Don't propagate copies if we are residualizing a call.
- (log 'residualize-lexical-call gensym op)
- (residualize-lexical op))
- ((var-set? (operand-var op))
- ;; Assigned lexicals don't copy-propagate.
- (log 'assigned-var gensym op)
- (residualize-lexical op))
- ((not (operand-copyable? op))
- ;; We already know that this operand is not copyable.
- (log 'not-copyable gensym op)
- (residualize-lexical op))
- ((and=> (operand-constant-value op)
- (lambda (x) (or (const? x) (void? x) (primitive-ref? x))))
- ;; A cache hit.
- (let ((val (operand-constant-value op)))
- (log 'memoized-constant gensym val)
- (for-tail val)))
- ((visit-operand op counter (if (eq? ctx 'values) 'value ctx)
- recursive-effort-limit operand-size-limit)
- =>
- ;; If we end up deciding to residualize this value instead of
- ;; copying it, save that residualized value.
- (lambda (val)
- (cond
- ((not (constant-expression? val))
- (log 'not-constant gensym op)
- ;; At this point, ctx is operator, test, or value. A
- ;; value that is non-constant in one context will be
- ;; non-constant in the others, so it's safe to record
- ;; that here, and avoid future visits.
- (set-operand-copyable?! op #f)
- (residualize-lexical op ctx val))
- ((or (const? val)
- (void? val)
- (primitive-ref? val))
- ;; Always propagate simple values that cannot lead to
- ;; code bloat.
- (log 'copy-simple gensym val)
- ;; It could be this constant is the result of folding.
- ;; If that is the case, cache it. This helps loop
- ;; unrolling get farther.
- (if (or (eq? ctx 'value) (eq? ctx 'values))
- (begin
- (log 'memoize-constant gensym val)
- (set-operand-constant-value! op val)))
- val)
- ((= 1 (var-refcount (operand-var op)))
- ;; Always propagate values referenced only once.
- (log 'copy-single gensym val)
- val)
- ;; FIXME: do demand-driven size accounting rather than
- ;; these heuristics.
- ((eq? ctx 'operator)
- ;; A pure expression in the operator position. Inline
- ;; if it's a lambda that's small enough.
- (if (and (lambda? val)
- (small-expression? val operator-size-limit))
- (begin
- (log 'copy-operator gensym val)
- val)
- (begin
- (log 'too-big-for-operator gensym val)
- (residualize-lexical op ctx val))))
- (else
- ;; A pure expression, processed for call or for value.
- ;; Don't inline lambdas, because they will probably won't
- ;; fold because we don't know the operator.
- (if (and (small-expression? val value-size-limit)
- (not (tree-il-any lambda? val)))
- (begin
- (log 'copy-value gensym val)
- val)
- (begin
- (log 'too-big-or-has-lambda gensym val)
- (residualize-lexical op ctx val)))))))
- (else
- ;; Visit failed. Either the operand isn't bound, as in
- ;; lambda formal parameters, or the copy was aborted.
- (log 'unbound-or-aborted gensym op)
- (residualize-lexical op)))))
- (($ <lexical-set> src name gensym exp)
- (let ((op (lookup gensym)))
- (if (zero? (var-refcount (operand-var op)))
- (let ((exp (for-effect exp)))
- (if (void? exp)
- exp
- (make-sequence src (list exp (make-void #f)))))
- (begin
- (record-operand-use op)
- (make-lexical-set src name (operand-sym op) (for-value exp))))))
- (($ <let> src
- (names ... rest)
- (gensyms ... rest-sym)
- (vals ... ($ <application> _ ($ <primitive-ref> _ 'list) rest-args))
- ($ <application> asrc
- ($ <primitive-ref> _ (or 'apply '@apply))
- (proc args ...
- ($ <lexical-ref> _
- (? (cut eq? <> rest))
- (? (lambda (sym)
- (and (eq? sym rest-sym)
- (= (lexical-refcount sym) 1))))))))
- (let* ((tmps (make-list (length rest-args) 'tmp))
- (tmp-syms (fresh-temporaries tmps)))
- (for-tail
- (make-let src
- (append names tmps)
- (append gensyms tmp-syms)
- (append vals rest-args)
- (make-application
- asrc
- proc
- (append args
- (map (cut make-lexical-ref #f <> <>)
- tmps tmp-syms)))))))
- (($ <let> src names gensyms vals body)
- (define (lookup-alias exp)
- ;; It's very common for macros to introduce something like:
- ;;
- ;; ((lambda (x y) ...) x-exp y-exp)
- ;;
- ;; In that case you might end up trying to inline something like:
- ;;
- ;; (let ((x x-exp) (y y-exp)) ...)
- ;;
- ;; But if x-exp is itself a lexical-ref that aliases some much
- ;; larger expression, perhaps it will fail to inline due to
- ;; size. However we don't want to introduce a useless alias
- ;; (in this case, x). So if the RHS of a let expression is a
- ;; lexical-ref, we record that expression. If we end up having
- ;; to residualize X, then instead we residualize X-EXP, as long
- ;; as it isn't assigned.
- ;;
- (match exp
- (($ <lexical-ref> _ _ sym)
- (let ((op (lookup sym)))
- (and (not (var-set? (operand-var op))) op)))
- (_ #f)))
-
- (let* ((vars (map lookup-var gensyms))
- (new (fresh-gensyms vars))
- (ops (make-bound-operands vars new vals
- (lambda (exp counter ctx)
- (loop exp env counter ctx))
- (map lookup-alias vals)))
- (env (fold extend-env env gensyms ops))
- (body (loop body env counter ctx)))
- (cond
- ((const? body)
- (for-tail (make-sequence src (append vals (list body)))))
- ((and (lexical-ref? body)
- (memq (lexical-ref-gensym body) new))
- (let ((sym (lexical-ref-gensym body))
- (pairs (map cons new vals)))
- ;; (let ((x foo) (y bar) ...) x) => (begin bar ... foo)
- (for-tail
- (make-sequence
- src
- (append (map cdr (alist-delete sym pairs eq?))
- (list (assq-ref pairs sym)))))))
- (else
- ;; Only include bindings for which lexical references
- ;; have been residualized.
- (prune-bindings ops #f body counter ctx
- (lambda (names gensyms vals body)
- (if (null? names) (error "what!" names))
- (make-let src names gensyms vals body)))))))
- (($ <letrec> src in-order? names gensyms vals body)
- ;; Note the difference from the `let' case: here we use letrec*
- ;; so that the `visit' procedure for the new operands closes over
- ;; an environment that includes the operands. Also we don't try
- ;; to elide aliases, because we can't sensibly reduce something
- ;; like (letrec ((a b) (b a)) a).
- (letrec* ((visit (lambda (exp counter ctx)
- (loop exp env* counter ctx)))
- (vars (map lookup-var gensyms))
- (new (fresh-gensyms vars))
- (ops (make-bound-operands vars new vals visit))
- (env* (fold extend-env env gensyms ops))
- (body* (visit body counter ctx)))
- (if (and (const? body*) (every constant-expression? vals))
- ;; We may have folded a loop completely, even though there
- ;; might be cyclical references between the bound values.
- ;; Handle this degenerate case specially.
- body*
- (prune-bindings ops in-order? body* counter ctx
- (lambda (names gensyms vals body)
- (make-letrec src in-order?
- names gensyms vals body))))))
- (($ <fix> src names gensyms vals body)
- (letrec* ((visit (lambda (exp counter ctx)
- (loop exp env* counter ctx)))
- (vars (map lookup-var gensyms))
- (new (fresh-gensyms vars))
- (ops (make-bound-operands vars new vals visit))
- (env* (fold extend-env env gensyms ops))
- (body* (visit body counter ctx)))
- (if (const? body*)
- body*
- (prune-bindings ops #f body* counter ctx
- (lambda (names gensyms vals body)
- (make-fix src names gensyms vals body))))))
- (($ <let-values> lv-src producer consumer)
- ;; Peval the producer, then try to inline the consumer into
- ;; the producer. If that succeeds, peval again. Otherwise
- ;; reconstruct the let-values, pevaling the consumer.
- (let ((producer (for-values producer)))
- (or (match consumer
- (($ <lambda-case> src req opt rest #f inits gensyms body #f)
- (let* ((nmin (length req))
- (nmax (and (not rest) (+ nmin (if opt (length opt) 0)))))
- (cond
- ((inline-values lv-src producer nmin nmax consumer)
- => for-tail)
- (else #f))))
- (_ #f))
- (make-let-values lv-src producer (for-tail consumer)))))
- (($ <dynwind> src winder body unwinder)
- (let ((pre (for-value winder))
- (body (for-tail body))
- (post (for-value unwinder)))
- (cond
- ((not (constant-expression? pre))
- (cond
- ((not (constant-expression? post))
- (let ((pre-sym (gensym "pre-")) (post-sym (gensym "post-")))
- (record-new-temporary! 'pre pre-sym 1)
- (record-new-temporary! 'post post-sym 1)
- (make-let src '(pre post) (list pre-sym post-sym) (list pre post)
- (make-dynwind src
- (make-lexical-ref #f 'pre pre-sym)
- body
- (make-lexical-ref #f 'post post-sym)))))
- (else
- (let ((pre-sym (gensym "pre-")))
- (record-new-temporary! 'pre pre-sym 1)
- (make-let src '(pre) (list pre-sym) (list pre)
- (make-dynwind src
- (make-lexical-ref #f 'pre pre-sym)
- body
- post))))))
- ((not (constant-expression? post))
- (let ((post-sym (gensym "post-")))
- (record-new-temporary! 'post post-sym 1)
- (make-let src '(post) (list post-sym) (list post)
- (make-dynwind src
- pre
- body
- (make-lexical-ref #f 'post post-sym)))))
- (else
- (make-dynwind src pre body post)))))
- (($ <dynlet> src fluids vals body)
- (make-dynlet src (map for-value fluids) (map for-value vals)
- (for-tail body)))
- (($ <dynref> src fluid)
- (make-dynref src (for-value fluid)))
- (($ <dynset> src fluid exp)
- (make-dynset src (for-value fluid) (for-value exp)))
- (($ <toplevel-ref> src (? effect-free-primitive? name))
- (if (local-toplevel? name)
- exp
- (let ((exp (resolve-primitives! exp cenv)))
- (if (primitive-ref? exp)
- (for-tail exp)
- exp))))
- (($ <toplevel-ref>)
- ;; todo: open private local bindings.
- exp)
- (($ <module-ref> src module (? effect-free-primitive? name) #f)
- (let ((module (false-if-exception
- (resolve-module module #\ensure #f))))
- (if (module? module)
- (let ((var (module-variable module name)))
- (if (eq? var (module-variable the-scm-module name))
- (make-primitive-ref src name)
- exp))
- exp)))
- (($ <module-ref>)
- exp)
- (($ <module-set> src mod name public? exp)
- (make-module-set src mod name public? (for-value exp)))
- (($ <toplevel-define> src name exp)
- (make-toplevel-define src name (for-value exp)))
- (($ <toplevel-set> src name exp)
- (make-toplevel-set src name (for-value exp)))
- (($ <primitive-ref>)
- (case ctx
- ((effect) (make-void #f))
- ((test) (make-const #f #t))
- (else exp)))
- (($ <conditional> src condition subsequent alternate)
- (define (call-with-failure-thunk exp proc)
- (match exp
- (($ <application> _ _ ()) (proc exp))
- (($ <const>) (proc exp))
- (($ <void>) (proc exp))
- (($ <lexical-ref>) (proc exp))
- (_
- (let ((t (gensym "failure-")))
- (record-new-temporary! 'failure t 2)
- (make-let
- src (list 'failure) (list t)
- (list
- (make-lambda
- #f '()
- (make-lambda-case #f '() #f #f #f '() '() exp #f)))
- (proc (make-application #f (make-lexical-ref #f 'failure t)
- '())))))))
- (define (simplify-conditional c)
- (match c
- ;; Swap the arms of (if (not FOO) A B), to simplify.
- (($ <conditional> src
- ($ <application> _ ($ <primitive-ref> _ 'not) (pred))
- subsequent alternate)
- (simplify-conditional
- (make-conditional src pred alternate subsequent)))
- ;; Special cases for common tests in the predicates of chains
- ;; of if expressions.
- (($ <conditional> src
- ($ <conditional> src* outer-test inner-test ($ <const> _ #f))
- inner-subsequent
- alternate)
- (let lp ((alternate alternate))
- (match alternate
- ;; Lift a common repeated test out of a chain of if
- ;; expressions.
- (($ <conditional> _ (? (cut tree-il=? outer-test <>))
- other-subsequent alternate)
- (make-conditional
- src outer-test
- (simplify-conditional
- (make-conditional src* inner-test inner-subsequent
- other-subsequent))
- alternate))
- ;; Likewise, but punching through any surrounding
- ;; failure continuations.
- (($ <let> let-src (name) (sym) ((and thunk ($ <lambda>))) body)
- (make-let
- let-src (list name) (list sym) (list thunk)
- (lp body)))
- ;; Otherwise, rotate AND tests to expose a simple
- ;; condition in the front. Although this may result in
- ;; lexically binding failure thunks, the thunks will be
- ;; compiled to labels allocation, so there's no actual
- ;; code growth.
- (_
- (call-with-failure-thunk
- alternate
- (lambda (failure)
- (make-conditional
- src outer-test
- (simplify-conditional
- (make-conditional src* inner-test inner-subsequent failure))
- failure)))))))
- (_ c)))
- (match (for-test condition)
- (($ <const> _ val)
- (if val
- (for-tail subsequent)
- (for-tail alternate)))
- (c
- (simplify-conditional
- (make-conditional src c (for-tail subsequent)
- (for-tail alternate))))))
- (($ <application> src
- ($ <primitive-ref> _ '@call-with-values)
- (producer
- ($ <lambda> _ _
- (and consumer
- ;; No optional or kwargs.
- ($ <lambda-case>
- _ req #f rest #f () gensyms body #f)))))
- (for-tail (make-let-values src (make-application src producer '())
- consumer)))
- (($ <application> src ($ <primitive-ref> _ 'values) exps)
- (cond
- ((null? exps)
- (if (eq? ctx 'effect)
- (make-void #f)
- exp))
- (else
- (let ((vals (map for-value exps)))
- (if (and (case ctx
- ((value test effect) #t)
- (else (null? (cdr vals))))
- (every singly-valued-expression? vals))
- (for-tail (make-sequence src (append (cdr vals) (list (car vals)))))
- (make-application src (make-primitive-ref #f 'values) vals))))))
- (($ <application> src (and apply ($ <primitive-ref> _ (or 'apply '@apply)))
- (proc args ... tail))
- (let lp ((tail* (find-definition tail 1)) (speculative? #t))
- (define (copyable? x)
- ;; Inlining a result from find-definition effectively copies it,
- ;; relying on the let-pruning to remove its original binding. We
- ;; shouldn't copy non-constant expressions.
- (or (not speculative?) (constant-expression? x)))
- (match tail*
- (($ <const> _ (args* ...))
- (let ((args* (map (cut make-const #f <>) args*)))
- (for-tail (make-application src proc (append args args*)))))
- (($ <application> _ ($ <primitive-ref> _ 'cons)
- ((and head (? copyable?)) (and tail (? copyable?))))
- (for-tail (make-application src apply
- (cons proc
- (append args (list head tail))))))
- (($ <application> _ ($ <primitive-ref> _ 'list)
- (and args* ((? copyable?) ...)))
- (for-tail (make-application src proc (append args args*))))
- (tail*
- (if speculative?
- (lp (for-value tail) #f)
- (let ((args (append (map for-value args) (list tail*))))
- (make-application src apply
- (cons (for-value proc) args))))))))
- (($ <application> src orig-proc orig-args)
- ;; todo: augment the global env with specialized functions
- (let revisit-proc ((proc (visit orig-proc 'operator)))
- (match proc
- (($ <primitive-ref> _ (? constructor-primitive? name))
- (cond
- ((and (memq ctx '(effect test))
- (match (cons name orig-args)
- ((or ('cons _ _)
- ('list . _)
- ('vector . _)
- ('make-prompt-tag)
- ('make-prompt-tag ($ <const> _ (? string?))))
- #t)
- (_ #f)))
- ;; Some expressions can be folded without visiting the
- ;; arguments for value.
- (let ((res (if (eq? ctx 'effect)
- (make-void #f)
- (make-const #f #t))))
- (for-tail (make-sequence src (append orig-args (list res))))))
- (else
- (match (cons name (map for-value orig-args))
- (('cons head tail)
- (match tail
- (($ <const> src (? (cut eq? <> '())))
- (make-application src (make-primitive-ref #f 'list)
- (list head)))
- (($ <application> src ($ <primitive-ref> _ 'list) elts)
- (make-application src (make-primitive-ref #f 'list)
- (cons head elts)))
- (_ (make-application src proc (list head tail)))))
- ((_ . args)
- (make-application src proc args))))))
- (($ <primitive-ref> _ (? accessor-primitive? name))
- (match (cons name (map for-value orig-args))
- ;; FIXME: these for-tail recursions could take place outside
- ;; an effort counter.
- (('car ($ <application> src ($ <primitive-ref> _ 'cons) (head tail)))
- (for-tail (make-sequence src (list tail head))))
- (('cdr ($ <application> src ($ <primitive-ref> _ 'cons) (head tail)))
- (for-tail (make-sequence src (list head tail))))
- (('car ($ <application> src ($ <primitive-ref> _ 'list) (head . tail)))
- (for-tail (make-sequence src (append tail (list head)))))
- (('cdr ($ <application> src ($ <primitive-ref> _ 'list) (head . tail)))
- (for-tail (make-sequence
- src
- (list head
- (make-application
- src (make-primitive-ref #f 'list) tail)))))
-
- (('car ($ <const> src (head . tail)))
- (for-tail (make-const src head)))
- (('cdr ($ <const> src (head . tail)))
- (for-tail (make-const src tail)))
- (((or 'memq 'memv) k ($ <const> _ (elts ...)))
- ;; FIXME: factor
- (case ctx
- ((effect)
- (for-tail
- (make-sequence src (list k (make-void #f)))))
- ((test)
- (cond
- ((const? k)
- ;; A shortcut. The `else' case would handle it, but
- ;; this way is faster.
- (let ((member (case name ((memq) memq) ((memv) memv))))
- (make-const #f (and (member (const-exp k) elts) #t))))
- ((null? elts)
- (for-tail
- (make-sequence src (list k (make-const #f #f)))))
- (else
- (let ((t (gensym "t-"))
- (eq (if (eq? name 'memq) 'eq? 'eqv?)))
- (record-new-temporary! 't t (length elts))
- (for-tail
- (make-let
- src (list 't) (list t) (list k)
- (let lp ((elts elts))
- (define test
- (make-application
- #f (make-primitive-ref #f eq)
- (list (make-lexical-ref #f 't t)
- (make-const #f (car elts)))))
- (if (null? (cdr elts))
- test
- (make-conditional src test
- (make-const #f #t)
- (lp (cdr elts)))))))))))
- (else
- (cond
- ((const? k)
- (let ((member (case name ((memq) memq) ((memv) memv))))
- (make-const #f (member (const-exp k) elts))))
- ((null? elts)
- (for-tail (make-sequence src (list k (make-const #f #f)))))
- (else
- (make-application src proc (list k (make-const #f elts))))))))
- ((_ . args)
- (or (fold-constants src name args ctx)
- (make-application src proc args)))))
- (($ <primitive-ref> _ (? effect-free-primitive? name))
- (let ((args (map for-value orig-args)))
- (or (fold-constants src name args ctx)
- (make-application src proc args))))
- (($ <lambda> _ _
- ($ <lambda-case> _ req opt rest #f inits gensyms body #f))
- ;; Simple case: no keyword arguments.
- ;; todo: handle the more complex cases
- (let* ((nargs (length orig-args))
- (nreq (length req))
- (nopt (if opt (length opt) 0))
- (key (source-expression proc)))
- (define (inlined-application)
- (cond
- ((= nargs (+ nreq nopt))
- (make-let src
- (append req
- (or opt '())
- (if rest (list rest) '()))
- gensyms
- (append orig-args
- (if rest
- (list (make-const #f '()))
- '()))
- body))
- ((> nargs (+ nreq nopt))
- (make-let src
- (append req
- (or opt '())
- (list rest))
- gensyms
- (append (take orig-args (+ nreq nopt))
- (list (make-application
- #f
- (make-primitive-ref #f 'list)
- (drop orig-args (+ nreq nopt)))))
- body))
- (else
- ;; Here we handle the case where nargs < nreq + nopt,
- ;; so the rest argument (if any) will be empty, and
- ;; there will be optional arguments that rely on their
- ;; default initializers.
- ;;
- ;; The default initializers of optional arguments
- ;; may refer to earlier arguments, so in the general
- ;; case we must expand into a series of nested let
- ;; expressions.
- ;;
- ;; In the generated code, the outermost let
- ;; expression will bind all arguments provided by
- ;; the application's argument list, as well as the
- ;; empty rest argument, if any. Each remaining
- ;; optional argument that relies on its default
- ;; initializer will be bound within an inner let.
- ;;
- ;; rest-gensyms, rest-vars and rest-inits will have
- ;; either 0 or 1 elements. They are oddly named, but
- ;; allow simpler code below.
- (let*-values
- (((non-rest-gensyms rest-gensyms)
- (split-at gensyms (+ nreq nopt)))
- ((provided-gensyms default-gensyms)
- (split-at non-rest-gensyms nargs))
- ((provided-vars default-vars)
- (split-at (append req opt) nargs))
- ((rest-vars)
- (if rest (list rest) '()))
- ((rest-inits)
- (if rest
- (list (make-const #f '()))
- '()))
- ((default-inits)
- (drop inits (- nargs nreq))))
- (make-let src
- (append provided-vars rest-vars)
- (append provided-gensyms rest-gensyms)
- (append orig-args rest-inits)
- (fold-right (lambda (var gensym init body)
- (make-let src
- (list var)
- (list gensym)
- (list init)
- body))
- body
- default-vars
- default-gensyms
- default-inits))))))
-
- (cond
- ((or (< nargs nreq) (and (not rest) (> nargs (+ nreq nopt))))
- ;; An error, or effecting arguments.
- (make-application src (for-call orig-proc)
- (map for-value orig-args)))
- ((or (and=> (find-counter key counter) counter-recursive?)
- (lambda? orig-proc))
- ;; A recursive call, or a lambda in the operator
- ;; position of the source expression. Process again in
- ;; tail context.
- ;;
- ;; In the recursive case, mark intervening counters as
- ;; recursive, so we can handle a toplevel counter that
- ;; recurses mutually with some other procedure.
- ;; Otherwise, the next time we see the other procedure,
- ;; the effort limit would be clamped to 100.
- ;;
- (let ((found (find-counter key counter)))
- (if (and found (counter-recursive? found))
- (let lp ((counter counter))
- (if (not (eq? counter found))
- (begin
- (set-counter-recursive?! counter #t)
- (lp (counter-prev counter)))))))
-
- (log 'inline-recurse key)
- (loop (inlined-application) env counter ctx))
- (else
- ;; An integration at the top-level, the first
- ;; recursion of a recursive procedure, or a nested
- ;; integration of a procedure that hasn't been seen
- ;; yet.
- (log 'inline-begin exp)
- (let/ec k
- (define (abort)
- (log 'inline-abort exp)
- (k (make-application src (for-call orig-proc)
- (map for-value orig-args))))
- (define new-counter
- (cond
- ;; These first two cases will transfer effort
- ;; from the current counter into the new
- ;; counter.
- ((find-counter key counter)
- => (lambda (prev)
- (make-recursive-counter recursive-effort-limit
- operand-size-limit
- prev counter)))
- (counter
- (make-nested-counter abort key counter))
- ;; This case opens a new account, effectively
- ;; printing money. It should only do so once
- ;; for each call site in the source program.
- (else
- (make-top-counter effort-limit operand-size-limit
- abort key))))
- (define result
- (loop (inlined-application) env new-counter ctx))
-
- (if counter
- ;; The nested inlining attempt succeeded.
- ;; Deposit the unspent effort and size back
- ;; into the current counter.
- (transfer! new-counter counter))
-
- (log 'inline-end result exp)
- result)))))
- (($ <let> _ _ _ vals _)
- ;; Attempt to inline `let' in the operator position.
- ;;
- ;; We have to re-visit the proc in value mode, since the
- ;; `let' bindings might have been introduced or renamed,
- ;; whereas the lambda (if any) in operator position has not
- ;; been renamed.
- (if (or (and-map constant-expression? vals)
- (and-map constant-expression? orig-args))
- ;; The arguments and the let-bound values commute.
- (match (for-value orig-proc)
- (($ <let> lsrc names syms vals body)
- (log 'inline-let orig-proc)
- (for-tail
- (make-let lsrc names syms vals
- (make-application src body orig-args))))
- ;; It's possible for a `let' to go away after the
- ;; visit due to the fact that visiting a procedure in
- ;; value context will prune unused bindings, whereas
- ;; visiting in operator mode can't because it doesn't
- ;; traverse through lambdas. In that case re-visit
- ;; the procedure.
- (proc (revisit-proc proc)))
- (make-application src (for-call orig-proc)
- (map for-value orig-args))))
- (_
- (make-application src (for-call orig-proc)
- (map for-value orig-args))))))
- (($ <lambda> src meta body)
- (case ctx
- ((effect) (make-void #f))
- ((test) (make-const #f #t))
- ((operator) exp)
- (else (record-source-expression!
- exp
- (make-lambda src meta (and body (for-values body)))))))
- (($ <lambda-case> src req opt rest kw inits gensyms body alt)
- (define (lift-applied-lambda body gensyms)
- (and (not opt) rest (not kw)
- (match body
- (($ <application> _
- ($ <primitive-ref> _ '@apply)
- (($ <lambda> _ _ (and lcase ($ <lambda-case>)))
- ($ <lexical-ref> _ _ sym)
- ...))
- (and (equal? sym gensyms)
- (not (lambda-case-alternate lcase))
- lcase))
- (_ #f))))
- (let* ((vars (map lookup-var gensyms))
- (new (fresh-gensyms vars))
- (env (fold extend-env env gensyms
- (make-unbound-operands vars new)))
- (new-sym (lambda (old)
- (operand-sym (cdr (vhash-assq old env)))))
- (body (loop body env counter ctx)))
- (or
- ;; (lambda args (apply (lambda ...) args)) => (lambda ...)
- (lift-applied-lambda body new)
- (make-lambda-case src req opt rest
- (match kw
- ((aok? (kw name old) ...)
- (cons aok? (map list kw name (map new-sym old))))
- (_ #f))
- (map (cut loop <> env counter 'value) inits)
- new
- body
- (and alt (for-tail alt))))))
- (($ <sequence> src exps)
- (let lp ((exps exps) (effects '()))
- (match exps
- ((last)
- (if (null? effects)
- (for-tail last)
- (make-sequence
- src
- (reverse (cons (for-tail last) effects)))))
- ((head . rest)
- (let ((head (for-effect head)))
- (cond
- ((sequence? head)
- (lp (append (sequence-exps head) rest) effects))
- ((void? head)
- (lp rest effects))
- (else
- (lp rest (cons head effects)))))))))
- (($ <prompt> src tag body handler)
- (define (make-prompt-tag? x)
- (match x
- (($ <application> _ ($ <primitive-ref> _ 'make-prompt-tag)
- (or () ((? constant-expression?))))
- #t)
- (_ #f)))
-
- (let ((tag (for-value tag))
- (body (for-values body)))
- (cond
- ((find-definition tag 1)
- (lambda (val op)
- (make-prompt-tag? val))
- => (lambda (val op)
- ;; There is no way that an <abort> could know the tag
- ;; for this <prompt>, so we can elide the <prompt>
- ;; entirely.
- (unrecord-operand-uses op 1)
- body))
- ((find-definition tag 2)
- (lambda (val op)
- (and (make-prompt-tag? val)
- (abort? body)
- (tree-il=? (abort-tag body) tag)))
- => (lambda (val op)
- ;; (let ((t (make-prompt-tag)))
- ;; (call-with-prompt t
- ;; (lambda () (abort-to-prompt t val ...))
- ;; (lambda (k arg ...) e ...)))
- ;; => (let-values (((k arg ...) (values values val ...)))
- ;; e ...)
- (unrecord-operand-uses op 2)
- (for-tail
- (make-let-values
- src
- (make-application #f (make-primitive-ref #f 'apply)
- `(,(make-primitive-ref #f 'values)
- ,(make-primitive-ref #f 'values)
- ,@(abort-args body)
- ,(abort-tail body)))
- (for-tail handler)))))
- (else
- (make-prompt src tag body (for-tail handler))))))
- (($ <abort> src tag args tail)
- (make-abort src (for-value tag) (map for-value args)
- (for-value tail))))))
-;;; open-coding primitive procedures
-
-;; Copyright (C) 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language tree-il primitives)
- #\use-module (system base pmatch)
- #\use-module (ice-9 match)
- #\use-module (rnrs bytevectors)
- #\use-module (system base syntax)
- #\use-module (language tree-il)
- #\use-module (srfi srfi-4)
- #\use-module (srfi srfi-16)
- #\export (resolve-primitives! add-interesting-primitive!
- expand-primitives!
- effect-free-primitive? effect+exception-free-primitive?
- constructor-primitive? accessor-primitive?
- singly-valued-primitive? bailout-primitive?
- negate-primitive))
-
-;; When adding to this, be sure to update *multiply-valued-primitives*
-;; if appropriate.
-(define *interesting-primitive-names*
- '(apply @apply
- call-with-values @call-with-values
- call-with-current-continuation @call-with-current-continuation
- call/cc
- dynamic-wind
- @dynamic-wind
- values
- eq? eqv? equal?
- memq memv
- = < > <= >= zero? positive? negative?
- + * - / 1- 1+ quotient remainder modulo
- ash logand logior logxor lognot
- not
- pair? null? list? symbol? vector? string? struct? number? char?
-
- complex? real? rational? inf? nan? integer? exact? inexact? even? odd?
-
- char<? char<=? char>=? char>?
-
- integer->char char->integer number->string string->number
-
- acons cons cons*
-
- list vector
-
- car cdr
- set-car! set-cdr!
-
- caar cadr cdar cddr
-
- caaar caadr cadar caddr cdaar cdadr cddar cdddr
-
- caaaar caaadr caadar caaddr cadaar cadadr caddar cadddr
- cdaaar cdaadr cdadar cdaddr cddaar cddadr cdddar cddddr
-
- vector-ref vector-set!
- variable-ref variable-set!
- variable-bound?
-
- fluid-ref fluid-set!
-
- @prompt call-with-prompt @abort abort-to-prompt
- make-prompt-tag
-
- throw error scm-error
-
- string-length string-ref string-set!
-
- struct-vtable make-struct struct-ref struct-set!
-
- bytevector-u8-ref bytevector-u8-set!
- bytevector-s8-ref bytevector-s8-set!
- u8vector-ref u8vector-set! s8vector-ref s8vector-set!
-
- bytevector-u16-ref bytevector-u16-set!
- bytevector-u16-native-ref bytevector-u16-native-set!
- bytevector-s16-ref bytevector-s16-set!
- bytevector-s16-native-ref bytevector-s16-native-set!
- u16vector-ref u16vector-set! s16vector-ref s16vector-set!
-
- bytevector-u32-ref bytevector-u32-set!
- bytevector-u32-native-ref bytevector-u32-native-set!
- bytevector-s32-ref bytevector-s32-set!
- bytevector-s32-native-ref bytevector-s32-native-set!
- u32vector-ref u32vector-set! s32vector-ref s32vector-set!
-
- bytevector-u64-ref bytevector-u64-set!
- bytevector-u64-native-ref bytevector-u64-native-set!
- bytevector-s64-ref bytevector-s64-set!
- bytevector-s64-native-ref bytevector-s64-native-set!
- u64vector-ref u64vector-set! s64vector-ref s64vector-set!
-
- bytevector-ieee-single-ref bytevector-ieee-single-set!
- bytevector-ieee-single-native-ref bytevector-ieee-single-native-set!
- bytevector-ieee-double-ref bytevector-ieee-double-set!
- bytevector-ieee-double-native-ref bytevector-ieee-double-native-set!
- f32vector-ref f32vector-set! f64vector-ref f64vector-set!))
-
-(define (add-interesting-primitive! name)
- (hashq-set! *interesting-primitive-vars*
- (or (module-variable (current-module) name)
- (error "unbound interesting primitive" name))
- name))
-
-(define *interesting-primitive-vars* (make-hash-table))
-
-(for-each add-interesting-primitive! *interesting-primitive-names*)
-
-(define *primitive-constructors*
- ;; Primitives that return a fresh object.
- '(acons cons cons* list vector make-struct make-struct/no-tail
- make-prompt-tag))
-
-(define *primitive-accessors*
- ;; Primitives that are pure, but whose result depends on the mutable
- ;; memory pointed to by their operands.
- '(vector-ref
- car cdr
- memq memv
- struct-ref
- string-ref
- bytevector-u8-ref bytevector-s8-ref
- bytevector-u16-ref bytevector-u16-native-ref
- bytevector-s16-ref bytevector-s16-native-ref
- bytevector-u32-ref bytevector-u32-native-ref
- bytevector-s32-ref bytevector-s32-native-ref
- bytevector-u64-ref bytevector-u64-native-ref
- bytevector-s64-ref bytevector-s64-native-ref
- bytevector-ieee-single-ref bytevector-ieee-single-native-ref
- bytevector-ieee-double-ref bytevector-ieee-double-native-ref))
-
-(define *effect-free-primitives*
- `(values
- eq? eqv? equal?
- = < > <= >= zero? positive? negative?
- ash logand logior logxor lognot
- + * - / 1- 1+ quotient remainder modulo
- not
- pair? null? list? symbol? vector? struct? string? number? char?
- complex? real? rational? inf? nan? integer? exact? inexact? even? odd?
- char<? char<=? char>=? char>?
- integer->char char->integer number->string string->number
- struct-vtable
- string-length
- ;; These all should get expanded out by expand-primitives!.
- caar cadr cdar cddr
- caaar caadr cadar caddr cdaar cdadr cddar cdddr
- caaaar caaadr caadar caaddr cadaar cadadr caddar cadddr
- cdaaar cdaadr cdadar cdaddr cddaar cddadr cdddar cddddr
- ,@*primitive-constructors*
- ,@*primitive-accessors*))
-
-;; Like *effect-free-primitives* above, but further restricted in that they
-;; cannot raise exceptions.
-(define *effect+exception-free-primitives*
- '(values
- eq? eqv? equal?
- not
- pair? null? list? symbol? vector? struct? string? number? char?
- acons cons cons* list vector))
-
-;; Primitives that don't always return one value.
-(define *multiply-valued-primitives*
- '(apply @apply
- call-with-values @call-with-values
- call-with-current-continuation @call-with-current-continuation
- call/cc
- dynamic-wind
- @dynamic-wind
- values
- @prompt call-with-prompt @abort abort-to-prompt))
-
-;; Procedures that cause a nonlocal, non-resumable abort.
-(define *bailout-primitives*
- '(throw error scm-error))
-
-;; Negatable predicates.
-(define *negatable-primitives*
- '((even? . odd?)
- (exact? . inexact?)
- ;; (< <= > >=) are not negatable because of NaNs.
- (char<? . char>=?)
- (char>? . char<=?)))
-
-(define *effect-free-primitive-table* (make-hash-table))
-(define *effect+exceptions-free-primitive-table* (make-hash-table))
-(define *multiply-valued-primitive-table* (make-hash-table))
-(define *bailout-primitive-table* (make-hash-table))
-(define *negatable-primitive-table* (make-hash-table))
-
-(for-each (lambda (x)
- (hashq-set! *effect-free-primitive-table* x #t))
- *effect-free-primitives*)
-(for-each (lambda (x)
- (hashq-set! *effect+exceptions-free-primitive-table* x #t))
- *effect+exception-free-primitives*)
-(for-each (lambda (x)
- (hashq-set! *multiply-valued-primitive-table* x #t))
- *multiply-valued-primitives*)
-(for-each (lambda (x)
- (hashq-set! *bailout-primitive-table* x #t))
- *bailout-primitives*)
-(for-each (lambda (x)
- (hashq-set! *negatable-primitive-table* (car x) (cdr x))
- (hashq-set! *negatable-primitive-table* (cdr x) (car x)))
- *negatable-primitives*)
-
-(define (constructor-primitive? prim)
- (memq prim *primitive-constructors*))
-(define (accessor-primitive? prim)
- (memq prim *primitive-accessors*))
-(define (effect-free-primitive? prim)
- (hashq-ref *effect-free-primitive-table* prim))
-(define (effect+exception-free-primitive? prim)
- (hashq-ref *effect+exceptions-free-primitive-table* prim))
-(define (singly-valued-primitive? prim)
- (not (hashq-ref *multiply-valued-primitive-table* prim)))
-(define (bailout-primitive? prim)
- (hashq-ref *bailout-primitive-table* prim))
-(define (negate-primitive prim)
- (hashq-ref *negatable-primitive-table* prim))
-
-(define (resolve-primitives! x mod)
- (post-order!
- (lambda (x)
- (record-case x
- ((<toplevel-ref> src name)
- (and=> (hashq-ref *interesting-primitive-vars*
- (module-variable mod name))
- (lambda (name) (make-primitive-ref src name))))
- ((<module-ref> src mod name public?)
- (and=> (and=> (resolve-module mod)
- (if public?
- module-public-interface
- identity))
- (lambda (m)
- (and=> (hashq-ref *interesting-primitive-vars*
- (module-variable m name))
- (lambda (name)
- (make-primitive-ref src name))))))
- (else #f)))
- x))
-
-
-
-(define *primitive-expand-table* (make-hash-table))
-
-(define (expand-primitives! x)
- (pre-order!
- (lambda (x)
- (record-case x
- ((<application> src proc args)
- (and (primitive-ref? proc)
- (let ((expand (hashq-ref *primitive-expand-table*
- (primitive-ref-name proc))))
- (and expand (apply expand src args)))))
- (else #f)))
- x))
-
-;;; I actually did spend about 10 minutes trying to redo this with
-;;; syntax-rules. Patches appreciated.
-;;;
-(define-macro (define-primitive-expander sym . clauses)
- (define (inline-args args)
- (let lp ((in args) (out '()))
- (cond ((null? in) `(list ,@(reverse out)))
- ((symbol? in) `(cons* ,@(reverse out) ,in))
- ((pair? (car in))
- (lp (cdr in)
- (cons (if (eq? (caar in) 'quote)
- `(make-const src ,@(cdar in))
- `(make-application src (make-primitive-ref src ',(caar in))
- ,(inline-args (cdar in))))
- out)))
- ((symbol? (car in))
- ;; assume it's locally bound
- (lp (cdr in) (cons (car in) out)))
- ((self-evaluating? (car in))
- (lp (cdr in) (cons `(make-const src ,(car in)) out)))
- (else
- (error "what what" (car in))))))
- (define (consequent exp)
- (cond
- ((pair? exp)
- (pmatch exp
- ((if ,test ,then ,else)
- `(if ,test
- ,(consequent then)
- ,(consequent else)))
- (else
- `(make-application src (make-primitive-ref src ',(car exp))
- ,(inline-args (cdr exp))))))
- ((symbol? exp)
- ;; assume locally bound
- exp)
- ((number? exp)
- `(make-const src ,exp))
- ((not exp)
- ;; failed match
- #f)
- (else (error "bad consequent yall" exp))))
- `(hashq-set! *primitive-expand-table*
- ',sym
- (match-lambda*
- ,@(let lp ((in clauses) (out '()))
- (if (null? in)
- (reverse (cons '(_ #f) out))
- (lp (cddr in)
- (cons `((src . ,(car in))
- ,(consequent (cadr in)))
- out)))))))
-
-(define-primitive-expander zero? (x)
- (= x 0))
-
-(define-primitive-expander positive? (x)
- (> x 0))
-
-(define-primitive-expander negative? (x)
- (< x 0))
-
-;; FIXME: All the code that uses `const?' is redundant with `peval'.
-
-(define-primitive-expander +
- () 0
- (x) (values x)
- (x y) (if (and (const? y) (eqv? (const-exp y) 1))
- (1+ x)
- (if (and (const? y) (eqv? (const-exp y) -1))
- (1- x)
- (if (and (const? x) (eqv? (const-exp x) 1))
- (1+ y)
- (if (and (const? x) (eqv? (const-exp x) -1))
- (1- y)
- (+ x y)))))
- (x y z ... last) (+ (+ x y . z) last))
-
-(define-primitive-expander *
- () 1
- (x) (values x)
- (x y z ... last) (* (* x y . z) last))
-
-(define-primitive-expander -
- (x) (- 0 x)
- (x y) (if (and (const? y) (eqv? (const-exp y) 1))
- (1- x)
- (- x y))
- (x y z ... last) (- (- x y . z) last))
-
-(define-primitive-expander /
- (x) (/ 1 x)
- (x y z ... last) (/ (/ x y . z) last))
-
-(define-primitive-expander logior
- () 0
- (x) (logior x 0)
- (x y) (logior x y)
- (x y z ... last) (logior (logior x y . z) last))
-
-(define-primitive-expander logand
- () -1
- (x) (logand x -1)
- (x y) (logand x y)
- (x y z ... last) (logand (logand x y . z) last))
-
-(define-primitive-expander caar (x) (car (car x)))
-(define-primitive-expander cadr (x) (car (cdr x)))
-(define-primitive-expander cdar (x) (cdr (car x)))
-(define-primitive-expander cddr (x) (cdr (cdr x)))
-(define-primitive-expander caaar (x) (car (car (car x))))
-(define-primitive-expander caadr (x) (car (car (cdr x))))
-(define-primitive-expander cadar (x) (car (cdr (car x))))
-(define-primitive-expander caddr (x) (car (cdr (cdr x))))
-(define-primitive-expander cdaar (x) (cdr (car (car x))))
-(define-primitive-expander cdadr (x) (cdr (car (cdr x))))
-(define-primitive-expander cddar (x) (cdr (cdr (car x))))
-(define-primitive-expander cdddr (x) (cdr (cdr (cdr x))))
-(define-primitive-expander caaaar (x) (car (car (car (car x)))))
-(define-primitive-expander caaadr (x) (car (car (car (cdr x)))))
-(define-primitive-expander caadar (x) (car (car (cdr (car x)))))
-(define-primitive-expander caaddr (x) (car (car (cdr (cdr x)))))
-(define-primitive-expander cadaar (x) (car (cdr (car (car x)))))
-(define-primitive-expander cadadr (x) (car (cdr (car (cdr x)))))
-(define-primitive-expander caddar (x) (car (cdr (cdr (car x)))))
-(define-primitive-expander cadddr (x) (car (cdr (cdr (cdr x)))))
-(define-primitive-expander cdaaar (x) (cdr (car (car (car x)))))
-(define-primitive-expander cdaadr (x) (cdr (car (car (cdr x)))))
-(define-primitive-expander cdadar (x) (cdr (car (cdr (car x)))))
-(define-primitive-expander cdaddr (x) (cdr (car (cdr (cdr x)))))
-(define-primitive-expander cddaar (x) (cdr (cdr (car (car x)))))
-(define-primitive-expander cddadr (x) (cdr (cdr (car (cdr x)))))
-(define-primitive-expander cdddar (x) (cdr (cdr (cdr (car x)))))
-(define-primitive-expander cddddr (x) (cdr (cdr (cdr (cdr x)))))
-
-(define-primitive-expander cons*
- (x) (values x)
- (x y) (cons x y)
- (x y . rest) (cons x (cons* y . rest)))
-
-(define-primitive-expander acons (x y z)
- (cons (cons x y) z))
-
-(define-primitive-expander apply (f a0 . args)
- (@apply f a0 . args))
-
-(define-primitive-expander call-with-values (producer consumer)
- (@call-with-values producer consumer))
-
-(define-primitive-expander call-with-current-continuation (proc)
- (@call-with-current-continuation proc))
-
-(define-primitive-expander call/cc (proc)
- (@call-with-current-continuation proc))
-
-(define-primitive-expander make-struct (vtable tail-size . args)
- (if (and (const? tail-size)
- (let ((n (const-exp tail-size)))
- (and (number? n) (exact? n) (zero? n))))
- (make-struct/no-tail vtable . args)
- #f))
-
-(define-primitive-expander u8vector-ref (vec i)
- (bytevector-u8-ref vec i))
-(define-primitive-expander u8vector-set! (vec i x)
- (bytevector-u8-set! vec i x))
-(define-primitive-expander s8vector-ref (vec i)
- (bytevector-s8-ref vec i))
-(define-primitive-expander s8vector-set! (vec i x)
- (bytevector-s8-set! vec i x))
-
-(define-primitive-expander u16vector-ref (vec i)
- (bytevector-u16-native-ref vec (* i 2)))
-(define-primitive-expander u16vector-set! (vec i x)
- (bytevector-u16-native-set! vec (* i 2) x))
-(define-primitive-expander s16vector-ref (vec i)
- (bytevector-s16-native-ref vec (* i 2)))
-(define-primitive-expander s16vector-set! (vec i x)
- (bytevector-s16-native-set! vec (* i 2) x))
-
-(define-primitive-expander u32vector-ref (vec i)
- (bytevector-u32-native-ref vec (* i 4)))
-(define-primitive-expander u32vector-set! (vec i x)
- (bytevector-u32-native-set! vec (* i 4) x))
-(define-primitive-expander s32vector-ref (vec i)
- (bytevector-s32-native-ref vec (* i 4)))
-(define-primitive-expander s32vector-set! (vec i x)
- (bytevector-s32-native-set! vec (* i 4) x))
-
-(define-primitive-expander u64vector-ref (vec i)
- (bytevector-u64-native-ref vec (* i 8)))
-(define-primitive-expander u64vector-set! (vec i x)
- (bytevector-u64-native-set! vec (* i 8) x))
-(define-primitive-expander s64vector-ref (vec i)
- (bytevector-s64-native-ref vec (* i 8)))
-(define-primitive-expander s64vector-set! (vec i x)
- (bytevector-s64-native-set! vec (* i 8) x))
-
-(define-primitive-expander f32vector-ref (vec i)
- (bytevector-ieee-single-native-ref vec (* i 4)))
-(define-primitive-expander f32vector-set! (vec i x)
- (bytevector-ieee-single-native-set! vec (* i 4) x))
-(define-primitive-expander f32vector-ref (vec i)
- (bytevector-ieee-single-native-ref vec (* i 4)))
-(define-primitive-expander f32vector-set! (vec i x)
- (bytevector-ieee-single-native-set! vec (* i 4) x))
-
-(define-primitive-expander f64vector-ref (vec i)
- (bytevector-ieee-double-native-ref vec (* i 8)))
-(define-primitive-expander f64vector-set! (vec i x)
- (bytevector-ieee-double-native-set! vec (* i 8) x))
-(define-primitive-expander f64vector-ref (vec i)
- (bytevector-ieee-double-native-ref vec (* i 8)))
-(define-primitive-expander f64vector-set! (vec i x)
- (bytevector-ieee-double-native-set! vec (* i 8) x))
-
-(define (chained-comparison-expander prim-name)
- (case-lambda
- ((src) (make-const src #t))
- ((src a) #f)
- ((src a b) #f)
- ((src a b . rest)
- (let* ((prim (make-primitive-ref src prim-name))
- (b-sym (gensym "b"))
- (b* (make-lexical-ref src 'b b-sym)))
- (make-let src
- '(b)
- (list b-sym)
- (list b)
- (make-conditional src
- (make-application src prim (list a b*))
- (make-application src prim (cons b* rest))
- (make-const src #f)))))))
-
-(for-each (lambda (prim-name)
- (hashq-set! *primitive-expand-table* prim-name
- (chained-comparison-expander prim-name)))
- '(< > <= >= =))
-
-;; Appropriate for use with either 'eqv?' or 'equal?'.
-(define maybe-simplify-to-eq
- (case-lambda
- ((src a b)
- ;; Simplify cases where either A or B is constant.
- (define (maybe-simplify a b)
- (and (const? a)
- (let ((v (const-exp a)))
- (and (or (memq v '(#f #t () #nil))
- (symbol? v)
- (and (integer? v)
- (exact? v)
- (<= most-negative-fixnum v most-positive-fixnum)))
- (make-application src (make-primitive-ref #f 'eq?)
- (list a b))))))
- (or (maybe-simplify a b) (maybe-simplify b a)))
- (else #f)))
-
-(hashq-set! *primitive-expand-table* 'eqv? maybe-simplify-to-eq)
-(hashq-set! *primitive-expand-table* 'equal? maybe-simplify-to-eq)
-
-(hashq-set! *primitive-expand-table*
- 'dynamic-wind
- (case-lambda
- ((src pre thunk post)
- (let ((PRE (gensym "pre-"))
- (THUNK (gensym "thunk-"))
- (POST (gensym "post-")))
- (make-let
- src
- '(pre thunk post)
- (list PRE THUNK POST)
- (list pre thunk post)
- (make-dynwind
- src
- (make-lexical-ref #f 'pre PRE)
- (make-application #f (make-lexical-ref #f 'thunk THUNK) '())
- (make-lexical-ref #f 'post POST)))))
- (else #f)))
-
-(hashq-set! *primitive-expand-table*
- '@dynamic-wind
- (case-lambda
- ((src pre expr post)
- (let ((PRE (gensym "pre-"))
- (POST (gensym "post-")))
- (make-let
- src
- '(pre post)
- (list PRE POST)
- (list pre post)
- (make-dynwind
- src
- (make-lexical-ref #f 'pre PRE)
- expr
- (make-lexical-ref #f 'post POST)))))))
-
-(hashq-set! *primitive-expand-table*
- 'fluid-ref
- (case-lambda
- ((src fluid) (make-dynref src fluid))
- (else #f)))
-
-(hashq-set! *primitive-expand-table*
- 'fluid-set!
- (case-lambda
- ((src fluid exp) (make-dynset src fluid exp))
- (else #f)))
-
-(hashq-set! *primitive-expand-table*
- '@prompt
- (case-lambda
- ((src tag exp handler)
- (let ((args-sym (gensym)))
- (make-prompt
- src tag exp
- ;; If handler itself is a lambda, the inliner can do some
- ;; trickery here.
- (make-lambda-case
- (tree-il-src handler) '() #f 'args #f '() (list args-sym)
- (make-application #f (make-primitive-ref #f 'apply)
- (list handler
- (make-lexical-ref #f 'args args-sym)))
- #f))))
- (else #f)))
-
-(hashq-set! *primitive-expand-table*
- 'call-with-prompt
- (case-lambda
- ((src tag thunk handler)
- (let ((handler-sym (gensym))
- (args-sym (gensym)))
- (make-let
- src '(handler) (list handler-sym) (list handler)
- (make-prompt
- src tag (make-application #f thunk '())
- ;; If handler itself is a lambda, the inliner can do some
- ;; trickery here.
- (make-lambda-case
- (tree-il-src handler) '() #f 'args #f '() (list args-sym)
- (make-application
- #f (make-primitive-ref #f 'apply)
- (list (make-lexical-ref #f 'handler handler-sym)
- (make-lexical-ref #f 'args args-sym)))
- #f)))))
- (else #f)))
-
-(hashq-set! *primitive-expand-table*
- '@abort
- (case-lambda
- ((src tag tail-args)
- (make-abort src tag '() tail-args))
- (else #f)))
-(hashq-set! *primitive-expand-table*
- 'abort-to-prompt
- (case-lambda
- ((src tag . args)
- (make-abort src tag args (make-const #f '())))
- (else #f)))
-;;; Tree Intermediate Language
-
-;; Copyright (C) 2009, 2010, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language tree-il spec)
- #\use-module (system base language)
- #\use-module (system base pmatch)
- #\use-module (language glil)
- #\use-module (language tree-il)
- #\use-module (language tree-il compile-glil)
- #\export (tree-il))
-
-(define (write-tree-il exp . port)
- (apply write (unparse-tree-il exp) port))
-
-(define (join exps env)
- (pmatch exps
- (() (make-void #f))
- ((,x) x)
- (else (make-sequence #f exps))))
-
-(define-language tree-il
- #\title "Tree Intermediate Language"
- #\reader (lambda (port env) (read port))
- #\printer write-tree-il
- #\parser parse-tree-il
- #\joiner join
- #\compilers `((glil . ,compile-glil))
- #\for-humans? #f
- )
-;;; Guile Lowlevel Intermediate Language
-
-;; Copyright (C) 2001, 2010, 2013 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language value spec)
- #\use-module (system base language)
- #\export (value))
-
-(define-language value
- #\title "Values"
- #\reader #f
- #\printer write
- #\for-humans? #f
- )
-;;; installed-scm-file
-
-;;;; Copyright (C) 1998,1999,2000,2001,2002, 2003, 2006, 2009, 2010, 2011, 2014, 2015 Free Software Foundation, Inc.
-;;;; Copyright (C) 1993-1998 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-;;;;
-;;;; This file was based upon stklos.stk from the STk distribution
-;;;; version 4.0.1 by Erick Gallesio <eg@unice.fr>.
-;;;;
-
-(define-module (oop goops)
- #\use-module (srfi srfi-1)
- #\use-module (ice-9 match)
- #\use-module (oop goops util)
- #\export-syntax (define-class class standard-define-class
- define-generic define-accessor define-method
- define-extended-generic define-extended-generics
- method)
- #\export (is-a? class-of
- ensure-metaclass ensure-metaclass-with-supers
- make-class
- make-generic ensure-generic
- make-extended-generic
- make-accessor ensure-accessor
- add-method!
- class-slot-ref class-slot-set! slot-unbound slot-missing
- slot-definition-name slot-definition-options
- slot-definition-allocation
- slot-definition-getter slot-definition-setter
- slot-definition-accessor
- slot-definition-init-value slot-definition-init-form
- slot-definition-init-thunk slot-definition-init-keyword
- slot-init-function class-slot-definition
- method-source
- compute-cpl compute-std-cpl compute-get-n-set compute-slots
- compute-getter-method compute-setter-method
- allocate-instance initialize make-instance make
- no-next-method no-applicable-method no-method
- change-class update-instance-for-different-class
- shallow-clone deep-clone
- class-redefinition
- apply-generic apply-method apply-methods
- compute-applicable-methods %compute-applicable-methods
- method-more-specific? sort-applicable-methods
- class-subclasses class-methods
- goops-error
- min-fixnum max-fixnum
- ;;; *fixme* Should go into goops.c
- instance? slot-ref-using-class
- slot-set-using-class! slot-bound-using-class?
- slot-exists-using-class? slot-ref slot-set! slot-bound?
- class-name class-direct-supers class-direct-subclasses
- class-direct-methods class-direct-slots class-precedence-list
- class-slots
- generic-function-name
- generic-function-methods method-generic-function
- method-specializers method-formals
- primitive-generic-generic enable-primitive-generic!
- method-procedure accessor-method-slot-definition
- slot-exists? make find-method get-keyword))
-
-(define *goops-module* (current-module))
-
-;; First initialize the builtin part of GOOPS
-(eval-when (expand load eval)
- (%init-goops-builtins))
-
-(eval-when (expand load eval)
- (use-modules ((language tree-il primitives) \:select (add-interesting-primitive!)))
- (add-interesting-primitive! 'class-of))
-
-;; Then load the rest of GOOPS
-(use-modules (oop goops dispatch))
-
-;;;
-;;; Compiling next methods into method bodies
-;;;
-
-;;; So, for the reader: there basic idea is that, given that the
-;;; semantics of `next-method' depend on the concrete types being
-;;; dispatched, why not compile a specific procedure to handle each type
-;;; combination that we see at runtime.
-;;;
-;;; In theory we can do much better than a bytecode compilation, because
-;;; we know the *exact* types of the arguments. It's ideal for native
-;;; compilation. A task for the future.
-;;;
-;;; I think this whole generic application mess would benefit from a
-;;; strict MOP.
-
-(define (compute-cmethod methods types)
- (match methods
- ((method . methods)
- (let ((make-procedure (slot-ref method 'make-procedure)))
- (if make-procedure
- (make-procedure
- (if (null? methods)
- (lambda args
- (no-next-method (method-generic-function method) args))
- (compute-cmethod methods types)))
- (method-procedure method))))))
-
-
-(eval-when (expand load eval)
- (define min-fixnum (- (expt 2 29)))
- (define max-fixnum (- (expt 2 29) 1)))
-
-;;
-;; goops-error
-;;
-(define (goops-error format-string . args)
- (scm-error 'goops-error #f format-string args '()))
-
-;;
-;; is-a?
-;;
-(define (is-a? obj class)
- (and (memq class (class-precedence-list (class-of obj))) #t))
-
-
-;;;
-;;; {Meta classes}
-;;;
-
-(define ensure-metaclass-with-supers
- (let ((table-of-metas '()))
- (lambda (meta-supers)
- (let ((entry (assoc meta-supers table-of-metas)))
- (if entry
- ;; Found a previously created metaclass
- (cdr entry)
- ;; Create a new meta-class which inherit from "meta-supers"
- (let ((new (make <class> #\dsupers meta-supers
- #\slots '()
- #\name (gensym "metaclass"))))
- (set! table-of-metas (cons (cons meta-supers new) table-of-metas))
- new))))))
-
-(define (ensure-metaclass supers)
- (if (null? supers)
- <class>
- (let* ((all-metas (map (lambda (x) (class-of x)) supers))
- (all-cpls (append-map (lambda (m)
- (cdr (class-precedence-list m)))
- all-metas))
- (needed-metas '()))
- ;; Find the most specific metaclasses. The new metaclass will be
- ;; a subclass of these.
- (for-each
- (lambda (meta)
- (if (and (not (member meta all-cpls))
- (not (member meta needed-metas)))
- (set! needed-metas (append needed-metas (list meta)))))
- all-metas)
- ;; Now return a subclass of the metaclasses we found.
- (if (null? (cdr needed-metas))
- (car needed-metas) ; If there's only one, just use it.
- (ensure-metaclass-with-supers needed-metas)))))
-
-;;;
-;;; {Classes}
-;;;
-
-;;; (define-class NAME (SUPER ...) SLOT-DEFINITION ... OPTION ...)
-;;;
-;;; SLOT-DEFINITION ::= SLOT-NAME | (SLOT-NAME OPTION ...)
-;;; OPTION ::= KEYWORD VALUE
-;;;
-
-(define (kw-do-map mapper f kwargs)
- (define (keywords l)
- (cond
- ((null? l) '())
- ((or (null? (cdr l)) (not (keyword? (car l))))
- (goops-error "malformed keyword arguments: ~a" kwargs))
- (else (cons (car l) (keywords (cddr l))))))
- (define (args l)
- (if (null? l) '() (cons (cadr l) (args (cddr l)))))
- ;; let* to check keywords first
- (let* ((k (keywords kwargs))
- (a (args kwargs)))
- (mapper f k a)))
-
-(define (make-class supers slots . options)
- (let* ((name (get-keyword #\name options (make-unbound)))
- (supers (if (not (or-map (lambda (class)
- (memq <object>
- (class-precedence-list class)))
- supers))
- (append supers (list <object>))
- supers))
- (metaclass (or (get-keyword #\metaclass options #f)
- (ensure-metaclass supers))))
-
- ;; Verify that all direct slots are different and that we don't inherit
- ;; several time from the same class
- (let ((tmp1 (find-duplicate supers))
- (tmp2 (find-duplicate (map slot-definition-name slots))))
- (if tmp1
- (goops-error "make-class: super class ~S is duplicate in class ~S"
- tmp1 name))
- (if tmp2
- (goops-error "make-class: slot ~S is duplicate in class ~S"
- tmp2 name)))
-
- ;; Everything seems correct, build the class
- (apply make metaclass
- #\dsupers supers
- #\slots slots
- #\name name
- options)))
-
-;;; (class (SUPER ...) SLOT-DEFINITION ... OPTION ...)
-;;;
-;;; SLOT-DEFINITION ::= SLOT-NAME | (SLOT-NAME OPTION ...)
-;;; OPTION ::= KEYWORD VALUE
-;;;
-(define-macro (class supers . slots)
- (define (make-slot-definition-forms slots)
- (map
- (lambda (def)
- (cond
- ((pair? def)
- `(list ',(car def)
- ,@(kw-do-map append-map
- (lambda (kw arg)
- (case kw
- ((#\init-form)
- `(#\init-form ',arg
- #\init-thunk (lambda () ,arg)))
- (else (list kw arg))))
- (cdr def))))
- (else
- `(list ',def))))
- slots))
- (if (not (list? supers))
- (goops-error "malformed superclass list: ~S" supers))
- (let ((slots (take-while (lambda (x) (not (keyword? x))) slots))
- (options (or (find-tail keyword? slots) '())))
- `(make-class
- ;; evaluate super class variables
- (list ,@supers)
- ;; evaluate slot definitions, except the slot name!
- (list ,@(make-slot-definition-forms slots))
- ;; evaluate class options
- ,@options)))
-
-(define-syntax define-class-pre-definition
- (lambda (x)
- (syntax-case x ()
- ((_ (k arg rest ...) out ...)
- (keyword? (syntax->datum #'k))
- (case (syntax->datum #'k)
- ((#\getter #\setter)
- #'(define-class-pre-definition (rest ...)
- out ...
- (if (or (not (defined? 'arg))
- (not (is-a? arg <generic>)))
- (toplevel-define!
- 'arg
- (ensure-generic (if (defined? 'arg) arg #f) 'arg)))))
- ((#\accessor)
- #'(define-class-pre-definition (rest ...)
- out ...
- (if (or (not (defined? 'arg))
- (not (is-a? arg <accessor>)))
- (toplevel-define!
- 'arg
- (ensure-accessor (if (defined? 'arg) arg #f) 'arg)))))
- (else
- #'(define-class-pre-definition (rest ...) out ...))))
- ((_ () out ...)
- #'(begin out ...)))))
-
-;; Some slot options require extra definitions to be made. In
-;; particular, we want to make sure that the generic function objects
-;; which represent accessors exist before `make-class' tries to add
-;; methods to them.
-(define-syntax define-class-pre-definitions
- (lambda (x)
- (syntax-case x ()
- ((_ () out ...)
- #'(begin out ...))
- ((_ (slot rest ...) out ...)
- (keyword? (syntax->datum #'slot))
- #'(begin out ...))
- ((_ (slot rest ...) out ...)
- (identifier? #'slot)
- #'(define-class-pre-definitions (rest ...)
- out ...))
- ((_ ((slotname slotopt ...) rest ...) out ...)
- #'(define-class-pre-definitions (rest ...)
- out ... (define-class-pre-definition (slotopt ...)))))))
-
-(define-syntax-rule (define-class name supers slot ...)
- (begin
- (define-class-pre-definitions (slot ...))
- (if (and (defined? 'name)
- (is-a? name <class>)
- (memq <object> (class-precedence-list name)))
- (class-redefinition name
- (class supers slot ... #\name 'name))
- (toplevel-define! 'name (class supers slot ... #\name 'name)))))
-
-(define-syntax-rule (standard-define-class arg ...)
- (define-class arg ...))
-
-;;;
-;;; {Generic functions and accessors}
-;;;
-
-;; Apparently the desired semantics are that we extend previous
-;; procedural definitions, but that if `name' was already a generic, we
-;; overwrite its definition.
-(define-macro (define-generic name)
- (if (not (symbol? name))
- (goops-error "bad generic function name: ~S" name))
- `(define ,name
- (if (and (defined? ',name) (is-a? ,name <generic>))
- (make <generic> #\name ',name)
- (ensure-generic (if (defined? ',name) ,name #f) ',name))))
-
-(define-macro (define-extended-generic name val)
- (if (not (symbol? name))
- (goops-error "bad generic function name: ~S" name))
- `(define ,name (make-extended-generic ,val ',name)))
-
-(define-macro (define-extended-generics names . args)
- (let ((prefixes (get-keyword #\prefix args #f)))
- (if prefixes
- `(begin
- ,@(map (lambda (name)
- `(define-extended-generic ,name
- (list ,@(map (lambda (prefix)
- (symbol-append prefix name))
- prefixes))))
- names))
- (goops-error "no prefixes supplied"))))
-
-(define* (make-generic #\optional name)
- (make <generic> #\name name))
-
-(define* (make-extended-generic gfs #\optional name)
- (let* ((gfs (if (list? gfs) gfs (list gfs)))
- (gws? (any (lambda (gf) (is-a? gf <generic-with-setter>)) gfs)))
- (let ((ans (if gws?
- (let* ((sname (and name (make-setter-name name)))
- (setters
- (append-map (lambda (gf)
- (if (is-a? gf <generic-with-setter>)
- (list (ensure-generic (setter gf)
- sname))
- '()))
- gfs))
- (es (make <extended-generic-with-setter>
- #\name name
- #\extends gfs
- #\setter (make <extended-generic>
- #\name sname
- #\extends setters))))
- (extended-by! setters (setter es))
- es)
- (make <extended-generic>
- #\name name
- #\extends gfs))))
- (extended-by! gfs ans)
- ans)))
-
-(define (extended-by! gfs eg)
- (for-each (lambda (gf)
- (slot-set! gf 'extended-by
- (cons eg (slot-ref gf 'extended-by))))
- gfs)
- (invalidate-method-cache! eg))
-
-(define (not-extended-by! gfs eg)
- (for-each (lambda (gf)
- (slot-set! gf 'extended-by
- (delq! eg (slot-ref gf 'extended-by))))
- gfs)
- (invalidate-method-cache! eg))
-
-(define* (ensure-generic old-definition #\optional name)
- (cond ((is-a? old-definition <generic>) old-definition)
- ((procedure-with-setter? old-definition)
- (make <generic-with-setter>
- #\name name
- #\default (procedure old-definition)
- #\setter (setter old-definition)))
- ((procedure? old-definition)
- (if (generic-capability? old-definition) old-definition
- (make <generic> #\name name #\default old-definition)))
- (else (make <generic> #\name name))))
-
-;; same semantics as <generic>
-(define-syntax-rule (define-accessor name)
- (define name
- (cond ((not (defined? 'name)) (ensure-accessor #f 'name))
- ((is-a? name <accessor>) (make <accessor> #\name 'name))
- (else (ensure-accessor name 'name)))))
-
-(define (make-setter-name name)
- (string->symbol (string-append "setter:" (symbol->string name))))
-
-(define* (make-accessor #\optional name)
- (make <accessor>
- #\name name
- #\setter (make <generic>
- #\name (and name (make-setter-name name)))))
-
-(define* (ensure-accessor proc #\optional name)
- (cond ((and (is-a? proc <accessor>)
- (is-a? (setter proc) <generic>))
- proc)
- ((is-a? proc <generic-with-setter>)
- (upgrade-accessor proc (setter proc)))
- ((is-a? proc <generic>)
- (upgrade-accessor proc (make-generic name)))
- ((procedure-with-setter? proc)
- (make <accessor>
- #\name name
- #\default (procedure proc)
- #\setter (ensure-generic (setter proc) name)))
- ((procedure? proc)
- (ensure-accessor (if (generic-capability? proc)
- (make <generic> #\name name #\default proc)
- (ensure-generic proc name))
- name))
- (else
- (make-accessor name))))
-
-(define (upgrade-accessor generic setter)
- (let ((methods (slot-ref generic 'methods))
- (gws (make (if (is-a? generic <extended-generic>)
- <extended-generic-with-setter>
- <accessor>)
- #\name (generic-function-name generic)
- #\extended-by (slot-ref generic 'extended-by)
- #\setter setter)))
- (if (is-a? generic <extended-generic>)
- (let ((gfs (slot-ref generic 'extends)))
- (not-extended-by! gfs generic)
- (slot-set! gws 'extends gfs)
- (extended-by! gfs gws)))
- ;; Steal old methods
- (for-each (lambda (method)
- (slot-set! method 'generic-function gws))
- methods)
- (slot-set! gws 'methods methods)
- (invalidate-method-cache! gws)
- gws))
-
-;;;
-;;; {Methods}
-;;;
-
-(define (toplevel-define! name val)
- (module-define! (current-module) name val))
-
-(define-syntax define-method
- (syntax-rules (setter)
- ((_ ((setter name) . args) body ...)
- (begin
- (if (or (not (defined? 'name))
- (not (is-a? name <accessor>)))
- (toplevel-define! 'name
- (ensure-accessor
- (if (defined? 'name) name #f) 'name)))
- (add-method! (setter name) (method args body ...))))
- ((_ (name . args) body ...)
- (begin
- ;; FIXME: this code is how it always was, but it's quite cracky:
- ;; it will only define the generic function if it was undefined
- ;; before (ok), or *was defined to #f*. The latter is crack. But
- ;; there are bootstrap issues about fixing this -- change it to
- ;; (is-a? name <generic>) and see.
- (if (or (not (defined? 'name))
- (not name))
- (toplevel-define! 'name (make <generic> #\name 'name)))
- (add-method! name (method args body ...))))))
-
-(define-syntax method
- (lambda (x)
- (define (parse-args args)
- (let lp ((ls args) (formals '()) (specializers '()))
- (syntax-case ls ()
- (((f s) . rest)
- (and (identifier? #'f) (identifier? #'s))
- (lp #'rest
- (cons #'f formals)
- (cons #'s specializers)))
- ((f . rest)
- (identifier? #'f)
- (lp #'rest
- (cons #'f formals)
- (cons #'<top> specializers)))
- (()
- (list (reverse formals)
- (reverse (cons #''() specializers))))
- (tail
- (identifier? #'tail)
- (list (append (reverse formals) #'tail)
- (reverse (cons #'<top> specializers)))))))
-
- (define (find-free-id exp referent)
- (syntax-case exp ()
- ((x . y)
- (or (find-free-id #'x referent)
- (find-free-id #'y referent)))
- (x
- (identifier? #'x)
- (let ((id (datum->syntax #'x referent)))
- (and (free-identifier=? #'x id) id)))
- (_ #f)))
-
- (define (compute-procedure formals body)
- (syntax-case body ()
- ((body0 ...)
- (with-syntax ((formals formals))
- #'(lambda formals body0 ...)))))
-
- (define (->proper args)
- (let lp ((ls args) (out '()))
- (syntax-case ls ()
- ((x . xs) (lp #'xs (cons #'x out)))
- (() (reverse out))
- (tail (reverse (cons #'tail out))))))
-
- (define (compute-make-procedure formals body next-method)
- (syntax-case body ()
- ((body ...)
- (with-syntax ((next-method next-method))
- (syntax-case formals ()
- ((formal ...)
- #'(lambda (real-next-method)
- (lambda (formal ...)
- (let ((next-method (lambda args
- (if (null? args)
- (real-next-method formal ...)
- (apply real-next-method args)))))
- body ...))))
- (formals
- (with-syntax (((formal ...) (->proper #'formals)))
- #'(lambda (real-next-method)
- (lambda formals
- (let ((next-method (lambda args
- (if (null? args)
- (apply real-next-method formal ...)
- (apply real-next-method args)))))
- body ...))))))))))
-
- (define (compute-procedures formals body)
- ;; So, our use of this is broken, because it operates on the
- ;; pre-expansion source code. It's equivalent to just searching
- ;; for referent in the datums. Ah well.
- (let ((id (find-free-id body 'next-method)))
- (if id
- ;; return a make-procedure
- (values #'#f
- (compute-make-procedure formals body id))
- (values (compute-procedure formals body)
- #'#f))))
-
- (syntax-case x ()
- ((_ args) #'(method args (if #f #f)))
- ((_ args body0 body1 ...)
- (with-syntax (((formals (specializer ...)) (parse-args #'args)))
- (call-with-values
- (lambda ()
- (compute-procedures #'formals #'(body0 body1 ...)))
- (lambda (procedure make-procedure)
- (with-syntax ((procedure procedure)
- (make-procedure make-procedure))
- #'(make <method>
- #\specializers (cons* specializer ...)
- #\formals 'formals
- #\body '(body0 body1 ...)
- #\make-procedure make-procedure
- #\procedure procedure)))))))))
-
-;;;
-;;; {add-method!}
-;;;
-
-(define (add-method-in-classes! m)
- ;; Add method in all the classes which appears in its specializers list
- (for-each* (lambda (x)
- (let ((dm (class-direct-methods x)))
- (if (not (memq m dm))
- (slot-set! x 'direct-methods (cons m dm)))))
- (method-specializers m)))
-
-(define (remove-method-in-classes! m)
- ;; Remove method in all the classes which appears in its specializers list
- (for-each* (lambda (x)
- (slot-set! x
- 'direct-methods
- (delv! m (class-direct-methods x))))
- (method-specializers m)))
-
-(define (compute-new-list-of-methods gf new)
- (let ((new-spec (method-specializers new))
- (methods (slot-ref gf 'methods)))
- (let loop ((l methods))
- (if (null? l)
- (cons new methods)
- (if (equal? (method-specializers (car l)) new-spec)
- (begin
- ;; This spec. list already exists. Remove old method from dependents
- (remove-method-in-classes! (car l))
- (set-car! l new)
- methods)
- (loop (cdr l)))))))
-
-(define (method-n-specializers m)
- (length* (slot-ref m 'specializers)))
-
-(define (calculate-n-specialized gf)
- (fold (lambda (m n) (max n (method-n-specializers m)))
- 0
- (generic-function-methods gf)))
-
-(define (invalidate-method-cache! gf)
- (%invalidate-method-cache! gf)
- (slot-set! gf 'n-specialized (calculate-n-specialized gf))
- (for-each (lambda (gf) (invalidate-method-cache! gf))
- (slot-ref gf 'extended-by)))
-
-(define internal-add-method!
- (method ((gf <generic>) (m <method>))
- (slot-set! m 'generic-function gf)
- (slot-set! gf 'methods (compute-new-list-of-methods gf m))
- (invalidate-method-cache! gf)
- (add-method-in-classes! m)
- *unspecified*))
-
-(define-generic add-method!)
-
-((method-procedure internal-add-method!) add-method! internal-add-method!)
-
-(define-method (add-method! (proc <procedure>) (m <method>))
- (if (generic-capability? proc)
- (begin
- (enable-primitive-generic! proc)
- (add-method! proc m))
- (next-method)))
-
-(define-method (add-method! (pg <primitive-generic>) (m <method>))
- (add-method! (primitive-generic-generic pg) m))
-
-(define-method (add-method! obj (m <method>))
- (goops-error "~S is not a valid generic function" obj))
-
-;;;
-;;; {Access to meta objects}
-;;;
-
-;;;
-;;; Methods
-;;;
-(define-method (method-source (m <method>))
- (let* ((spec (map* class-name (slot-ref m 'specializers)))
- (src (procedure-source (slot-ref m 'procedure))))
- (and src
- (let ((args (cadr src))
- (body (cddr src)))
- (cons 'method
- (cons (map* list args spec)
- body))))))
-
-(define-method (method-formals (m <method>))
- (slot-ref m 'formals))
-
-;;;
-;;; Slots
-;;;
-(define slot-definition-name car)
-
-(define slot-definition-options cdr)
-
-(define (slot-definition-allocation s)
- (get-keyword #\allocation (cdr s) #\instance))
-
-(define (slot-definition-getter s)
- (get-keyword #\getter (cdr s) #f))
-
-(define (slot-definition-setter s)
- (get-keyword #\setter (cdr s) #f))
-
-(define (slot-definition-accessor s)
- (get-keyword #\accessor (cdr s) #f))
-
-(define (slot-definition-init-value s)
- ;; can be #f, so we can't use #f as non-value
- (get-keyword #\init-value (cdr s) (make-unbound)))
-
-(define (slot-definition-init-form s)
- (get-keyword #\init-form (cdr s) (make-unbound)))
-
-(define (slot-definition-init-thunk s)
- (get-keyword #\init-thunk (cdr s) #f))
-
-(define (slot-definition-init-keyword s)
- (get-keyword #\init-keyword (cdr s) #f))
-
-(define (class-slot-definition class slot-name)
- (assq slot-name (class-slots class)))
-
-(define (slot-init-function class slot-name)
- (cadr (assq slot-name (slot-ref class 'getters-n-setters))))
-
-(define (accessor-method-slot-definition obj)
- "Return the slot definition of the accessor @var{obj}."
- (slot-ref obj 'slot-definition))
-
-
-;;;
-;;; {Standard methods used by the C runtime}
-;;;
-
-;;; Methods to compare objects
-;;;
-
-;; Have to do this in a strange order because equal? is used in the
-;; add-method! implementation; we need to make sure that when the
-;; primitive is extended, that the generic has a method. =
-(define g-equal? (make-generic 'equal?))
-;; When this generic gets called, we will have already checked eq? and
-;; eqv? -- the purpose of this generic is to extend equality. So by
-;; default, there is no extension, thus the #f return.
-(add-method! g-equal? (method (x y) #f))
-(set-primitive-generic! equal? g-equal?)
-
-;;;
-;;; methods to display/write an object
-;;;
-
-; Code for writing objects must test that the slots they use are
-; bound. Otherwise a slot-unbound method will be called and will
-; conduct to an infinite loop.
-
-;; Write
-(define (display-address o file)
- (display (number->string (object-address o) 16) file))
-
-(define-method (write o file)
- (display "#<instance " file)
- (display-address o file)
- (display #\> file))
-
-(define write-object (primitive-generic-generic write))
-
-(define-method (write (o <object>) file)
- (let ((class (class-of o)))
- (if (slot-bound? class 'name)
- (begin
- (display "#<" file)
- (display (class-name class) file)
- (display #\space file)
- (display-address o file)
- (display #\> file))
- (next-method))))
-
-(define-method (write (class <class>) file)
- (let ((meta (class-of class)))
- (if (and (slot-bound? class 'name)
- (slot-bound? meta 'name))
- (begin
- (display "#<" file)
- (display (class-name meta) file)
- (display #\space file)
- (display (class-name class) file)
- (display #\space file)
- (display-address class file)
- (display #\> file))
- (next-method))))
-
-(define-method (write (gf <generic>) file)
- (let ((meta (class-of gf)))
- (if (and (slot-bound? meta 'name)
- (slot-bound? gf 'methods))
- (begin
- (display "#<" file)
- (display (class-name meta) file)
- (let ((name (generic-function-name gf)))
- (if name
- (begin
- (display #\space file)
- (display name file))))
- (display " (" file)
- (display (length (generic-function-methods gf)) file)
- (display ")>" file))
- (next-method))))
-
-(define-method (write (o <method>) file)
- (let ((meta (class-of o)))
- (if (and (slot-bound? meta 'name)
- (slot-bound? o 'specializers))
- (begin
- (display "#<" file)
- (display (class-name meta) file)
- (display #\space file)
- (display (map* (lambda (spec)
- (if (slot-bound? spec 'name)
- (slot-ref spec 'name)
- spec))
- (method-specializers o))
- file)
- (display #\space file)
- (display-address o file)
- (display #\> file))
- (next-method))))
-
-;; Display (do the same thing as write by default)
-(define-method (display o file)
- (write-object o file))
-
-;;;
-;;; Handling of duplicate bindings in the module system
-;;;
-
-(define-method (merge-generics (module <module>)
- (name <symbol>)
- (int1 <module>)
- (val1 <top>)
- (int2 <module>)
- (val2 <top>)
- (var <top>)
- (val <top>))
- #f)
-
-(define-method (merge-generics (module <module>)
- (name <symbol>)
- (int1 <module>)
- (val1 <generic>)
- (int2 <module>)
- (val2 <generic>)
- (var <top>)
- (val <boolean>))
- (and (not (eq? val1 val2))
- (make-variable (make-extended-generic (list val2 val1) name))))
-
-(define-method (merge-generics (module <module>)
- (name <symbol>)
- (int1 <module>)
- (val1 <generic>)
- (int2 <module>)
- (val2 <generic>)
- (var <top>)
- (gf <extended-generic>))
- (and (not (memq val2 (slot-ref gf 'extends)))
- (begin
- (slot-set! gf
- 'extends
- (cons val2 (delq! val2 (slot-ref gf 'extends))))
- (slot-set! val2
- 'extended-by
- (cons gf (delq! gf (slot-ref val2 'extended-by))))
- (invalidate-method-cache! gf)
- var)))
-
-(module-define! duplicate-handlers 'merge-generics merge-generics)
-
-(define-method (merge-accessors (module <module>)
- (name <symbol>)
- (int1 <module>)
- (val1 <top>)
- (int2 <module>)
- (val2 <top>)
- (var <top>)
- (val <top>))
- #f)
-
-(define-method (merge-accessors (module <module>)
- (name <symbol>)
- (int1 <module>)
- (val1 <accessor>)
- (int2 <module>)
- (val2 <accessor>)
- (var <top>)
- (val <top>))
- (merge-generics module name int1 val1 int2 val2 var val))
-
-(module-define! duplicate-handlers 'merge-accessors merge-accessors)
-
-;;;
-;;; slot access
-;;;
-
-(define (class-slot-g-n-s class slot-name)
- (let* ((this-slot (assq slot-name (slot-ref class 'slots)))
- (g-n-s (cddr (or (assq slot-name (slot-ref class 'getters-n-setters))
- (slot-missing class slot-name)))))
- (if (not (memq (slot-definition-allocation this-slot)
- '(#\class #\each-subclass)))
- (slot-missing class slot-name))
- g-n-s))
-
-(define (class-slot-ref class slot)
- (let ((x ((car (class-slot-g-n-s class slot)) #f)))
- (if (unbound? x)
- (slot-unbound class slot)
- x)))
-
-(define (class-slot-set! class slot value)
- ((cadr (class-slot-g-n-s class slot)) #f value))
-
-(define-method (slot-unbound (c <class>) (o <object>) s)
- (goops-error "Slot `~S' is unbound in object ~S" s o))
-
-(define-method (slot-unbound (c <class>) s)
- (goops-error "Slot `~S' is unbound in class ~S" s c))
-
-(define-method (slot-unbound (o <object>))
- (goops-error "Unbound slot in object ~S" o))
-
-(define-method (slot-missing (c <class>) (o <object>) s)
- (goops-error "No slot with name `~S' in object ~S" s o))
-
-(define-method (slot-missing (c <class>) s)
- (goops-error "No class slot with name `~S' in class ~S" s c))
-
-
-(define-method (slot-missing (c <class>) (o <object>) s value)
- (slot-missing c o s))
-
-;;; Methods for the possible error we can encounter when calling a gf
-
-(define-method (no-next-method (gf <generic>) args)
- (goops-error "No next method when calling ~S\nwith arguments ~S" gf args))
-
-(define-method (no-applicable-method (gf <generic>) args)
- (goops-error "No applicable method for ~S in call ~S"
- gf (cons (generic-function-name gf) args)))
-
-(define-method (no-method (gf <generic>) args)
- (goops-error "No method defined for ~S" gf))
-
-;;;
-;;; {Cloning functions (from rdeline@CS.CMU.EDU)}
-;;;
-
-(define-method (shallow-clone (self <object>))
- (let ((clone (%allocate-instance (class-of self) '()))
- (slots (map slot-definition-name
- (class-slots (class-of self)))))
- (for-each (lambda (slot)
- (if (slot-bound? self slot)
- (slot-set! clone slot (slot-ref self slot))))
- slots)
- clone))
-
-(define-method (deep-clone (self <object>))
- (let ((clone (%allocate-instance (class-of self) '()))
- (slots (map slot-definition-name
- (class-slots (class-of self)))))
- (for-each (lambda (slot)
- (if (slot-bound? self slot)
- (slot-set! clone slot
- (let ((value (slot-ref self slot)))
- (if (instance? value)
- (deep-clone value)
- value)))))
- slots)
- clone))
-
-;;;
-;;; {Class redefinition utilities}
-;;;
-
-;;; (class-redefinition OLD NEW)
-;;;
-
-;;; Has correct the following conditions:
-
-;;; Methods
-;;;
-;;; 1. New accessor specializers refer to new header
-;;;
-;;; Classes
-;;;
-;;; 1. New class cpl refers to the new class header
-;;; 2. Old class header exists on old super classes direct-subclass lists
-;;; 3. New class header exists on new super classes direct-subclass lists
-
-(define-method (class-redefinition (old <class>) (new <class>))
- ;; Work on direct methods:
- ;; 1. Remove accessor methods from the old class
- ;; 2. Patch the occurences of new in the specializers by old
- ;; 3. Displace the methods from old to new
- (remove-class-accessors! old) ;; -1-
- (let ((methods (class-direct-methods new)))
- (for-each (lambda (m)
- (update-direct-method! m new old)) ;; -2-
- methods)
- (slot-set! new
- 'direct-methods
- (append methods (class-direct-methods old))))
-
- ;; Substitute old for new in new cpl
- (set-car! (slot-ref new 'cpl) old)
-
- ;; Remove the old class from the direct-subclasses list of its super classes
- (for-each (lambda (c) (slot-set! c 'direct-subclasses
- (delv! old (class-direct-subclasses c))))
- (class-direct-supers old))
-
- ;; Replace the new class with the old in the direct-subclasses of the supers
- (for-each (lambda (c)
- (slot-set! c 'direct-subclasses
- (cons old (delv! new (class-direct-subclasses c)))))
- (class-direct-supers new))
-
- ;; Swap object headers
- (%modify-class old new)
-
- ;; Now old is NEW!
-
- ;; Redefine all the subclasses of old to take into account modification
- (for-each
- (lambda (c)
- (update-direct-subclass! c new old))
- (class-direct-subclasses new))
-
- ;; Invalidate class so that subsequent instances slot accesses invoke
- ;; change-object-class
- (slot-set! new 'redefined old)
- (%invalidate-class new) ;must come after slot-set!
-
- old)
-
-;;;
-;;; remove-class-accessors!
-;;;
-
-(define-method (remove-class-accessors! (c <class>))
- (for-each (lambda (m)
- (if (is-a? m <accessor-method>)
- (let ((gf (slot-ref m 'generic-function)))
- ;; remove the method from its GF
- (slot-set! gf 'methods
- (delq1! m (slot-ref gf 'methods)))
- (invalidate-method-cache! gf)
- ;; remove the method from its specializers
- (remove-method-in-classes! m))))
- (class-direct-methods c)))
-
-;;;
-;;; update-direct-method!
-;;;
-
-(define-method (update-direct-method! (m <method>)
- (old <class>)
- (new <class>))
- (let loop ((l (method-specializers m)))
- ;; Note: the <top> in dotted list is never used.
- ;; So we can work as if we had only proper lists.
- (if (pair? l)
- (begin
- (if (eqv? (car l) old)
- (set-car! l new))
- (loop (cdr l))))))
-
-;;;
-;;; update-direct-subclass!
-;;;
-
-(define-method (update-direct-subclass! (c <class>)
- (old <class>)
- (new <class>))
- (class-redefinition c
- (make-class (class-direct-supers c)
- (class-direct-slots c)
- #\name (class-name c)
- #\metaclass (class-of c))))
-
-;;;
-;;; {Utilities for INITIALIZE methods}
-;;;
-
-;;; compute-slot-accessors
-;;;
-(define (compute-slot-accessors class slots)
- (for-each
- (lambda (s g-n-s)
- (let ((getter-function (slot-definition-getter s))
- (setter-function (slot-definition-setter s))
- (accessor (slot-definition-accessor s)))
- (if getter-function
- (add-method! getter-function
- (compute-getter-method class g-n-s)))
- (if setter-function
- (add-method! setter-function
- (compute-setter-method class g-n-s)))
- (if accessor
- (begin
- (add-method! accessor
- (compute-getter-method class g-n-s))
- (add-method! (setter accessor)
- (compute-setter-method class g-n-s))))))
- slots (slot-ref class 'getters-n-setters)))
-
-(define-method (compute-getter-method (class <class>) g-n-s)
- (let ((init-thunk (cadr g-n-s))
- (g-n-s (cddr g-n-s)))
- (make <accessor-method>
- #\specializers (list class)
- #\procedure (cond ((pair? g-n-s)
- (make-generic-bound-check-getter (car g-n-s)))
- (init-thunk
- (standard-get g-n-s))
- (else
- (bound-check-get g-n-s)))
- #\slot-definition g-n-s)))
-
-(define-method (compute-setter-method (class <class>) g-n-s)
- (let ((init-thunk (cadr g-n-s))
- (g-n-s (cddr g-n-s)))
- (make <accessor-method>
- #\specializers (list class <top>)
- #\procedure (if (pair? g-n-s)
- (cadr g-n-s)
- (standard-set g-n-s))
- #\slot-definition g-n-s)))
-
-(define (make-generic-bound-check-getter proc)
- (lambda (o) (assert-bound (proc o) o)))
-
-;; the idea is to compile the index into the procedure, for fastest
-;; lookup.
-
-(eval-when (expand load eval)
- (define num-standard-pre-cache 20))
-
-(define-macro (define-standard-accessor-method form . body)
- (let ((name (caar form))
- (n-var (cadar form))
- (args (cdr form)))
- (define (make-one x)
- (define (body-trans form)
- (cond ((not (pair? form)) form)
- ((eq? (car form) 'struct-ref)
- `(,(car form) ,(cadr form) ,x))
- ((eq? (car form) 'struct-set!)
- `(,(car form) ,(cadr form) ,x ,(cadddr form)))
- (else
- (map body-trans form))))
- `(lambda ,args ,@(map body-trans body)))
- `(define ,name
- (let ((cache (vector ,@(map make-one (iota num-standard-pre-cache)))))
- (lambda (n)
- (if (< n ,num-standard-pre-cache)
- (vector-ref cache n)
- ((lambda (,n-var) (lambda ,args ,@body)) n)))))))
-
-(define-standard-accessor-method ((bound-check-get n) o)
- (let ((x (struct-ref o n)))
- (if (unbound? x)
- (slot-unbound o)
- x)))
-
-(define-standard-accessor-method ((standard-get n) o)
- (struct-ref o n))
-
-(define-standard-accessor-method ((standard-set n) o v)
- (struct-set! o n v))
-
-;;; compute-getters-n-setters
-;;;
-(define (compute-getters-n-setters class slots)
-
- (define (compute-slot-init-function name s)
- (or (let ((thunk (slot-definition-init-thunk s)))
- (and thunk
- (if (thunk? thunk)
- thunk
- (goops-error "Bad init-thunk for slot `~S' in ~S: ~S"
- name class thunk))))
- (let ((init (slot-definition-init-value s)))
- (and (not (unbound? init))
- (lambda () init)))))
-
- (define (verify-accessors slot l)
- (cond ((integer? l))
- ((not (and (list? l) (= (length l) 2)))
- (goops-error "Bad getter and setter for slot `~S' in ~S: ~S"
- slot class l))
- (else
- (let ((get (car l))
- (set (cadr l)))
- (if (not (procedure? get))
- (goops-error "Bad getter closure for slot `~S' in ~S: ~S"
- slot class get))
- (if (not (procedure? set))
- (goops-error "Bad setter closure for slot `~S' in ~S: ~S"
- slot class set))))))
-
- (map (lambda (s)
- ;; The strange treatment of nfields is due to backward compatibility.
- (let* ((index (slot-ref class 'nfields))
- (g-n-s (compute-get-n-set class s))
- (size (- (slot-ref class 'nfields) index))
- (name (slot-definition-name s)))
- ;; NOTE: The following is interdependent with C macros
- ;; defined above goops.c:scm_sys_prep_layout_x.
- ;;
- ;; For simple instance slots, we have the simplest form
- ;; '(name init-function . index)
- ;; For other slots we have
- ;; '(name init-function getter setter . alloc)
- ;; where alloc is:
- ;; '(index size) for instance allocated slots
- ;; '() for other slots
- (verify-accessors name g-n-s)
- (case (slot-definition-allocation s)
- ((#\each-subclass #\class)
- (unless (and (zero? size) (pair? g-n-s))
- (error "Class-allocated slots should not reserve fields"))
- ;; Don't initialize the slot; that's handled when the slot
- ;; is allocated, in compute-get-n-set.
- (cons name (cons #f g-n-s)))
- (else
- (cons name
- (cons (compute-slot-init-function name s)
- (if (or (integer? g-n-s)
- (zero? size))
- g-n-s
- (append g-n-s (list index size)))))))))
- slots))
-
-;;; compute-cpl
-;;;
-;;; Correct behaviour:
-;;;
-;;; (define-class food ())
-;;; (define-class fruit (food))
-;;; (define-class spice (food))
-;;; (define-class apple (fruit))
-;;; (define-class cinnamon (spice))
-;;; (define-class pie (apple cinnamon))
-;;; => cpl (pie) = pie apple fruit cinnamon spice food object top
-;;;
-;;; (define-class d ())
-;;; (define-class e ())
-;;; (define-class f ())
-;;; (define-class b (d e))
-;;; (define-class c (e f))
-;;; (define-class a (b c))
-;;; => cpl (a) = a b d c e f object top
-;;;
-
-(define-method (compute-cpl (class <class>))
- (compute-std-cpl class class-direct-supers))
-
-;; Support
-
-(define (only-non-null lst)
- (filter (lambda (l) (not (null? l))) lst))
-
-(define (compute-std-cpl c get-direct-supers)
- (let ((c-direct-supers (get-direct-supers c)))
- (merge-lists (list c)
- (only-non-null (append (map class-precedence-list
- c-direct-supers)
- (list c-direct-supers))))))
-
-(define (merge-lists reversed-partial-result inputs)
- (cond
- ((every null? inputs)
- (reverse! reversed-partial-result))
- (else
- (let* ((candidate (lambda (c)
- (and (not (any (lambda (l)
- (memq c (cdr l)))
- inputs))
- c)))
- (candidate-car (lambda (l)
- (and (not (null? l))
- (candidate (car l)))))
- (next (any candidate-car inputs)))
- (if (not next)
- (goops-error "merge-lists: Inconsistent precedence graph"))
- (let ((remove-next (lambda (l)
- (if (eq? (car l) next)
- (cdr l)
- l))))
- (merge-lists (cons next reversed-partial-result)
- (only-non-null (map remove-next inputs))))))))
-
-;; Modified from TinyClos:
-;;
-;; A simple topological sort.
-;;
-;; It's in this file so that both TinyClos and Objects can use it.
-;;
-;; This is a fairly modified version of code I originally got from Anurag
-;; Mendhekar <anurag@moose.cs.indiana.edu>.
-;;
-
-(define (compute-clos-cpl c get-direct-supers)
- (top-sort ((build-transitive-closure get-direct-supers) c)
- ((build-constraints get-direct-supers) c)
- (std-tie-breaker get-direct-supers)))
-
-
-(define (top-sort elements constraints tie-breaker)
- (let loop ((elements elements)
- (constraints constraints)
- (result '()))
- (if (null? elements)
- result
- (let ((can-go-in-now
- (filter
- (lambda (x)
- (every (lambda (constraint)
- (or (not (eq? (cadr constraint) x))
- (memq (car constraint) result)))
- constraints))
- elements)))
- (if (null? can-go-in-now)
- (goops-error "top-sort: Invalid constraints")
- (let ((choice (if (null? (cdr can-go-in-now))
- (car can-go-in-now)
- (tie-breaker result
- can-go-in-now))))
- (loop
- (filter (lambda (x) (not (eq? x choice)))
- elements)
- constraints
- (append result (list choice)))))))))
-
-(define (std-tie-breaker get-supers)
- (lambda (partial-cpl min-elts)
- (let loop ((pcpl (reverse partial-cpl)))
- (let ((current-elt (car pcpl)))
- (let ((ds-of-ce (get-supers current-elt)))
- (let ((common (filter (lambda (x)
- (memq x ds-of-ce))
- min-elts)))
- (if (null? common)
- (if (null? (cdr pcpl))
- (goops-error "std-tie-breaker: Nothing valid")
- (loop (cdr pcpl)))
- (car common))))))))
-
-
-(define (build-transitive-closure get-follow-ons)
- (lambda (x)
- (let track ((result '())
- (pending (list x)))
- (if (null? pending)
- result
- (let ((next (car pending)))
- (if (memq next result)
- (track result (cdr pending))
- (track (cons next result)
- (append (get-follow-ons next)
- (cdr pending)))))))))
-
-(define (build-constraints get-follow-ons)
- (lambda (x)
- (let loop ((elements ((build-transitive-closure get-follow-ons) x))
- (this-one '())
- (result '()))
- (if (or (null? this-one) (null? (cdr this-one)))
- (if (null? elements)
- result
- (loop (cdr elements)
- (cons (car elements)
- (get-follow-ons (car elements)))
- result))
- (loop elements
- (cdr this-one)
- (cons (list (car this-one) (cadr this-one))
- result))))))
-
-;;; compute-get-n-set
-;;;
-(define-method (compute-get-n-set (class <class>) s)
- (define (class-slot-init-value)
- (let ((thunk (slot-definition-init-thunk s)))
- (if thunk
- (thunk)
- (slot-definition-init-value s))))
-
- (case (slot-definition-allocation s)
- ((#\instance) ;; Instance slot
- ;; get-n-set is just its offset
- (let ((already-allocated (slot-ref class 'nfields)))
- (slot-set! class 'nfields (+ already-allocated 1))
- already-allocated))
-
- ((#\class) ;; Class slot
- ;; Class-slots accessors are implemented as 2 closures around
- ;; a Scheme variable. As instance slots, class slots must be
- ;; unbound at init time.
- (let ((name (slot-definition-name s)))
- (if (memq name (map slot-definition-name (class-direct-slots class)))
- ;; This slot is direct; create a new shared variable
- (make-closure-variable class (class-slot-init-value))
- ;; Slot is inherited. Find its definition in superclass
- (let loop ((l (cdr (class-precedence-list class))))
- (let ((r (assoc name (slot-ref (car l) 'getters-n-setters))))
- (if r
- (cddr r)
- (loop (cdr l))))))))
-
- ((#\each-subclass) ;; slot shared by instances of direct subclass.
- ;; (Thomas Buerger, April 1998)
- (make-closure-variable class (class-slot-init-value)))
-
- ((#\virtual) ;; No allocation
- ;; slot-ref and slot-set! function must be given by the user
- (let ((get (get-keyword #\slot-ref (slot-definition-options s) #f))
- (set (get-keyword #\slot-set! (slot-definition-options s) #f)))
- (if (not (and get set))
- (goops-error "You must supply a #:slot-ref and a #:slot-set! in ~S"
- s))
- (list get set)))
- (else (next-method))))
-
-(define (make-closure-variable class value)
- (list (lambda (o) value)
- (lambda (o v) (set! value v))))
-
-(define-method (compute-get-n-set (o <object>) s)
- (goops-error "Allocation \"~S\" is unknown" (slot-definition-allocation s)))
-
-(define-method (compute-slots (class <class>))
- (%compute-slots class))
-
-;;;
-;;; {Initialize}
-;;;
-
-(define-method (initialize (object <object>) initargs)
- (%initialize-object object initargs))
-
-(define-method (initialize (class <class>) initargs)
- (next-method)
- (let ((dslots (get-keyword #\slots initargs '()))
- (supers (get-keyword #\dsupers initargs '())))
- (slot-set! class 'name (get-keyword #\name initargs '???))
- (slot-set! class 'direct-supers supers)
- (slot-set! class 'direct-slots dslots)
- (slot-set! class 'direct-subclasses '())
- (slot-set! class 'direct-methods '())
- (slot-set! class 'cpl (compute-cpl class))
- (slot-set! class 'redefined #f)
- (let ((slots (compute-slots class)))
- (slot-set! class 'slots slots)
- (slot-set! class 'nfields 0)
- (slot-set! class 'getters-n-setters (compute-getters-n-setters class
- slots))
- ;; Build getters - setters - accessors
- (compute-slot-accessors class slots))
-
- ;; Update the "direct-subclasses" of each inherited classes
- (for-each (lambda (x)
- (slot-set! x
- 'direct-subclasses
- (cons class (slot-ref x 'direct-subclasses))))
- supers)
-
- ;; Support for the underlying structs:
-
- ;; Set the layout slot
- (%prep-layout! class)
- ;; Inherit class flags (invisible on scheme level) from supers
- (%inherit-magic! class supers)))
-
-(define (initialize-object-procedure object initargs)
- (let ((proc (get-keyword #\procedure initargs #f)))
- (cond ((not proc))
- ((pair? proc)
- (apply slot-set! object 'procedure proc))
- (else
- (slot-set! object 'procedure proc)))))
-
-(define-method (initialize (applicable-struct <applicable-struct>) initargs)
- (next-method)
- (initialize-object-procedure applicable-struct initargs))
-
-(define-method (initialize (generic <generic>) initargs)
- (let ((previous-definition (get-keyword #\default initargs #f))
- (name (get-keyword #\name initargs #f)))
- (next-method)
- (slot-set! generic 'methods (if (is-a? previous-definition <procedure>)
- (list (method args
- (apply previous-definition args)))
- '()))
- (if name
- (set-procedure-property! generic 'name name))
- ))
-
-(define-method (initialize (gws <generic-with-setter>) initargs)
- (next-method)
- (%set-object-setter! gws (get-keyword #\setter initargs #f)))
-
-(define-method (initialize (eg <extended-generic>) initargs)
- (next-method)
- (slot-set! eg 'extends (get-keyword #\extends initargs '())))
-
-(define dummy-procedure (lambda args *unspecified*))
-
-(define-method (initialize (method <method>) initargs)
- (next-method)
- (slot-set! method 'generic-function (get-keyword #\generic-function initargs #f))
- (slot-set! method 'specializers (get-keyword #\specializers initargs '()))
- (slot-set! method 'procedure
- (get-keyword #\procedure initargs #f))
- (slot-set! method 'formals (get-keyword #\formals initargs '()))
- (slot-set! method 'body (get-keyword #\body initargs '()))
- (slot-set! method 'make-procedure (get-keyword #\make-procedure initargs #f)))
-
-
-;;;
-;;; {Change-class}
-;;;
-
-(define (change-object-class old-instance old-class new-class)
- (let ((new-instance (allocate-instance new-class '())))
- ;; Initialize the slots of the new instance
- (for-each (lambda (slot)
- (if (and (slot-exists-using-class? old-class old-instance slot)
- (eq? (slot-definition-allocation
- (class-slot-definition old-class slot))
- #\instance)
- (slot-bound-using-class? old-class old-instance slot))
- ;; Slot was present and allocated in old instance; copy it
- (slot-set-using-class!
- new-class
- new-instance
- slot
- (slot-ref-using-class old-class old-instance slot))
- ;; slot was absent; initialize it with its default value
- (let ((init (slot-init-function new-class slot)))
- (if init
- (slot-set-using-class!
- new-class
- new-instance
- slot
- (apply init '()))))))
- (map slot-definition-name (class-slots new-class)))
- ;; Exchange old and new instance in place to keep pointers valid
- (%modify-instance old-instance new-instance)
- ;; Allow class specific updates of instances (which now are swapped)
- (update-instance-for-different-class new-instance old-instance)
- old-instance))
-
-
-(define-method (update-instance-for-different-class (old-instance <object>)
- (new-instance
- <object>))
- ;;not really important what we do, we just need a default method
- new-instance)
-
-(define-method (change-class (old-instance <object>) (new-class <class>))
- (change-object-class old-instance (class-of old-instance) new-class))
-
-;;;
-;;; {make}
-;;;
-;;; A new definition which overwrites the previous one which was built-in
-;;;
-
-(define-method (allocate-instance (class <class>) initargs)
- (%allocate-instance class initargs))
-
-(define-method (make-instance (class <class>) . initargs)
- (let ((instance (allocate-instance class initargs)))
- (initialize instance initargs)
- instance))
-
-(define make make-instance)
-
-;;;
-;;; {apply-generic}
-;;;
-;;; Protocol for calling standard generic functions. This protocol is
-;;; not used for real <generic> functions (in this case we use a
-;;; completely C hard-coded protocol). Apply-generic is used by
-;;; goops for calls to subclasses of <generic> and <generic-with-setter>.
-;;; The code below is similar to the first MOP described in AMOP. In
-;;; particular, it doesn't used the currified approach to gf
-;;; call. There are 2 reasons for that:
-;;; - the protocol below is exposed to mimic completely the one written in C
-;;; - the currified protocol would be imho inefficient in C.
-;;;
-
-(define-method (apply-generic (gf <generic>) args)
- (if (null? (slot-ref gf 'methods))
- (no-method gf args))
- (let ((methods (compute-applicable-methods gf args)))
- (if methods
- (apply-methods gf (sort-applicable-methods gf methods args) args)
- (no-applicable-method gf args))))
-
-;; compute-applicable-methods is bound to %compute-applicable-methods.
-;; *fixme* use let
-(define %%compute-applicable-methods
- (make <generic> #\name 'compute-applicable-methods))
-
-(define-method (%%compute-applicable-methods (gf <generic>) args)
- (%compute-applicable-methods gf args))
-
-(set! compute-applicable-methods %%compute-applicable-methods)
-
-(define-method (sort-applicable-methods (gf <generic>) methods args)
- (let ((targs (map class-of args)))
- (sort methods (lambda (m1 m2) (method-more-specific? m1 m2 targs)))))
-
-(define-method (method-more-specific? (m1 <method>) (m2 <method>) targs)
- (%method-more-specific? m1 m2 targs))
-
-(define-method (apply-method (gf <generic>) methods build-next args)
- (apply (method-procedure (car methods))
- (build-next (cdr methods) args)
- args))
-
-(define-method (apply-methods (gf <generic>) (l <list>) args)
- (letrec ((next (lambda (procs args)
- (lambda new-args
- (let ((a (if (null? new-args) args new-args)))
- (if (null? procs)
- (no-next-method gf a)
- (apply-method gf procs next a)))))))
- (apply-method gf l next args)))
-
-;; We don't want the following procedure to turn up in backtraces:
-(for-each (lambda (proc)
- (set-procedure-property! proc 'system-procedure #t))
- (list slot-unbound
- slot-missing
- no-next-method
- no-applicable-method
- no-method
- ))
-
-;;;
-;;; {<composite-metaclass> and <active-metaclass>}
-;;;
-
-;(autoload "active-slot" <active-metaclass>)
-;(autoload "composite-slot" <composite-metaclass>)
-;(export <composite-metaclass> <active-metaclass>)
-
-;;;
-;;; {Tools}
-;;;
-
-;; list2set
-;;
-;; duplicate the standard list->set function but using eq instead of
-;; eqv which really sucks a lot, uselessly here
-;;
-(define (list2set l)
- (let loop ((l l)
- (res '()))
- (cond
- ((null? l) res)
- ((memq (car l) res) (loop (cdr l) res))
- (else (loop (cdr l) (cons (car l) res))))))
-
-(define (class-subclasses c)
- (letrec ((allsubs (lambda (c)
- (cons c (mapappend allsubs
- (class-direct-subclasses c))))))
- (list2set (cdr (allsubs c)))))
-
-(define (class-methods c)
- (list2set (mapappend class-direct-methods
- (cons c (class-subclasses c)))))
-
-;;;
-;;; {Final initialization}
-;;;
-
-;; Tell C code that the main bulk of Goops has been loaded
-(%goops-loaded)
-;;;; Copyright (C) 1999, 2000, 2005, 2006 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-(define-module (oop goops accessors)
- \:use-module (oop goops)
- \:re-export (standard-define-class)
- \:export (define-class-with-accessors
- define-class-with-accessors-keywords))
-
-(define-macro (define-class-with-accessors name supers . slots)
- (let ((eat? #f))
- `(standard-define-class
- ,name ,supers
- ,@(map-in-order
- (lambda (slot)
- (cond (eat?
- (set! eat? #f)
- slot)
- ((keyword? slot)
- (set! eat? #t)
- slot)
- ((pair? slot)
- (if (get-keyword #\accessor (cdr slot) #f)
- slot
- (let ((name (car slot)))
- `(,name #\accessor ,name ,@(cdr slot)))))
- (else
- `(,slot #\accessor ,slot))))
- slots))))
-
-(define-macro (define-class-with-accessors-keywords name supers . slots)
- (let ((eat? #f))
- `(standard-define-class
- ,name ,supers
- ,@(map-in-order
- (lambda (slot)
- (cond (eat?
- (set! eat? #f)
- slot)
- ((keyword? slot)
- (set! eat? #t)
- slot)
- ((pair? slot)
- (let ((slot
- (if (get-keyword #\accessor (cdr slot) #f)
- slot
- (let ((name (car slot)))
- `(,name #\accessor ,name ,@(cdr slot))))))
- (if (get-keyword #\init-keyword (cdr slot) #f)
- slot
- (let* ((name (car slot))
- (keyword (symbol->keyword name)))
- `(,name #\init-keyword ,keyword ,@(cdr slot))))))
- (else
- `(,slot #\accessor ,slot
- #\init-keyword ,(symbol->keyword slot)))))
- slots))))
-;;; installed-scm-file
-
-;;;; Copyright (C) 1999, 2001, 2006, 2009 Free Software Foundation, Inc.
-;;;; Copyright (C) 1993-1998 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-;;;;
-;;;; This file was based upon active-slot.stklos from the STk distribution
-;;;; version 4.0.1 by Erick Gallesio <eg@unice.fr>.
-;;;;
-
-(define-module (oop goops active-slot)
- \:use-module (oop goops internal)
- \:export (<active-class>))
-
-(define-class <active-class> (<class>))
-
-(define-method (compute-get-n-set (class <active-class>) slot)
- (if (eq? (slot-definition-allocation slot) #\active)
- (let* ((index (slot-ref class 'nfields))
- (s (cdr slot))
- (before-ref (get-keyword #\before-slot-ref s #f))
- (after-ref (get-keyword #\after-slot-ref s #f))
- (before-set! (get-keyword #\before-slot-set! s #f))
- (after-set! (get-keyword #\after-slot-set! s #f))
- (unbound (make-unbound)))
- (slot-set! class 'nfields (+ index 1))
- (list (lambda (o)
- (if before-ref
- (if (before-ref o)
- (let ((res (%fast-slot-ref o index)))
- (and after-ref (not (eqv? res unbound)) (after-ref o))
- res)
- (make-unbound))
- (let ((res (%fast-slot-ref o index)))
- (and after-ref (not (eqv? res unbound)) (after-ref o))
- res)))
-
- (lambda (o v)
- (if before-set!
- (if (before-set! o v)
- (begin
- (%fast-slot-set! o index v)
- (and after-set! (after-set! o v))))
- (begin
- (%fast-slot-set! o index v)
- (and after-set! (after-set! o v)))))))
- (next-method)))
-;;;; Copyright (C) 1999, 2001, 2006, 2009, 2015 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-(define-module (oop goops compile)
- #\use-module (oop goops internal)
- #\re-export (compute-cmethod))
-;;; installed-scm-file
-
-;;;; Copyright (C) 1999, 2000, 2001, 2006 Free Software Foundation, Inc.
-;;;; Copyright (C) 1993-1998 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-;;;;
-;;;; This file was based upon composite-slot.stklos from the STk distribution
-;;;; version 4.0.1 by Erick Gallesio <eg@unice.fr>.
-;;;;
-
-(define-module (oop goops composite-slot)
- \:use-module (oop goops)
- \:export (<composite-class>))
-
-;;;
-;;; (define-class CLASS SUPERS
-;;; ...
-;;; (OBJECT ...)
-;;; ...
-;;; (SLOT #\allocation #\propagated
-;;; #\propagate-to '(PROPAGATION ...))
-;;; ...
-;;; #\metaclass <composite-class>)
-;;;
-;;; PROPAGATION ::= OBJECT | (OBJECT TARGETSLOT)
-;;;
-;;; The slot SLOT will be propagated to the slot TARGETSLOT in the object
-;;; stored in slot OBJECT. If TARGETSLOT is omitted, assume that the target
-;;; slot is named SLOT.
-;;;
-
-(define-class <composite-class> (<class>))
-
-(define-method (compute-get-n-set (class <composite-class>) slot)
- (if (eq? (slot-definition-allocation slot) #\propagated)
- (compute-propagated-get-n-set slot)
- (next-method)))
-
-(define (compute-propagated-get-n-set s)
- (let ((prop (get-keyword #\propagate-to (cdr s) #f))
- (s-name (slot-definition-name s)))
-
- (if (not prop)
- (goops-error "Propagation not specified for slot ~S" s-name))
- (if (not (pair? prop))
- (goops-error "Bad propagation list for slot ~S" s-name))
-
- (let ((objects (map (lambda (p) (if (pair? p) (car p) p)) prop))
- (slots (map (lambda (p) (if (pair? p) (cadr p) s-name)) prop)))
- (let ((first-object (car objects))
- (first-slot (car slots)))
- (list
- ;; The getter
- (lambda (o)
- (slot-ref (slot-ref o first-object) first-slot))
-
- ;; The setter
- (if (null? (cdr objects))
- (lambda (o v)
- (slot-set! (slot-ref o first-object) first-slot v))
- (lambda (o v)
- (for-each (lambda (object slot)
- (slot-set! (slot-ref o object) slot v))
- objects
- slots))))))))
-;;; installed-scm-file
-
-;;;; Copyright (C) 1998, 1999, 2001, 2006, 2008, 2009 Free Software Foundation, Inc.
-;;;; Copyright (C) 1993-1998 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-;;;;
-;;;; This file was based upon describe.stklos from the STk distribution
-;;;; version 4.0.1 by Erick Gallesio <eg@unice.fr>.
-;;;;
-
-(define-module (oop goops describe)
- \:use-module (oop goops)
- \:use-module (ice-9 session)
- \:use-module (ice-9 format)
- \:export (describe)) ; Export the describe generic function
-
-;;;
-;;; describe for simple objects
-;;;
-(define-method (describe (x <top>))
- (format #t "~s is " x)
- (cond
- ((integer? x) (format #t "an integer"))
- ((real? x) (format #t "a real"))
- ((complex? x) (format #t "a complex number"))
- ((null? x) (format #t "an empty list"))
- ((boolean? x) (format #t "a boolean value (~s)" (if x 'true 'false)))
- ((char? x) (format #t "a character, ascii value is ~s"
- (char->integer x)))
- ((symbol? x) (format #t "a symbol"))
- ((list? x) (format #t "a list"))
- ((pair? x) (if (pair? (cdr x))
- (format #t "an improper list")
- (format #t "a pair")))
- ((string? x) (if (eqv? x "")
- (format #t "an empty string")
- (format #t "a string of length ~s" (string-length x))))
- ((vector? x) (if (eqv? x '#())
- (format #t "an empty vector")
- (format #t "a vector of length ~s" (vector-length x))))
- ((eof-object? x) (format #t "the end-of-file object"))
- (else (format #t "an unknown object (~s)" x)))
- (format #t ".~%")
- *unspecified*)
-
-(define-method (describe (x <procedure>))
- (let ((name (procedure-name x)))
- (if name
- (format #t "`~s'" name)
- (display x))
- (display " is ")
- (display (if name #\a "an anonymous"))
- (display " procedure")
- (display " with ")
- (arity x)))
-
-;;;
-;;; describe for GOOPS instances
-;;;
-(define (safe-class-name class)
- (if (slot-bound? class 'name)
- (class-name class)
- class))
-
-(define-method (describe (x <object>))
- (format #t "~S is an instance of class ~A~%"
- x (safe-class-name (class-of x)))
-
- ;; print all the instance slots
- (format #t "Slots are: ~%")
- (for-each (lambda (slot)
- (let ((name (slot-definition-name slot)))
- (format #t " ~S = ~A~%"
- name
- (if (slot-bound? x name)
- (format #f "~S" (slot-ref x name))
- "#<unbound>"))))
- (class-slots (class-of x)))
- *unspecified*)
-
-;;;
-;;; Describe for classes
-;;;
-(define-method (describe (x <class>))
- (format #t "~S is a class. It's an instance of ~A~%"
- (safe-class-name x) (safe-class-name (class-of x)))
-
- ;; Super classes
- (format #t "Superclasses are:~%")
- (for-each (lambda (class) (format #t " ~A~%" (safe-class-name class)))
- (class-direct-supers x))
-
- ;; Direct slots
- (let ((slots (class-direct-slots x)))
- (if (null? slots)
- (format #t "(No direct slot)~%")
- (begin
- (format #t "Directs slots are:~%")
- (for-each (lambda (s)
- (format #t " ~A~%" (slot-definition-name s)))
- slots))))
-
-
- ;; Direct subclasses
- (let ((classes (class-direct-subclasses x)))
- (if (null? classes)
- (format #t "(No direct subclass)~%")
- (begin
- (format #t "Directs subclasses are:~%")
- (for-each (lambda (s)
- (format #t " ~A~%" (safe-class-name s)))
- classes))))
-
- ;; CPL
- (format #t "Class Precedence List is:~%")
- (for-each (lambda (s) (format #t " ~A~%" (safe-class-name s)))
- (class-precedence-list x))
-
- ;; Direct Methods
- (let ((methods (class-direct-methods x)))
- (if (null? methods)
- (format #t "(No direct method)~%")
- (begin
- (format #t "Class direct methods are:~%")
- (for-each describe methods))))
-
-; (format #t "~%Field Initializers ~% ")
-; (write (slot-ref x 'initializers)) (newline)
-
-; (format #t "~%Getters and Setters~% ")
-; (write (slot-ref x 'getters-n-setters)) (newline)
-)
-
-;;;
-;;; Describe for generic functions
-;;;
-(define-method (describe (x <generic>))
- (let ((name (generic-function-name x))
- (methods (generic-function-methods x)))
- ;; Title
- (format #t "~S is a generic function. It's an instance of ~A.~%"
- name (safe-class-name (class-of x)))
- ;; Methods
- (if (null? methods)
- (format #t "(No method defined for ~S)~%" name)
- (begin
- (format #t "Methods defined for ~S~%" name)
- (for-each (lambda (x) (describe x #t)) methods)))))
-
-;;;
-;;; Describe for methods
-;;;
-(define-method (describe (x <method>) . omit-generic)
- (letrec ((print-args (lambda (args)
- ;; take care of dotted arg lists
- (cond ((null? args) (newline))
- ((pair? args)
- (display #\space)
- (display (safe-class-name (car args)))
- (print-args (cdr args)))
- (else
- (display #\space)
- (display (safe-class-name args))
- (newline))))))
-
- ;; Title
- (format #t " Method ~A~%" x)
-
- ;; Associated generic
- (if (null? omit-generic)
- (let ((gf (method-generic-function x)))
- (if gf
- (format #t "\t Generic: ~A~%" (generic-function-name gf))
- (format #t "\t(No generic)~%"))))
-
- ;; GF specializers
- (format #t "\tSpecializers:")
- (print-args (method-specializers x))))
-
-(provide 'describe)
-;;;; Copyright (C) 1999, 2000, 2001, 2003, 2006, 2009, 2012, 2015 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-;; There are circularities here; you can't import (oop goops compile)
-;; before (oop goops). So when compiling, make sure that things are
-;; kosher.
-(eval-when (expand) (resolve-module '(oop goops)))
-
-(define-module (oop goops dispatch)
- #\use-module (oop goops)
- #\use-module (oop goops util)
- #\use-module (system base target)
- #\export (memoize-method!)
- #\no-backtrace)
-
-
-(define *dispatch-module* (current-module))
-
-;;;
-;;; Generic functions have an applicable-methods cache associated with
-;;; them. Every distinct set of types that is dispatched through a
-;;; generic adds an entry to the cache. This cache gets compiled out to
-;;; a dispatch procedure. In steady-state, this dispatch procedure is
-;;; never recompiled; but during warm-up there is some churn, both to
-;;; the cache and to the dispatch procedure.
-;;;
-;;; So what is the deal if warm-up happens in a multithreaded context?
-;;; There is indeed a window between missing the cache for a certain set
-;;; of arguments, and then updating the cache with the newly computed
-;;; applicable methods. One of the updaters is liable to lose their new
-;;; entry.
-;;;
-;;; This is actually OK though, because a subsequent cache miss for the
-;;; race loser will just cause memoization to try again. The cache will
-;;; eventually be consistent. We're not mutating the old part of the
-;;; cache, just consing on the new entry.
-;;;
-;;; It doesn't even matter if the dispatch procedure and the cache are
-;;; inconsistent -- most likely the type-set that lost the dispatch
-;;; procedure race will simply re-trigger a memoization, but since the
-;;; winner isn't in the effective-methods cache, it will likely also
-;;; re-trigger a memoization, and the cache will finally be consistent.
-;;; As you can see there is a possibility for ping-pong effects, but
-;;; it's unlikely given the shortness of the window between slot-set!
-;;; invocations. We could add a mutex, but it is strictly unnecessary,
-;;; and would add runtime cost and complexity.
-;;;
-
-(define (emit-linear-dispatch gf-sym nargs methods free rest?)
- (define (gen-syms n stem)
- (let lp ((n (1- n)) (syms '()))
- (if (< n 0)
- syms
- (lp (1- n) (cons (gensym stem) syms)))))
- (let* ((args (gen-syms nargs "a"))
- (types (gen-syms nargs "t")))
- (let lp ((methods methods)
- (free free)
- (exp `(cache-miss ,gf-sym
- ,(if rest?
- `(cons* ,@args rest)
- `(list ,@args)))))
- (cond
- ((null? methods)
- (values `(,(if rest? `(,@args . rest) args)
- (let ,(map (lambda (t a)
- `(,t (class-of ,a)))
- types args)
- ,exp))
- free))
- (else
- ;; jeez
- (let preddy ((free free)
- (types types)
- (specs (vector-ref (car methods) 1))
- (checks '()))
- (if (null? types)
- (let ((m-sym (gensym "p")))
- (lp (cdr methods)
- (acons (vector-ref (car methods) 3)
- m-sym
- free)
- `(if (and . ,checks)
- ,(if rest?
- `(apply ,m-sym ,@args rest)
- `(,m-sym . ,args))
- ,exp)))
- (let ((var (assq-ref free (car specs))))
- (if var
- (preddy free
- (cdr types)
- (cdr specs)
- (cons `(eq? ,(car types) ,var)
- checks))
- (let ((var (gensym "c")))
- (preddy (acons (car specs) var free)
- (cdr types)
- (cdr specs)
- (cons `(eq? ,(car types) ,var)
- checks))))))))))))
-
-(define (compute-dispatch-procedure gf cache)
- (define (scan)
- (let lp ((ls cache) (nreq -1) (nrest -1))
- (cond
- ((null? ls)
- (collate (make-vector (1+ nreq) '())
- (make-vector (1+ nrest) '())))
- ((vector-ref (car ls) 2) ; rest
- (lp (cdr ls) nreq (max nrest (vector-ref (car ls) 0))))
- (else ; req
- (lp (cdr ls) (max nreq (vector-ref (car ls) 0)) nrest)))))
- (define (collate req rest)
- (let lp ((ls cache))
- (cond
- ((null? ls)
- (emit req rest))
- ((vector-ref (car ls) 2) ; rest
- (let ((n (vector-ref (car ls) 0)))
- (vector-set! rest n (cons (car ls) (vector-ref rest n)))
- (lp (cdr ls))))
- (else ; req
- (let ((n (vector-ref (car ls) 0)))
- (vector-set! req n (cons (car ls) (vector-ref req n)))
- (lp (cdr ls)))))))
- (define (emit req rest)
- (let ((gf-sym (gensym "g")))
- (define (emit-rest n clauses free)
- (if (< n (vector-length rest))
- (let ((methods (vector-ref rest n)))
- (cond
- ((null? methods)
- (emit-rest (1+ n) clauses free))
- ;; FIXME: hash dispatch
- (else
- (call-with-values
- (lambda ()
- (emit-linear-dispatch gf-sym n methods free #t))
- (lambda (clause free)
- (emit-rest (1+ n) (cons clause clauses) free))))))
- (emit-req (1- (vector-length req)) clauses free)))
- (define (emit-req n clauses free)
- (if (< n 0)
- (comp `(lambda ,(map cdr free)
- (case-lambda ,@clauses))
- (map car free))
- (let ((methods (vector-ref req n)))
- (cond
- ((null? methods)
- (emit-req (1- n) clauses free))
- ;; FIXME: hash dispatch
- (else
- (call-with-values
- (lambda ()
- (emit-linear-dispatch gf-sym n methods free #f))
- (lambda (clause free)
- (emit-req (1- n) (cons clause clauses) free))))))))
-
- (emit-rest 0
- (if (or (zero? (vector-length rest))
- (null? (vector-ref rest 0)))
- (list `(args (cache-miss ,gf-sym args)))
- '())
- (acons gf gf-sym '()))))
- (define (comp exp vals)
- ;; When cross-compiling Guile itself, the native Guile must generate
- ;; code for the host.
- (with-target %host-type
- (lambda ()
- (let ((p ((@ (system base compile) compile) exp
- #\env *dispatch-module*
- #\from 'scheme
- #\opts '(#\partial-eval? #f #\cse? #f))))
- (apply p vals)))))
-
- ;; kick it.
- (scan))
-
-;; o/~ ten, nine, eight
-;; sometimes that's just how it goes
-;; three, two, one
-;;
-;; get out before it blows o/~
-;;
-(define timer-init 30)
-(define (delayed-compile gf)
- (let ((timer timer-init))
- (lambda args
- (set! timer (1- timer))
- (cond
- ((zero? timer)
- (let ((dispatch (compute-dispatch-procedure
- gf (slot-ref gf 'effective-methods))))
- (slot-set! gf 'procedure dispatch)
- (apply dispatch args)))
- (else
- ;; interestingly, this catches recursive compilation attempts as
- ;; well; in that case, timer is negative
- (cache-dispatch gf args))))))
-
-(define (cache-dispatch gf args)
- (define (map-until n f ls)
- (if (or (zero? n) (null? ls))
- '()
- (cons (f (car ls)) (map-until (1- n) f (cdr ls)))))
- (define (equal? x y) ; can't use the stock equal? because it's a generic...
- (cond ((pair? x) (and (pair? y)
- (eq? (car x) (car y))
- (equal? (cdr x) (cdr y))))
- ((null? x) (null? y))
- (else #f)))
- (if (slot-ref gf 'n-specialized)
- (let ((types (map-until (slot-ref gf 'n-specialized) class-of args)))
- (let lp ((cache (slot-ref gf 'effective-methods)))
- (cond ((null? cache)
- (cache-miss gf args))
- ((equal? (vector-ref (car cache) 1) types)
- (apply (vector-ref (car cache) 3) args))
- (else (lp (cdr cache))))))
- (cache-miss gf args)))
-
-(define (cache-miss gf args)
- (apply (memoize-method! gf args) args))
-
-(define (memoize-effective-method! gf args applicable)
- (define (first-n ls n)
- (if (or (zero? n) (null? ls))
- '()
- (cons (car ls) (first-n (cdr ls) (- n 1)))))
- (define (parse n ls)
- (cond ((null? ls)
- (memoize n #f (map class-of args)))
- ((= n (slot-ref gf 'n-specialized))
- (memoize n #t (map class-of (first-n args n))))
- (else
- (parse (1+ n) (cdr ls)))))
- (define (memoize len rest? types)
- (let* ((cmethod ((@@ (oop goops) compute-cmethod) applicable types))
- (cache (cons (vector len types rest? cmethod)
- (slot-ref gf 'effective-methods))))
- (slot-set! gf 'effective-methods cache)
- (slot-set! gf 'procedure (delayed-compile gf))
- cmethod))
- (parse 0 args))
-
-
-;;;
-;;; Memoization
-;;;
-
-(define (memoize-method! gf args)
- (let ((applicable ((if (eq? gf compute-applicable-methods)
- %compute-applicable-methods
- compute-applicable-methods)
- gf args)))
- (cond (applicable
- (memoize-effective-method! gf args applicable))
- (else
- (no-applicable-method gf args)))))
-
-(set-procedure-property! memoize-method! 'system-procedure #t)
-;;; installed-scm-file
-
-;;;; Copyright (C) 1999, 2001, 2006 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-(define-module (oop goops internal)
- \:use-module (oop goops))
-
-;; Export all the bindings that are internal to `(oop goops)'.
-(let ((public-i (module-public-interface (current-module))))
- (module-for-each (lambda (name var)
- (if (eq? name '%module-public-interface)
- #t
- (module-add! public-i name var)))
- (resolve-module '(oop goops))))
-;;; installed-scm-file
-
-;;;; Copyright (C) 2000,2001,2002, 2006, 2009, 2010, 2013 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-(define-module (oop goops save)
- \:use-module (oop goops internal)
- \:use-module (oop goops util)
- \:re-export (make-unbound)
- \:export (save-objects load-objects restore
- enumerate! enumerate-component!
- write-readably write-component write-component-procedure
- literal? readable make-readable))
-
-;;;
-;;; save-objects ALIST PORT [EXCLUDED] [USES]
-;;;
-;;; ALIST ::= ((NAME . OBJECT) ...)
-;;;
-;;; Save OBJECT ... to PORT so that when the data is read and evaluated
-;;; OBJECT ... are re-created under names NAME ... .
-;;; Exclude any references to objects in the list EXCLUDED.
-;;; Add a (use-modules . USES) line to the top of the saved text.
-;;;
-;;; In some instances, when `save-object' doesn't know how to produce
-;;; readable syntax for an object, you can explicitly register read
-;;; syntax for an object using the special form `readable'.
-;;;
-;;; Example:
-;;;
-;;; The function `foo' produces an object of obscure structure.
-;;; Only `foo' can construct such objects. Because of this, an
-;;; object such as
-;;;
-;;; (define x (vector 1 (foo)))
-;;;
-;;; cannot be saved by `save-objects'. But if you instead write
-;;;
-;;; (define x (vector 1 (readable (foo))))
-;;;
-;;; `save-objects' will happily produce the necessary read syntax.
-;;;
-;;; To add new read syntax, hang methods on `enumerate!' and
-;;; `write-readably'.
-;;;
-;;; enumerate! OBJECT ENV
-;;; Should call `enumerate-component!' (which takes same args) on
-;;; each component object. Should return #t if the composite object
-;;; can be written as a literal. (`enumerate-component!' returns #t
-;;; if the component is a literal.
-;;;
-;;; write-readably OBJECT PORT ENV
-;;; Should write a readable representation of OBJECT to PORT.
-;;; Should use `write-component' to print each component object.
-;;; Use `literal?' to decide if a component is a literal.
-;;;
-;;; Utilities:
-;;;
-;;; enumerate-component! OBJECT ENV
-;;;
-;;; write-component OBJECT PATCHER PORT ENV
-;;; PATCHER is an expression which, when evaluated, stores OBJECT
-;;; into its current location.
-;;;
-;;; Example:
-;;;
-;;; (write-component (car ls) `(set-car! ,ls ,(car ls)) file env)
-;;;
-;;; write-component is a macro.
-;;;
-;;; literal? COMPONENT ENV
-;;;
-
-(define-method (immediate? (o <top>)) #f)
-
-(define-method (immediate? (o <null>)) #t)
-(define-method (immediate? (o <number>)) #t)
-(define-method (immediate? (o <boolean>)) #t)
-(define-method (immediate? (o <symbol>)) #t)
-(define-method (immediate? (o <char>)) #t)
-(define-method (immediate? (o <keyword>)) #t)
-
-;;; enumerate! OBJECT ENVIRONMENT
-;;;
-;;; Return #t if object is a literal.
-;;;
-(define-method (enumerate! (o <top>) env) #t)
-
-(define-method (write-readably (o <top>) file env)
- ;;(goops-error "No read-syntax defined for object `~S'" o)
- (write o file) ;doesn't catch bugs, but is much more flexible
- )
-
-;;;
-;;; Readables
-;;;
-
-(define readables (make-weak-key-hash-table 61))
-
-(define-macro (readable exp)
- `(make-readable ,exp ',(copy-tree exp)))
-
-(define (make-readable obj expr)
- (hashq-set! readables obj expr)
- obj)
-
-(define (readable-expression obj)
- `(readable ,(hashq-ref readables obj)))
-
-;; FIXME: if obj is nil or false, this can return a false value. OTOH
-;; usually this is only for non-immediates.
-(define (readable? obj)
- (hashq-ref readables obj))
-
-;;;
-;;; Writer helpers
-;;;
-
-(define (write-component-procedure o file env)
- "Return #f if circular reference"
- (cond ((immediate? o) (write o file) #t)
- ((readable? o) (write (readable-expression o) file) #t)
- ((excluded? o env) (display #f file) #t)
- (else
- (let ((info (object-info o env)))
- (cond ((not (binding? info)) (write-readably o file env) #t)
- ((not (eq? (visiting info) #\defined)) #f) ;forward reference
- (else (display (binding info) file) #t))))))
-
-;;; write-component OBJECT PATCHER FILE ENV
-;;;
-(define-macro (write-component object patcher file env)
- `(or (write-component-procedure ,object ,file ,env)
- (begin
- (display #f ,file)
- (add-patcher! ,patcher ,env))))
-
-;;;
-;;; Strings
-;;;
-
-(define-method (enumerate! (o <string>) env) #f)
-
-;;;
-;;; Vectors
-;;;
-
-(define-method (enumerate! (o <vector>) env)
- (or (not (vector? o))
- (let ((literal? #t))
- (array-for-each (lambda (o)
- (if (not (enumerate-component! o env))
- (set! literal? #f)))
- o)
- literal?)))
-
-(define-method (write-readably (o <vector>) file env)
- (if (not (vector? o))
- (write o file)
- (let ((n (vector-length o)))
- (if (zero? n)
- (display "#()" file)
- (let ((not-literal? (not (literal? o env))))
- (display (if not-literal?
- "(vector "
- "#(")
- file)
- (if (and not-literal?
- (literal? (vector-ref o 0) env))
- (display #\' file))
- (write-component (vector-ref o 0)
- `(vector-set! ,o 0 ,(vector-ref o 0))
- file
- env)
- (do ((i 1 (+ 1 i)))
- ((= i n))
- (display #\space file)
- (if (and not-literal?
- (literal? (vector-ref o i) env))
- (display #\' file))
- (write-component (vector-ref o i)
- `(vector-set! ,o ,i ,(vector-ref o i))
- file
- env))
- (display #\) file))))))
-
-
-;;;
-;;; Arrays
-;;;
-
-(define-method (enumerate! (o <array>) env)
- (enumerate-component! (shared-array-root o) env))
-
-(define (make-mapper array)
- (let* ((n (array-rank array))
- (indices (reverse (if (<= n 11)
- (list-tail '(t s r q p n m l k j i) (- 11 n))
- (let loop ((n n)
- (ls '()))
- (if (zero? n)
- ls
- (loop (- n 1)
- (cons (gensym "i") ls))))))))
- `(lambda ,indices
- (+ ,(shared-array-offset array)
- ,@(map (lambda (ind dim inc)
- `(* ,inc ,(if (pair? dim) `(- ,ind ,(car dim)) ind)))
- indices
- (array-dimensions array)
- (shared-array-increments array))))))
-
-(define (write-array prefix o not-literal? file env)
- (letrec ((inner (lambda (n indices)
- (if (not (zero? n))
- (let ((el (apply array-ref o
- (reverse (cons 0 indices)))))
- (if (and not-literal?
- (literal? el env))
- (display #\' file))
- (write-component
- el
- `(array-set! ,o ,el ,@indices)
- file
- env)))
- (do ((i 1 (+ 1 i)))
- ((= i n))
- (display #\space file)
- (let ((el (apply array-ref o
- (reverse (cons i indices)))))
- (if (and not-literal?
- (literal? el env))
- (display #\' file))
- (write-component
- el
- `(array-set! ,o ,el ,@indices)
- file
- env))))))
- (display prefix file)
- (let loop ((dims (array-dimensions o))
- (indices '()))
- (cond ((null? (cdr dims))
- (inner (car dims) indices))
- (else
- (let ((n (car dims)))
- (do ((i 0 (+ 1 i)))
- ((= i n))
- (if (> i 0)
- (display #\space file))
- (display prefix file)
- (loop (cdr dims) (cons i indices))
- (display #\) file))))))
- (display #\) file)))
-
-(define-method (write-readably (o <array>) file env)
- (let ((root (shared-array-root o)))
- (cond ((literal? o env)
- (if (not (vector? root))
- (write o file)
- (begin
- (display #\# file)
- (display (array-rank o) file)
- (write-array #\( o #f file env))))
- ((binding? root env)
- (display "(make-shared-array " file)
- (if (literal? root env)
- (display #\' file))
- (write-component root
- (goops-error "write-readably(<array>): internal error")
- file
- env)
- (display #\space file)
- (display (make-mapper o) file)
- (for-each (lambda (dim)
- (display #\space file)
- (display dim file))
- (array-dimensions o))
- (display #\) file))
- (else
- (display "(list->uniform-array " file)
- (display (array-rank o) file)
- (display " '() " file)
- (write-array "(list " o #f file env)))))
-
-;;;
-;;; Pairs
-;;;
-
-;;; These methods have more complex structure than is required for
-;;; most objects, since they take over some of the logic of
-;;; `write-component'.
-;;;
-
-(define-method (enumerate! (o <pair>) env)
- (let ((literal? (enumerate-component! (car o) env)))
- (and (enumerate-component! (cdr o) env)
- literal?)))
-
-(define-method (write-readably (o <pair>) file env)
- (let ((proper? (let loop ((ls o))
- (or (null? ls)
- (and (pair? ls)
- (not (binding? (cdr ls) env))
- (loop (cdr ls))))))
- (1? (or (not (pair? (cdr o)))
- (binding? (cdr o) env)))
- (not-literal? (not (literal? o env)))
- (infos '())
- (refs (ref-stack env)))
- (display (cond ((not not-literal?) #\()
- (proper? "(list ")
- (1? "(cons ")
- (else "(cons* "))
- file)
- (if (and not-literal?
- (literal? (car o) env))
- (display #\' file))
- (write-component (car o) `(set-car! ,o ,(car o)) file env)
- (do ((ls (cdr o) (cdr ls))
- (prev o ls))
- ((or (not (pair? ls))
- (binding? ls env))
- (if (not (null? ls))
- (begin
- (if (not not-literal?)
- (display " ." file))
- (display #\space file)
- (if (and not-literal?
- (literal? ls env))
- (display #\' file))
- (write-component ls `(set-cdr! ,prev ,ls) file env)))
- (display #\) file))
- (display #\space file)
- (set! infos (cons (object-info ls env) infos))
- (push-ref! ls env) ;*fixme* optimize
- (set! (visiting? (car infos)) #t)
- (if (and not-literal?
- (literal? (car ls) env))
- (display #\' file))
- (write-component (car ls) `(set-car! ,ls ,(car ls)) file env)
- )
- (for-each (lambda (info)
- (set! (visiting? info) #f))
- infos)
- (set! (ref-stack env) refs)
- ))
-
-;;;
-;;; Objects
-;;;
-
-;;; Doesn't yet handle unbound slots
-
-;; Don't export this function! This is all very temporary.
-;;
-(define (get-set-for-each proc class)
- (for-each (lambda (slotdef g-n-s)
- (let ((g-n-s (cddr g-n-s)))
- (cond ((integer? g-n-s)
- (proc (standard-get g-n-s) (standard-set g-n-s)))
- ((not (memq (slot-definition-allocation slotdef)
- '(#\class #\each-subclass)))
- (proc (car g-n-s) (cadr g-n-s))))))
- (class-slots class)
- (slot-ref class 'getters-n-setters)))
-
-(define (access-for-each proc class)
- (for-each (lambda (slotdef g-n-s)
- (let ((g-n-s (cddr g-n-s))
- (a (slot-definition-accessor slotdef)))
- (cond ((integer? g-n-s)
- (proc (slot-definition-name slotdef)
- (and a (generic-function-name a))
- (standard-get g-n-s)
- (standard-set g-n-s)))
- ((not (memq (slot-definition-allocation slotdef)
- '(#\class #\each-subclass)))
- (proc (slot-definition-name slotdef)
- (and a (generic-function-name a))
- (car g-n-s)
- (cadr g-n-s))))))
- (class-slots class)
- (slot-ref class 'getters-n-setters)))
-
-(define-macro (restore class slots . exps)
- "(restore CLASS (SLOT-NAME1 ...) EXP1 ...)"
- `(let ((o ((@@ (oop goops) %allocate-instance) ,class '())))
- (for-each (lambda (name val)
- (slot-set! o name val))
- ',slots
- (list ,@exps))
- o))
-
-(define-method (enumerate! (o <object>) env)
- (get-set-for-each (lambda (get set)
- (let ((val (get o)))
- (if (not (unbound? val))
- (enumerate-component! val env))))
- (class-of o))
- #f)
-
-(define-method (write-readably (o <object>) file env)
- (let ((class (class-of o)))
- (display "(restore " file)
- (display (class-name class) file)
- (display " (" file)
- (let ((slotdefs
- (filter (lambda (slotdef)
- (not (or (memq (slot-definition-allocation slotdef)
- '(#\class #\each-subclass))
- (and (slot-bound? o (slot-definition-name slotdef))
- (excluded?
- (slot-ref o (slot-definition-name slotdef))
- env)))))
- (class-slots class))))
- (if (not (null? slotdefs))
- (begin
- (display (slot-definition-name (car slotdefs)) file)
- (for-each (lambda (slotdef)
- (display #\space file)
- (display (slot-definition-name slotdef) file))
- (cdr slotdefs)))))
- (display #\) file)
- (access-for-each (lambda (name aname get set)
- (display #\space file)
- (let ((val (get o)))
- (cond ((unbound? val)
- (display '(make-unbound) file))
- ((excluded? val env))
- (else
- (if (literal? val env)
- (display #\' file))
- (write-component val
- (if aname
- `(set! (,aname ,o) ,val)
- `(slot-set! ,o ',name ,val))
- file env)))))
- class)
- (display #\) file)))
-
-;;;
-;;; Classes
-;;;
-
-;;; Currently, we don't support reading in class objects
-;;;
-
-(define-method (enumerate! (o <class>) env) #f)
-
-(define-method (write-readably (o <class>) file env)
- (display (class-name o) file))
-
-;;;
-;;; Generics
-;;;
-
-;;; Currently, we don't support reading in generic functions
-;;;
-
-(define-method (enumerate! (o <generic>) env) #f)
-
-(define-method (write-readably (o <generic>) file env)
- (display (generic-function-name o) file))
-
-;;;
-;;; Method
-;;;
-
-;;; Currently, we don't support reading in methods
-;;;
-
-(define-method (enumerate! (o <method>) env) #f)
-
-(define-method (write-readably (o <method>) file env)
- (goops-error "No read-syntax for <method> defined"))
-
-;;;
-;;; Environments
-;;;
-
-(define-class <environment> ()
- (object-info #\accessor object-info
- #\init-form (make-hash-table 61))
- (excluded #\accessor excluded
- #\init-form (make-hash-table 61))
- (pass-2? #\accessor pass-2?
- #\init-value #f)
- (ref-stack #\accessor ref-stack
- #\init-value '())
- (objects #\accessor objects
- #\init-value '())
- (pre-defines #\accessor pre-defines
- #\init-value '())
- (locals #\accessor locals
- #\init-value '())
- (stand-ins #\accessor stand-ins
- #\init-value '())
- (post-defines #\accessor post-defines
- #\init-value '())
- (patchers #\accessor patchers
- #\init-value '())
- (multiple-bound #\accessor multiple-bound
- #\init-value '())
- )
-
-(define-method (initialize (env <environment>) initargs)
- (next-method)
- (cond ((get-keyword #\excluded initargs #f)
- => (lambda (excludees)
- (for-each (lambda (e)
- (hashq-create-handle! (excluded env) e #f))
- excludees)))))
-
-(define-method (object-info o env)
- (hashq-ref (object-info env) o))
-
-(define-method ((setter object-info) o env x)
- (hashq-set! (object-info env) o x))
-
-(define (excluded? o env)
- (hashq-get-handle (excluded env) o))
-
-(define (add-patcher! patcher env)
- (set! (patchers env) (cons patcher (patchers env))))
-
-(define (push-ref! o env)
- (set! (ref-stack env) (cons o (ref-stack env))))
-
-(define (pop-ref! env)
- (set! (ref-stack env) (cdr (ref-stack env))))
-
-(define (container env)
- (car (ref-stack env)))
-
-(define-class <object-info> ()
- (visiting #\accessor visiting
- #\init-value #f)
- (binding #\accessor binding
- #\init-value #f)
- (literal? #\accessor literal?
- #\init-value #f)
- )
-
-(define visiting? visiting)
-
-(define-method (binding (info <boolean>))
- #f)
-
-(define-method (binding o env)
- (binding (object-info o env)))
-
-(define binding? binding)
-
-(define-method (literal? (info <boolean>))
- #t)
-
-;;; Note that this method is intended to be used only during the
-;;; writing pass
-;;;
-(define-method (literal? o env)
- (or (immediate? o)
- (excluded? o env)
- (let ((info (object-info o env)))
- ;; write-component sets all bindings first to #\defining,
- ;; then to #\defined
- (and (or (not (binding? info))
- ;; we might be using `literal?' in a write-readably method
- ;; to query about the object being defined
- (and (eq? (visiting info) #\defining)
- (null? (cdr (ref-stack env)))))
- (literal? info)))))
-
-;;;
-;;; Enumeration
-;;;
-
-;;; Enumeration has two passes.
-;;;
-;;; Pass 1: Detect common substructure, circular references and order
-;;;
-;;; Pass 2: Detect literals
-
-(define (enumerate-component! o env)
- (cond ((immediate? o) #t)
- ((readable? o) #f)
- ((excluded? o env) #t)
- ((pass-2? env)
- (let ((info (object-info o env)))
- (if (binding? info)
- ;; if circular reference, we print as a literal
- ;; (note that during pass-2, circular references are
- ;; forward references, i.e. *not* yet marked with #\pass-2
- (not (eq? (visiting? info) #\pass-2))
- (and (enumerate! o env)
- (begin
- (set! (literal? info) #t)
- #t)))))
- ((object-info o env)
- => (lambda (info)
- (set! (binding info) #t)
- (if (visiting? info)
- ;; circular reference--mark container
- (set! (binding (object-info (container env) env)) #t))))
- (else
- (let ((info (make <object-info>)))
- (set! (object-info o env) info)
- (push-ref! o env)
- (set! (visiting? info) #t)
- (enumerate! o env)
- (set! (visiting? info) #f)
- (pop-ref! env)
- (set! (objects env) (cons o (objects env)))))))
-
-
-;;;
-;;; Main engine
-;;;
-
-(define binding-name car)
-(define binding-object cdr)
-
-(define (pass-1! alist env)
- ;; Determine object order and necessary bindings
- (for-each (lambda (binding)
- (enumerate-component! (binding-object binding) env))
- alist))
-
-(define (make-local i)
- (string->symbol (string-append "%o" (number->string i))))
-
-(define (name-bindings! alist env)
- ;; Name top-level bindings
- (for-each (lambda (b)
- (let ((o (binding-object b)))
- (if (not (or (immediate? o)
- (readable? o)
- (excluded? o env)))
- (let ((info (object-info o env)))
- (if (symbol? (binding info))
- ;; already bound to a variable
- (set! (multiple-bound env)
- (acons (binding info)
- (binding-name b)
- (multiple-bound env)))
- (set! (binding info)
- (binding-name b)))))))
- alist)
- ;; Name rest of bindings and create stand-in and definition lists
- (let post-loop ((ls (objects env))
- (post-defs '()))
- (cond ((or (null? ls)
- (eq? (binding (car ls) env) #t))
- (set! (post-defines env) post-defs)
- (set! (objects env) ls))
- ((not (binding (car ls) env))
- (post-loop (cdr ls) post-defs))
- (else
- (post-loop (cdr ls) (cons (car ls) post-defs)))))
- (let pre-loop ((ls (reverse (objects env)))
- (i 0)
- (pre-defs '())
- (locs '())
- (sins '()))
- (if (null? ls)
- (begin
- (set! (pre-defines env) (reverse pre-defs))
- (set! (locals env) (reverse locs))
- (set! (stand-ins env) (reverse sins)))
- (let ((info (object-info (car ls) env)))
- (cond ((not (binding? info))
- (pre-loop (cdr ls) i pre-defs locs sins))
- ((boolean? (binding info))
- ;; local
- (set! (binding info) (make-local i))
- (pre-loop (cdr ls)
- (+ 1 i)
- pre-defs
- (cons (car ls) locs)
- sins))
- ((null? locs)
- (pre-loop (cdr ls)
- i
- (cons (car ls) pre-defs)
- locs
- sins))
- (else
- (let ((real-name (binding info)))
- (set! (binding info) (make-local i))
- (pre-loop (cdr ls)
- (+ 1 i)
- pre-defs
- (cons (car ls) locs)
- (acons (binding info) real-name sins)))))))))
-
-(define (pass-2! env)
- (set! (pass-2? env) #t)
- (for-each (lambda (o)
- (let ((info (object-info o env)))
- (set! (literal? info) (enumerate! o env))
- (set! (visiting info) #\pass-2)))
- (append (pre-defines env)
- (locals env)
- (post-defines env))))
-
-(define (write-define! name val literal? file)
- (display "(define " file)
- (display name file)
- (display #\space file)
- (if literal? (display #\' file))
- (write val file)
- (display ")\n" file))
-
-(define (write-empty-defines! file env)
- (for-each (lambda (stand-in)
- (write-define! (cdr stand-in) #f #f file))
- (stand-ins env))
- (for-each (lambda (o)
- (write-define! (binding o env) #f #f file))
- (post-defines env)))
-
-(define (write-definition! prefix o file env)
- (display prefix file)
- (let ((info (object-info o env)))
- (display (binding info) file)
- (display #\space file)
- (if (literal? info)
- (display #\' file))
- (push-ref! o env)
- (set! (visiting info) #\defining)
- (write-readably o file env)
- (set! (visiting info) #\defined)
- (pop-ref! env)
- (display #\) file)))
-
-(define (write-let*-head! file env)
- (display "(let* (" file)
- (write-definition! "(" (car (locals env)) file env)
- (for-each (lambda (o)
- (write-definition! "\n (" o file env))
- (cdr (locals env)))
- (display ")\n" file))
-
-(define (write-rebindings! prefix bindings file env)
- (for-each (lambda (patch)
- (display prefix file)
- (display (cdr patch) file)
- (display #\space file)
- (display (car patch) file)
- (display ")\n" file))
- bindings))
-
-(define (write-definitions! selector prefix file env)
- (for-each (lambda (o)
- (write-definition! prefix o file env)
- (newline file))
- (selector env)))
-
-(define (write-patches! prefix file env)
- (for-each (lambda (patch)
- (display prefix file)
- (display (let name-objects ((patcher patch))
- (cond ((binding patcher env)
- => (lambda (name)
- (cond ((assq name (stand-ins env))
- => cdr)
- (else name))))
- ((pair? patcher)
- (cons (name-objects (car patcher))
- (name-objects (cdr patcher))))
- (else patcher)))
- file)
- (newline file))
- (reverse (patchers env))))
-
-(define (write-immediates! alist file)
- (for-each (lambda (b)
- (if (immediate? (binding-object b))
- (write-define! (binding-name b)
- (binding-object b)
- #t
- file)))
- alist))
-
-(define (write-readables! alist file env)
- (let ((written '()))
- (for-each (lambda (b)
- (cond ((not (readable? (binding-object b))))
- ((assq (binding-object b) written)
- => (lambda (p)
- (set! (multiple-bound env)
- (acons (cdr p)
- (binding-name b)
- (multiple-bound env)))))
- (else
- (write-define! (binding-name b)
- (readable-expression (binding-object b))
- #f
- file)
- (set! written (acons (binding-object b)
- (binding-name b)
- written)))))
- alist)))
-
-(define-method (save-objects (alist <pair>) (file <string>) . rest)
- (let ((port (open-output-file file)))
- (apply save-objects alist port rest)
- (close-port port)
- *unspecified*))
-
-(define-method (save-objects (alist <pair>) (file <output-port>) . rest)
- (let ((excluded (if (>= (length rest) 1) (car rest) '()))
- (uses (if (>= (length rest) 2) (cadr rest) '())))
- (let ((env (make <environment> #\excluded excluded)))
- (pass-1! alist env)
- (name-bindings! alist env)
- (pass-2! env)
- (if (not (null? uses))
- (begin
- (write `(use-modules ,@uses) file)
- (newline file)))
- (write-immediates! alist file)
- (if (null? (locals env))
- (begin
- (write-definitions! post-defines "(define " file env)
- (write-patches! "" file env))
- (begin
- (write-definitions! pre-defines "(define " file env)
- (write-empty-defines! file env)
- (write-let*-head! file env)
- (write-rebindings! " (set! " (stand-ins env) file env)
- (write-definitions! post-defines " (set! " file env)
- (write-patches! " " file env)
- (display " )\n" file)))
- (write-readables! alist file env)
- (write-rebindings! "(define " (reverse (multiple-bound env)) file env))))
-
-(define-method (load-objects (file <string>))
- (let* ((port (open-input-file file))
- (objects (load-objects port)))
- (close-port port)
- objects))
-
-(define iface (module-public-interface (current-module)))
-
-(define-method (load-objects (file <input-port>))
- (let ((m (make-module)))
- (module-use! m the-scm-module)
- (module-use! m iface)
- (save-module-excursion
- (lambda ()
- (set-current-module m)
- (let loop ((sexp (read file)))
- (if (not (eof-object? sexp))
- (begin
- (eval sexp m)
- (loop (read file)))))))
- (module-map (lambda (name var)
- (cons name (variable-ref var)))
- m)))
-;;; installed-scm-file
-
-;;;; Copyright (C) 2005, 2006, 2010, 2011 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-(define-module (oop goops simple)
- \:use-module (oop goops accessors)
- \:export (define-class)
- \:no-backtrace)
-
-(define-syntax-rule (define-class arg ...)
- (define-class-with-accessors-keywords arg ...))
-
-(module-use! (module-public-interface (current-module))
- (resolve-interface '(oop goops)))
-;;;; Copyright (C) 1999,2002, 2006, 2010, 2011 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-(define-module (oop goops stklos)
- \:use-module (oop goops internal)
- \:no-backtrace
- )
-
-;;;
-;;; This is the stklos compatibility module.
-;;;
-;;; WARNING: This module is under construction. While we expect to be able
-;;; to run most stklos code without problems in the future, this is not the
-;;; case now. The current compatibility is only superficial.
-;;;
-;;; Any comments/complaints/patches are welcome. Tell us about
-;;; your incompatibility problems (bug-guile@gnu.org).
-;;;
-
-;; Export all bindings that are exported from (oop goops)...
-(module-for-each (lambda (sym var)
- (module-add! (module-public-interface (current-module))
- sym var))
- (resolve-interface '(oop goops)))
-
-;; ...but replace the following bindings:
-(export define-class define-method)
-
-;; Also export the following
-(export write-object)
-
-;;; Enable keyword support (*fixme*---currently this has global effect)
-(read-set! keywords 'prefix)
-
-(define-syntax-rule (define-class name supers (slot ...) rest ...)
- (standard-define-class name supers slot ... rest ...))
-
-(define (toplevel-define! name val)
- (module-define! (current-module) name val))
-
-(define-syntax define-method
- (syntax-rules (setter)
- ((_ (setter name) rest ...)
- (begin
- (if (or (not (defined? 'name))
- (not (is-a? name <generic-with-setter>)))
- (toplevel-define! 'name
- (ensure-accessor
- (if (defined? 'name) name #f) 'name)))
- (add-method! (setter name) (method rest ...))))
- ((_ name rest ...)
- (begin
- (if (or (not (defined? 'name))
- (not (or (is-a? name <generic>)
- (is-a? name <primitive-generic>))))
- (toplevel-define! 'name
- (ensure-generic
- (if (defined? 'name) name #f) 'name)))
- (add-method! name (method rest ...))))))
-;;;; Copyright (C) 1999, 2000, 2001, 2003, 2006, 2008 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-(define-module (oop goops util)
- \:export (mapappend find-duplicate
- map* for-each* length* improper->proper)
- \:use-module (srfi srfi-1)
- \:re-export (any every)
- \:no-backtrace
- )
-
-
-;;;
-;;; {Utilities}
-;;;
-
-(define mapappend append-map)
-
-(define (find-duplicate l) ; find a duplicate in a list; #f otherwise
- (cond
- ((null? l) #f)
- ((memv (car l) (cdr l)) (car l))
- (else (find-duplicate (cdr l)))))
-
-(begin-deprecated
- (define (top-level-env)
- (let ((mod (current-module)))
- (if mod
- (module-eval-closure mod)
- '())))
-
- (define (top-level-env? env)
- (or (null? env)
- (procedure? (car env))))
-
- (export top-level-env? top-level-env))
-
-(define (map* fn . l) ; A map which accepts dotted lists (arg lists
- (cond ; must be "isomorph"
- ((null? (car l)) '())
- ((pair? (car l)) (cons (apply fn (map car l))
- (apply map* fn (map cdr l))))
- (else (apply fn l))))
-
-(define (for-each* fn . l) ; A for-each which accepts dotted lists (arg lists
- (cond ; must be "isomorph"
- ((null? (car l)) '())
- ((pair? (car l)) (apply fn (map car l)) (apply for-each* fn (map cdr l)))
- (else (apply fn l))))
-
-(define (length* ls)
- (do ((n 0 (+ 1 n))
- (ls ls (cdr ls)))
- ((not (pair? ls)) n)))
-
-(define (improper->proper ls)
- (if (pair? ls)
- (cons (car ls) (improper->proper (cdr ls)))
- (list ls)))
-;;; rnrs.scm --- The R6RS composite library
-
-;; Copyright (C) 2010, 2011 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs (6))
- (export ;; (rnrs arithmetic bitwise)
-
- bitwise-not bitwise-and bitwise-ior bitwise-xor bitwise-if
- bitwise-bit-count bitwise-length bitwise-first-bit-set
- bitwise-bit-set? bitwise-copy-bit bitwise-bit-field
- bitwise-copy-bit-field bitwise-arithmetic-shift
- bitwise-arithmetic-shift-left bitwise-arithmetic-shift-right
- bitwise-rotate-bit-field bitwise-reverse-bit-field
-
- ;; (rnrs arithmetic fixnums)
-
- fixnum? fixnum-width least-fixnum greatest-fixnum fx=? fx>? fx<? fx>=?
- fx<=? fxzero? fxpositive? fxnegative? fxodd? fxeven? fxmax fxmin fx+
- fx* fx- fxdiv-and-mod fxdiv fxmod fxdiv0-and-mod0 fxdiv0 fxmod0
- fx+/carry fx-/carry fx*/carry fxnot fxand fxior fxxor fxif fxbit-count
- fxlength fxfirst-bit-set fxbit-set? fxcopy-bit fxbit-field
- fxcopy-bit-field fxarithmetic-shift fxarithmetic-shift-left
- fxarithmetic-shift-right fxrotate-bit-field fxreverse-bit-field
-
- ;; (rnrs arithmetic flonums)
-
- flonum? real->flonum fl=? fl<? fl<=? fl>? fl>=? flinteger? flzero?
- flpositive? flnegative? flodd? fleven? flfinite? flinfinite? flnan?
- flmax flmin fl+ fl* fl- fl/ flabs fldiv-and-mod fldiv flmod
- fldiv0-and-mod0 fldiv0 flmod0 flnumerator fldenominator flfloor
- flceiling fltruncate flround flexp fllog flsin flcos fltan flacos
- flasin flatan flsqrt flexpt &no-infinities
- make-no-infinities-violation no-infinities-violation? &no-nans
- make-no-nans-violation no-nans-violation? fixnum->flonum
-
- ;; (rnrs base)
-
- boolean? symbol? char? vector? null? pair? number? string? procedure?
- define define-syntax syntax-rules lambda let let* let-values
- let*-values letrec letrec* begin quote lambda if set! cond case or
- and not eqv? equal? eq? + - * / max min abs numerator denominator gcd
- lcm floor ceiling truncate round rationalize real-part imag-part
- make-rectangular angle div mod div-and-mod div0 mod0 div0-and-mod0
- expt exact-integer-sqrt sqrt exp log sin cos tan asin acos atan
- make-polar magnitude angle complex? real? rational? integer? exact?
- inexact? real-valued? rational-valued? integer-valued? zero?
- positive? negative? odd? even? nan? finite? infinite? exact inexact =
- < > <= >= number->string string->number boolean=? cons car cdr caar
- cadr cdar cddr caaar caadr cadar cdaar caddr cdadr cddar cdddr caaaar
- caaadr caadar cadaar cdaaar cddaar cdadar cdaadr cadadr caaddr caddar
- cadddr cdaddr cddadr cdddar cddddr list? list length append reverse
- list-tail list-ref map for-each symbol->string string->symbol symbol=?
- char->integer integer->char char=? char<? char>? char<=? char>=?
- make-string string string-length string-ref string=? string<? string>?
- string<=? string>=? substring string-append string->list list->string
- string-for-each string-copy vector? make-vector vector vector-length
- vector-ref vector-set! vector->list list->vector vector-fill!
- vector-map vector-for-each error assertion-violation assert
- call-with-current-continuation call/cc call-with-values dynamic-wind
- values apply quasiquote unquote unquote-splicing let-syntax
- letrec-syntax syntax-rules identifier-syntax
-
- ;; (rnrs bytevectors)
-
- endianness native-endianness bytevector? make-bytevector
- bytevector-length bytevector=? bytevector-fill! bytevector-copy!
- bytevector-copy uniform-array->bytevector bytevector-u8-ref
- bytevector-s8-ref bytevector-u8-set! bytevector-s8-set!
- bytevector->u8-list u8-list->bytevector bytevector-uint-ref
- bytevector-uint-set! bytevector-sint-ref bytevector-sint-set!
- bytevector->sint-list bytevector->uint-list uint-list->bytevector
- sint-list->bytevector bytevector-u16-ref bytevector-s16-ref
- bytevector-u16-set! bytevector-s16-set! bytevector-u16-native-ref
- bytevector-s16-native-ref bytevector-u16-native-set!
- bytevector-s16-native-set! bytevector-u32-ref bytevector-s32-ref
- bytevector-u32-set! bytevector-s32-set! bytevector-u32-native-ref
- bytevector-s32-native-ref bytevector-u32-native-set!
- bytevector-s32-native-set! bytevector-u64-ref bytevector-s64-ref
- bytevector-u64-set! bytevector-s64-set! bytevector-u64-native-ref
- bytevector-s64-native-ref bytevector-u64-native-set!
- bytevector-s64-native-set! bytevector-ieee-single-ref
- bytevector-ieee-single-set! bytevector-ieee-single-native-ref
- bytevector-ieee-single-native-set! bytevector-ieee-double-ref
- bytevector-ieee-double-set! bytevector-ieee-double-native-ref
- bytevector-ieee-double-native-set! string->utf8 string->utf16
- string->utf32 utf8->string utf16->string utf32->string
-
- ;; (rnrs conditions)
-
- &condition condition simple-conditions condition? condition-predicate
- condition-accessor define-condition-type &message
- make-message-condition message-condition? condition-message &warning
- make-warning warning? &serious make-serious-condition
- serious-condition? &error make-error error? &violation make-violation
- violation? &assertion make-assertion-violation assertion-violation?
- &irritants make-irritants-condition irritants-condition?
- condition-irritants &who make-who-condition who-condition?
- condition-who &non-continuable make-non-continuable-violation
- non-continuable-violation? &implementation-restriction
- make-implementation-restriction-violation
- implementation-restriction-violation? &lexical make-lexical-violation
- lexical-violation? &syntax make-syntax-violation syntax-violation?
- syntax-violation-form syntax-violation-subform &undefined
- make-undefined-violation undefined-violation?
-
- ;; (rnrs control)
-
- when unless do case-lambda
-
- ;; (rnrs enums)
-
- make-enumeration enum-set-universe enum-set-indexer
- enum-set-constructor enum-set->list enum-set-member? enum-set-subset?
- enum-set=? enum-set-union enum-set-intersection enum-set-difference
- enum-set-complement enum-set-projection define-enumeration
-
- ;; (rnrs exceptions)
-
- guard with-exception-handler raise raise-continuable
-
- ;; (rnrs files)
-
- file-exists? delete-file &i/o make-i/o-error i/o-error? &i/o-read
- make-i/o-read-error i/o-read-error? &i/o-write make-i/o-write-error
- i/o-write-error? &i/o-invalid-position
- make-i/o-invalid-position-error i/o-invalid-position-error?
- i/o-error-position &i/o-filename make-i/o-filename-error
- i/o-filename-error? i/o-error-filename &i/o-file-protection
- make-i/o-file-protection-error i/o-file-protection-error?
- &i/o-file-is-read-only make-i/o-file-is-read-only-error
- i/o-file-is-read-only-error? &i/o-file-already-exists
- make-i/o-file-already-exists-error i/o-file-already-exists-error?
- &i/o-file-does-not-exist make-i/o-file-does-not-exist-error
- i/o-file-does-not-exist-error? &i/o-port make-i/o-port-error
- i/o-port-error? i/o-error-port
-
- ;; (rnrs hashtables)
-
- make-eq-hashtable make-eqv-hashtable make-hashtable hashtable?
- hashtable-size hashtable-ref hashtable-set! hashtable-delete!
- hashtable-contains? hashtable-update! hashtable-copy hashtable-clear!
- hashtable-keys hashtable-entries hashtable-equivalence-function
- hashtable-hash-function hashtable-mutable? equal-hash string-hash
- string-ci-hash symbol-hash
-
- ;; (rnrs io ports)
-
- file-options buffer-mode buffer-mode?
- eol-style native-eol-style error-handling-mode
- make-transcoder transcoder-codec transcoder-eol-style
- transcoder-error-handling-mode native-transcoder
- latin-1-codec utf-8-codec utf-16-codec
-
- eof-object? port? input-port? output-port? eof-object port-eof?
- port-transcoder
- binary-port? textual-port? transcoded-port
- port-position set-port-position!
- port-has-port-position? port-has-set-port-position!?
- close-port call-with-port
- open-bytevector-input-port make-custom-binary-input-port get-u8
- lookahead-u8 get-bytevector-n get-bytevector-n! get-bytevector-some
- get-bytevector-all open-bytevector-output-port
- make-custom-binary-output-port put-u8 put-bytevector
- open-string-input-port open-string-output-port
- call-with-bytevector-output-port
- call-with-string-output-port
- latin-1-codec utf-8-codec utf-16-codec
- open-file-input-port open-file-output-port open-file-input/output-port
- make-custom-textual-output-port
- call-with-string-output-port
- flush-output-port put-string
- get-char get-datum get-line get-string-all get-string-n get-string-n!
- lookahead-char
- put-char put-datum put-string
- standard-input-port standard-output-port standard-error-port
-
- ;; (rnrs io simple)
-
- call-with-input-file call-with-output-file current-input-port
- current-output-port current-error-port with-input-from-file
- with-output-to-file open-input-file open-output-file close-input-port
- close-output-port read-char peek-char read write-char newline display
- write
-
- ;; (rnrs lists)
-
- find for-all exists filter partition fold-left fold-right remp remove
- remv remq memp member memv memq assp assoc assv assq cons*
-
- ;; (rnrs programs)
-
- command-line exit
-
- ;; (rnrs records inspection)
-
- record? record-rtd record-type-name record-type-parent
- record-type-uid record-type-generative? record-type-sealed?
- record-type-opaque? record-type-field-names record-field-mutable?
-
- ;; (rnrs records procedural)
-
- make-record-type-descriptor record-type-descriptor?
- make-record-constructor-descriptor record-constructor record-predicate
- record-accessor record-mutator
-
- ;; (rnrs records syntactic)
-
- define-record-type record-type-descriptor
- record-constructor-descriptor
-
- ;; (rnrs sorting)
-
- list-sort vector-sort vector-sort!
-
- ;; (rnrs syntax-case)
-
- make-variable-transformer syntax
- ;; Until the deprecated support for a unified modules and
- ;; bindings namespace is removed, we need to manually resolve
- ;; a conflict between two bindings: that of the (rnrs
- ;; syntax-case) module, and the imported `syntax-case'
- ;; binding. We do so here and below by renaming the macro
- ;; import.
- (rename (syntax-case-hack syntax-case))
- identifier? bound-identifier=? free-identifier=?
- syntax->datum datum->syntax generate-temporaries with-syntax
- quasisyntax unsyntax unsyntax-splicing syntax-violation
-
- ;; (rnrs unicode)
-
- char-upcase char-downcase char-titlecase char-foldcase
- char-ci=? char-ci<? char-ci>? char-ci<=? char-ci>=?
- char-alphabetic? char-numeric? char-whitespace? char-upper-case?
- char-lower-case? char-title-case? char-general-category
- string-upcase string-downcase string-titlecase string-foldcase
- string-ci=? string-ci<? string-ci>? string-ci<=? string-ci>=?
- string-normalize-nfd string-normalize-nfkd string-normalize-nfc
- string-normalize-nfkc)
-
- (import (rnrs arithmetic bitwise (6))
- (rnrs arithmetic fixnums (6))
- (rnrs arithmetic flonums (6))
- (rnrs base (6))
-
- (rnrs bytevectors (6))
-
- (rnrs conditions (6))
- (rnrs control (6))
- (rnrs enums (6))
- (rnrs exceptions (6))
-
- (rnrs files (6))
-
- (rnrs hashtables (6))
-
- (rnrs io ports (6))
-
- (rnrs io simple (6))
- (rnrs lists (6))
- (rnrs programs (6))
- (rnrs records inspection (6))
- (rnrs records procedural (6))
- (rnrs records syntactic (6))
- (rnrs sorting (6))
- ;; See note above on exporting syntax-case.
- (rename (rnrs syntax-case (6))
- (syntax-case syntax-case-hack))
- (rnrs unicode (6))))
-;;; bitwise.scm --- The R6RS bitwise arithmetic operations library
-
-;; Copyright (C) 2010, 2013 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs arithmetic bitwise (6))
- (export bitwise-not
-
- bitwise-and
- bitwise-ior
- bitwise-xor
-
- bitwise-if
- bitwise-bit-count
- bitwise-length
-
- bitwise-first-bit-set
- bitwise-bit-set?
- bitwise-copy-bit
- bitwise-bit-field
- bitwise-copy-bit-field
-
- bitwise-arithmetic-shift
- bitwise-arithmetic-shift-left
- bitwise-arithmetic-shift-right
- bitwise-rotate-bit-field
- bitwise-reverse-bit-field)
- (import (rnrs base (6))
- (rnrs control (6))
- (rename (only (srfi srfi-60) bitwise-if
- integer-length
- first-set-bit
- copy-bit
- bit-field
- copy-bit-field
- rotate-bit-field
- reverse-bit-field)
- (integer-length bitwise-length)
- (first-set-bit bitwise-first-bit-set)
- (bit-field bitwise-bit-field)
- (reverse-bit-field bitwise-reverse-bit-field))
- (rename (only (guile) lognot
- logand
- logior
- logxor
- logcount
- logbit?
- modulo
- ash)
- (lognot bitwise-not)
- (logand bitwise-and)
- (logior bitwise-ior)
- (logxor bitwise-xor)
- (ash bitwise-arithmetic-shift)))
-
- (define (bitwise-bit-count ei)
- (if (negative? ei)
- (bitwise-not (logcount ei))
- (logcount ei)))
-
- (define (bitwise-bit-set? ei1 ei2) (logbit? ei2 ei1))
-
- (define (bitwise-copy-bit ei1 ei2 ei3)
- ;; The specification states that ei3 should be either 0 or 1.
- ;; However, other values have been tolerated by both Guile 2.0.x and
- ;; the sample implementation given the R6RS library document, so for
- ;; backward compatibility we continue to permit it.
- (copy-bit ei2 ei1 (logbit? 0 ei3)))
-
- (define (bitwise-copy-bit-field ei1 ei2 ei3 ei4)
- (copy-bit-field ei1 ei4 ei2 ei3))
-
- (define (bitwise-rotate-bit-field ei1 ei2 ei3 ei4)
- (rotate-bit-field ei1 ei4 ei2 ei3))
-
- (define bitwise-arithmetic-shift-left bitwise-arithmetic-shift)
- (define (bitwise-arithmetic-shift-right ei1 ei2)
- (bitwise-arithmetic-shift ei1 (- ei2))))
-;;; fixnums.scm --- The R6RS fixnums arithmetic library
-
-;; Copyright (C) 2010, 2011, 2013 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs arithmetic fixnums (6))
- (export fixnum?
-
- fixnum-width
- least-fixnum
- greatest-fixnum
-
- fx=?
- fx>?
- fx<?
- fx>=?
- fx<=?
-
- fxzero?
- fxpositive?
- fxnegative?
- fxodd?
- fxeven?
-
- fxmax
- fxmin
-
- fx+
- fx*
- fx-
-
- fxdiv-and-mod
- fxdiv
- fxmod
- fxdiv0-and-mod0
- fxdiv0
- fxmod0
-
- fx+/carry
- fx-/carry
- fx*/carry
-
- fxnot
- fxand
- fxior
- fxxor
- fxif
-
- fxbit-count
- fxlength
- fxfirst-bit-set
- fxbit-set?
- fxcopy-bit
- fxbit-field
- fxcopy-bit-field
-
- fxarithmetic-shift
- fxarithmetic-shift-left
- fxarithmetic-shift-right
-
- fxrotate-bit-field
- fxreverse-bit-field)
- (import (only (guile) ash
- cons*
- define-inlinable
- inexact->exact
- logand
- logbit?
- logcount
- logior
- lognot
- logxor
- most-positive-fixnum
- most-negative-fixnum
- object-address)
- (ice-9 optargs)
- (rnrs base (6))
- (rnrs control (6))
- (rnrs arithmetic bitwise (6))
- (rnrs conditions (6))
- (rnrs exceptions (6))
- (rnrs lists (6)))
-
- (define fixnum-width
- (let ((w (do ((i 0 (+ 1 i))
- (n 1 (* 2 n)))
- ((> n most-positive-fixnum)
- (+ 1 i)))))
- (lambda () w)))
-
- (define (greatest-fixnum) most-positive-fixnum)
- (define (least-fixnum) most-negative-fixnum)
-
- (define (fixnum? obj)
- (not (= 0 (logand 2 (object-address obj)))))
-
- (define-inlinable (inline-fixnum? obj)
- (not (= 0 (logand 2 (object-address obj)))))
-
- (define-syntax assert-fixnum
- (syntax-rules ()
- ((_ arg ...)
- (or (and (inline-fixnum? arg) ...)
- (raise (make-assertion-violation))))))
-
- (define (assert-fixnums args)
- (or (for-all inline-fixnum? args) (raise (make-assertion-violation))))
-
- (define-syntax define-fxop*
- (syntax-rules ()
- ((_ name op)
- (define name
- (case-lambda
- ((x y)
- (assert-fixnum x y)
- (op x y))
- (args
- (assert-fixnums args)
- (apply op args)))))))
-
- ;; All these predicates don't check their arguments for fixnum-ness,
- ;; as this doesn't seem to be strictly required by R6RS.
-
- (define fx=? =)
- (define fx>? >)
- (define fx<? <)
- (define fx>=? >=)
- (define fx<=? <=)
-
- (define fxzero? zero?)
- (define fxpositive? positive?)
- (define fxnegative? negative?)
- (define fxodd? odd?)
- (define fxeven? even?)
-
- (define-fxop* fxmax max)
- (define-fxop* fxmin min)
-
- (define (fx+ fx1 fx2)
- (assert-fixnum fx1 fx2)
- (let ((r (+ fx1 fx2)))
- (or (inline-fixnum? r)
- (raise (make-implementation-restriction-violation)))
- r))
-
- (define (fx* fx1 fx2)
- (assert-fixnum fx1 fx2)
- (let ((r (* fx1 fx2)))
- (or (inline-fixnum? r)
- (raise (make-implementation-restriction-violation)))
- r))
-
- (define* (fx- fx1 #\optional fx2)
- (assert-fixnum fx1)
- (if fx2
- (begin
- (assert-fixnum fx2)
- (let ((r (- fx1 fx2)))
- (or (inline-fixnum? r) (raise (make-assertion-violation)))
- r))
- (let ((r (- fx1)))
- (or (inline-fixnum? r) (raise (make-assertion-violation)))
- r)))
-
- (define (fxdiv fx1 fx2)
- (assert-fixnum fx1 fx2)
- (div fx1 fx2))
-
- (define (fxmod fx1 fx2)
- (assert-fixnum fx1 fx2)
- (mod fx1 fx2))
-
- (define (fxdiv-and-mod fx1 fx2)
- (assert-fixnum fx1 fx2)
- (div-and-mod fx1 fx2))
-
- (define (fxdiv0 fx1 fx2)
- (assert-fixnum fx1 fx2)
- (div0 fx1 fx2))
-
- (define (fxmod0 fx1 fx2)
- (assert-fixnum fx1 fx2)
- (mod0 fx1 fx2))
-
- (define (fxdiv0-and-mod0 fx1 fx2)
- (assert-fixnum fx1 fx2)
- (div0-and-mod0 fx1 fx2))
-
- (define (fx+/carry fx1 fx2 fx3)
- (assert-fixnum fx1 fx2 fx3)
- (let* ((s (+ fx1 fx2 fx3))
- (s0 (mod0 s (expt 2 (fixnum-width))))
- (s1 (div0 s (expt 2 (fixnum-width)))))
- (values s0 s1)))
-
- (define (fx-/carry fx1 fx2 fx3)
- (assert-fixnum fx1 fx2 fx3)
- (let* ((d (- fx1 fx2 fx3))
- (d0 (mod0 d (expt 2 (fixnum-width))))
- (d1 (div0 d (expt 2 (fixnum-width)))))
- (values d0 d1)))
-
- (define (fx*/carry fx1 fx2 fx3)
- (assert-fixnum fx1 fx2 fx3)
- (let* ((s (+ (* fx1 fx2) fx3))
- (s0 (mod0 s (expt 2 (fixnum-width))))
- (s1 (div0 s (expt 2 (fixnum-width)))))
- (values s0 s1)))
-
- (define (fxnot fx) (assert-fixnum fx) (lognot fx))
- (define-fxop* fxand logand)
- (define-fxop* fxior logior)
- (define-fxop* fxxor logxor)
-
- (define (fxif fx1 fx2 fx3)
- (assert-fixnum fx1 fx2 fx3)
- (bitwise-if fx1 fx2 fx3))
-
- (define (fxbit-count fx)
- (assert-fixnum fx)
- (if (negative? fx)
- (bitwise-not (logcount fx))
- (logcount fx)))
-
- (define (fxlength fx) (assert-fixnum fx) (bitwise-length fx))
- (define (fxfirst-bit-set fx) (assert-fixnum fx) (bitwise-first-bit-set fx))
- (define (fxbit-set? fx1 fx2) (assert-fixnum fx1 fx2) (logbit? fx2 fx1))
-
- (define (fxcopy-bit fx1 fx2 fx3)
- (assert-fixnum fx1 fx2 fx3)
- (bitwise-copy-bit fx1 fx2 fx3))
-
- (define (fxbit-field fx1 fx2 fx3)
- (assert-fixnum fx1 fx2 fx3)
- (bitwise-bit-field fx1 fx2 fx3))
-
- (define (fxcopy-bit-field fx1 fx2 fx3 fx4)
- (assert-fixnum fx1 fx2 fx3 fx4)
- (bitwise-copy-bit-field fx1 fx2 fx3 fx4))
-
- (define (fxarithmetic-shift fx1 fx2) (assert-fixnum fx1 fx2) (ash fx1 fx2))
- (define fxarithmetic-shift-left fxarithmetic-shift)
-
- (define (fxarithmetic-shift-right fx1 fx2)
- (assert-fixnum fx1 fx2) (ash fx1 (- fx2)))
-
- (define (fxrotate-bit-field fx1 fx2 fx3 fx4)
- (assert-fixnum fx1 fx2 fx3 fx4)
- (bitwise-rotate-bit-field fx1 fx2 fx3 fx4))
-
- (define (fxreverse-bit-field fx1 fx2 fx3)
- (assert-fixnum fx1 fx2 fx3)
- (bitwise-reverse-bit-field fx1 fx2 fx3))
-
-)
-;;; flonums.scm --- The R6RS flonums arithmetic library
-
-;; Copyright (C) 2010, 2011, 2013 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs arithmetic flonums (6))
- (export flonum?
- real->flonum
-
- fl=? fl<? fl<=? fl>? fl>=?
-
- flinteger? flzero? flpositive? flnegative? flodd? fleven? flfinite?
- flinfinite? flnan?
-
- flmax flmin
-
- fl+ fl* fl- fl/
-
- flabs
-
- fldiv-and-mod
- fldiv
- flmod
- fldiv0-and-mod0
- fldiv0
- flmod0
-
- flnumerator
- fldenominator
-
- flfloor flceiling fltruncate flround
-
- flexp fllog flsin flcos fltan flacos flasin flatan
-
- flsqrt flexpt
-
- &no-infinities
- make-no-infinities-violation
- no-infinities-violation?
-
- &no-nans
- make-no-nans-violation
- no-nans-violation?
-
- fixnum->flonum)
- (import (ice-9 optargs)
- (only (guile) inf?)
- (rnrs arithmetic fixnums (6))
- (rnrs base (6))
- (rnrs control (6))
- (rnrs conditions (6))
- (rnrs exceptions (6))
- (rnrs lists (6))
- (rnrs r5rs (6)))
-
- (define (flonum? obj) (and (real? obj) (inexact? obj)))
- (define (assert-flonum . args)
- (or (for-all flonum? args) (raise (make-assertion-violation))))
- (define (assert-iflonum . args)
- (or (for-all (lambda (i) (and (flonum? i) (integer? i))) args)
- (raise (make-assertion-violation))))
-
- (define (ensure-flonum z)
- (cond ((real? z) z)
- ((zero? (imag-part z)) (real-part z))
- (else +nan.0)))
-
- (define (real->flonum x)
- (or (real? x) (raise (make-assertion-violation)))
- (exact->inexact x))
-
- (define (fl=? . args) (apply assert-flonum args) (apply = args))
- (define (fl<? . args) (apply assert-flonum args) (apply < args))
- (define (fl<=? . args) (apply assert-flonum args) (apply <= args))
- (define (fl>? . args) (apply assert-flonum args) (apply > args))
- (define (fl>=? . args) (apply assert-flonum args) (apply >= args))
-
- (define (flinteger? fl) (assert-flonum fl) (integer? fl))
- (define (flzero? fl) (assert-flonum fl) (zero? fl))
- (define (flpositive? fl) (assert-flonum fl) (positive? fl))
- (define (flnegative? fl) (assert-flonum fl) (negative? fl))
- (define (flodd? ifl) (assert-iflonum ifl) (odd? ifl))
- (define (fleven? ifl) (assert-iflonum ifl) (even? ifl))
- (define (flfinite? fl) (assert-flonum fl) (not (or (inf? fl) (nan? fl))))
- (define (flinfinite? fl) (assert-flonum fl) (inf? fl))
- (define (flnan? fl) (assert-flonum fl) (nan? fl))
-
- (define (flmax fl1 . args)
- (let ((flargs (cons fl1 args)))
- (apply assert-flonum flargs)
- (apply max flargs)))
-
- (define (flmin fl1 . args)
- (let ((flargs (cons fl1 args)))
- (apply assert-flonum flargs)
- (apply min flargs)))
-
- (define (fl+ . args)
- (apply assert-flonum args)
- (if (null? args) 0.0 (apply + args)))
-
- (define (fl* . args)
- (apply assert-flonum args)
- (if (null? args) 1.0 (apply * args)))
-
- (define (fl- fl1 . args)
- (let ((flargs (cons fl1 args)))
- (apply assert-flonum flargs)
- (apply - flargs)))
-
- (define (fl/ fl1 . args)
- (let ((flargs (cons fl1 args)))
- (apply assert-flonum flargs)
- (apply / flargs)))
-
- (define (flabs fl) (assert-flonum fl) (abs fl))
-
- (define (fldiv-and-mod fl1 fl2)
- (assert-iflonum fl1 fl2)
- (div-and-mod fl1 fl2))
-
- (define (fldiv fl1 fl2)
- (assert-iflonum fl1 fl2)
- (div fl1 fl2))
-
- (define (flmod fl1 fl2)
- (assert-iflonum fl1 fl2)
- (mod fl1 fl2))
-
- (define (fldiv0-and-mod0 fl1 fl2)
- (assert-iflonum fl1 fl2)
- (div0-and-mod0 fl1 fl2))
-
- (define (fldiv0 fl1 fl2)
- (assert-iflonum fl1 fl2)
- (div0 fl1 fl2))
-
- (define (flmod0 fl1 fl2)
- (assert-iflonum fl1 fl2)
- (mod0 fl1 fl2))
-
- (define (flnumerator fl) (assert-flonum fl) (numerator fl))
- (define (fldenominator fl) (assert-flonum fl) (denominator fl))
-
- (define (flfloor fl) (assert-flonum fl) (floor fl))
- (define (flceiling fl) (assert-flonum fl) (ceiling fl))
- (define (fltruncate fl) (assert-flonum fl) (truncate fl))
- (define (flround fl) (assert-flonum fl) (round fl))
-
- (define (flexp fl) (assert-flonum fl) (exp fl))
- (define fllog
- (case-lambda
- ((fl)
- (assert-flonum fl)
- ;; add 0.0 to fl, to change -0.0 to 0.0,
- ;; so that (fllog -0.0) will be -inf.0, not -inf.0+pi*i.
- (ensure-flonum (log (+ fl 0.0))))
- ((fl fl2)
- (assert-flonum fl fl2)
- (ensure-flonum (/ (log (+ fl 0.0))
- (log (+ fl2 0.0)))))))
-
- (define (flsin fl) (assert-flonum fl) (sin fl))
- (define (flcos fl) (assert-flonum fl) (cos fl))
- (define (fltan fl) (assert-flonum fl) (tan fl))
- (define (flasin fl) (assert-flonum fl) (ensure-flonum (asin fl)))
- (define (flacos fl) (assert-flonum fl) (ensure-flonum (acos fl)))
- (define flatan
- (case-lambda
- ((fl) (assert-flonum fl) (atan fl))
- ((fl fl2) (assert-flonum fl fl2) (atan fl fl2))))
-
- (define (flsqrt fl) (assert-flonum fl) (ensure-flonum (sqrt fl)))
- (define (flexpt fl1 fl2) (assert-flonum fl1 fl2) (ensure-flonum (expt fl1 fl2)))
-
- (define-condition-type &no-infinities
- &implementation-restriction
- make-no-infinities-violation
- no-infinities-violation?)
-
- (define-condition-type &no-nans
- &implementation-restriction
- make-no-nans-violation
- no-nans-violation?)
-
- (define (fixnum->flonum fx)
- (or (fixnum? fx) (raise (make-assertion-violation)))
- (exact->inexact fx))
-)
-;;; base.scm --- The R6RS base library
-
-;; Copyright (C) 2010, 2011 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs base (6))
- (export boolean? symbol? char? vector? null? pair? number? string? procedure?
-
- define define-syntax syntax-rules lambda let let* let-values
- let*-values letrec letrec* begin
-
- quote lambda if set! cond case
-
- or and not
-
- eqv? equal? eq?
-
- + - * / max min abs numerator denominator gcd lcm floor ceiling
- truncate round rationalize real-part imag-part make-rectangular angle
- div mod div-and-mod div0 mod0 div0-and-mod0
-
- expt exact-integer-sqrt sqrt exp log sin cos tan asin acos atan
- make-polar magnitude angle
-
- complex? real? rational? integer? exact? inexact? real-valued?
- rational-valued? integer-valued? zero? positive? negative? odd? even?
- nan? finite? infinite?
-
- exact inexact = < > <= >=
-
- number->string string->number
-
- boolean=?
-
- cons car cdr caar cadr cdar cddr caaar caadr cadar cdaar caddr cdadr
- cddar cdddr caaaar caaadr caadar cadaar cdaaar cddaar cdadar cdaadr
- cadadr caaddr caddar cadddr cdaddr cddadr cdddar cddddr
-
- list? list length append reverse list-tail list-ref map for-each
-
- symbol->string string->symbol symbol=?
-
- char->integer integer->char char=? char<? char>? char<=? char>=?
-
- make-string string string-length string-ref string=? string<? string>?
- string<=? string>=? substring string-append string->list list->string
- string-for-each string-copy
-
- vector? make-vector vector vector-length vector-ref vector-set!
- vector->list list->vector vector-fill! vector-map vector-for-each
-
- error assertion-violation assert
-
- call-with-current-continuation call/cc call-with-values dynamic-wind
- values apply
-
- quasiquote unquote unquote-splicing
-
- let-syntax letrec-syntax
-
- syntax-rules identifier-syntax)
- (import (rename (except (guile) error raise map string-for-each)
- (log log-internal)
- (euclidean-quotient div)
- (euclidean-remainder mod)
- (euclidean/ div-and-mod)
- (centered-quotient div0)
- (centered-remainder mod0)
- (centered/ div0-and-mod0)
- (inf? infinite?)
- (exact->inexact inexact)
- (inexact->exact exact))
- (srfi srfi-11))
-
- (define string-for-each
- (case-lambda
- ((proc string)
- (let ((end (string-length string)))
- (let loop ((i 0))
- (unless (= i end)
- (proc (string-ref string i))
- (loop (+ i 1))))))
- ((proc string1 string2)
- (let ((end1 (string-length string1))
- (end2 (string-length string2)))
- (unless (= end1 end2)
- (assertion-violation 'string-for-each
- "string arguments must all have the same length"
- string1 string2))
- (let loop ((i 0))
- (unless (= i end1)
- (proc (string-ref string1 i)
- (string-ref string2 i))
- (loop (+ i 1))))))
- ((proc string . strings)
- (let ((end (string-length string))
- (ends (map string-length strings)))
- (for-each (lambda (x)
- (unless (= end x)
- (apply assertion-violation
- 'string-for-each
- "string arguments must all have the same length"
- string strings)))
- ends)
- (let loop ((i 0))
- (unless (= i end)
- (apply proc
- (string-ref string i)
- (map (lambda (s) (string-ref s i)) strings))
- (loop (+ i 1))))))))
-
- (define map
- (case-lambda
- ((f l)
- (let map1 ((hare l) (tortoise l) (move? #f) (out '()))
- (if (pair? hare)
- (if move?
- (if (eq? tortoise hare)
- (scm-error 'wrong-type-arg "map" "Circular list: ~S"
- (list l) #f)
- (map1 (cdr hare) (cdr tortoise) #f
- (cons (f (car hare)) out)))
- (map1 (cdr hare) tortoise #t
- (cons (f (car hare)) out)))
- (if (null? hare)
- (reverse out)
- (scm-error 'wrong-type-arg "map" "Not a list: ~S"
- (list l) #f)))))
-
- ((f l1 l2)
- (let map2 ((h1 l1) (h2 l2) (t1 l1) (t2 l2) (move? #f) (out '()))
- (cond
- ((pair? h1)
- (cond
- ((not (pair? h2))
- (scm-error 'wrong-type-arg "map"
- (if (list? h2)
- "List of wrong length: ~S"
- "Not a list: ~S")
- (list l2) #f))
- ((not move?)
- (map2 (cdr h1) (cdr h2) t1 t2 #t
- (cons (f (car h1) (car h2)) out)))
- ((eq? t1 h1)
- (scm-error 'wrong-type-arg "map" "Circular list: ~S"
- (list l1) #f))
- ((eq? t2 h2)
- (scm-error 'wrong-type-arg "map" "Circular list: ~S"
- (list l2) #f))
- (else
- (map2 (cdr h1) (cdr h2) (cdr t1) (cdr t2) #f
- (cons (f (car h1) (car h2)) out)))))
-
- ((and (null? h1) (null? h2))
- (reverse out))
-
- ((null? h1)
- (scm-error 'wrong-type-arg "map"
- (if (list? h2)
- "List of wrong length: ~S"
- "Not a list: ~S")
- (list l2) #f))
- (else
- (scm-error 'wrong-type-arg "map"
- "Not a list: ~S"
- (list l1) #f)))))
-
- ((f l1 . rest)
- (let ((len (length l1)))
- (let mapn ((rest rest))
- (or (null? rest)
- (if (= (length (car rest)) len)
- (mapn (cdr rest))
- (scm-error 'wrong-type-arg "map" "List of wrong length: ~S"
- (list (car rest)) #f)))))
- (let mapn ((l1 l1) (rest rest) (out '()))
- (if (null? l1)
- (reverse out)
- (mapn (cdr l1) (map cdr rest)
- (cons (apply f (car l1) (map car rest)) out)))))))
-
- (define log
- (case-lambda
- ((n)
- (log-internal n))
- ((n base)
- (/ (log n)
- (log base)))))
-
- (define (boolean=? . bools)
- (define (boolean=?-internal lst last)
- (or (null? lst)
- (let ((bool (car lst)))
- (and (eqv? bool last) (boolean=?-internal (cdr lst) bool)))))
- (or (null? bools)
- (let ((bool (car bools)))
- (and (boolean? bool) (boolean=?-internal (cdr bools) bool)))))
-
- (define (symbol=? . syms)
- (define (symbol=?-internal lst last)
- (or (null? lst)
- (let ((sym (car lst)))
- (and (eq? sym last) (symbol=?-internal (cdr lst) sym)))))
- (or (null? syms)
- (let ((sym (car syms)))
- (and (symbol? sym) (symbol=?-internal (cdr syms) sym)))))
-
- (define (real-valued? x)
- (and (complex? x)
- (zero? (imag-part x))))
-
- (define (rational-valued? x)
- (and (real-valued? x)
- (rational? (real-part x))))
-
- (define (integer-valued? x)
- (and (rational-valued? x)
- (= x (floor (real-part x)))))
-
- (define (vector-for-each proc . vecs)
- (apply for-each (cons proc (map vector->list vecs))))
- (define (vector-map proc . vecs)
- (list->vector (apply map (cons proc (map vector->list vecs)))))
-
- (define-syntax define-proxy
- (syntax-rules (@)
- ;; Define BINDING to point to (@ MODULE ORIGINAL). This hack is to
- ;; make sure MODULE is loaded lazily, at run-time, when BINDING is
- ;; encountered, rather than being loaded while compiling and
- ;; loading (rnrs base).
- ;; This avoids circular dependencies among modules and makes
- ;; (rnrs base) more lightweight.
- ((_ binding (@ module original))
- (define-syntax binding
- (identifier-syntax
- (module-ref (resolve-interface 'module) 'original))))))
-
- (define-proxy raise
- (@ (rnrs exceptions) raise))
-
- (define-proxy condition
- (@ (rnrs conditions) condition))
- (define-proxy make-error
- (@ (rnrs conditions) make-error))
- (define-proxy make-assertion-violation
- (@ (rnrs conditions) make-assertion-violation))
- (define-proxy make-who-condition
- (@ (rnrs conditions) make-who-condition))
- (define-proxy make-message-condition
- (@ (rnrs conditions) make-message-condition))
- (define-proxy make-irritants-condition
- (@ (rnrs conditions) make-irritants-condition))
-
- (define (error who message . irritants)
- (raise (apply condition
- (append (list (make-error))
- (if who (list (make-who-condition who)) '())
- (list (make-message-condition message)
- (make-irritants-condition irritants))))))
-
- (define (assertion-violation who message . irritants)
- (raise (apply condition
- (append (list (make-assertion-violation))
- (if who (list (make-who-condition who)) '())
- (list (make-message-condition message)
- (make-irritants-condition irritants))))))
-
- (define-syntax assert
- (syntax-rules ()
- ((_ expression)
- (or expression
- (raise (condition
- (make-assertion-violation)
- (make-message-condition
- (format #f "assertion failed: ~s" 'expression))))))))
-
-)
-;;;; bytevectors.scm --- R6RS bytevector API -*- coding: utf-8 -*-
-
-;;;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Ludovic Courtès <ludo@gnu.org>
-
-;;; Commentary:
-;;;
-;;; A "bytevector" is a raw bit string. This module provides procedures to
-;;; manipulate bytevectors and interpret their contents in a number of ways:
-;;; bytevector contents can be accessed as signed or unsigned integer of
-;;; various sizes and endianness, as IEEE-754 floating point numbers, or as
-;;; strings. It is a useful tool to decode binary data.
-;;;
-;;; Code:
-
-(define-module (rnrs bytevectors)
- #\version (6)
- #\export-syntax (endianness)
- #\export (native-endianness bytevector?
- make-bytevector bytevector-length bytevector=? bytevector-fill!
- bytevector-copy! bytevector-copy
- uniform-array->bytevector
- bytevector-u8-ref bytevector-s8-ref
- bytevector-u8-set! bytevector-s8-set! bytevector->u8-list
- u8-list->bytevector
- bytevector-uint-ref bytevector-uint-set!
- bytevector-sint-ref bytevector-sint-set!
- bytevector->sint-list bytevector->uint-list
- uint-list->bytevector sint-list->bytevector
-
- bytevector-u16-ref bytevector-s16-ref
- bytevector-u16-set! bytevector-s16-set!
- bytevector-u16-native-ref bytevector-s16-native-ref
- bytevector-u16-native-set! bytevector-s16-native-set!
-
- bytevector-u32-ref bytevector-s32-ref
- bytevector-u32-set! bytevector-s32-set!
- bytevector-u32-native-ref bytevector-s32-native-ref
- bytevector-u32-native-set! bytevector-s32-native-set!
-
- bytevector-u64-ref bytevector-s64-ref
- bytevector-u64-set! bytevector-s64-set!
- bytevector-u64-native-ref bytevector-s64-native-ref
- bytevector-u64-native-set! bytevector-s64-native-set!
-
- bytevector-ieee-single-ref
- bytevector-ieee-single-set!
- bytevector-ieee-single-native-ref
- bytevector-ieee-single-native-set!
-
- bytevector-ieee-double-ref
- bytevector-ieee-double-set!
- bytevector-ieee-double-native-ref
- bytevector-ieee-double-native-set!
-
- string->utf8 string->utf16 string->utf32
- utf8->string utf16->string utf32->string))
-
-
-(load-extension (string-append "libguile-" (effective-version))
- "scm_init_bytevectors")
-
-(define-macro (endianness sym)
- (if (memq sym '(big little))
- `(quote ,sym)
- (error "unsupported endianness" sym)))
-
-;;; bytevector.scm ends here
-;;; conditions.scm --- The R6RS conditions library
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs conditions (6))
- (export &condition
- condition
- simple-conditions
- condition?
- condition-predicate
- condition-accessor
- define-condition-type
-
- &message
- make-message-condition
- message-condition?
- condition-message
-
- &warning
- make-warning
- warning?
-
- &serious
- make-serious-condition
- serious-condition?
-
- &error
- make-error
- error?
-
- &violation
- make-violation
- violation?
-
- &assertion
- make-assertion-violation
- assertion-violation?
-
- &irritants
- make-irritants-condition
- irritants-condition?
- condition-irritants
-
- &who
- make-who-condition
- who-condition?
- condition-who
-
- &non-continuable
- make-non-continuable-violation
- non-continuable-violation?
-
- &implementation-restriction
- make-implementation-restriction-violation
- implementation-restriction-violation?
-
- &lexical
- make-lexical-violation
- lexical-violation?
-
- &syntax
- make-syntax-violation
- syntax-violation?
- syntax-violation-form
- syntax-violation-subform
-
- &undefined
- make-undefined-violation
- undefined-violation?)
- (import (only (guile) and=> @@)
- (rnrs base (6))
- (rnrs lists (6))
- (rnrs records procedural (6)))
-
- (define &compound-condition (make-record-type-descriptor
- '&compound-condition #f #f #f #f
- '#((immutable components))))
- (define compound-condition? (record-predicate &compound-condition))
-
- (define make-compound-condition
- (record-constructor (make-record-constructor-descriptor
- &compound-condition #f #f)))
- (define simple-conditions
- (let ((compound-ref (record-accessor &compound-condition 0)))
- (lambda (condition)
- (cond ((compound-condition? condition)
- (compound-ref condition))
- ((condition-internal? condition)
- (list condition))
- (else
- (assertion-violation 'simple-conditions
- "not a condition"
- condition))))))
-
- (define (condition? obj)
- (or (compound-condition? obj) (condition-internal? obj)))
-
- (define condition
- (lambda conditions
- (define (flatten cond)
- (if (compound-condition? cond) (simple-conditions cond) (list cond)))
- (or (for-all condition? conditions)
- (assertion-violation 'condition "non-condition argument" conditions))
- (if (or (null? conditions) (> (length conditions) 1))
- (make-compound-condition (apply append (map flatten conditions)))
- (car conditions))))
-
- (define-syntax define-condition-type
- (syntax-rules ()
- ((_ condition-type supertype constructor predicate
- (field accessor) ...)
- (letrec-syntax
- ((transform-fields
- (syntax-rules ()
- ((_ (f a) . rest)
- (cons '(immutable f a) (transform-fields . rest)))
- ((_) '())))
-
- (generate-accessors
- (syntax-rules ()
- ((_ counter (f a) . rest)
- (begin (define a
- (condition-accessor
- condition-type
- (record-accessor condition-type counter)))
- (generate-accessors (+ counter 1) . rest)))
- ((_ counter) (begin)))))
- (begin
- (define condition-type
- (make-record-type-descriptor
- 'condition-type supertype #f #f #f
- (list->vector (transform-fields (field accessor) ...))))
- (define constructor
- (record-constructor
- (make-record-constructor-descriptor condition-type #f #f)))
- (define predicate (condition-predicate condition-type))
- (generate-accessors 0 (field accessor) ...))))))
-
- (define &condition (@@ (rnrs records procedural) &condition))
- (define &condition-constructor-descriptor
- (make-record-constructor-descriptor &condition #f #f))
- (define condition-internal? (record-predicate &condition))
-
- (define (condition-predicate rtd)
- (let ((rtd-predicate (record-predicate rtd)))
- (lambda (obj)
- (cond ((compound-condition? obj)
- (exists rtd-predicate (simple-conditions obj)))
- ((condition-internal? obj) (rtd-predicate obj))
- (else #f)))))
-
- (define (condition-accessor rtd proc)
- (let ((rtd-predicate (record-predicate rtd)))
- (lambda (obj)
- (cond ((rtd-predicate obj) (proc obj))
- ((compound-condition? obj)
- (and=> (find rtd-predicate (simple-conditions obj)) proc))
- (else #f)))))
-
- (define-condition-type &message &condition
- make-message-condition message-condition?
- (message condition-message))
-
- (define-condition-type &warning &condition make-warning warning?)
-
- (define &serious (@@ (rnrs records procedural) &serious))
- (define make-serious-condition
- (@@ (rnrs records procedural) make-serious-condition))
- (define serious-condition? (condition-predicate &serious))
-
- (define-condition-type &error &serious make-error error?)
-
- (define &violation (@@ (rnrs records procedural) &violation))
- (define make-violation (@@ (rnrs records procedural) make-violation))
- (define violation? (condition-predicate &violation))
-
- (define &assertion (@@ (rnrs records procedural) &assertion))
- (define make-assertion-violation
- (@@ (rnrs records procedural) make-assertion-violation))
- (define assertion-violation? (condition-predicate &assertion))
-
- (define-condition-type &irritants &condition
- make-irritants-condition irritants-condition?
- (irritants condition-irritants))
-
- (define-condition-type &who &condition
- make-who-condition who-condition?
- (who condition-who))
-
- (define-condition-type &non-continuable &violation
- make-non-continuable-violation
- non-continuable-violation?)
-
- (define-condition-type &implementation-restriction
- &violation
- make-implementation-restriction-violation
- implementation-restriction-violation?)
-
- (define-condition-type &lexical &violation
- make-lexical-violation lexical-violation?)
-
- (define-condition-type &syntax &violation
- make-syntax-violation syntax-violation?
- (form syntax-violation-form)
- (subform syntax-violation-subform))
-
- (define-condition-type &undefined &violation
- make-undefined-violation undefined-violation?)
-
-)
-;;; control.scm --- The R6RS control structures library
-
-;; Copyright (C) 2010, 2012 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs control (6))
- (export when unless do case-lambda)
- (import (only (guile) when unless do case-lambda)))
-;;; enums.scm --- The R6RS enumerations library
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs enums (6))
- (export make-enumeration enum-set-universe enum-set-indexer
- enum-set-constructor enum-set->list enum-set-member? enum-set-subset?
- enum-set=? enum-set-union enum-set-intersection enum-set-difference
- enum-set-complement enum-set-projection define-enumeration)
- (import (only (guile) and=>)
- (rnrs base (6))
- (rnrs conditions (6))
- (rnrs exceptions (6))
- (rnrs records procedural (6))
- (rnrs syntax-case (6))
- (srfi 1))
-
- (define enum-set-rtd (make-record-type-descriptor
- 'enum-set #f #f #f #f '#((mutable universe)
- (immutable set))))
-
- (define make-enum-set
- (record-constructor
- (make-record-constructor-descriptor enum-set-rtd #f #f)))
-
- (define enum-set-universe-internal (record-accessor enum-set-rtd 0))
- (define enum-set-universe-set! (record-mutator enum-set-rtd 0))
-
- (define enum-set-set (record-accessor enum-set-rtd 1))
-
- (define (make-enumeration symbol-list)
- (let ((es (make-enum-set #f symbol-list)))
- (enum-set-universe-set! es es)))
-
- (define (enum-set-universe enum-set)
- (or (enum-set-universe-internal enum-set)
- enum-set))
-
- (define (enum-set-indexer enum-set)
- (let* ((symbols (enum-set->list (enum-set-universe enum-set)))
- (cardinality (length symbols)))
- (lambda (x)
- (and=> (memq x symbols)
- (lambda (probe) (- cardinality (length probe)))))))
-
- (define (enum-set-constructor enum-set)
- (lambda (symbol-list)
- (make-enum-set (enum-set-universe enum-set)
- (list-copy symbol-list))))
-
- (define (enum-set->list enum-set)
- (lset-intersection eq?
- (enum-set-set (enum-set-universe enum-set))
- (enum-set-set enum-set)))
-
- (define (enum-set-member? symbol enum-set)
- (and (memq symbol (enum-set-set enum-set)) #t))
-
- (define (enum-set-subset? enum-set-1 enum-set-2)
- (and (lset<= eq?
- (enum-set-set (enum-set-universe enum-set-1))
- (enum-set-set (enum-set-universe enum-set-2)))
- (lset<= eq? (enum-set-set enum-set-1) (enum-set-set enum-set-2))))
-
- (define (enum-set=? enum-set-1 enum-set-2)
- (and (enum-set-subset? enum-set-1 enum-set-2)
- (enum-set-subset? enum-set-2 enum-set-1)))
-
- (define (enum-set-union enum-set-1 enum-set-2)
- (if (equal? (enum-set-universe enum-set-1)
- (enum-set-universe enum-set-2))
- (make-enum-set (enum-set-universe enum-set-1)
- (lset-union eq?
- (enum-set-set enum-set-1)
- (enum-set-set enum-set-2)))
- (raise (make-assertion-violation))))
-
- (define (enum-set-intersection enum-set-1 enum-set-2)
- (if (equal? (enum-set-universe enum-set-1)
- (enum-set-universe enum-set-2))
- (make-enum-set (enum-set-universe enum-set-1)
- (lset-intersection eq?
- (enum-set-set enum-set-1)
- (enum-set-set enum-set-2)))
- (raise (make-assertion-violation))))
-
- (define (enum-set-difference enum-set-1 enum-set-2)
- (if (equal? (enum-set-universe enum-set-1)
- (enum-set-universe enum-set-2))
- (make-enum-set (enum-set-universe enum-set-1)
- (lset-difference eq?
- (enum-set-set enum-set-1)
- (enum-set-set enum-set-2)))
- (raise (make-assertion-violation))))
-
- (define (enum-set-complement enum-set)
- (let ((universe (enum-set-universe enum-set)))
- (make-enum-set universe
- (lset-difference
- eq? (enum-set->list universe) (enum-set-set enum-set)))))
-
- (define (enum-set-projection enum-set-1 enum-set-2)
- (make-enum-set (enum-set-universe enum-set-2)
- (lset-intersection eq?
- (enum-set-set enum-set-1)
- (enum-set->list
- (enum-set-universe enum-set-2)))))
-
- (define-syntax define-enumeration
- (syntax-rules ()
- ((_ type-name (symbol ...) constructor-syntax)
- (begin
- (define-syntax type-name
- (lambda (s)
- (syntax-case s ()
- ((type-name sym)
- (if (memq (syntax->datum #'sym) '(symbol ...))
- #'(quote sym)
- (syntax-violation (symbol->string 'type-name)
- "not a member of the set"
- #f))))))
- (define-syntax constructor-syntax
- (lambda (s)
- (syntax-case s ()
- ((_ sym (... ...))
- (let* ((universe '(symbol ...))
- (syms (syntax->datum #'(sym (... ...))))
- (quoted-universe
- (datum->syntax s (list 'quote universe)))
- (quoted-syms (datum->syntax s (list 'quote syms))))
- (or (every (lambda (x) (memq x universe)) syms)
- (syntax-violation (symbol->string 'constructor-syntax)
- "not a subset of the universe"
- #f))
- #`((enum-set-constructor (make-enumeration #,quoted-universe))
- #,quoted-syms))))))))))
-)
-;;; eval.scm --- The R6RS `eval' library
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs eval (6))
- (export eval environment)
- (import (only (guile) eval
- make-module
- module-uses
- beautify-user-module!
- set-module-uses!)
- (rnrs base (6))
- (rnrs io simple (6))
- (rnrs lists (6)))
-
- (define (environment . import-specs)
- (let ((module (make-module))
- (needs-purify? (not (member '(guile) import-specs))))
- (beautify-user-module! module)
- (for-each (lambda (import-spec) (eval (list 'import import-spec) module))
- import-specs)
- (if needs-purify? (set-module-uses! module (cdr (module-uses module))))
- module))
-)
-;;; exceptions.scm --- The R6RS exceptions library
-
-;; Copyright (C) 2010, 2011, 2013 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs exceptions (6))
- (export guard with-exception-handler raise raise-continuable)
- (import (rnrs base (6))
- (rnrs control (6))
- (rnrs conditions (6))
- (rnrs records procedural (6))
- (rnrs records inspection (6))
- (only (guile)
- format
- newline
- display
- filter
- acons
- assv-ref
- throw
- set-exception-printer!
- with-throw-handler
- *unspecified*
- @@))
-
- ;; When a native guile exception is caught by an R6RS exception
- ;; handler, we convert it to an R6RS compound condition that includes
- ;; not only the standard condition objects expected by R6RS code, but
- ;; also a special &guile condition that preserves the original KEY and
- ;; ARGS passed to the native Guile catch handler.
-
- (define-condition-type &guile &condition
- make-guile-condition guile-condition?
- (key guile-condition-key)
- (args guile-condition-args))
-
- (define (default-guile-condition-converter key args)
- (condition (make-serious-condition)
- (guile-common-conditions key args)))
-
- (define (guile-common-conditions key args)
- (apply (case-lambda
- ((subr msg margs . _)
- (condition (make-who-condition subr)
- (make-message-condition msg)
- (make-irritants-condition margs)))
- (_ (make-irritants-condition args)))
- args))
-
- (define (convert-guile-condition key args)
- (let ((converter (assv-ref guile-condition-converters key)))
- (condition (or (and converter (converter key args))
- (default-guile-condition-converter key args))
- ;; Preserve the original KEY and ARGS in the R6RS
- ;; condition object.
- (make-guile-condition key args))))
-
- ;; If an R6RS exception handler chooses not to handle a given
- ;; condition, it will re-raise the condition to pass it on to the next
- ;; handler. If the condition was converted from a native Guile
- ;; exception, we must re-raise using the native Guile facilities and
- ;; the original exception KEY and ARGS. We arrange for this in
- ;; 'raise' so that native Guile exception handlers will continue to
- ;; work when mixed with R6RS code.
-
- (define (raise obj)
- (if (guile-condition? obj)
- (apply throw (guile-condition-key obj) (guile-condition-args obj))
- ((@@ (rnrs records procedural) r6rs-raise) obj)))
- (define raise-continuable
- (@@ (rnrs records procedural) r6rs-raise-continuable))
-
- (define raise-object-wrapper?
- (@@ (rnrs records procedural) raise-object-wrapper?))
- (define raise-object-wrapper-obj
- (@@ (rnrs records procedural) raise-object-wrapper-obj))
- (define raise-object-wrapper-continuation
- (@@ (rnrs records procedural) raise-object-wrapper-continuation))
-
- (define (with-exception-handler handler thunk)
- (with-throw-handler #t
- thunk
- (lambda (key . args)
- (cond ((not (eq? key 'r6rs:exception))
- (let ((obj (convert-guile-condition key args)))
- (handler obj)
- (raise (make-non-continuable-violation))))
- ((and (not (null? args))
- (raise-object-wrapper? (car args)))
- (let* ((cargs (car args))
- (obj (raise-object-wrapper-obj cargs))
- (continuation (raise-object-wrapper-continuation cargs))
- (handler-return (handler obj)))
- (if continuation
- (continuation handler-return)
- (raise (make-non-continuable-violation)))))))))
-
- (define-syntax guard0
- (syntax-rules ()
- ((_ (variable cond-clause ...) . body)
- (call/cc (lambda (continuation)
- (with-exception-handler
- (lambda (variable)
- (continuation (cond cond-clause ...)))
- (lambda () . body)))))))
-
- (define-syntax guard
- (syntax-rules (else)
- ((_ (variable cond-clause ... . ((else else-clause ...))) . body)
- (guard0 (variable cond-clause ... (else else-clause ...)) . body))
- ((_ (variable cond-clause ...) . body)
- (guard0 (variable cond-clause ... (else (raise variable))) . body))))
-
- ;;; Exception printing
-
- (define (exception-printer port key args punt)
- (cond ((and (= 1 (length args))
- (raise-object-wrapper? (car args)))
- (let ((obj (raise-object-wrapper-obj (car args))))
- (cond ((condition? obj)
- (display "ERROR: R6RS exception:\n" port)
- (format-condition port obj))
- (else
- (format port "ERROR: R6RS exception: `~s'" obj)))))
- (else
- (punt))))
-
- (define (format-condition port condition)
- (let ((components (simple-conditions condition)))
- (if (null? components)
- (format port "Empty condition object")
- (let loop ((i 1) (components components))
- (cond ((pair? components)
- (format port " ~a. " i)
- (format-simple-condition port (car components))
- (when (pair? (cdr components))
- (newline port))
- (loop (+ i 1) (cdr components))))))))
-
- (define (format-simple-condition port condition)
- (define (print-rtd-fields rtd field-names)
- (let ((n-fields (vector-length field-names)))
- (do ((i 0 (+ i 1)))
- ((>= i n-fields))
- (format port " ~a: ~s"
- (vector-ref field-names i)
- ((record-accessor rtd i) condition))
- (unless (= i (- n-fields 1))
- (newline port)))))
- (let ((condition-name (record-type-name (record-rtd condition))))
- (let loop ((rtd (record-rtd condition))
- (rtd.fields-list '())
- (n-fields 0))
- (cond (rtd
- (let ((field-names (record-type-field-names rtd)))
- (loop (record-type-parent rtd)
- (cons (cons rtd field-names) rtd.fields-list)
- (+ n-fields (vector-length field-names)))))
- (else
- (let ((rtd.fields-list
- (filter (lambda (rtd.fields)
- (not (zero? (vector-length (cdr rtd.fields)))))
- (reverse rtd.fields-list))))
- (case n-fields
- ((0) (format port "~a" condition-name))
- ((1) (format port "~a: ~s"
- condition-name
- ((record-accessor (caar rtd.fields-list) 0)
- condition)))
- (else
- (format port "~a:\n" condition-name)
- (let loop ((lst rtd.fields-list))
- (when (pair? lst)
- (let ((rtd.fields (car lst)))
- (print-rtd-fields (car rtd.fields) (cdr rtd.fields))
- (when (pair? (cdr lst))
- (newline port))
- (loop (cdr lst)))))))))))))
-
- (set-exception-printer! 'r6rs:exception exception-printer)
-
- ;; Guile condition converters
- ;;
- ;; Each converter is a procedure (converter KEY ARGS) that returns
- ;; either an R6RS condition or #f. If #f is returned,
- ;; 'default-guile-condition-converter' will be used.
-
- (define (guile-syntax-violation-converter key args)
- (apply (case-lambda
- ((who what where form subform . extra)
- (condition (make-syntax-violation form subform)
- (make-who-condition who)
- (make-message-condition what)))
- (_ #f))
- args))
-
- (define (guile-lexical-violation-converter key args)
- (condition (make-lexical-violation) (guile-common-conditions key args)))
-
- (define (guile-assertion-violation-converter key args)
- (condition (make-assertion-violation) (guile-common-conditions key args)))
-
- (define (guile-undefined-violation-converter key args)
- (condition (make-undefined-violation) (guile-common-conditions key args)))
-
- (define (guile-implementation-restriction-converter key args)
- (condition (make-implementation-restriction-violation)
- (guile-common-conditions key args)))
-
- (define (guile-error-converter key args)
- (condition (make-error) (guile-common-conditions key args)))
-
- (define (guile-system-error-converter key args)
- (apply (case-lambda
- ((subr msg msg-args errno . rest)
- ;; XXX TODO we should return a more specific error
- ;; (usually an I/O error) as expected by R6RS programs.
- ;; Unfortunately this often requires the 'filename' (or
- ;; other?) which is not currently provided by the native
- ;; Guile exceptions.
- (condition (make-error) (guile-common-conditions key args)))
- (_ (guile-error-converter key args)))
- args))
-
- ;; TODO: Arrange to have the needed information included in native
- ;; Guile I/O exceptions, and arrange here to convert them to the
- ;; proper conditions. Remove the earlier exception conversion
- ;; mechanism: search for 'with-throw-handler' in the 'rnrs'
- ;; tree, e.g. 'with-i/o-filename-conditions' and
- ;; 'with-i/o-port-error' in (rnrs io ports).
-
- ;; XXX TODO: How should we handle the 'misc-error', 'vm-error', and
- ;; 'signal' native Guile exceptions?
-
- ;; XXX TODO: Should we handle the 'quit' exception specially?
-
- ;; An alist mapping native Guile exception keys to converters.
- (define guile-condition-converters
- `((read-error . ,guile-lexical-violation-converter)
- (syntax-error . ,guile-syntax-violation-converter)
- (unbound-variable . ,guile-undefined-violation-converter)
- (wrong-number-of-args . ,guile-assertion-violation-converter)
- (wrong-type-arg . ,guile-assertion-violation-converter)
- (keyword-argument-error . ,guile-assertion-violation-converter)
- (out-of-range . ,guile-assertion-violation-converter)
- (regular-expression-syntax . ,guile-assertion-violation-converter)
- (program-error . ,guile-assertion-violation-converter)
- (goops-error . ,guile-assertion-violation-converter)
- (null-pointer-error . ,guile-assertion-violation-converter)
- (system-error . ,guile-system-error-converter)
- (host-not-found . ,guile-error-converter)
- (getaddrinfo-error . ,guile-error-converter)
- (no-data . ,guile-error-converter)
- (no-recovery . ,guile-error-converter)
- (try-again . ,guile-error-converter)
- (stack-overflow . ,guile-implementation-restriction-converter)
- (numerical-overflow . ,guile-implementation-restriction-converter)
- (memory-allocation-error . ,guile-implementation-restriction-converter)))
-
- (define (set-guile-condition-converter! key proc)
- (set! guile-condition-converters
- (acons key proc guile-condition-converters))))
-;;; files.scm --- The R6RS file system library
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs files (6))
- (export file-exists?
- delete-file
-
- &i/o make-i/o-error i/o-error?
- &i/o-read make-i/o-read-error i/o-read-error?
- &i/o-write make-i/o-write-error i/o-write-error?
-
- &i/o-invalid-position
- make-i/o-invalid-position-error
- i/o-invalid-position-error?
- i/o-error-position
-
- &i/o-filename
- make-i/o-filename-error
- i/o-filename-error?
- i/o-error-filename
-
- &i/o-file-protection
- make-i/o-file-protection-error
- i/o-file-protection-error?
-
- &i/o-file-is-read-only
- make-i/o-file-is-read-only-error
- i/o-file-is-read-only-error?
-
- &i/o-file-already-exists
- make-i/o-file-already-exists-error
- i/o-file-already-exists-error?
-
- &i/o-file-does-not-exist
- make-i/o-file-does-not-exist-error
- i/o-file-does-not-exist-error?
-
- &i/o-port
- make-i/o-port-error
- i/o-port-error?
- i/o-error-port)
-
- (import (rename (only (guile) file-exists? delete-file catch @@)
- (delete-file delete-file-internal))
- (rnrs base (6))
- (rnrs conditions (6))
- (rnrs exceptions (6)))
-
- (define (delete-file filename)
- (catch #t
- (lambda () (delete-file-internal filename))
- (lambda (key . args) (raise (make-i/o-filename-error filename)))))
-
- ;; Condition types that are used by (rnrs files), (rnrs io ports), and
- ;; (rnrs io simple). These are defined here so as to be easily shareable by
- ;; these three libraries.
-
- (define-condition-type &i/o &error make-i/o-error i/o-error?)
- (define-condition-type &i/o-read &i/o make-i/o-read-error i/o-read-error?)
- (define-condition-type &i/o-write &i/o make-i/o-write-error i/o-write-error?)
- (define-condition-type &i/o-invalid-position
- &i/o make-i/o-invalid-position-error i/o-invalid-position-error?
- (position i/o-error-position))
- (define-condition-type &i/o-filename
- &i/o make-i/o-filename-error i/o-filename-error?
- (filename i/o-error-filename))
- (define-condition-type &i/o-file-protection
- &i/o-filename make-i/o-file-protection-error i/o-file-protection-error?)
- (define-condition-type &i/o-file-is-read-only
- &i/o-file-protection make-i/o-file-is-read-only-error
- i/o-file-is-read-only-error?)
- (define-condition-type &i/o-file-already-exists
- &i/o-filename make-i/o-file-already-exists-error
- i/o-file-already-exists-error?)
- (define-condition-type &i/o-file-does-not-exist
- &i/o-filename make-i/o-file-does-not-exist-error
- i/o-file-does-not-exist-error?)
- (define-condition-type &i/o-port &i/o make-i/o-port-error i/o-port-error?
- (port i/o-error-port))
-)
-;;; hashtables.scm --- The R6RS hashtables library
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs hashtables (6))
- (export make-eq-hashtable
- make-eqv-hashtable
- make-hashtable
-
- hashtable?
- hashtable-size
- hashtable-ref
- hashtable-set!
- hashtable-delete!
- hashtable-contains?
- hashtable-update!
- hashtable-copy
- hashtable-clear!
- hashtable-keys
- hashtable-entries
-
- hashtable-equivalence-function
- hashtable-hash-function
- hashtable-mutable?
-
- equal-hash
- string-hash
- string-ci-hash
- symbol-hash)
- (import (rename (only (guile) string-hash-ci
- string-hash
- hashq
- hashv
- modulo
- *unspecified*
- @@)
- (string-hash-ci string-ci-hash))
- (only (ice-9 optargs) define*)
- (rename (only (srfi 69) make-hash-table
- hash
- hash-by-identity
- hash-table-size
- hash-table-ref/default
- hash-table-set!
- hash-table-delete!
- hash-table-exists?
- hash-table-update!/default
- hash-table-copy
- hash-table-equivalence-function
- hash-table-hash-function
- hash-table-keys
- hash-table-fold)
- (hash equal-hash)
- (hash-by-identity symbol-hash))
- (rnrs base (6))
- (rnrs records procedural (6)))
-
- (define r6rs:hashtable
- (make-record-type-descriptor
- 'r6rs:hashtable #f #f #t #t
- '#((mutable wrapped-table)
- (immutable orig-hash-function)
- (immutable mutable))))
-
- (define hashtable? (record-predicate r6rs:hashtable))
- (define make-r6rs-hashtable
- (record-constructor (make-record-constructor-descriptor
- r6rs:hashtable #f #f)))
- (define r6rs:hashtable-wrapped-table (record-accessor r6rs:hashtable 0))
- (define r6rs:hashtable-set-wrapped-table! (record-mutator r6rs:hashtable 0))
- (define r6rs:hashtable-orig-hash-function (record-accessor r6rs:hashtable 1))
- (define r6rs:hashtable-mutable? (record-accessor r6rs:hashtable 2))
-
- (define hashtable-mutable? r6rs:hashtable-mutable?)
-
- (define hash-by-value ((@@ (srfi srfi-69) caller-with-default-size) hashv))
- (define (wrap-hash-function proc)
- (lambda (key capacity) (modulo (proc key) capacity)))
-
- (define* (make-eq-hashtable #\optional k)
- (make-r6rs-hashtable
- (if k (make-hash-table eq? hashq k) (make-hash-table eq? symbol-hash))
- symbol-hash
- #t))
-
- (define* (make-eqv-hashtable #\optional k)
- (make-r6rs-hashtable
- (if k (make-hash-table eqv? hashv k) (make-hash-table eqv? hash-by-value))
- hash-by-value
- #t))
-
- (define* (make-hashtable hash-function equiv #\optional k)
- (let ((wrapped-hash-function (wrap-hash-function hash-function)))
- (make-r6rs-hashtable
- (if k
- (make-hash-table equiv wrapped-hash-function k)
- (make-hash-table equiv wrapped-hash-function))
- hash-function
- #t)))
-
- (define (hashtable-size hashtable)
- (hash-table-size (r6rs:hashtable-wrapped-table hashtable)))
-
- (define (hashtable-ref hashtable key default)
- (hash-table-ref/default
- (r6rs:hashtable-wrapped-table hashtable) key default))
-
- (define (hashtable-set! hashtable key obj)
- (if (r6rs:hashtable-mutable? hashtable)
- (hash-table-set! (r6rs:hashtable-wrapped-table hashtable) key obj))
- *unspecified*)
-
- (define (hashtable-delete! hashtable key)
- (if (r6rs:hashtable-mutable? hashtable)
- (hash-table-delete! (r6rs:hashtable-wrapped-table hashtable) key))
- *unspecified*)
-
- (define (hashtable-contains? hashtable key)
- (hash-table-exists? (r6rs:hashtable-wrapped-table hashtable) key))
-
- (define (hashtable-update! hashtable key proc default)
- (if (r6rs:hashtable-mutable? hashtable)
- (hash-table-update!/default
- (r6rs:hashtable-wrapped-table hashtable) key proc default))
- *unspecified*)
-
- (define* (hashtable-copy hashtable #\optional mutable)
- (make-r6rs-hashtable
- (hash-table-copy (r6rs:hashtable-wrapped-table hashtable))
- (r6rs:hashtable-orig-hash-function hashtable)
- (and mutable #t)))
-
- (define* (hashtable-clear! hashtable #\optional k)
- (if (r6rs:hashtable-mutable? hashtable)
- (let* ((ht (r6rs:hashtable-wrapped-table hashtable))
- (equiv (hash-table-equivalence-function ht))
- (hash-function (r6rs:hashtable-orig-hash-function hashtable))
- (wrapped-hash-function (wrap-hash-function hash-function)))
- (r6rs:hashtable-set-wrapped-table!
- hashtable
- (if k
- (make-hash-table equiv wrapped-hash-function k)
- (make-hash-table equiv wrapped-hash-function)))))
- *unspecified*)
-
- (define (hashtable-keys hashtable)
- (list->vector (hash-table-keys (r6rs:hashtable-wrapped-table hashtable))))
-
- (define (hashtable-entries hashtable)
- (let* ((ht (r6rs:hashtable-wrapped-table hashtable))
- (size (hash-table-size ht))
- (keys (make-vector size))
- (vals (make-vector size)))
- (hash-table-fold (r6rs:hashtable-wrapped-table hashtable)
- (lambda (k v i)
- (vector-set! keys i k)
- (vector-set! vals i v)
- (+ i 1))
- 0)
- (values keys vals)))
-
- (define (hashtable-equivalence-function hashtable)
- (hash-table-equivalence-function (r6rs:hashtable-wrapped-table hashtable)))
-
- (define (hashtable-hash-function hashtable)
- (r6rs:hashtable-orig-hash-function hashtable)))
-;;;; ports.scm --- R6RS port API -*- coding: utf-8 -*-
-
-;;;; Copyright (C) 2009, 2010, 2011, 2013 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Ludovic Courtès <ludo@gnu.org>
-
-;;; Commentary:
-;;;
-;;; The I/O port API of the R6RS is provided by this module. In many areas
-;;; it complements or refines Guile's own historical port API. For instance,
-;;; it allows for binary I/O with bytevectors.
-;;;
-;;; Code:
-
-(library (rnrs io ports (6))
- (export eof-object eof-object?
-
- ;; auxiliary types
- file-options buffer-mode buffer-mode?
- eol-style native-eol-style error-handling-mode
- make-transcoder transcoder-codec transcoder-eol-style
- transcoder-error-handling-mode native-transcoder
- latin-1-codec utf-8-codec utf-16-codec
-
- ;; input & output ports
- port? input-port? output-port?
- port-eof?
- port-transcoder binary-port? textual-port? transcoded-port
- port-position set-port-position!
- port-has-port-position? port-has-set-port-position!?
- call-with-port close-port
-
- ;; input ports
- open-bytevector-input-port
- open-string-input-port
- open-file-input-port
- make-custom-binary-input-port
-
- ;; binary input
- get-u8 lookahead-u8
- get-bytevector-n get-bytevector-n!
- get-bytevector-some get-bytevector-all
-
- ;; output ports
- open-bytevector-output-port
- open-string-output-port
- open-file-output-port
- make-custom-binary-output-port
- call-with-bytevector-output-port
- call-with-string-output-port
- make-custom-textual-output-port
- flush-output-port
-
- ;; input/output ports
- open-file-input/output-port
-
- ;; binary output
- put-u8 put-bytevector
-
- ;; textual input
- get-char get-datum get-line get-string-all get-string-n get-string-n!
- lookahead-char
-
- ;; textual output
- put-char put-datum put-string
-
- ;; standard ports
- standard-input-port standard-output-port standard-error-port
- current-input-port current-output-port current-error-port
-
- ;; condition types
- &i/o i/o-error? make-i/o-error
- &i/o-read i/o-read-error? make-i/o-read-error
- &i/o-write i/o-write-error? make-i/o-write-error
- &i/o-invalid-position i/o-invalid-position-error?
- make-i/o-invalid-position-error
- &i/o-filename i/o-filename-error? make-i/o-filename-error
- i/o-error-filename
- &i/o-file-protection i/o-file-protection-error?
- make-i/o-file-protection-error
- &i/o-file-is-read-only i/o-file-is-read-only-error?
- make-i/o-file-is-read-only-error
- &i/o-file-already-exists i/o-file-already-exists-error?
- make-i/o-file-already-exists-error
- &i/o-file-does-not-exist i/o-file-does-not-exist-error?
- make-i/o-file-does-not-exist-error
- &i/o-port i/o-port-error? make-i/o-port-error
- i/o-error-port
- &i/o-decoding-error i/o-decoding-error?
- make-i/o-decoding-error
- &i/o-encoding-error i/o-encoding-error?
- make-i/o-encoding-error i/o-encoding-error-char)
- (import (ice-9 binary-ports)
- (only (rnrs base) assertion-violation)
- (rnrs enums)
- (rnrs records syntactic)
- (rnrs exceptions)
- (rnrs conditions)
- (rnrs files) ;for the condition types
- (srfi srfi-8)
- (ice-9 rdelim)
- (except (guile) raise display)
- (prefix (only (guile) display)
- guile\:))
-
-
-
-;;;
-;;; Auxiliary types
-;;;
-
-(define-enumeration file-option
- (no-create no-fail no-truncate)
- file-options)
-
-(define-enumeration buffer-mode
- (none line block)
- buffer-modes)
-
-(define (buffer-mode? symbol)
- (enum-set-member? symbol (enum-set-universe (buffer-modes))))
-
-(define-enumeration eol-style
- (lf cr crlf nel crnel ls none)
- eol-styles)
-
-(define (native-eol-style)
- (eol-style none))
-
-(define-enumeration error-handling-mode
- (ignore raise replace)
- error-handling-modes)
-
-(define-record-type (transcoder %make-transcoder transcoder?)
- (fields codec eol-style error-handling-mode))
-
-(define* (make-transcoder codec
- #\optional
- (eol-style (native-eol-style))
- (handling-mode (error-handling-mode replace)))
- (%make-transcoder codec eol-style handling-mode))
-
-(define (native-transcoder)
- (make-transcoder (or (fluid-ref %default-port-encoding)
- (latin-1-codec))))
-
-(define (latin-1-codec)
- "ISO-8859-1")
-
-(define (utf-8-codec)
- "UTF-8")
-
-(define (utf-16-codec)
- "UTF-16")
-
-
-;;;
-;;; Internal helpers
-;;;
-
-(define (with-i/o-filename-conditions filename thunk)
- (with-throw-handler 'system-error
- thunk
- (lambda args
- (let ((errno (system-error-errno args)))
- (let ((construct-condition
- (cond ((= errno EACCES)
- make-i/o-file-protection-error)
- ((= errno EEXIST)
- make-i/o-file-already-exists-error)
- ((= errno ENOENT)
- make-i/o-file-does-not-exist-error)
- ((= errno EROFS)
- make-i/o-file-is-read-only-error)
- (else
- make-i/o-filename-error))))
- (raise (construct-condition filename)))))))
-
-(define (with-i/o-port-error port make-primary-condition thunk)
- (with-throw-handler 'system-error
- thunk
- (lambda args
- (let ((errno (system-error-errno args)))
- (if (memv errno (list EIO EFBIG ENOSPC EPIPE))
- (raise (condition (make-primary-condition)
- (make-i/o-port-error port)))
- (apply throw args))))))
-
-(define-syntax with-textual-output-conditions
- (syntax-rules ()
- ((_ port body0 body ...)
- (with-i/o-port-error port make-i/o-write-error
- (lambda () (with-i/o-encoding-error body0 body ...))))))
-
-(define-syntax with-textual-input-conditions
- (syntax-rules ()
- ((_ port body0 body ...)
- (with-i/o-port-error port make-i/o-read-error
- (lambda () (with-i/o-decoding-error body0 body ...))))))
-
-
-;;;
-;;; Input and output ports.
-;;;
-
-(define (port-transcoder port)
- "Return the transcoder object associated with @var{port}, or @code{#f}
-if the port has no transcoder."
- (cond ((port-encoding port)
- => (lambda (encoding)
- (make-transcoder
- encoding
- (native-eol-style)
- (case (port-conversion-strategy port)
- ((error) 'raise)
- ((substitute) 'replace)
- (else
- (assertion-violation 'port-transcoder
- "unsupported error handling mode"))))))
- (else
- #f)))
-
-(define (binary-port? port)
- "Returns @code{#t} if @var{port} does not have an associated encoding,
-@code{#f} otherwise."
- (not (port-encoding port)))
-
-(define (textual-port? port)
- "Always returns @code{#t}, as all ports can be used for textual I/O in
-Guile."
- #t)
-
-(define (port-eof? port)
- (eof-object? (if (binary-port? port)
- (lookahead-u8 port)
- (lookahead-char port))))
-
-(define (transcoded-port port transcoder)
- "Return a new textual port based on @var{port}, using
-@var{transcoder} to encode and decode data written to or
-read from its underlying binary port @var{port}."
- ;; Hackily get at %make-transcoded-port.
- (let ((result ((@@ (ice-9 binary-ports) %make-transcoded-port) port)))
- (set-port-encoding! result (transcoder-codec transcoder))
- (case (transcoder-error-handling-mode transcoder)
- ((raise)
- (set-port-conversion-strategy! result 'error))
- ((replace)
- (set-port-conversion-strategy! result 'substitute))
- (else
- (error "unsupported error handling mode"
- (transcoder-error-handling-mode transcoder))))
- result))
-
-(define (port-position port)
- "Return the offset (an integer) indicating where the next octet will be
-read from/written to in @var{port}."
-
- ;; FIXME: We should raise an `&assertion' error when not supported.
- (seek port 0 SEEK_CUR))
-
-(define (set-port-position! port offset)
- "Set the position where the next octet will be read from/written to
-@var{port}."
-
- ;; FIXME: We should raise an `&assertion' error when not supported.
- (seek port offset SEEK_SET))
-
-(define (port-has-port-position? port)
- "Return @code{#t} is @var{port} supports @code{port-position}."
- (and (false-if-exception (port-position port)) #t))
-
-(define (port-has-set-port-position!? port)
- "Return @code{#t} is @var{port} supports @code{set-port-position!}."
- (and (false-if-exception (set-port-position! port (port-position port)))
- #t))
-
-(define (call-with-port port proc)
- "Call @var{proc}, passing it @var{port} and closing @var{port} upon exit of
-@var{proc}. Return the return values of @var{proc}."
- (call-with-values
- (lambda () (proc port))
- (lambda vals
- (close-port port)
- (apply values vals))))
-
-(define* (call-with-bytevector-output-port proc #\optional (transcoder #f))
- (receive (port extract) (open-bytevector-output-port transcoder)
- (call-with-port port proc)
- (extract)))
-
-(define (open-string-input-port str)
- "Open an input port that will read from @var{str}."
- (with-fluids ((%default-port-encoding "UTF-8"))
- (open-input-string str)))
-
-(define (r6rs-open filename mode buffer-mode transcoder)
- (let ((port (with-i/o-filename-conditions filename
- (lambda ()
- (with-fluids ((%default-port-encoding #f))
- (open filename mode))))))
- (cond (transcoder
- (set-port-encoding! port (transcoder-codec transcoder))))
- port))
-
-(define (file-options->mode file-options base-mode)
- (logior base-mode
- (if (enum-set-member? 'no-create file-options)
- 0
- O_CREAT)
- (if (enum-set-member? 'no-truncate file-options)
- 0
- O_TRUNC)
- (if (enum-set-member? 'no-fail file-options)
- 0
- O_EXCL)))
-
-(define* (open-file-input-port filename
- #\optional
- (file-options (file-options))
- (buffer-mode (buffer-mode block))
- transcoder)
- "Return an input port for reading from @var{filename}."
- (r6rs-open filename O_RDONLY buffer-mode transcoder))
-
-(define* (open-file-input/output-port filename
- #\optional
- (file-options (file-options))
- (buffer-mode (buffer-mode block))
- transcoder)
- "Return a port for reading from and writing to @var{filename}."
- (r6rs-open filename
- (file-options->mode file-options O_RDWR)
- buffer-mode
- transcoder))
-
-(define (open-string-output-port)
- "Return two values: an output port that will collect characters written to it
-as a string, and a thunk to retrieve the characters associated with that port."
- (let ((port (with-fluids ((%default-port-encoding "UTF-8"))
- (open-output-string))))
- (values port
- (lambda ()
- (let ((s (get-output-string port)))
- (seek port 0 SEEK_SET)
- (truncate-file port 0)
- s)))))
-
-(define* (open-file-output-port filename
- #\optional
- (file-options (file-options))
- (buffer-mode (buffer-mode block))
- maybe-transcoder)
- "Return an output port for writing to @var{filename}."
- (r6rs-open filename
- (file-options->mode file-options O_WRONLY)
- buffer-mode
- maybe-transcoder))
-
-(define (call-with-string-output-port proc)
- "Call @var{proc}, passing it a string output port. When @var{proc} returns,
-return the characters accumulated in that port."
- (let ((port (open-output-string)))
- (proc port)
- (get-output-string port)))
-
-(define (make-custom-textual-output-port id
- write!
- get-position
- set-position!
- close)
- (make-soft-port (vector (lambda (c) (write! (string c) 0 1))
- (lambda (s) (write! s 0 (string-length s)))
- #f ;flush
- #f ;read character
- close)
- "w"))
-
-(define (flush-output-port port)
- (force-output port))
-
-
-;;;
-;;; Textual output.
-;;;
-
-(define-condition-type &i/o-encoding &i/o-port
- make-i/o-encoding-error i/o-encoding-error?
- (char i/o-encoding-error-char))
-
-(define-syntax with-i/o-encoding-error
- (syntax-rules ()
- "Convert Guile throws to `encoding-error' to `&i/o-encoding-error'."
- ((_ body ...)
- ;; XXX: This is heavyweight for small functions like `put-char'.
- (with-throw-handler 'encoding-error
- (lambda ()
- (begin body ...))
- (lambda (key subr message errno port chr)
- (raise (make-i/o-encoding-error port chr)))))))
-
-(define (put-char port char)
- (with-textual-output-conditions port (write-char char port)))
-
-(define (put-datum port datum)
- (with-textual-output-conditions port (write datum port)))
-
-(define* (put-string port s #\optional start count)
- (with-textual-output-conditions port
- (cond ((not (string? s))
- (assertion-violation 'put-string "expected string" s))
- ((and start count)
- (display (substring/shared s start (+ start count)) port))
- (start
- (display (substring/shared s start (string-length s)) port))
- (else
- (display s port)))))
-
-;; Defined here to be able to make use of `with-i/o-encoding-error', but
-;; not exported from here, but from `(rnrs io simple)'.
-(define* (display object #\optional (port (current-output-port)))
- (with-textual-output-conditions port (guile:display object port)))
-
-
-;;;
-;;; Textual input.
-;;;
-
-(define-condition-type &i/o-decoding &i/o-port
- make-i/o-decoding-error i/o-decoding-error?)
-
-(define-syntax with-i/o-decoding-error
- (syntax-rules ()
- "Convert Guile throws to `decoding-error' to `&i/o-decoding-error'."
- ((_ body ...)
- ;; XXX: This is heavyweight for small functions like `get-char' and
- ;; `lookahead-char'.
- (with-throw-handler 'decoding-error
- (lambda ()
- (begin body ...))
- (lambda (key subr message errno port)
- (raise (make-i/o-decoding-error port)))))))
-
-(define (get-char port)
- (with-textual-input-conditions port (read-char port)))
-
-(define (get-datum port)
- (with-textual-input-conditions port (read port)))
-
-(define (get-line port)
- (with-textual-input-conditions port (read-line port 'trim)))
-
-(define (get-string-all port)
- (with-textual-input-conditions port (read-string port)))
-
-(define (get-string-n port count)
- "Read up to @var{count} characters from @var{port}.
-If no characters could be read before encountering the end of file,
-return the end-of-file object, otherwise return a string containing
-the characters read."
- (let* ((s (make-string count))
- (rv (get-string-n! port s 0 count)))
- (cond ((eof-object? rv) rv)
- ((= rv count) s)
- (else (substring/shared s 0 rv)))))
-
-(define (lookahead-char port)
- (with-textual-input-conditions port (peek-char port)))
-
-
-;;;
-;;; Standard ports.
-;;;
-
-(define (standard-input-port)
- (with-fluids ((%default-port-encoding #f))
- (dup->inport 0)))
-
-(define (standard-output-port)
- (with-fluids ((%default-port-encoding #f))
- (dup->outport 1)))
-
-(define (standard-error-port)
- (with-fluids ((%default-port-encoding #f))
- (dup->outport 2)))
-
-)
-
-;;; ports.scm ends here
-;;; simple.scm --- The R6RS simple I/O library
-
-;; Copyright (C) 2010, 2011, 2014 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs io simple (6))
- (export eof-object
- eof-object?
-
- call-with-input-file
- call-with-output-file
-
- input-port?
- output-port?
-
- current-input-port
- current-output-port
- current-error-port
-
- with-input-from-file
- with-output-to-file
-
- open-input-file
- open-output-file
-
- close-input-port
- close-output-port
-
- read-char
- peek-char
- read
- write-char
- newline
- display
- write
-
- &i/o make-i/o-error i/o-error?
- &i/o-read make-i/o-read-error i/o-read-error?
- &i/o-write make-i/o-write-error i/o-write-error?
-
- &i/o-invalid-position
- make-i/o-invalid-position-error
- i/o-invalid-position-error?
- i/o-error-position
-
- &i/o-filename
- make-i/o-filename-error
- i/o-filename-error?
- i/o-error-filename
-
- &i/o-file-protection
- make-i/o-file-protection-error
- i/o-file-protection-error?
-
- &i/o-file-is-read-only
- make-i/o-file-is-read-only-error
- i/o-file-is-read-only-error?
-
- &i/o-file-already-exists
- make-i/o-file-already-exists-error
- i/o-file-already-exists-error?
-
- &i/o-file-does-not-exist
- make-i/o-file-does-not-exist-error
- i/o-file-does-not-exist-error?
-
- &i/o-port
- make-i/o-port-error
- i/o-port-error?
- i/o-error-port)
-
- (import (only (rnrs io ports)
- call-with-port
- close-port
- open-file-input-port
- open-file-output-port
- eof-object
- eof-object?
- file-options
- buffer-mode
- native-transcoder
- get-char
- lookahead-char
- get-datum
- put-char
- put-datum
-
- input-port?
- output-port?)
- (only (guile)
- @@
- current-input-port
- current-output-port
- current-error-port
-
- define*
-
- with-input-from-port
- with-output-to-port)
- (rnrs base (6))
- (rnrs files (6)) ;for the condition types
- )
-
- (define display (@@ (rnrs io ports) display))
-
- (define (call-with-input-file filename proc)
- (call-with-port (open-file-input-port filename) proc))
-
- (define (call-with-output-file filename proc)
- (call-with-port (open-file-output-port filename) proc))
-
- (define (with-input-from-file filename thunk)
- (call-with-input-file filename
- (lambda (port) (with-input-from-port port thunk))))
-
- (define (with-output-to-file filename thunk)
- (call-with-output-file filename
- (lambda (port) (with-output-to-port port thunk))))
-
- (define (open-input-file filename)
- (open-file-input-port filename
- (file-options)
- (buffer-mode block)
- (native-transcoder)))
-
- (define (open-output-file filename)
- (open-file-output-port filename
- (file-options)
- (buffer-mode block)
- (native-transcoder)))
-
- (define close-input-port close-port)
- (define close-output-port close-port)
-
- (define* (read-char #\optional (port (current-input-port)))
- (get-char port))
-
- (define* (peek-char #\optional (port (current-input-port)))
- (lookahead-char port))
-
- (define* (read #\optional (port (current-input-port)))
- (get-datum port))
-
- (define* (write-char char #\optional (port (current-output-port)))
- (put-char port char))
-
- (define* (newline #\optional (port (current-output-port)))
- (put-char port #\newline))
-
- (define* (write object #\optional (port (current-output-port)))
- (put-datum port object))
-
- )
-;;; lists.scm --- The R6RS list utilities library
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs lists (6))
- (export find for-all exists filter partition fold-left fold-right remp remove
- remv remq memp member memv memq assp assoc assv assq cons*)
- (import (rnrs base (6))
- (only (guile) filter member memv memq assoc assv assq cons*)
- (rename (only (srfi srfi-1) any
- every
- remove
- member
- assoc
- find
- partition
- fold-right
- filter-map)
- (any exists)
- (every for-all)
- (remove remp)
-
- (member memp-internal)
- (assoc assp-internal)))
-
- (define (fold-left combine nil list . lists)
- (define (fold nil lists)
- (if (exists null? lists)
- nil
- (fold (apply combine nil (map car lists))
- (map cdr lists))))
- (fold nil (cons list lists)))
-
- (define (remove obj list) (remp (lambda (elt) (equal? obj elt)) list))
- (define (remv obj list) (remp (lambda (elt) (eqv? obj elt)) list))
- (define (remq obj list) (remp (lambda (elt) (eq? obj elt)) list))
-
- (define (memp pred list) (memp-internal #f list (lambda (x y) (pred y))))
- (define (assp pred list) (assp-internal #f list (lambda (x y) (pred y))))
-)
-;;; mutable-pairs.scm --- The R6RS mutable pair library
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-
-(library (rnrs mutable-pairs (6))
- (export set-car! set-cdr!)
- (import (only (guile) set-car! set-cdr!)))
-;;; mutable-strings.scm --- The R6RS mutable string library
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-
-(library (rnrs mutable-strings (6))
- (export string-set! string-fill!)
- (import (only (guile) string-set! string-fill!)))
-;;; programs.scm --- The R6RS process management library
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs programs (6))
- (export command-line exit)
- (import (only (guile) command-line exit)))
-;;; r5rs.scm --- The R6RS / R5RS compatibility library
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs r5rs (6))
- (export exact->inexact inexact->exact
-
- quotient remainder modulo
-
- delay force
-
- null-environment scheme-report-environment)
- (import (only (guile) exact->inexact inexact->exact
-
- quotient remainder modulo
-
- delay force)
- (only (ice-9 r5rs) scheme-report-environment)
- (only (ice-9 safe-r5rs) null-environment)))
-;;; inspection.scm --- Inspection support for R6RS records
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs records inspection (6))
- (export record?
- record-rtd
- record-type-name
- record-type-parent
- record-type-uid
- record-type-generative?
- record-type-sealed?
- record-type-opaque?
- record-type-field-names
- record-field-mutable?)
- (import (rnrs arithmetic bitwise (6))
- (rnrs base (6))
- (rnrs records procedural (6))
- (only (guile) struct-ref struct-vtable vtable-index-layout @@))
-
- (define record-internal? (@@ (rnrs records procedural) record-internal?))
-
- (define rtd-index-name (@@ (rnrs records procedural) rtd-index-name))
- (define rtd-index-parent (@@ (rnrs records procedural) rtd-index-parent))
- (define rtd-index-uid (@@ (rnrs records procedural) rtd-index-uid))
- (define rtd-index-sealed? (@@ (rnrs records procedural) rtd-index-sealed?))
- (define rtd-index-opaque? (@@ (rnrs records procedural) rtd-index-opaque?))
- (define rtd-index-field-names
- (@@ (rnrs records procedural) rtd-index-field-names))
- (define rtd-index-field-bit-field
- (@@ (rnrs records procedural) rtd-index-field-bit-field))
-
- (define (record? obj)
- (and (record-internal? obj)
- (not (record-type-opaque? (struct-vtable obj)))))
-
- (define (record-rtd record)
- (or (and (record-internal? record)
- (let ((rtd (struct-vtable record)))
- (and (not (struct-ref rtd rtd-index-opaque?)) rtd)))
- (assertion-violation 'record-rtd "not a record" record)))
-
- (define (guarantee-rtd who rtd)
- (if (record-type-descriptor? rtd)
- rtd
- (assertion-violation who "not a record type descriptor" rtd)))
-
- (define (record-type-name rtd)
- (struct-ref (guarantee-rtd 'record-type-name rtd) rtd-index-name))
- (define (record-type-parent rtd)
- (struct-ref (guarantee-rtd 'record-type-parent rtd) rtd-index-parent))
- (define (record-type-uid rtd)
- (struct-ref (guarantee-rtd 'record-type-uid rtd) rtd-index-uid))
- (define (record-type-generative? rtd)
- (not (record-type-uid (guarantee-rtd 'record-type-generative? rtd))))
- (define (record-type-sealed? rtd)
- (struct-ref (guarantee-rtd 'record-type-sealed? rtd) rtd-index-sealed?))
- (define (record-type-opaque? rtd)
- (struct-ref (guarantee-rtd 'record-type-opaque? rtd) rtd-index-opaque?))
- (define (record-type-field-names rtd)
- (struct-ref (guarantee-rtd 'record-type-field-names rtd) rtd-index-field-names))
- (define (record-field-mutable? rtd k)
- (bitwise-bit-set? (struct-ref (guarantee-rtd 'record-field-mutable? rtd)
- rtd-index-field-bit-field)
- k))
-)
-;;; procedural.scm --- Procedural interface to R6RS records
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs records procedural (6))
- (export make-record-type-descriptor
- record-type-descriptor?
- make-record-constructor-descriptor
-
- record-constructor
- record-predicate
- record-accessor
- record-mutator)
-
- (import (rnrs base (6))
- (only (guile) cons*
- logand
- logior
- ash
-
- and=>
- throw
- display
- make-struct
- make-vtable
- map
- simple-format
- string-append
- symbol-append
-
- struct?
- struct-layout
- struct-ref
- struct-set!
- struct-vtable
- vtable-index-layout
-
- make-hash-table
- hashq-ref
- hashq-set!
-
- vector->list)
- (ice-9 receive)
- (only (srfi 1) fold split-at take))
-
- (define (record-internal? obj)
- (and (struct? obj) (record-type-descriptor? (struct-vtable obj))))
-
- (define rtd-index-name 8)
- (define rtd-index-uid 9)
- (define rtd-index-parent 10)
- (define rtd-index-sealed? 11)
- (define rtd-index-opaque? 12)
- (define rtd-index-predicate 13)
- (define rtd-index-field-names 14)
- (define rtd-index-field-bit-field 15)
- (define rtd-index-field-binder 16)
-
- (define rctd-index-rtd 0)
- (define rctd-index-parent 1)
- (define rctd-index-protocol 2)
-
- (define vtable-base-layout (symbol->string (struct-layout (make-vtable ""))))
-
- (define record-type-vtable
- (make-vtable (string-append vtable-base-layout "prprprprprprprprprpr")
- (lambda (obj port)
- (simple-format port "#<r6rs:record-type:~A>"
- (struct-ref obj rtd-index-name)))))
-
- (define record-constructor-vtable
- (make-vtable "prprpr"
- (lambda (obj port)
- (simple-format port "#<r6rs:record-constructor:~A>"
- (struct-ref (struct-ref obj rctd-index-rtd)
- rtd-index-name)))))
-
- (define uid-table (make-hash-table))
-
- (define (make-record-type-descriptor name parent uid sealed? opaque? fields)
- (define fields-pair
- (let loop ((field-list (vector->list fields))
- (layout-sym 'pr)
- (layout-bit-field 0)
- (counter 0))
- (if (null? field-list)
- (cons layout-sym layout-bit-field)
- (case (caar field-list)
- ((immutable)
- (loop (cdr field-list)
- (symbol-append layout-sym 'pr)
- layout-bit-field
- (+ counter 1)))
- ((mutable)
- (loop (cdr field-list)
- (symbol-append layout-sym 'pw)
- (logior layout-bit-field (ash 1 counter))
- (+ counter 1)))
- (else (r6rs-raise (make-assertion-violation)))))))
-
- (define fields-layout (car fields-pair))
- (define fields-bit-field (cdr fields-pair))
-
- (define field-names (list->vector (map cadr (vector->list fields))))
- (define late-rtd #f)
-
- (define (private-record-predicate obj)
- (and (record-internal? obj)
- (or (eq? (struct-vtable obj) late-rtd)
- (and=> (struct-ref obj 0) private-record-predicate))))
-
- (define (field-binder parent-struct . args)
- (apply make-struct (cons* late-rtd 0 parent-struct args)))
-
- (if (and parent (struct-ref parent rtd-index-sealed?))
- (r6rs-raise (make-assertion-violation)))
-
- (let ((matching-rtd (and uid (hashq-ref uid-table uid)))
- (opaque? (or opaque? (and parent (struct-ref
- parent rtd-index-opaque?)))))
- (if matching-rtd
- (if (equal? (list name
- parent
- sealed?
- opaque?
- field-names
- fields-bit-field)
- (list (struct-ref matching-rtd rtd-index-name)
- (struct-ref matching-rtd rtd-index-parent)
- (struct-ref matching-rtd rtd-index-sealed?)
- (struct-ref matching-rtd rtd-index-opaque?)
- (struct-ref matching-rtd rtd-index-field-names)
- (struct-ref matching-rtd
- rtd-index-field-bit-field)))
- matching-rtd
- (r6rs-raise (make-assertion-violation)))
-
- (let ((rtd (make-struct record-type-vtable 0
-
- fields-layout
- (lambda (obj port)
- (simple-format
- port "#<r6rs:record:~A>" name))
-
- name
- uid
- parent
- sealed?
- opaque?
-
- private-record-predicate
- field-names
- fields-bit-field
- field-binder)))
- (set! late-rtd rtd)
- (if uid (hashq-set! uid-table uid rtd))
- rtd))))
-
- (define (record-type-descriptor? obj)
- (and (struct? obj) (eq? (struct-vtable obj) record-type-vtable)))
-
- (define (make-record-constructor-descriptor rtd
- parent-constructor-descriptor
- protocol)
- (define rtd-arity (vector-length (struct-ref rtd rtd-index-field-names)))
- (define (default-inherited-protocol n)
- (lambda args
- (receive
- (n-args p-args)
- (split-at args (- (length args) rtd-arity))
- (let ((p (apply n n-args)))
- (apply p p-args)))))
- (define (default-protocol p) p)
-
- (let* ((prtd (struct-ref rtd rtd-index-parent))
- (pcd (or parent-constructor-descriptor
- (and=> prtd (lambda (d) (make-record-constructor-descriptor
- prtd #f #f)))))
- (prot (or protocol (if pcd
- default-inherited-protocol
- default-protocol))))
- (make-struct record-constructor-vtable 0 rtd pcd prot)))
-
- (define (record-constructor rctd)
- (let* ((rtd (struct-ref rctd rctd-index-rtd))
- (parent-rctd (struct-ref rctd rctd-index-parent))
- (protocol (struct-ref rctd rctd-index-protocol)))
- (protocol
- (if parent-rctd
- (let ((parent-record-constructor (record-constructor parent-rctd))
- (parent-rtd (struct-ref parent-rctd rctd-index-rtd)))
- (lambda args
- (let ((struct (apply parent-record-constructor args)))
- (lambda args
- (apply (struct-ref rtd rtd-index-field-binder)
- (cons struct args))))))
- (lambda args (apply (struct-ref rtd rtd-index-field-binder)
- (cons #f args)))))))
-
- (define (record-predicate rtd) (struct-ref rtd rtd-index-predicate))
-
- (define (record-accessor rtd k)
- (define (record-accessor-inner obj)
- (if (eq? (struct-vtable obj) rtd)
- (struct-ref obj (+ k 1))
- (and=> (struct-ref obj 0) record-accessor-inner)))
- (lambda (obj)
- (if (not (record-internal? obj))
- (r6rs-raise (make-assertion-violation)))
- (record-accessor-inner obj)))
-
- (define (record-mutator rtd k)
- (define (record-mutator-inner obj val)
- (and obj (or (and (eq? (struct-vtable obj) rtd)
- (struct-set! obj (+ k 1) val))
- (record-mutator-inner (struct-ref obj 0) val))))
- (let ((bit-field (struct-ref rtd rtd-index-field-bit-field)))
- (if (zero? (logand bit-field (ash 1 k)))
- (r6rs-raise (make-assertion-violation))))
- (lambda (obj val) (record-mutator-inner obj val)))
-
- ;; Condition types that are used in the current library. These are defined
- ;; here and not in (rnrs conditions) to avoid a circular dependency.
-
- (define &condition (make-record-type-descriptor '&condition #f #f #f #f '#()))
- (define &condition-constructor-descriptor
- (make-record-constructor-descriptor &condition #f #f))
-
- (define &serious (make-record-type-descriptor
- '&serious &condition #f #f #f '#()))
- (define &serious-constructor-descriptor
- (make-record-constructor-descriptor
- &serious &condition-constructor-descriptor #f))
-
- (define make-serious-condition
- (record-constructor &serious-constructor-descriptor))
-
- (define &violation (make-record-type-descriptor
- '&violation &serious #f #f #f '#()))
- (define &violation-constructor-descriptor
- (make-record-constructor-descriptor
- &violation &serious-constructor-descriptor #f))
- (define make-violation (record-constructor &violation-constructor-descriptor))
-
- (define &assertion (make-record-type-descriptor
- '&assertion &violation #f #f #f '#()))
- (define make-assertion-violation
- (record-constructor
- (make-record-constructor-descriptor
- &assertion &violation-constructor-descriptor #f)))
-
- ;; Exception wrapper type, along with a wrapping `throw' implementation.
- ;; These are used in the current library, and so they are defined here and not
- ;; in (rnrs exceptions) to avoid a circular dependency.
-
- (define &raise-object-wrapper
- (make-record-type-descriptor '&raise-object-wrapper #f #f #f #f
- '#((immutable obj) (immutable continuation))))
- (define make-raise-object-wrapper
- (record-constructor (make-record-constructor-descriptor
- &raise-object-wrapper #f #f)))
- (define raise-object-wrapper? (record-predicate &raise-object-wrapper))
- (define raise-object-wrapper-obj (record-accessor &raise-object-wrapper 0))
- (define raise-object-wrapper-continuation
- (record-accessor &raise-object-wrapper 1))
-
- (define (r6rs-raise obj)
- (throw 'r6rs:exception (make-raise-object-wrapper obj #f)))
- (define (r6rs-raise-continuable obj)
- (define (r6rs-raise-continuable-internal continuation)
- (throw 'r6rs:exception (make-raise-object-wrapper obj continuation)))
- (call/cc r6rs-raise-continuable-internal))
-)
-;;; syntactic.scm --- Syntactic support for R6RS records
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs records syntactic (6))
- (export define-record-type
- record-type-descriptor
- record-constructor-descriptor)
- (import (only (guile) and=> gensym)
- (rnrs base (6))
- (rnrs conditions (6))
- (rnrs exceptions (6))
- (rnrs hashtables (6))
- (rnrs lists (6))
- (rnrs records procedural (6))
- (rnrs syntax-case (6))
- (only (srfi 1) take))
-
- (define record-type-registry (make-eq-hashtable))
-
- (define (guess-constructor-name record-name)
- (string->symbol (string-append "make-" (symbol->string record-name))))
- (define (guess-predicate-name record-name)
- (string->symbol (string-append (symbol->string record-name) "?")))
- (define (register-record-type name rtd rcd)
- (hashtable-set! record-type-registry name (cons rtd rcd)))
- (define (lookup-record-type-descriptor name)
- (and=> (hashtable-ref record-type-registry name #f) car))
- (define (lookup-record-constructor-descriptor name)
- (and=> (hashtable-ref record-type-registry name #f) cdr))
-
- (define-syntax define-record-type
- (lambda (stx)
- (syntax-case stx ()
- ((_ (record-name constructor-name predicate-name) record-clause ...)
- #'(define-record-type0
- (record-name constructor-name predicate-name)
- record-clause ...))
- ((_ record-name record-clause ...)
- (let* ((record-name-sym (syntax->datum #'record-name))
- (constructor-name
- (datum->syntax
- #'record-name (guess-constructor-name record-name-sym)))
- (predicate-name
- (datum->syntax
- #'record-name (guess-predicate-name record-name-sym))))
- #`(define-record-type0
- (record-name #,constructor-name #,predicate-name)
- record-clause ...))))))
-
- (define (sequence n)
- (define (seq-inner n) (if (= n 0) '(0) (cons n (seq-inner (- n 1)))))
- (reverse (seq-inner n)))
- (define (number-fields fields)
- (define (number-fields-inner fields counter)
- (if (null? fields)
- '()
- (cons (cons fields counter)
- (number-fields-inner (cdr fields) (+ counter 1)))))
- (number-fields-inner fields 0))
-
- (define (process-fields record-name fields)
- (define (wrap x) (datum->syntax record-name x))
- (define (id->string x)
- (symbol->string (syntax->datum x)))
- (define record-name-str (id->string record-name))
- (define (guess-accessor-name field-name)
- (wrap
- (string->symbol (string-append
- record-name-str "-" (id->string field-name)))))
- (define (guess-mutator-name field-name)
- (wrap
- (string->symbol
- (string-append
- record-name-str "-" (id->string field-name) "-set!"))))
- (define (f x)
- (syntax-case x (immutable mutable)
- [(immutable name)
- (list (wrap `(immutable ,(syntax->datum #'name)))
- (guess-accessor-name #'name)
- #f)]
- [(immutable name accessor)
- (list (wrap `(immutable ,(syntax->datum #'name))) #'accessor #f)]
- [(mutable name)
- (list (wrap `(mutable ,(syntax->datum #'name)))
- (guess-accessor-name #'name)
- (guess-mutator-name #'name))]
- [(mutable name accessor mutator)
- (list (wrap `(mutable ,(syntax->datum #'name))) #'accessor #'mutator)]
- [name
- (identifier? #'name)
- (list (wrap `(immutable ,(syntax->datum #'name)))
- (guess-accessor-name #'name)
- #f)]
- [else
- (syntax-violation 'define-record-type "invalid field specifier" x)]))
- (map f fields))
-
- (define-syntax define-record-type0
- (lambda (stx)
- (define *unspecified* (cons #f #f))
- (define (unspecified? obj)
- (eq? *unspecified* obj))
- (syntax-case stx ()
- ((_ (record-name constructor-name predicate-name) record-clause ...)
- (let loop ((_fields *unspecified*)
- (_parent *unspecified*)
- (_protocol *unspecified*)
- (_sealed *unspecified*)
- (_opaque *unspecified*)
- (_nongenerative *unspecified*)
- (_constructor *unspecified*)
- (_parent-rtd *unspecified*)
- (record-clauses #'(record-clause ...)))
- (syntax-case record-clauses
- (fields parent protocol sealed opaque nongenerative
- constructor parent-rtd)
- [()
- (let* ((fields (if (unspecified? _fields) '() _fields))
- (field-names (list->vector (map car fields)))
- (field-accessors
- (fold-left (lambda (lst x c)
- (cons #`(define #,(cadr x)
- (record-accessor record-name #,c))
- lst))
- '() fields (sequence (length fields))))
- (field-mutators
- (fold-left (lambda (lst x c)
- (if (caddr x)
- (cons #`(define #,(caddr x)
- (record-mutator record-name
- #,c))
- lst)
- lst))
- '() fields (sequence (length fields))))
- (parent-cd (cond ((not (unspecified? _parent))
- #`(record-constructor-descriptor
- #,_parent))
- ((not (unspecified? _parent-rtd))
- (cadr _parent-rtd))
- (else #f)))
- (parent-rtd (cond ((not (unspecified? _parent))
- #`(record-type-descriptor #,_parent))
- ((not (unspecified? _parent-rtd))
- (car _parent-rtd))
- (else #f)))
- (protocol (if (unspecified? _protocol) #f _protocol))
- (uid (if (unspecified? _nongenerative) #f _nongenerative))
- (sealed? (if (unspecified? _sealed) #f _sealed))
- (opaque? (if (unspecified? _opaque) #f _opaque)))
- #`(begin
- (define record-name
- (make-record-type-descriptor
- (quote record-name)
- #,parent-rtd #,uid #,sealed? #,opaque?
- #,field-names))
- (define constructor-name
- (record-constructor
- (make-record-constructor-descriptor
- record-name #,parent-cd #,protocol)))
- (define dummy
- (let ()
- (register-record-type
- (quote record-name)
- record-name (make-record-constructor-descriptor
- record-name #,parent-cd #,protocol))
- 'dummy))
- (define predicate-name (record-predicate record-name))
- #,@field-accessors
- #,@field-mutators))]
- [((fields record-fields ...) . rest)
- (if (unspecified? _fields)
- (loop (process-fields #'record-name #'(record-fields ...))
- _parent _protocol _sealed _opaque _nongenerative
- _constructor _parent-rtd #'rest)
- (raise (make-assertion-violation)))]
- [((parent parent-name) . rest)
- (if (not (unspecified? _parent-rtd))
- (raise (make-assertion-violation))
- (if (unspecified? _parent)
- (loop _fields #'parent-name _protocol _sealed _opaque
- _nongenerative _constructor _parent-rtd #'rest)
- (raise (make-assertion-violation))))]
- [((protocol expression) . rest)
- (if (unspecified? _protocol)
- (loop _fields _parent #'expression _sealed _opaque
- _nongenerative _constructor _parent-rtd #'rest)
- (raise (make-assertion-violation)))]
- [((sealed sealed?) . rest)
- (if (unspecified? _sealed)
- (loop _fields _parent _protocol #'sealed? _opaque
- _nongenerative _constructor _parent-rtd #'rest)
- (raise (make-assertion-violation)))]
- [((opaque opaque?) . rest)
- (if (unspecified? _opaque)
- (loop _fields _parent _protocol _sealed #'opaque?
- _nongenerative _constructor _parent-rtd #'rest)
- (raise (make-assertion-violation)))]
- [((nongenerative) . rest)
- (if (unspecified? _nongenerative)
- (loop _fields _parent _protocol _sealed _opaque
- #`(quote #,(datum->syntax #'record-name (gensym)))
- _constructor _parent-rtd #'rest)
- (raise (make-assertion-violation)))]
- [((nongenerative uid) . rest)
- (if (unspecified? _nongenerative)
- (loop _fields _parent _protocol _sealed
- _opaque #''uid _constructor
- _parent-rtd #'rest)
- (raise (make-assertion-violation)))]
- [((parent-rtd rtd cd) . rest)
- (if (not (unspecified? _parent))
- (raise (make-assertion-violation))
- (if (unspecified? _parent-rtd)
- (loop _fields _parent _protocol _sealed _opaque
- _nongenerative _constructor #'(rtd cd)
- #'rest)
- (raise (make-assertion-violation))))]))))))
-
- (define-syntax record-type-descriptor
- (lambda (stx)
- (syntax-case stx ()
- ((_ name) #`(lookup-record-type-descriptor
- #,(datum->syntax
- stx (list 'quote (syntax->datum #'name))))))))
-
- (define-syntax record-constructor-descriptor
- (lambda (stx)
- (syntax-case stx ()
- ((_ name) #`(lookup-record-constructor-descriptor
- #,(datum->syntax
- stx (list 'quote (syntax->datum #'name))))))))
-)
-;;; sorting.scm --- The R6RS sorting library
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs sorting (6))
- (export list-sort vector-sort vector-sort!)
- (import (rnrs base (6))
- (only (guile) *unspecified* stable-sort sort!))
-
- (define (list-sort proc list) (stable-sort list proc))
- (define (vector-sort proc vector) (stable-sort vector proc))
- (define (vector-sort! proc vector) (sort! vector proc) *unspecified*))
-;;; syntax-case.scm --- R6RS support for `syntax-case' macros
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs syntax-case (6))
- (export make-variable-transformer
- syntax-case
- syntax
-
- identifier?
- bound-identifier=?
- free-identifier=?
-
- syntax->datum
- datum->syntax
- generate-temporaries
- with-syntax
-
- quasisyntax
- unsyntax
- unsyntax-splicing
-
- syntax-violation)
- (import (only (guile) make-variable-transformer
- syntax-case
- syntax
-
- identifier?
- bound-identifier=?
- free-identifier=?
-
- syntax->datum
- datum->syntax
- generate-temporaries
- with-syntax
-
- quasisyntax
- unsyntax
- unsyntax-splicing)
- (ice-9 optargs)
- (rnrs base (6))
- (rnrs conditions (6))
- (rnrs exceptions (6))
- (rnrs records procedural (6)))
-
- (define* (syntax-violation who message form #\optional subform)
- (let* ((conditions (list (make-message-condition message)
- (make-syntax-violation form subform)))
- (conditions (if who
- (cons (make-who-condition who) conditions)
- conditions)))
- (raise (apply condition conditions))))
-)
-;;; unicode.scm --- The R6RS Unicode library
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (rnrs unicode (6))
- (export char-upcase
- char-downcase
- char-titlecase
- char-foldcase
-
- char-ci=?
- char-ci<?
- char-ci>?
- char-ci<=?
- char-ci>=?
-
- char-alphabetic?
- char-numeric?
- char-whitespace?
- char-upper-case?
- char-lower-case?
- char-title-case?
-
- char-general-category
-
- string-upcase
- string-downcase
- string-titlecase
- string-foldcase
-
- string-ci=?
- string-ci<?
- string-ci>?
- string-ci<=?
- string-ci>=?
-
- string-normalize-nfd
- string-normalize-nfkd
- string-normalize-nfc
- string-normalize-nfkc)
- (import (only (guile) char-upcase
- char-downcase
- char-titlecase
-
- char-ci=?
- char-ci<?
- char-ci>?
- char-ci<=?
- char-ci>=?
-
- char-alphabetic?
- char-numeric?
- char-whitespace?
- char-upper-case?
- char-lower-case?
-
- char-set-contains?
- char-set:title-case
-
- char-general-category
-
- char-upcase
- char-downcase
- char-titlecase
-
- string-upcase
- string-downcase
- string-titlecase
-
- string-ci=?
- string-ci<?
- string-ci>?
- string-ci<=?
- string-ci>=?
-
- string-normalize-nfd
- string-normalize-nfkd
- string-normalize-nfc
- string-normalize-nfkc)
- (rnrs base (6)))
-
- (define (char-foldcase char)
- (if (or (eqv? char #\460) (eqv? char #\461))
- char (char-downcase (char-upcase char))))
-
- (define (char-title-case? char) (char-set-contains? char-set:title-case char))
-
- (define (string-foldcase str) (string-downcase (string-upcase str)))
-)
-;;; api-diff --- diff guile-api.alist files
-
-;; Copyright (C) 2002, 2006, 2011 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Thien-Thi Nguyen <ttn@gnu.org>
-
-;;; Commentary:
-
-;; Usage: api-diff [-d GROUPS] ALIST-FILE-A ALIST-FILE-B
-;;
-;; Read in the alists from files ALIST-FILE-A and ALIST-FILE-B
-;; and display a (count) summary of the groups defined therein.
-;; Optional arg "--details" (or "-d") specifies a comma-separated
-;; list of groups, in which case api-diff displays instead the
-;; elements added and deleted for each of the specified groups.
-;;
-;; For scheme programming, this module exports the proc:
-;; (api-diff A-file B-file)
-;;
-;; Note that the convention is that the "older" alist/file is
-;; specified first.
-;;
-;; TODO: Develop scheme interface.
-
-;;; Code:
-
-(define-module (scripts api-diff)
- \:use-module (ice-9 common-list)
- \:use-module (ice-9 format)
- \:use-module (ice-9 getopt-long)
- \:autoload (srfi srfi-13) (string-tokenize)
- \:export (api-diff))
-
-(define %include-in-guild-list #f)
-(define %summary "Show differences between two scan-api files.")
-
-(define (read-alist-file file)
- (with-input-from-file file
- (lambda () (read))))
-
-(define put set-object-property!)
-(define get object-property)
-
-(define (read-api-alist-file file)
- (let* ((alist (read-alist-file file))
- (meta (assq-ref alist 'meta))
- (interface (assq-ref alist 'interface)))
- (put interface 'meta meta)
- (put interface 'groups (let ((ht (make-hash-table 31)))
- (for-each (lambda (group)
- (hashq-set! ht group '()))
- (assq-ref meta 'groups))
- ht))
- interface))
-
-(define (hang-by-the-roots interface)
- (let ((ht (get interface 'groups)))
- (for-each (lambda (x)
- (for-each (lambda (group)
- (hashq-set! ht group
- (cons (car x)
- (hashq-ref ht group))))
- (assq-ref x 'groups)))
- interface))
- interface)
-
-(define (diff? a b)
- (let ((result (set-difference a b)))
- (if (null? result)
- #f ; CL weenies bite me
- result)))
-
-(define (diff+note! a b note-removals note-additions note-same)
- (let ((same? #t))
- (cond ((diff? a b) => (lambda (x) (note-removals x) (set! same? #f))))
- (cond ((diff? b a) => (lambda (x) (note-additions x) (set! same? #f))))
- (and same? (note-same))))
-
-(define (group-diff i-old i-new . options)
- (let* ((i-old (hang-by-the-roots i-old))
- (g-old (hash-fold acons '() (get i-old 'groups)))
- (g-old-names (map car g-old))
- (i-new (hang-by-the-roots i-new))
- (g-new (hash-fold acons '() (get i-new 'groups)))
- (g-new-names (map car g-new)))
- (cond ((null? options)
- (diff+note! g-old-names g-new-names
- (lambda (removals)
- (format #t "groups-removed: ~A\n" removals))
- (lambda (additions)
- (format #t "groups-added: ~A\n" additions))
- (lambda () #t))
- (for-each (lambda (group)
- (let* ((old (assq-ref g-old group))
- (new (assq-ref g-new group))
- (old-count (and old (length old)))
- (new-count (and new (length new)))
- (delta (and old new (- new-count old-count))))
- (format #t " ~5@A ~5@A : "
- (or old-count "-")
- (or new-count "-"))
- (cond ((and old new)
- (let ((add-count 0) (sub-count 0))
- (diff+note!
- old new
- (lambda (subs)
- (set! sub-count (length subs)))
- (lambda (adds)
- (set! add-count (length adds)))
- (lambda () #t))
- (format #t "~5@D ~5@D : ~5@D"
- add-count (- sub-count) delta)))
- (else
- (format #t "~5@A ~5@A : ~5@A" "-" "-" "-")))
- (format #t " ~A\n" group)))
- (sort (union g-old-names g-new-names)
- (lambda (a b)
- (string<? (symbol->string a)
- (symbol->string b))))))
- ((assq-ref options 'details)
- => (lambda (groups)
- (for-each (lambda (group)
- (let* ((old (or (assq-ref g-old group) '()))
- (new (or (assq-ref g-new group) '()))
- (>>! (lambda (label ls)
- (format #t "~A ~A:\n" group label)
- (for-each (lambda (x)
- (format #t " ~A\n" x))
- ls))))
- (diff+note! old new
- (lambda (removals)
- (>>! 'removals removals))
- (lambda (additions)
- (>>! 'additions additions))
- (lambda ()
- (format #t "~A: no changes\n"
- group)))))
- groups)))
- (else
- (error "api-diff: group-diff: bad options")))))
-
-(define (api-diff . args)
- (let* ((p (getopt-long (cons 'api-diff args)
- '((details (single-char #\d)
- (value #t))
- ;; Add options here.
- )))
- (rest (option-ref p '() '("/dev/null" "/dev/null")))
- (i-old (read-api-alist-file (car rest)))
- (i-new (read-api-alist-file (cadr rest)))
- (options '()))
- (cond ((option-ref p 'details #f)
- => (lambda (groups)
- (set! options (cons (cons 'details
- (map string->symbol
- (string-tokenize
- groups
- #\,)))
- options)))))
- (apply group-diff i-old i-new options)))
-
-(define main api-diff)
-
-;;; api-diff ends here
-;;; autofrisk --- Generate module checks for use with auto* tools
-
-;; Copyright (C) 2002, 2006, 2009, 2011 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Thien-Thi Nguyen <ttn@gnu.org>
-
-;;; Commentary:
-
-;; Usage: autofrisk [file]
-;;
-;; This program looks for the file modules.af in the current directory
-;; and writes out modules.af.m4 containing autoconf definitions.
-;; If given, look for FILE instead of modules.af and output to FILE.m4.
-;;
-;; After running autofrisk, you should add to configure.ac the lines:
-;; AUTOFRISK_CHECKS
-;; AUTOFRISK_SUMMARY
-;; Then run "aclocal -I ." to update aclocal.m4, and finally autoconf.
-;;
-;; The modules.af file consists of a series of configuration forms (Scheme
-;; lists), which have one of the following formats:
-;; (files-glob PATTERN ...)
-;; (non-critical-external MODULE ...)
-;; (non-critical-internal MODULE ...)
-;; (programs (MODULE PROG ...) ...)
-;; (pww-varname VARNAME)
-;; PATTERN is a string that may contain "*" and "?" characters to be
-;; expanded into filenames. MODULE is a list of symbols naming a
-;; module, such as `(srfi srfi-1)'. VARNAME is a shell-safe name to use
-;; instead of "probably_wont_work", the default. This var is passed to
-;; `AC_SUBST'. PROG is a string.
-;;
-;; Only the `files-glob' form is required.
-;;
-;; TODO: Write better commentary.
-;; Make "please see README" configurable.
-
-;;; Code:
-
-(define-module (scripts autofrisk)
- \:autoload (ice-9 popen) (open-input-pipe)
- \:use-module (srfi srfi-1)
- \:use-module (srfi srfi-8)
- \:use-module (srfi srfi-13)
- \:use-module (srfi srfi-14)
- \:use-module (scripts read-scheme-source)
- \:use-module (scripts frisk)
- \:export (autofrisk))
-
-(define %include-in-guild-list #f)
-(define %summary "Generate snippets for use in configure.ac files.")
-
-(define *recognized-keys* '(files-glob
- non-critical-external
- non-critical-internal
- programs
- pww-varname))
-
-(define (canonical-configuration forms)
- (let ((chk (lambda (condition . x)
- (or condition (apply error "syntax error:" x)))))
- (chk (list? forms) "input not a list")
- (chk (every list? forms) "non-list element")
- (chk (every (lambda (form) (< 1 (length form))) forms) "list too short")
- (let ((un #f))
- (chk (every (lambda (form)
- (let ((key (car form)))
- (and (symbol? key)
- (or (eq? 'quote key)
- (memq key *recognized-keys*)
- (begin
- (set! un key)
- #f)))))
- forms)
- "unrecognized key:" un))
- (let ((bunched (map (lambda (key)
- (fold (lambda (form so-far)
- (or (and (eq? (car form) key)
- (cdr form)
- (append so-far (cdr form)))
- so-far))
- (list key)
- forms))
- *recognized-keys*)))
- (lambda (key)
- (assq-ref bunched key)))))
-
-(define (>>strong modules)
- (for-each (lambda (module)
- (format #t "GUILE_MODULE_REQUIRED~A\n" module))
- modules))
-
-(define (safe-name module)
- (let ((var (object->string module)))
- (string-map! (lambda (c)
- (if (char-set-contains? char-set:letter+digit c)
- c
- #\_))
- var)
- var))
-
-(define *pww* "probably_wont_work")
-
-(define (>>weak weak-edges)
- (for-each (lambda (edge)
- (let* ((up (edge-up edge))
- (down (edge-down edge))
- (var (format #f "have_guile_module~A" (safe-name up))))
- (format #t "GUILE_MODULE_AVAILABLE(~A, ~A)\n" var up)
- (format #t "test \"$~A\" = no &&\n ~A=\"~A $~A\"~A"
- var *pww* down *pww* "\n\n")))
- weak-edges))
-
-(define (>>program module progs)
- (let ((vars (map (lambda (prog)
- (format #f "guile_module~Asupport_~A"
- (safe-name module)
- prog))
- progs)))
- (for-each (lambda (var prog)
- (format #t "AC_PATH_PROG(~A, ~A)\n" var prog))
- vars progs)
- (format #t "test \\\n")
- (for-each (lambda (var)
- (format #t " \"$~A\" = \"\" -o \\\n" var))
- vars)
- (format #t "~A &&\n~A=\"~A $~A\"\n\n"
- (list-ref (list "war = peace"
- "freedom = slavery"
- "ignorance = strength")
- (random 3))
- *pww* module *pww*)))
-
-(define (>>programs programs)
- (for-each (lambda (form)
- (>>program (car form) (cdr form)))
- programs))
-
-(define (unglob pattern)
- (let ((p (open-input-pipe (format #f "echo '(' ~A ')'" pattern))))
- (map symbol->string (read p))))
-
-(define (>>checks forms)
- (let* ((cfg (canonical-configuration forms))
- (files (apply append (map unglob (cfg 'files-glob))))
- (ncx (cfg 'non-critical-external))
- (nci (cfg 'non-critical-internal))
- (report ((make-frisker) files))
- (external (report 'external)))
- (let ((pww-varname (cfg 'pww-varname)))
- (or (null? pww-varname) (set! *pww* (car pww-varname))))
- (receive (weak strong)
- (partition (lambda (module)
- (or (member module ncx)
- (every (lambda (i)
- (member i nci))
- (map edge-down (mod-down-ls module)))))
- external)
- (format #t "AC_DEFUN([AUTOFRISK_CHECKS],[\n\n")
- (>>strong strong)
- (format #t "\n~A=~S\n\n" *pww* "")
- (>>weak (fold (lambda (module so-far)
- (append so-far (mod-down-ls module)))
- (list)
- weak))
- (>>programs (cfg 'programs))
- (format #t "AC_SUBST(~A)\n])\n\n" *pww*))))
-
-(define (>>summary)
- (format #t
- (symbol->string
- '#{
-AC_DEFUN([AUTOFRISK_SUMMARY],[
-if test ! "$~A" = "" ; then
- p=" ***"
- echo "$p"
- echo "$p NOTE:"
- echo "$p The following modules probably won't work:"
- echo "$p $~A"
- echo "$p They can be installed anyway, and will work if their"
- echo "$p dependencies are installed later. Please see README."
- echo "$p"
-fi
-])
-})
- *pww* *pww*))
-
-(define (autofrisk . args)
- (let ((file (if (null? args) "modules.af" (car args))))
- (or (file-exists? file)
- (error "could not find input file:" file))
- (with-output-to-file (format #f "~A.m4" file)
- (lambda ()
- (>>checks (read-scheme-source-silently file))
- (>>summary)))))
-
-(define main autofrisk)
-
-;; Local variables:
-;; eval: (put 'receive 'scheme-indent-function 2)
-;; End:
-
-;;; autofrisk ends here
-;;; Compile --- Command-line Guile Scheme compiler -*- coding: iso-8859-1 -*-
-
-;; Copyright 2005, 2008, 2009, 2010, 2011, 2014 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Ludovic Courts <ludo@gnu.org>
-;;; Author: Andy Wingo <wingo@pobox.com>
-
-;;; Commentary:
-
-;; Usage: compile [ARGS]
-;;
-;; A command-line interface to the Guile compiler.
-
-;;; Code:
-
-(define-module (scripts compile)
- #\use-module ((system base compile) #\select (compile-file))
- #\use-module (system base target)
- #\use-module (system base message)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-13)
- #\use-module (srfi srfi-37)
- #\use-module (ice-9 format)
- #\export (compile))
-
-(define %summary "Compile a file.")
-
-
-(define (fail . messages)
- (format (current-error-port) "error: ~{~a~}~%" messages)
- (exit 1))
-
-(define %options
- ;; Specifications of the command-line options.
- (list (option '(#\h "help") #f #f
- (lambda (opt name arg result)
- (alist-cons 'help? #t result)))
- (option '("version") #f #f
- (lambda (opt name arg result)
- (show-version)
- (exit 0)))
-
- (option '(#\L "load-path") #t #f
- (lambda (opt name arg result)
- (let ((load-path (assoc-ref result 'load-path)))
- (alist-cons 'load-path (cons arg load-path)
- result))))
- (option '(#\o "output") #t #f
- (lambda (opt name arg result)
- (if (assoc-ref result 'output-file)
- (fail "`-o' option cannot be specified more than once")
- (alist-cons 'output-file arg result))))
-
- (option '(#\W "warn") #t #f
- (lambda (opt name arg result)
- (if (string=? arg "help")
- (begin
- (show-warning-help)
- (exit 0))
- (let ((warnings (assoc-ref result 'warnings)))
- (alist-cons 'warnings
- (cons (string->symbol arg) warnings)
- (alist-delete 'warnings result))))))
-
- (option '(#\O "optimize") #f #f
- (lambda (opt name arg result)
- (alist-cons 'optimize? #t result)))
- (option '(#\f "from") #t #f
- (lambda (opt name arg result)
- (if (assoc-ref result 'from)
- (fail "`--from' option cannot be specified more than once")
- (alist-cons 'from (string->symbol arg) result))))
- (option '(#\t "to") #t #f
- (lambda (opt name arg result)
- (if (assoc-ref result 'to)
- (fail "`--to' option cannot be specified more than once")
- (alist-cons 'to (string->symbol arg) result))))
- (option '(#\T "target") #t #f
- (lambda (opt name arg result)
- (if (assoc-ref result 'target)
- (fail "`--target' option cannot be specified more than once")
- (alist-cons 'target arg result))))))
-
-(define (parse-args args)
- "Parse argument list @var{args} and return an alist with all the relevant
-options."
- (args-fold args %options
- (lambda (opt name arg result)
- (format (current-error-port) "~A: unrecognized option" name)
- (exit 1))
- (lambda (file result)
- (let ((input-files (assoc-ref result 'input-files)))
- (alist-cons 'input-files (cons file input-files)
- result)))
-
- ;; default option values
- '((input-files)
- (load-path)
- (warnings unsupported-warning))))
-
-(define (show-version)
- (format #t "compile (GNU Guile) ~A~%" (version))
- (format #t "Copyright (C) 2009, 2011 Free Software Foundation, Inc.
-License LGPLv3+: GNU LGPL version 3 or later <http://gnu.org/licenses/lgpl.html>.
-This is free software: you are free to change and redistribute it.
-There is NO WARRANTY, to the extent permitted by law.~%"))
-
-(define (show-warning-help)
- (format #t "The available warning types are:~%~%")
- (for-each (lambda (wt)
- (format #t " ~22A ~A~%"
- (format #f "`~A'" (warning-type-name wt))
- (warning-type-description wt)))
- %warning-types)
- (format #t "~%"))
-
-
-(define (compile . args)
- (let* ((options (parse-args args))
- (help? (assoc-ref options 'help?))
- (compile-opts (let ((o `(#\warnings
- ,(assoc-ref options 'warnings))))
- (if (assoc-ref options 'optimize?)
- (cons #\O o)
- o)))
- (from (or (assoc-ref options 'from) 'scheme))
- (to (or (assoc-ref options 'to) 'objcode))
- (target (or (assoc-ref options 'target) %host-type))
- (input-files (assoc-ref options 'input-files))
- (output-file (assoc-ref options 'output-file))
- (load-path (assoc-ref options 'load-path)))
- (if (or help? (null? input-files))
- (begin
- (format #t "Usage: compile [OPTION] FILE...
-Compile each Guile source file FILE into a Guile object.
-
- -h, --help print this help message
-
- -L, --load-path=DIR add DIR to the front of the module load path
- -o, --output=OFILE write output to OFILE
-
- -W, --warn=WARNING emit warnings of type WARNING; use `--warn=help'
- for a list of available warnings
-
- -f, --from=LANG specify a source language other than `scheme'
- -t, --to=LANG specify a target language other than `objcode'
- -T, --target=TRIPLET produce bytecode for host TRIPLET
-
-Note that auto-compilation will be turned off.
-
-Report bugs to <~A>.~%"
- %guile-bug-report-address)
- (exit 0)))
-
- (set! %load-path (append load-path %load-path))
- (set! %load-should-auto-compile #f)
-
- (if (and output-file
- (or (null? input-files)
- (not (null? (cdr input-files)))))
- (fail "`-o' option can only be specified "
- "when compiling a single file"))
-
- ;; Install a SIGINT handler. As a side effect, this gives unwind
- ;; handlers an opportunity to run upon SIGINT; this includes that of
- ;; 'call-with-output-file/atomic', called by 'compile-file', which
- ;; removes the temporary output file.
- (sigaction SIGINT
- (lambda args
- (fail "interrupted by the user")))
-
- (for-each (lambda (file)
- (format #t "wrote `~A'\n"
- (with-fluids ((*current-warning-prefix* ""))
- (with-target target
- (lambda ()
- (compile-file file
- #\output-file output-file
- #\from from
- #\to to
- #\opts compile-opts))))))
- input-files)))
-
-(define main compile)
-;;; Disassemble --- Disassemble .go files into something human-readable
-
-;; Copyright 2005, 2008, 2009, 2011, 2014 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Ludovic Courtès <ludo@gnu.org>
-;;; Author: Andy Wingo <wingo@pobox.com>
-
-;;; Commentary:
-
-;; Usage: disassemble [ARGS]
-
-;;; Code:
-
-(define-module (scripts disassemble)
- #\use-module (system vm objcode)
- #\use-module ((language assembly disassemble) #\prefix asm\:)
- #\export (disassemble))
-
-(define %summary "Disassemble a compiled .go file.")
-
-(define (disassemble . files)
- (for-each (lambda (file)
- (asm:disassemble (load-objcode file)))
- files))
-
-(define main disassemble)
-;;; display-commentary --- As advertized
-
-;; Copyright (C) 2001, 2006, 2011 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Thien-Thi Nguyen
-
-;;; Commentary:
-
-;; Usage: display-commentary REF1 REF2 ...
-;;
-;; Display Commentary section from REF1, REF2 and so on.
-;; Each REF may be a filename or module name (list of symbols).
-;; In the latter case, a filename is computed by searching `%load-path'.
-
-;;; Code:
-
-(define-module (scripts display-commentary)
- \:use-module (ice-9 documentation)
- \:export (display-commentary))
-
-(define %summary "Display the Commentary section from a file or module.")
-
-(define (display-commentary-one file)
- (format #t "~A commentary:\n~A" file (file-commentary file)))
-
-(define (module-name->filename-frag ls) ; todo: export or move
- (let ((ls (map symbol->string ls)))
- (let loop ((ls (cdr ls)) (acc (car ls)))
- (if (null? ls)
- acc
- (loop (cdr ls) (string-append acc "/" (car ls)))))))
-
-(define (display-module-commentary module-name)
- (cond ((%search-load-path (module-name->filename-frag module-name))
- => (lambda (file)
- (format #t "module ~A\n" module-name)
- (display-commentary-one file)))))
-
-(define (display-commentary . refs)
- (for-each (lambda (ref)
- (cond ((string? ref)
- (if (equal? 0 (string-index ref #\())
- (display-module-commentary
- (with-input-from-string ref read))
- (display-commentary-one ref)))
- ((list? ref)
- (display-module-commentary ref))))
- refs))
-
-(define main display-commentary)
-
-;;; display-commentary ends here
-;;; doc-snarf --- Extract documentation from source files
-
-;; Copyright (C) 2001, 2006, 2011 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Martin Grabmueller
-
-;;; Commentary:
-
-;; Usage: doc-snarf FILE
-;;
-;; This program reads in a Scheme source file and extracts docstrings
-;; in the format specified below. Additionally, a procedure protoype
-;; is infered from the procedure definition line starting with
-;; (define... ).
-;;
-;; Currently, two output modi are implemented: texinfo and plaintext.
-;; Default is plaintext, texinfo can be switched on with the
-;; `--texinfo, -t' command line option.
-;;
-;; Format: A docstring can span multiple lines and a docstring line
-;; begins with `;; ' (two semicoli and a space). A docstring is ended
-;; by either a line beginning with (define ...) or one or more lines
-;; beginning with `;;-' (two semicoli and a dash). These lines are
-;; called `options' and begin with a keyword, followed by a colon and
-;; a string.
-;;
-;; Additionally, "standard internal docstrings" (for Scheme source) are
-;; recognized and output as "options". The output formatting is likely
-;; to change in the future.
-;;
-;; Example:
-
-;; This procedure foos, or bars, depending on the argument @var{braz}.
-;;-Author: Martin Grabmueller
-(define (foo/bar braz)
- (if braz 'foo 'bar))
-
-;;; Which results in the following docstring if texinfo output is
-;;; enabled:
-
-;; TODO: Convert option lines to alist.
-;; More parameterization.
-;; (maybe) Use in Guile build itself.
-
-(define doc-snarf-version "0.0.2") ; please update before publishing!
-
-;;; Code:
-
-(define-module (scripts doc-snarf)
- \:use-module (ice-9 getopt-long)
- \:use-module (ice-9 regex)
- \:use-module (ice-9 string-fun)
- \:use-module (ice-9 rdelim)
- \:export (doc-snarf))
-
-(define %summary "Snarf out documentation from a file.")
-
-(define command-synopsis
- '((version (single-char #\v) (value #f))
- (help (single-char #\h) (value #f))
- (output (single-char #\o) (value #t))
- (texinfo (single-char #\t) (value #f))
- (lang (single-char #\l) (value #t))))
-
-;; Display version information and exit.
-;;-ttn-mod: use var
-(define (display-version)
- (display "doc-snarf ") (display doc-snarf-version) (newline))
-
-;; Display the usage help message and exit.
-;;-ttn-mod: change option "source" to "lang"
-(define (display-help)
- (display "Usage: doc-snarf [options...] inputfile\n")
- (display " --help, -h Show this usage information\n")
- (display " --version, -v Show version information\n")
- (display
- " --output=FILE, -o Specify output file [default=stdout]\n")
- (display " --texinfo, -t Format output as texinfo\n")
- (display " --lang=[c,scheme], -l Specify the input language\n"))
-
-;; Main program.
-;;-ttn-mod: canonicalize lang
-(define (doc-snarf . args)
- (let ((options (getopt-long (cons "doc-snarf" args) command-synopsis)))
- (let ((help-wanted (option-ref options 'help #f))
- (version-wanted (option-ref options 'version #f))
- (texinfo-wanted (option-ref options 'texinfo #f))
- (lang (string->symbol
- (string-downcase (option-ref options 'lang "scheme")))))
- (cond
- (version-wanted (display-version))
- (help-wanted (display-help))
- (else
- (let ((input (option-ref options '() #f))
- (output (option-ref options 'output #f)))
- (if
- ;; Bonard B. Timmons III says `(pair? input)' alone is sufficient.
- ;; (and input (pair? input))
- (pair? input)
- (snarf-file (car input) output texinfo-wanted lang)
- (display-help))))))))
-
-(define main doc-snarf)
-
-;; Supported languages and their parameters. Each element has form:
-;; (LANG DOC-START DOC-END DOC-PREFIX OPT-PREFIX SIG-START STD-INT-DOC?)
-;; LANG is a symbol, STD-INT-DOC? is a boolean indicating whether or not
-;; LANG supports "standard internal docstring" (a string after the formals),
-;; everything else is a string specifying a regexp.
-;;-ttn-mod: new var
-(define supported-languages
- '((c
- "^/\\*(.*)"
- "^ \\*/"
- "^ \\* (.*)"
- "^ \\*-(.*)"
- "NOTHING AT THIS TIME!!!"
- #f
- )
- (scheme
- "^;; (.*)"
- "^;;\\."
- "^;; (.*)"
- "^;;-(.*)"
- "^\\(define"
- #t
- )))
-
-;; Get @var{lang}'s @var{parameter}. Both args are symbols.
-;;-ttn-mod: new proc
-(define (lang-parm lang parm)
- (list-ref (assq-ref supported-languages lang)
- (case parm
- ((docstring-start) 0)
- ((docstring-end) 1)
- ((docstring-prefix) 2)
- ((option-prefix) 3)
- ((signature-start) 4)
- ((std-int-doc?) 5))))
-
-;; Snarf all docstrings from the file @var{input} and write them to
-;; file @var{output}. Use texinfo format for the output if
-;; @var{texinfo?} is true.
-;;-ttn-mod: don't use string comparison, consult table instead
-(define (snarf-file input output texinfo? lang)
- (or (memq lang (map car supported-languages))
- (error "doc-snarf: input language must be c or scheme."))
- (write-output (snarf input lang) output
- (if texinfo? format-texinfo format-plain)))
-
-;; fixme: this comment is required to trigger standard internal
-;; docstring snarfing... ideally, it wouldn't be necessary.
-;;-ttn-mod: new proc, from snarf-docs (aren't these names fun?)
-(define (find-std-int-doc line input-port)
- "Unread @var{line} from @var{input-port}, then read in the entire form and
-return the standard internal docstring if found. Return #f if not."
- (unread-string line input-port) ; ugh
- (let ((form (read input-port)))
- (cond ((and (list? form) ; (define (PROC ARGS) "DOC" ...)
- (< 3 (length form))
- (eq? 'define (car form))
- (pair? (cadr form))
- (symbol? (caadr form))
- (string? (caddr form)))
- (caddr form))
- ((and (list? form) ; (define VAR (lambda ARGS "DOC" ...))
- (< 2 (length form))
- (eq? 'define (car form))
- (symbol? (cadr form))
- (list? (caddr form))
- (< 3 (length (caddr form)))
- (eq? 'lambda (car (caddr form)))
- (string? (caddr (caddr form))))
- (caddr (caddr form)))
- (else #f))))
-
-;; Split @var{string} into lines, adding @var{prefix} to each.
-;;-ttn-mod: new proc
-(define (split-prefixed string prefix)
- (separate-fields-discarding-char
- #\newline string
- (lambda lines
- (map (lambda (line)
- (string-append prefix line))
- lines))))
-
-;; snarf input-file output-file
-;; Extract docstrings from the input file @var{input}, presumed
-;; to be written in language @var{lang}.
-;;-Author: Martin Grabmueller <mgrabmue@cs.tu-berlin.de>
-;;-Created: 2001-02-17
-;;-ttn-mod: regluarize lang parm lookup, add "std int doc" snarfing (2 places)
-(define (snarf input-file lang)
- (let* ((i-p (open-input-file input-file))
- (parm-regexp (lambda (parm) (make-regexp (lang-parm lang parm))))
- (docstring-start (parm-regexp 'docstring-start))
- (docstring-end (parm-regexp 'docstring-end))
- (docstring-prefix (parm-regexp 'docstring-prefix))
- (option-prefix (parm-regexp 'option-prefix))
- (signature-start (parm-regexp 'signature-start))
- (augmented-options
- (lambda (line i-p options)
- (let ((int-doc (and (lang-parm lang 'std-int-doc?)
- (let ((d (find-std-int-doc line i-p)))
- (and d (split-prefixed d "internal: "))))))
- (if int-doc
- (append (reverse int-doc) options)
- options)))))
-
- (let lp ((line (read-line i-p)) (state 'neutral) (doc-strings '())
- (options '()) (entries '()) (lno 0))
- (cond
- ((eof-object? line)
- (close-input-port i-p)
- (reverse entries))
-
- ;; State 'neutral: we're currently not within a docstring or
- ;; option section
- ((eq? state 'neutral)
- (let ((m (regexp-exec docstring-start line)))
- (if m
- (lp (read-line i-p) 'doc-string
- (list (match:substring m 1)) '() entries (+ lno 1))
- (lp (read-line i-p) state '() '() entries (+ lno 1)))))
-
- ;; State 'doc-string: we have started reading a docstring and
- ;; are waiting for more, for options or for a define.
- ((eq? state 'doc-string)
- (let ((m0 (regexp-exec docstring-prefix line))
- (m1 (regexp-exec option-prefix line))
- (m2 (regexp-exec signature-start line))
- (m3 (regexp-exec docstring-end line)))
- (cond
- (m0
- (lp (read-line i-p) 'doc-string
- (cons (match:substring m0 1) doc-strings) '() entries
- (+ lno 1)))
- (m1
- (lp (read-line i-p) 'options
- doc-strings (cons (match:substring m1 1) options) entries
- (+ lno 1)))
- (m2
- (let ((options (augmented-options line i-p options))) ; ttn-mod
- (lp (read-line i-p) 'neutral '() '()
- (cons (parse-entry doc-strings options line input-file lno)
- entries)
- (+ lno 1))))
- (m3
- (lp (read-line i-p) 'neutral '() '()
- (cons (parse-entry doc-strings options #f input-file lno)
- entries)
- (+ lno 1)))
- (else
- (lp (read-line i-p) 'neutral '() '() entries (+ lno 1))))))
-
- ;; State 'options: We're waiting for more options or for a
- ;; define.
- ((eq? state 'options)
- (let ((m1 (regexp-exec option-prefix line))
- (m2 (regexp-exec signature-start line))
- (m3 (regexp-exec docstring-end line)))
- (cond
- (m1
- (lp (read-line i-p) 'options
- doc-strings (cons (match:substring m1 1) options) entries
- (+ lno 1)))
- (m2
- (let ((options (augmented-options line i-p options))) ; ttn-mod
- (lp (read-line i-p) 'neutral '() '()
- (cons (parse-entry doc-strings options line input-file lno)
- entries)
- (+ lno 1))))
- (m3
- (lp (read-line i-p) 'neutral '() '()
- (cons (parse-entry doc-strings options #f input-file lno)
- entries)
- (+ lno 1)))
- (else
- (lp (read-line i-p) 'neutral '() '() entries (+ lno 1))))))))))
-
-(define (make-entry symbol signature docstrings options filename line)
- (vector 'entry symbol signature docstrings options filename line))
-(define (entry-symbol e)
- (vector-ref e 1))
-(define (entry-signature e)
- (vector-ref e 2))
-(define (entry-docstrings e)
- (vector-ref e 3))
-(define (entry-options e)
- (vector-ref e 4))
-(define (entry-filename e)
- (vector-ref e 5))
-(define (entry-line e)
- "This docstring will not be snarfed, unfortunately..."
- (vector-ref e 6))
-
-;; Create a docstring entry from the docstring line list
-;; @var{doc-strings}, the option line list @var{options} and the
-;; define line @var{def-line}
-(define (parse-entry docstrings options def-line filename line-no)
-; (write-line docstrings)
- (cond
- (def-line
- (make-entry (get-symbol def-line)
- (make-prototype def-line) (reverse docstrings)
- (reverse options) filename
- (+ (- line-no (length docstrings) (length options)) 1)))
- ((> (length docstrings) 0)
- (make-entry (string->symbol (car (reverse docstrings)))
- (car (reverse docstrings))
- (cdr (reverse docstrings))
- (reverse options) filename
- (+ (- line-no (length docstrings) (length options)) 1)))
- (else
- (make-entry 'foo "" (reverse docstrings) (reverse options) filename
- (+ (- line-no (length docstrings) (length options)) 1)))))
-
-;; Create a string which is a procedure prototype. The necessary
-;; information for constructing the prototype is taken from the line
-;; @var{def-line}, which is a line starting with @code{(define...}.
-(define (make-prototype def-line)
- (call-with-input-string
- def-line
- (lambda (s-p)
- (let* ((paren (read-char s-p))
- (keyword (read s-p))
- (tmp (read s-p)))
- (cond
- ((pair? tmp)
- (join-symbols tmp))
- ((symbol? tmp)
- (symbol->string tmp))
- (else
- ""))))))
-
-(define (get-symbol def-line)
- (call-with-input-string
- def-line
- (lambda (s-p)
- (let* ((paren (read-char s-p))
- (keyword (read s-p))
- (tmp (read s-p)))
- (cond
- ((pair? tmp)
- (car tmp))
- ((symbol? tmp)
- tmp)
- (else
- 'foo))))))
-
-;; Append the symbols in the string list @var{s}, separated with a
-;; space character.
-(define (join-symbols s)
- (cond ((null? s)
- "")
- ((symbol? s)
- (string-append ". " (symbol->string s)))
- ((null? (cdr s))
- (symbol->string (car s)))
- (else
- (string-append (symbol->string (car s)) " " (join-symbols (cdr s))))))
-
-;; Write @var{entries} to @var{output-file} using @var{writer}.
-;; @var{writer} is a proc that takes one entry.
-;; If @var{output-file} is #f, write to stdout.
-;;-ttn-mod: new proc
-(define (write-output entries output-file writer)
- (with-output-to-port (cond (output-file (open-output-file output-file))
- (else (current-output-port)))
- (lambda () (for-each writer entries))))
-
-;; Write an @var{entry} using texinfo format.
-;;-ttn-mod: renamed from `texinfo-output', distilled
-(define (format-texinfo entry)
- (display "\n\f")
- (display (entry-symbol entry))
- (newline)
- (display "@c snarfed from ")
- (display (entry-filename entry))
- (display ":")
- (display (entry-line entry))
- (newline)
- (display "@deffn procedure ")
- (display (entry-signature entry))
- (newline)
- (for-each (lambda (s) (write-line s))
- (entry-docstrings entry))
- (for-each (lambda (s) (display "@c ") (write-line s))
- (entry-options entry))
- (write-line "@end deffn"))
-
-;; Write an @var{entry} using plain format.
-;;-ttn-mod: renamed from `texinfo-output', distilled
-(define (format-plain entry)
- (display "Procedure: ")
- (display (entry-signature entry))
- (newline)
- (for-each (lambda (s) (write-line s))
- (entry-docstrings entry))
- (for-each (lambda (s) (display ";; ") (write-line s))
- (entry-options entry))
- (display "Snarfed from ")
- (display (entry-filename entry))
- (display ":")
- (display (entry-line entry))
- (newline)
- (write-line "\f"))
-
-;;; doc-snarf ends here
-;;; frisk --- Grok the module interfaces of a body of files
-
-;; Copyright (C) 2002, 2006, 2010, 2011 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Thien-Thi Nguyen <ttn@gnu.org>
-
-;;; Commentary:
-
-;; Usage: frisk [options] file ...
-;;
-;; Analyze FILE... module interfaces in aggregate (as a "body"),
-;; and display a summary. Modules that are `define-module'd are
-;; considered "internal" (and those not, "external"). When module X
-;; uses module Y, X is said to be "(a) downstream of" Y, and Y is
-;; "(an) upstream of" X.
-;;
-;; Normally, the summary displays external modules and their internal
-;; downstreams, as this is the usual question asked by a body. There
-;; are several options that modify this output.
-;;
-;; -u, --upstream show upstream edges
-;; -d, --downstream show downstream edges (default)
-;; -i, --internal show internal modules
-;; -x, --external show external modules (default)
-;;
-;; If given both `upstream' and `downstream' options ("frisk -ud"), the
-;; output is formatted: "C MODULE --- UP-LS --- DOWN-LS", where C is
-;; either `i' or `x', and each element of UP-LS and DOWN-LS is (TYPE
-;; MODULE-NAME ...).
-;;
-;; In all other cases, the "C MODULE" occupies its own line, and
-;; subsequent lines list the up- or downstream edges, respectively,
-;; indented by some non-zero amount of whitespace.
-;;
-;; Top-level `use-modules' (or `load' or 'primitive-load') forms in a
-;; file that do not follow a `define-module' result an edge where the
-;; downstream is the "default module", normally `(guile-user)'. This
-;; can be set to another value by using:
-;;
-;; -m, --default-module MOD set MOD as the default module
-
-;; Usage from a Scheme Program: (use-modules (scripts frisk))
-;;
-;; Module export list:
-;; (frisk . args)
-;; (make-frisker . options) => (lambda (files) ...) [see below]
-;; (mod-up-ls module) => upstream edges
-;; (mod-down-ls module) => downstream edges
-;; (mod-int? module) => is the module internal?
-;; (edge-type edge) => symbol: {regular,autoload,computed}
-;; (edge-up edge) => upstream module
-;; (edge-down edge) => downstream module
-;;
-;; OPTIONS is an alist. Recognized keys are:
-;; default-module
-;;
-;; `make-frisker' returns a procedure that takes a list of files, the
-;; FRISKER. FRISKER returns a closure, REPORT, that takes one of the
-;; keys:
-;; modules -- entire list of modules
-;; internal -- list of internal modules
-;; external -- list of external modules
-;; i-up -- list of modules upstream of internal modules
-;; x-up -- list of modules upstream of external modules
-;; i-down -- list of modules downstream of internal modules
-;; x-down -- list of modules downstream of external modules
-;; edges -- list of edges
-;; Note that `x-up' should always be null, since by (lack of!)
-;; definition, we only know external modules by reference.
-;;
-;; The module and edge objects managed by REPORT can be examined in
-;; detail by using the other (self-explanatory) procedures. Be careful
-;; not to confuse a freshly consed list of symbols, like `(a b c)' with
-;; the module `(a b c)'. If you want to find the module by that name,
-;; try: (cond ((member '(a b c) (REPORT 'modules)) => car)).
-
-;; TODO: Make "frisk -ud" output less ugly.
-;; Consider default module as internal; add option to invert.
-;; Support `edge-misc' data.
-
-;;; Code:
-
-(define-module (scripts frisk)
- \:autoload (ice-9 getopt-long) (getopt-long)
- \:use-module ((srfi srfi-1) \:select (filter remove))
- \:export (frisk
- make-frisker
- mod-up-ls mod-down-ls mod-int?
- edge-type edge-up edge-down))
-
-(define %include-in-guild-list #f)
-(define %summary "Show dependency information for a module.")
-
-(define *default-module* '(guile-user))
-
-(define (grok-proc default-module note-use!)
- (lambda (filename)
- (let* ((p (open-file filename "r"))
- (next (lambda () (read p)))
- (ferret (lambda (use) ;;; handle "((foo bar) \:select ...)"
- (let ((maybe (car use)))
- (if (list? maybe)
- maybe
- use))))
- (curmod #f))
- (let loop ((form (next)))
- (cond ((eof-object? form))
- ((not (list? form)) (loop (next)))
- (else (case (car form)
- ((define-module)
- (let ((module (cadr form)))
- (set! curmod module)
- (note-use! 'def module #f)
- (let loop ((ls form))
- (or (null? ls)
- (case (car ls)
- ((#\use-module \:use-module)
- (note-use! 'regular module (ferret (cadr ls)))
- (loop (cddr ls)))
- ((#\autoload \:autoload)
- (note-use! 'autoload module (cadr ls))
- (loop (cdddr ls)))
- (else (loop (cdr ls))))))))
- ((use-modules)
- (for-each (lambda (use)
- (note-use! 'regular
- (or curmod default-module)
- (ferret use)))
- (cdr form)))
- ((load primitive-load)
- (note-use! 'computed
- (or curmod default-module)
- (let ((file (cadr form)))
- (if (string? file)
- file
- (format #f "[computed in ~A]"
- filename))))))
- (loop (next))))))))
-
-(define up-ls (make-object-property)) ; list
-(define dn-ls (make-object-property)) ; list
-(define int? (make-object-property)) ; defined via `define-module'
-
-(define mod-up-ls up-ls)
-(define mod-down-ls dn-ls)
-(define mod-int? int?)
-
-(define (i-or-x module)
- (if (int? module) 'i 'x))
-
-(define edge-type (make-object-property)) ; symbol
-
-(define (make-edge type up down)
- (let ((new (cons up down)))
- (set! (edge-type new) type)
- new))
-
-(define edge-up car)
-(define edge-down cdr)
-
-(define (up-ls+! m new) (set! (up-ls m) (cons new (up-ls m))))
-(define (dn-ls+! m new) (set! (dn-ls m) (cons new (dn-ls m))))
-
-(define (make-body alist)
- (lambda (key)
- (assq-ref alist key)))
-
-(define (scan default-module files)
- (let* ((modules (list))
- (edges (list))
- (intern (lambda (module)
- (cond ((member module modules) => car)
- (else (set! (up-ls module) (list))
- (set! (dn-ls module) (list))
- (set! modules (cons module modules))
- module))))
- (grok (grok-proc default-module
- (lambda (type d u)
- (let ((d (intern d)))
- (if (eq? type 'def)
- (set! (int? d) #t)
- (let* ((u (intern u))
- (edge (make-edge type u d)))
- (set! edges (cons edge edges))
- (up-ls+! d edge)
- (dn-ls+! u edge))))))))
- (for-each grok files)
- (make-body
- `((modules . ,modules)
- (internal . ,(filter int? modules))
- (external . ,(remove int? modules))
- (i-up . ,(filter int? (map edge-down edges)))
- (x-up . ,(remove int? (map edge-down edges)))
- (i-down . ,(filter int? (map edge-up edges)))
- (x-down . ,(remove int? (map edge-up edges)))
- (edges . ,edges)))))
-
-(define (make-frisker . options)
- (let ((default-module (or (assq-ref options 'default-module)
- *default-module*)))
- (lambda (files)
- (scan default-module files))))
-
-(define (dump-updown modules)
- (for-each (lambda (m)
- (format #t "~A ~A --- ~A --- ~A\n"
- (i-or-x m) m
- (map (lambda (edge)
- (cons (edge-type edge)
- (edge-up edge)))
- (up-ls m))
- (map (lambda (edge)
- (cons (edge-type edge)
- (edge-down edge)))
- (dn-ls m))))
- modules))
-
-(define (dump-up modules)
- (for-each (lambda (m)
- (format #t "~A ~A\n" (i-or-x m) m)
- (for-each (lambda (edge)
- (format #t "\t\t\t ~A\t~A\n"
- (edge-type edge) (edge-up edge)))
- (up-ls m)))
- modules))
-
-(define (dump-down modules)
- (for-each (lambda (m)
- (format #t "~A ~A\n" (i-or-x m) m)
- (for-each (lambda (edge)
- (format #t "\t\t\t ~A\t~A\n"
- (edge-type edge) (edge-down edge)))
- (dn-ls m)))
- modules))
-
-(define (frisk . args)
- (let* ((parsed-opts (getopt-long
- (cons "frisk" args) ;;; kludge
- '((upstream (single-char #\u))
- (downstream (single-char #\d))
- (internal (single-char #\i))
- (external (single-char #\x))
- (default-module
- (single-char #\m)
- (value #t)))))
- (=u (option-ref parsed-opts 'upstream #f))
- (=d (option-ref parsed-opts 'downstream #f))
- (=i (option-ref parsed-opts 'internal #f))
- (=x (option-ref parsed-opts 'external #f))
- (files (option-ref parsed-opts '() (list)))
- (report ((make-frisker
- `(default-module
- . ,(option-ref parsed-opts 'default-module
- *default-module*)))
- files))
- (modules (report 'modules))
- (internal (report 'internal))
- (external (report 'external))
- (edges (report 'edges)))
- (format #t "~A ~A, ~A ~A (~A ~A, ~A ~A), ~A ~A\n\n"
- (length files) "files"
- (length modules) "modules"
- (length internal) "internal"
- (length external) "external"
- (length edges) "edges")
- ((cond ((and =u =d) dump-updown)
- (=u dump-up)
- (else dump-down))
- (cond ((and =i =x) modules)
- (=i internal)
- (else external)))))
-
-(define main frisk)
-
-;;; frisk ends here
-;;; generate-autoload --- Display define-module form with autoload info
-
-;; Copyright (C) 2001, 2006, 2011 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Thien-Thi Nguyen
-
-;;; Commentary:
-
-;; Usage: generate-autoload [OPTIONS] FILE1 FILE2 ...
-;;
-;; The autoload form is displayed to standard output:
-;;
-;; (define-module (guile-user)
-;; :autoload (ZAR FOO) (FOO-1 FOO-2 ...)
-;; :
-;; :
-;; :autoload (ZAR BAR) (BAR-1 BAR-2 ...))
-;;
-;; For each file, a symbol triggers an autoload if it is found in one
-;; of these situations:
-;; - in the `:export' clause of a `define-module' form
-;; - in a top-level `export' or `export-syntax' form
-;; - in a `define-public' form
-;; - in a `defmacro-public' form
-;;
-;; The module name is inferred from the `define-module' form. If either the
-;; module name or the exports list cannot be determined, no autoload entry is
-;; generated for that file.
-;;
-;; Options:
-;; --target MODULE-NAME -- Use MODULE-NAME instead of `(guile-user)'.
-;; Note that some shells may require you to
-;; quote the argument to handle parentheses
-;; and spaces.
-;;
-;; Usage examples from Scheme code as a module:
-;; (use-modules (scripts generate-autoload))
-;; (generate-autoload "generate-autoload")
-;; (generate-autoload "--target" "(my module)" "generate-autoload")
-;; (apply generate-autoload "--target" "(my module)" '("foo" "bar" "baz"))
-
-;;; Code:
-
-(define-module (scripts generate-autoload)
- \:export (generate-autoload))
-
-(define %include-in-guild-list #f)
-(define %summary "Generate #\autoload clauses for a module.")
-
-(define (autoload-info file)
- (let ((p (open-input-file file)))
- (let loop ((form (read p)) (module-name #f) (exports '()))
- (if (eof-object? form)
- (and module-name
- (not (null? exports))
- (list module-name exports)) ; ret
- (cond ((and (list? form)
- (< 1 (length form))
- (eq? 'define-module (car form)))
- (loop (read p)
- (cadr form)
- (cond ((member '\:export form)
- => (lambda (val)
- (append (cadr val) exports)))
- (else exports))))
- ((and (list? form)
- (< 1 (length form))
- (memq (car form) '(export export-syntax)))
- (loop (read p)
- module-name
- (append (cdr form) exports)))
- ((and (list? form)
- (< 2 (length form))
- (eq? 'define-public (car form))
- (list? (cadr form))
- (symbol? (caadr form)))
- (loop (read p)
- module-name
- (cons (caadr form) exports)))
- ((and (list? form)
- (< 2 (length form))
- (eq? 'define-public (car form))
- (symbol? (cadr form)))
- (loop (read p)
- module-name
- (cons (cadr form) exports)))
- ((and (list? form)
- (< 3 (length form))
- (eq? 'defmacro-public (car form))
- (symbol? (cadr form)))
- (loop (read p)
- module-name
- (cons (cadr form) exports)))
- (else (loop (read p) module-name exports)))))))
-
-(define (generate-autoload . args)
- (let* ((module-count 0)
- (syms-count 0)
- (target-override (cond ((member "--target" args) => cadr)
- (else #f)))
- (files (if target-override (cddr args) (cdr args))))
- (display ";;; do not edit --- generated ")
- (display (strftime "%Y-%m-%d %H:%M:%S" (localtime (current-time))))
- (newline)
- (display "(define-module ")
- (display (or target-override "(guile-user)"))
- (for-each (lambda (file)
- (cond ((autoload-info file)
- => (lambda (info)
- (and info
- (apply (lambda (module-name exports)
- (set! module-count (1+ module-count))
- (set! syms-count (+ (length exports)
- syms-count))
- (for-each display
- (list "\n :autoload "
- module-name " "
- exports)))
- info))))))
- files)
- (display ")")
- (newline)
- (for-each display (list " ;;; "
- syms-count " symbols in "
- module-count " modules\n"))))
-
-(define main generate-autoload)
-
-;;; generate-autoload ends here
-;;; Help --- Show help on guild commands
-
-;;;; Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free
-;;;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;;;; Boston, MA 02110-1301 USA
-
-;;; Commentary:
-
-;; Usage: help
-;;
-;; Show help for Guild scripts.
-
-;;; Code:
-
-(define-module (scripts help)
- #\use-module (ice-9 format)
- #\use-module (ice-9 documentation)
- #\use-module ((srfi srfi-1) #\select (fold append-map))
- #\export (show-help show-summary show-usage main))
-
-(define %summary "Show a brief help message.")
-(define %synopsis "help\nhelp --all\nhelp COMMAND")
-(define %help "
-Show help on guild commands. With --all, show arcane incantations as
-well. With COMMAND, show more detailed help for a particular command.
-")
-
-
-(define (directory-files dir)
- (if (and (file-exists? dir) (file-is-directory? dir))
- (let ((dir-stream (opendir dir)))
- (let loop ((new (readdir dir-stream))
- (acc '()))
- (if (eof-object? new)
- (begin
- (closedir dir-stream)
- acc)
- (loop (readdir dir-stream)
- (if (or (string=? "." new) ; ignore
- (string=? ".." new)) ; ignore
- acc
- (cons new acc))))))
- '()))
-
-(define (strip-extensions path)
- (or-map (lambda (ext)
- (and
- (string-suffix? ext path)
- ;; We really can't be adding e.g. ChangeLog-2008 to the set
- ;; of runnable scripts, just because "" is a valid
- ;; extension, by default. So hack around that here.
- (not (string-null? ext))
- (substring path 0
- (- (string-length path) (string-length ext)))))
- (append %load-compiled-extensions %load-extensions)))
-
-(define (unique l)
- (cond ((null? l) l)
- ((null? (cdr l)) l)
- ((equal? (car l) (cadr l)) (unique (cdr l)))
- (else (cons (car l) (unique (cdr l))))))
-
-(define (find-submodules head)
- (let ((shead (map symbol->string head)))
- (unique
- (sort
- (append-map (lambda (path)
- (fold (lambda (x rest)
- (let ((stripped (strip-extensions x)))
- (if stripped (cons stripped rest) rest)))
- '()
- (directory-files
- (fold (lambda (x y) (in-vicinity y x)) path shead))))
- %load-path)
- string<?))))
-
-(define (list-commands all?)
- (display "\\
-Usage: guild COMMAND [ARGS]
-Run command-line scripts provided by GNU Guile and related programs.
-
-Commands:
-")
-
- (for-each
- (lambda (name)
- (let* ((modname `(scripts ,(string->symbol name)))
- (mod (resolve-module modname #\ensure #f))
- (summary (and mod (and=> (module-variable mod '%summary)
- variable-ref))))
- (if (and mod
- (or all?
- (let ((v (module-variable mod '%include-in-guild-list)))
- (if v (variable-ref v) #t))))
- (if summary
- (format #t " ~A ~23t~a\n" name summary)
- (format #t " ~A\n" name)))))
- (find-submodules '(scripts)))
- (format #t "
-For help on a specific command, try \"guild help COMMAND\".
-
-Report guild bugs to ~a
-GNU Guile home page: <http://www.gnu.org/software/guile/>
-General help using GNU software: <http://www.gnu.org/gethelp/>
-For complete documentation, run: info guile 'Using Guile Tools'
-" %guile-bug-report-address))
-
-(define (module-commentary mod)
- (file-commentary
- (%search-load-path (module-filename mod))))
-
-(define (module-command-name mod)
- (symbol->string (car (last-pair (module-name mod)))))
-
-(define* (show-usage mod #\optional (port (current-output-port)))
- (let ((usages (string-split
- (let ((var (module-variable mod '%synopsis)))
- (if var
- (variable-ref var)
- (string-append (module-command-name mod)
- " OPTION...")))
- #\newline)))
- (display "Usage: guild " port)
- (display (car usages))
- (newline port)
- (for-each (lambda (u)
- (display " guild " port)
- (display u port)
- (newline port))
- (cdr usages))))
-
-(define* (show-summary mod #\optional (port (current-output-port)))
- (let ((var (module-variable mod '%summary)))
- (if var
- (begin
- (display (variable-ref var) port)
- (newline port)))))
-
-(define* (show-help mod #\optional (port (current-output-port)))
- (show-usage mod port)
- (show-summary mod port)
- (cond
- ((module-variable mod '%help)
- => (lambda (var)
- (display (variable-ref var) port)
- (newline port)))
- ((module-commentary mod)
- => (lambda (commentary)
- (newline port)
- (display commentary port)))
- (else
- (format #t "No documentation found for command \"~a\".\n"
- (module-command-name mod)))))
-
-(define %mod (current-module))
-(define (main . args)
- (cond
- ((null? args)
- (list-commands #f))
- ((or (equal? args '("--all")) (equal? args '("-a")))
- (list-commands #t))
- ((and (null? (cdr args)) (not (string-prefix? "-" (car args))))
- ;; help for particular command
- (let ((name (car args)))
- (cond
- ((resolve-module `(scripts ,(string->symbol name)) #\ensure #f)
- => (lambda (mod)
- (show-help mod)
- (exit 0)))
- (else
- (format #t "No command named \"~a\".\n" name)
- (exit 1)))))
- (else
- (show-help %mod (current-error-port))
- (exit 1))))
-;;; lint --- Preemptive checks for coding errors in Guile Scheme code
-
-;; Copyright (C) 2002, 2006, 2011 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Neil Jerram
-
-;;; Commentary:
-
-;; Usage: lint FILE1 FILE2 ...
-;;
-;; Perform various preemptive checks for coding errors in Guile Scheme
-;; code.
-;;
-;; Right now, there is only one check available, for unresolved free
-;; variables. The intention is that future lint-like checks will be
-;; implemented by adding to this script file.
-;;
-;; Unresolved free variables
-;; -------------------------
-;;
-;; Free variables are those whose definitions come from outside the
-;; module under investigation. In Guile, these definitions are
-;; imported from other modules using `#\use-module' forms.
-;;
-;; This tool scans the specified files for unresolved free variables -
-;; i.e. variables for which you may have forgotten the appropriate
-;; `#\use-module', or for which the module that is supposed to export
-;; them forgot to.
-;;
-;; It isn't guaranteed that the scan will find absolutely all such
-;; errors. Quoted (and quasiquoted) expressions are skipped, since
-;; they are most commonly used to describe constant data, not code, so
-;; code that is explicitly evaluated using `eval' will not be checked.
-;; For example, the `unresolved-var' in `(eval 'unresolved-var
-;; (current-module))' would be missed.
-;;
-;; False positives are also possible. Firstly, the tool doesn't
-;; understand all possible forms of implicit quoting; in particular,
-;; it doesn't detect and expand uses of macros. Secondly, it picks up
-;; explicit compatibility code like `(if (defined? 'x) (define y x))'.
-;; Thirdly, there are occasional oddities like `next-method'.
-;; However, the number of false positives for realistic code is
-;; hopefully small enough that they can be individually considered and
-;; ignored.
-;;
-;; Example
-;; -------
-;;
-;; Note: most of the unresolved variables found in this example are
-;; false positives, as you would hope. => scope for improvement.
-;;
-;; $ guild lint `guild`
-;; No unresolved free variables in PROGRAM
-;; No unresolved free variables in autofrisk
-;; No unresolved free variables in display-commentary
-;; Unresolved free variables in doc-snarf:
-;; doc-snarf-version
-;; No unresolved free variables in frisk
-;; No unresolved free variables in generate-autoload
-;; No unresolved free variables in lint
-;; No unresolved free variables in punify
-;; No unresolved free variables in read-scheme-source
-;; Unresolved free variables in snarf-check-and-output-texi:
-;; name
-;; pos
-;; line
-;; x
-;; rest
-;; ...
-;; do-argpos
-;; do-command
-;; do-args
-;; type
-;; num
-;; file
-;; do-arglist
-;; req
-;; opt
-;; var
-;; command
-;; do-directive
-;; s
-;; ?
-;; No unresolved free variables in use2dot
-
-;;; Code:
-
-(define-module (scripts lint)
- #\use-module (ice-9 common-list)
- #\use-module (ice-9 format)
- #\export (lint))
-
-(define %include-in-guild-list #f)
-(define %summary "Check for bugs and style errors in a Scheme file.")
-
-(define (lint filename)
- (let ((module-name (scan-file-for-module-name filename))
- (free-vars (uniq (scan-file-for-free-variables filename))))
- (let ((module (resolve-module module-name))
- (all-resolved? #t))
- (format #t "Resolved module: ~S\n" module)
- (let loop ((free-vars free-vars))
- (or (null? free-vars)
- (begin
- (catch #t
- (lambda ()
- (eval (car free-vars) module))
- (lambda args
- (if all-resolved?
- (format #t
- "Unresolved free variables in ~A:\n"
- filename))
- (write-char #\tab)
- (write (car free-vars))
- (newline)
- (set! all-resolved? #f)))
- (loop (cdr free-vars)))))
- (if all-resolved?
- (format #t
- "No unresolved free variables in ~A\n"
- filename)))))
-
-(define (scan-file-for-module-name filename)
- (with-input-from-file filename
- (lambda ()
- (let loop ((x (read)))
- (cond ((eof-object? x) #f)
- ((and (pair? x)
- (eq? (car x) 'define-module))
- (cadr x))
- (else (loop (read))))))))
-
-(define (scan-file-for-free-variables filename)
- (with-input-from-file filename
- (lambda ()
- (let loop ((x (read)) (fvlists '()))
- (if (eof-object? x)
- (apply append fvlists)
- (loop (read) (cons (detect-free-variables x '()) fvlists)))))))
-
-; guile> (detect-free-variables '(let ((a 1)) a) '())
-; ()
-; guile> (detect-free-variables '(let ((a 1)) b) '())
-; (b)
-; guile> (detect-free-variables '(let ((a 1) (b a)) b) '())
-; (a)
-; guile> (detect-free-variables '(let* ((a 1) (b a)) b) '())
-; ()
-; guile> (detect-free-variables '(define a 1) '())
-; ()
-; guile> (detect-free-variables '(define a b) '())
-; (b)
-; guile> (detect-free-variables '(define (a b c) b) '())
-; ()
-; guile> (detect-free-variables '(define (a b c) e) '())
-; (e)
-
-(define (detect-free-variables x locals)
- ;; Given an expression @var{x} and a list @var{locals} of local
- ;; variables (symbols) that are in scope for @var{x}, return a list
- ;; of free variable symbols.
- (cond ((symbol? x)
- (if (memq x locals) '() (list x)))
-
- ((pair? x)
- (case (car x)
- ((define-module define-generic quote quasiquote)
- ;; No code of interest in these expressions.
- '())
-
- ((let letrec)
- ;; Check for named let. If there is a name, transform the
- ;; expression so that it looks like an unnamed let with
- ;; the name as one of the bindings.
- (if (symbol? (cadr x))
- (set-cdr! x (cons (cons (list (cadr x) #f) (caddr x))
- (cdddr x))))
- ;; Unnamed let processing.
- (let ((letrec? (eq? (car x) 'letrec))
- (locals-for-let-body (append locals (map car (cadr x)))))
- (append (apply append
- (map (lambda (binding)
- (detect-free-variables (cadr binding)
- (if letrec?
- locals-for-let-body
- locals)))
- (cadr x)))
- (apply append
- (map (lambda (bodyform)
- (detect-free-variables bodyform
- locals-for-let-body))
- (cddr x))))))
-
- ((let* and-let*)
- ;; Handle bindings recursively.
- (if (null? (cadr x))
- (apply append
- (map (lambda (bodyform)
- (detect-free-variables bodyform locals))
- (cddr x)))
- (append (detect-free-variables (cadr (caadr x)) locals)
- (detect-free-variables `(let* ,(cdadr x) ,@(cddr x))
- (cons (caaadr x) locals)))))
-
- ((define define-public define-macro)
- (if (pair? (cadr x))
- (begin
- (set! locals (cons (caadr x) locals))
- (detect-free-variables `(lambda ,(cdadr x) ,@(cddr x))
- locals))
- (begin
- (set! locals (cons (cadr x) locals))
- (detect-free-variables (caddr x) locals))))
-
- ((lambda lambda*)
- (let ((locals-for-lambda-body (let loop ((locals locals)
- (args (cadr x)))
- (cond ((null? args) locals)
- ((pair? args)
- (loop (cons (car args) locals)
- (cdr args)))
- (else
- (cons args locals))))))
- (apply append
- (map (lambda (bodyform)
- (detect-free-variables bodyform
- locals-for-lambda-body))
- (cddr x)))))
-
- ((receive)
- (let ((locals-for-receive-body (append locals (cadr x))))
- (apply append
- (detect-free-variables (caddr x) locals)
- (map (lambda (bodyform)
- (detect-free-variables bodyform
- locals-for-receive-body))
- (cdddr x)))))
-
- ((define-method define*)
- (let ((locals-for-method-body (let loop ((locals locals)
- (args (cdadr x)))
- (cond ((null? args) locals)
- ((pair? args)
- (loop (cons (if (pair? (car args))
- (caar args)
- (car args))
- locals)
- (cdr args)))
- (else
- (cons args locals))))))
- (apply append
- (map (lambda (bodyform)
- (detect-free-variables bodyform
- locals-for-method-body))
- (cddr x)))))
-
- ((define-class)
- ;; Avoid picking up slot names at the start of slot
- ;; definitions.
- (apply append
- (map (lambda (slot/option)
- (detect-free-variables-noncar (if (pair? slot/option)
- (cdr slot/option)
- slot/option)
- locals))
- (cdddr x))))
-
- ((case)
- (apply append
- (detect-free-variables (cadr x) locals)
- (map (lambda (case)
- (detect-free-variables (cdr case) locals))
- (cddr x))))
-
- ((unquote unquote-splicing else =>)
- (detect-free-variables-noncar (cdr x) locals))
-
- (else (append (detect-free-variables (car x) locals)
- (detect-free-variables-noncar (cdr x) locals)))))
-
- (else '())))
-
-(define (detect-free-variables-noncar x locals)
- ;; Given an expression @var{x} and a list @var{locals} of local
- ;; variables (symbols) that are in scope for @var{x}, return a list
- ;; of free variable symbols.
- (cond ((symbol? x)
- (if (memq x locals) '() (list x)))
-
- ((pair? x)
- (case (car x)
- ((=>)
- (detect-free-variables-noncar (cdr x) locals))
-
- (else (append (detect-free-variables (car x) locals)
- (detect-free-variables-noncar (cdr x) locals)))))
-
- (else '())))
-
-(define (main . files)
- (for-each lint files))
-
-;;; lint ends here
-;;; List --- List scripts that can be invoked by guild -*- coding: iso-8859-1 -*-
-
-;;;; Copyright (C) 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free
-;;;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;;;; Boston, MA 02110-1301 USA
-
-;;; Commentary:
-
-;; Usage: list
-;;
-;; List scripts that can be invoked by guild.
-
-;;; Code:
-
-(define-module (scripts list)
- #\use-module (srfi srfi-1)
- #\export (list-scripts))
-
-(define %include-in-guild-list #f)
-(define %summary "An alias for \"help\".")
-
-
-(define (directory-files dir)
- (if (and (file-exists? dir) (file-is-directory? dir))
- (let ((dir-stream (opendir dir)))
- (let loop ((new (readdir dir-stream))
- (acc '()))
- (if (eof-object? new)
- (begin
- (closedir dir-stream)
- acc)
- (loop (readdir dir-stream)
- (if (or (string=? "." new) ; ignore
- (string=? ".." new)) ; ignore
- acc
- (cons new acc))))))
- '()))
-
-(define (strip-extensions path)
- (or-map (lambda (ext)
- (and
- (string-suffix? ext path)
- ;; We really can't be adding e.g. ChangeLog-2008 to the set
- ;; of runnable scripts, just because "" is a valid
- ;; extension, by default. So hack around that here.
- (not (string-null? ext))
- (substring path 0
- (- (string-length path) (string-length ext)))))
- (append %load-compiled-extensions %load-extensions)))
-
-(define (unique l)
- (cond ((null? l) l)
- ((null? (cdr l)) l)
- ((equal? (car l) (cadr l)) (unique (cdr l)))
- (else (cons (car l) (unique (cdr l))))))
-
-(define (find-submodules head)
- (let ((shead (map symbol->string head)))
- (unique
- (sort
- (append-map (lambda (path)
- (fold (lambda (x rest)
- (let ((stripped (strip-extensions x)))
- (if stripped (cons stripped rest) rest)))
- '()
- (directory-files
- (fold (lambda (x y) (in-vicinity y x)) path shead))))
- %load-path)
- string<?))))
-
-(define (list-scripts . args)
- (for-each (lambda (x)
- ;; would be nice to show a summary.
- (format #t "~A\n" x))
- (find-submodules '(scripts))))
-
-(define (main . args)
- (apply (@@ (scripts help) main) args))
-;;; punify --- Display Scheme code w/o unnecessary comments / whitespace
-
-;; Copyright (C) 2001, 2006 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Thien-Thi Nguyen
-
-;;; Commentary:
-
-;; Usage: punify FILE1 FILE2 ...
-;;
-;; Each file's forms are read and written to stdout.
-;; The effect is to remove comments and much non-essential whitespace.
-;; This is useful when installing Scheme source to space-limited media.
-;;
-;; Example:
-;; $ wc ./punify ; ./punify ./punify | wc
-;; 89 384 3031 ./punify
-;; 0 42 920
-;;
-;; TODO: Read from stdin.
-;; Handle vectors.
-;; Identifier punification.
-
-;;; Code:
-
-(define-module (scripts punify)
- \:export (punify))
-
-(define %include-in-guild-list #f)
-(define %summary "Strip comments and whitespace from a Scheme file.")
-
-(define (write-punily form)
- (cond ((and (list? form) (not (null? form)))
- (let ((first (car form)))
- (display "(")
- (write-punily first)
- (let loop ((ls (cdr form)) (last-was-list? (list? first)))
- (if (null? ls)
- (display ")")
- (let* ((new-first (car ls))
- (this-is-list? (list? new-first)))
- (and (not last-was-list?)
- (not this-is-list?)
- (display " "))
- (write-punily new-first)
- (loop (cdr ls) this-is-list?))))))
- ((and (symbol? form)
- (let ((ls (string->list (symbol->string form))))
- (and (char=? (car ls) #\:)
- (not (memq #\space ls))
- (list->string (cdr ls)))))
- => (lambda (symbol-name-after-colon)
- (display #\:)
- (display symbol-name-after-colon)))
- (else (write form))))
-
-(define (punify-one file)
- (with-input-from-file file
- (lambda ()
- (let ((toke (lambda () (read (current-input-port)))))
- (let loop ((form (toke)))
- (or (eof-object? form)
- (begin
- (write-punily form)
- (loop (toke)))))))))
-
-(define (punify . args)
- (for-each punify-one args))
-
-(define main punify)
-
-;;; punify ends here
-;;; read-rfc822 --- Validate RFC822 file by displaying it to stdout
-
-;; Copyright (C) 2002, 2004, 2006, 2011 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Thien-Thi Nguyen <ttn@gnu.org>
-
-;;; Commentary:
-
-;; Usage: read-rfc822 FILE
-;;
-;; Read FILE, assumed to be in RFC822 format, and display it to stdout.
-;; This is not very interesting, admittedly.
-;;
-;; For Scheme programming, this module exports two procs:
-;; (read-rfc822 . args) ; only first arg used
-;; (read-rfc822-silently port)
-;;
-;; Parse FILE (a string) or PORT, respectively, and return a query proc that
-;; takes a symbol COMP, and returns the message component COMP. Supported
-;; values for COMP (and the associated query return values) are:
-;; from -- #f (reserved for future mbox support)
-;; headers -- alist of (HEADER-SYMBOL . "VALUE-STRING") pairs, in order
-;; body -- rest of the mail message, a string
-;; body-lines -- rest of the mail message, as a list of lines
-;; Any other query results in a "bad component" error.
-;;
-;; TODO: Add "-m" option (mbox support).
-
-;;; Code:
-
-(define-module (scripts read-rfc822)
- \:use-module (ice-9 regex)
- \:use-module (ice-9 rdelim)
- \:autoload (srfi srfi-13) (string-join)
- \:export (read-rfc822 read-rfc822-silently))
-
-(define %include-in-guild-list #f)
-(define %summary "Validate an RFC822-style file.")
-
-(define from-line-rx (make-regexp "^From "))
-(define header-name-rx (make-regexp "^([^:]+):[ \t]*"))
-(define header-cont-rx (make-regexp "^[ \t]+"))
-
-(define option #f) ; for future "-m"
-
-(define (drain-message port)
- (let loop ((line (read-line port)) (acc '()))
- (cond ((eof-object? line)
- (reverse acc))
- ((and option (regexp-exec from-line-rx line))
- (for-each (lambda (c)
- (unread-char c port))
- (cons #\newline
- (reverse (string->list line))))
- (reverse acc))
- (else
- (loop (read-line port) (cons line acc))))))
-
-(define (parse-message port)
- (let* ((from (and option
- (match:suffix (regexp-exec from-line-rx
- (read-line port)))))
- (body-lines #f)
- (body #f)
- (headers '())
- (add-header! (lambda (reversed-hlines)
- (let* ((hlines (reverse reversed-hlines))
- (first (car hlines))
- (m (regexp-exec header-name-rx first))
- (name (string->symbol (match:substring m 1)))
- (data (string-join
- (cons (substring first (match:end m))
- (cdr hlines))
- " ")))
- (set! headers (acons name data headers))))))
- ;; "From " is only one line
- (let loop ((line (read-line port)) (current-header #f))
- (cond ((string-null? line)
- (and current-header (add-header! current-header))
- (set! body-lines (drain-message port)))
- ((regexp-exec header-cont-rx line)
- => (lambda (m)
- (loop (read-line port)
- (cons (match:suffix m) current-header))))
- (else
- (and current-header (add-header! current-header))
- (loop (read-line port) (list line)))))
- (set! headers (reverse headers))
- (lambda (component)
- (case component
- ((from) from)
- ((body-lines) body-lines)
- ((headers) headers)
- ((body) (or body
- (begin (set! body (string-join body-lines "\n" 'suffix))
- body)))
- (else (error "bad component:" component))))))
-
-(define (read-rfc822-silently port)
- (parse-message port))
-
-(define (display-rfc822 parse)
- (cond ((parse 'from) => (lambda (from) (format #t "From ~A\n" from))))
- (for-each (lambda (header)
- (format #t "~A: ~A\n" (car header) (cdr header)))
- (parse 'headers))
- (format #t "\n~A" (parse 'body)))
-
-(define (read-rfc822 . args)
- (let ((parse (read-rfc822-silently (open-file (car args) OPEN_READ))))
- (display-rfc822 parse))
- #t)
-
-(define main read-rfc822)
-
-;;; read-rfc822 ends here
-;;; read-scheme-source --- Read a file, recognizing scheme forms and comments
-
-;; Copyright (C) 2001, 2006, 2011 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Thien-Thi Nguyen
-
-;;; Commentary:
-
-;; Usage: read-scheme-source FILE1 FILE2 ...
-;;
-;; This program parses each FILE and writes to stdout sexps that describe the
-;; top-level structures of the file: scheme forms, single-line comments, and
-;; hash-bang comments. You can further process these (to associate comments
-;; w/ scheme forms as a kind of documentation, for example).
-;;
-;; The output sexps have one of these forms:
-;;
-;; (quote (filename FILENAME))
-;;
-;; (quote (comment :leading-semicolons N
-;; :text LINE))
-;;
-;; (quote (whitespace :text LINE))
-;;
-;; (quote (hash-bang-comment :line LINUM
-;; :line-count N
-;; :text-list (LINE1 LINE2 ...)))
-;;
-;; (quote (following-form-properties :line LINUM
-;; :line-count N)
-;; :type TYPE
-;; :signature SIGNATURE
-;; :std-int-doc DOCSTRING))
-;;
-;; SEXP
-;;
-;; The first four are straightforward (both FILENAME and LINE are strings sans
-;; newline, while LINUM and N are integers). The last two always go together,
-;; in that order. SEXP is scheme code processed only by `read' and then
-;; `write'.
-;;
-;; The :type field may be omitted if the form is not recognized. Otherwise,
-;; TYPE may be one of: procedure, alias, define-module, variable.
-;;
-;; The :signature field may be omitted if the form is not a procedure.
-;; Otherwise, SIGNATURE is a list showing the procedure's signature.
-;;
-;; If the type is `procedure' and the form has a standard internal docstring
-;; (first body form a string), that is extracted in full -- including any
-;; embedded newlines -- and recorded by field :std-int-doc.
-;;
-;;
-;; Usage from a program: The output list of sexps can be retrieved by scheme
-;; programs w/o having to capture stdout, like so:
-;;
-;; (use-modules (scripts read-scheme-source))
-;; (define source-forms (read-scheme-source-silently "FILE1" "FILE2" ...))
-;;
-;; There are also two convenience procs exported for use by Scheme programs:
-;;
-;; (clump FORMS) --- filter FORMS combining contiguous comment forms that
-;; have the same number of leading semicolons.
-;;
-;; (quoted? SYM FORM) --- see if FORM looks like: "(quote (SYM ...))", parse
-;; the ":tags", and return alist of (TAG . VAL) elems.
-;;
-;; TODO: Add option "--clump-comments", maybe w/ different clumping styles.
-;; Make `annotate!' extensible.
-
-;;; Code:
-
-(define-module (scripts read-scheme-source)
- \:use-module (ice-9 rdelim)
- \:export (read-scheme-source
- read-scheme-source-silently
- quoted?
- clump))
-
-(define %include-in-guild-list #f)
-(define %summary "Print a parsed representation of a Scheme file.")
-
-;; Try to figure out what FORM is and its various attributes.
-;; Call proc NOTE! with key (a symbol) and value.
-;;
-(define (annotate! form note!)
- (cond ((and (list? form)
- (< 2 (length form))
- (eq? 'define (car form))
- (pair? (cadr form))
- (symbol? (caadr form)))
- (note! '\:type 'procedure)
- (note! '\:signature (cadr form))
- (and (< 3 (length form))
- (string? (caddr form))
- (note! '\:std-int-doc (caddr form))))
- ((and (list? form)
- (< 2 (length form))
- (eq? 'define (car form))
- (symbol? (cadr form))
- (list? (caddr form))
- (< 3 (length (caddr form)))
- (eq? 'lambda (car (caddr form)))
- (string? (caddr (caddr form))))
- (note! '\:type 'procedure)
- (note! '\:signature (cons (cadr form) (cadr (caddr form))))
- (note! '\:std-int-doc (caddr (caddr form))))
- ((and (list? form)
- (= 3 (length form))
- (eq? 'define (car form))
- (symbol? (cadr form))
- (symbol? (caddr form)))
- (note! '\:type 'alias))
- ((and (list? form)
- (eq? 'define-module (car form)))
- (note! '\:type 'define-module))
- ;; Add other types here.
- (else (note! '\:type 'variable))))
-
-;; Process FILE, calling NB! on parsed top-level elements.
-;; Recognized: #!-!# and regular comments in addition to normal forms.
-;;
-(define (process file nb!)
- (nb! `'(filename ,file))
- (let ((hash-bang-rx (make-regexp "^#!"))
- (bang-hash-rx (make-regexp "^!#"))
- (all-comment-rx (make-regexp "^[ \t]*(;+)"))
- (all-whitespace-rx (make-regexp "^[ \t]*$"))
- (p (open-input-file file)))
- (let loop ((n (1+ (port-line p))) (line (read-line p)))
- (or (not n)
- (eof-object? line)
- (begin
- (cond ((regexp-exec hash-bang-rx line)
- (let loop ((line (read-line p))
- (text (list line)))
- (if (or (eof-object? line)
- (regexp-exec bang-hash-rx line))
- (nb! `'(hash-bang-comment
- \:line ,n
- \:line-count ,(1+ (length text))
- \:text-list ,(reverse
- (cons line text))))
- (loop (read-line p)
- (cons line text)))))
- ((regexp-exec all-whitespace-rx line)
- (nb! `'(whitespace \:text ,line)))
- ((regexp-exec all-comment-rx line)
- => (lambda (m)
- (nb! `'(comment
- \:leading-semicolons
- ,(let ((m1 (vector-ref m 1)))
- (- (cdr m1) (car m1)))
- \:text ,line))))
- (else
- (unread-string line p)
- (let* ((form (read p))
- (count (- (port-line p) n))
- (props (let* ((props '())
- (prop+ (lambda args
- (set! props
- (append props args)))))
- (annotate! form prop+)
- props)))
- (or (= count 1) ; ugh
- (begin
- (read-line p)
- (set! count (1+ count))))
- (nb! `'(following-form-properties
- \:line ,n
- \:line-count ,count
- ,@props))
- (nb! form))))
- (loop (1+ (port-line p)) (read-line p)))))))
-
-;;; entry points
-
-(define (read-scheme-source-silently . files)
- "See commentary in module (scripts read-scheme-source)."
- (let* ((res '()))
- (for-each (lambda (file)
- (process file (lambda (e) (set! res (cons e res)))))
- files)
- (reverse res)))
-
-(define (read-scheme-source . files)
- "See commentary in module (scripts read-scheme-source)."
- (for-each (lambda (file)
- (process file (lambda (e) (write e) (newline))))
- files))
-
-;; Recognize: (quote (SYM :TAG1 VAL1 :TAG2 VAL2 ...))
-;; and return alist: ((TAG1 . VAL1) (TAG2 . VAL2) ...)
-;; where the tags are symbols.
-;;
-(define (quoted? sym form)
- (and (list? form)
- (= 2 (length form))
- (eq? 'quote (car form))
- (let ((inside (cadr form)))
- (and (list? inside)
- (< 0 (length inside))
- (eq? sym (car inside))
- (let loop ((ls (cdr inside)) (alist '()))
- (if (null? ls)
- alist ; retval
- (let ((first (car ls)))
- (or (symbol? first)
- (error "bad list!"))
- (loop (cddr ls)
- (acons (string->symbol
- (substring (symbol->string first) 1))
- (cadr ls)
- alist)))))))))
-
-;; Filter FORMS, combining contiguous comment forms that have the same number
-;; of leading semicolons. Do not include in them whitespace lines.
-;; Whitespace lines outside of such comment groupings are ignored, as are
-;; hash-bang comments. All other forms are passed through unchanged.
-;;
-(define (clump forms)
- (let loop ((forms forms) (acc '()) (pass-this-one-through? #f))
- (if (null? forms)
- (reverse acc) ; retval
- (let ((form (car forms)))
- (cond (pass-this-one-through?
- (loop (cdr forms) (cons form acc) #f))
- ((quoted? 'following-form-properties form)
- (loop (cdr forms) (cons form acc) #t))
- ((quoted? 'whitespace form) ;;; ignore
- (loop (cdr forms) acc #f))
- ((quoted? 'hash-bang-comment form) ;;; ignore for now
- (loop (cdr forms) acc #f))
- ((quoted? 'comment form)
- => (lambda (alist)
- (let cloop ((inner-forms (cdr forms))
- (level (assq-ref alist 'leading-semicolons))
- (text (list (assq-ref alist 'text))))
- (let ((up (lambda ()
- (loop inner-forms
- (cons (cons level (reverse text))
- acc)
- #f))))
- (if (null? inner-forms)
- (up)
- (let ((inner-form (car inner-forms)))
- (cond ((quoted? 'comment inner-form)
- => (lambda (inner-alist)
- (let ((new-level
- (assq-ref
- inner-alist
- 'leading-semicolons)))
- (if (= new-level level)
- (cloop (cdr inner-forms)
- level
- (cons (assq-ref
- inner-alist
- 'text)
- text))
- (up)))))
- (else (up)))))))))
- (else (loop (cdr forms) (cons form acc) #f)))))))
-
-;;; script entry point
-
-(define main read-scheme-source)
-
-;;; read-scheme-source ends here
-;;; read-text-outline --- Read a text outline and display it as a sexp
-
-;; Copyright (C) 2002, 2006, 2011 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Thien-Thi Nguyen <ttn@gnu.org>
-
-;;; Commentary:
-
-;; Usage: read-text-outline OUTLINE
-;;
-;; Scan OUTLINE file and display a list of trees, the structure of
-;; each reflecting the "levels" in OUTLINE. The recognized outline
-;; format (used to indicate outline headings) is zero or more pairs of
-;; leading spaces followed by "-". Something like:
-;;
-;; - a 0
-;; - b 1
-;; - c 2
-;; - d 1
-;; - e 0
-;; - f 1
-;; - g 2
-;; - h 1
-;;
-;; In this example the levels are shown to the right. The output for
-;; such a file would be the single line:
-;;
-;; (("a" ("b" "c") "d") ("e" ("f" "g") "h"))
-;;
-;; Basically, anything at the beginning of a list is a parent, and the
-;; remaining elements of that list are its children.
-;;
-;;
-;; Usage from a Scheme program: These two procs are exported:
-;;
-;; (read-text-outline . args) ; only first arg is used
-;; (read-text-outline-silently port)
-;; (make-text-outline-reader re specs)
-;;
-;; `make-text-outline-reader' returns a proc that reads from PORT and
-;; returns a list of trees (similar to `read-text-outline-silently').
-;;
-;; RE is a regular expression (string) that is used to identify a header
-;; line of the outline (as opposed to a whitespace line or intervening
-;; text). RE must begin w/ a sub-expression to match the "level prefix"
-;; of the line. You can use `level-submatch-number' in SPECS (explained
-;; below) to specify a number other than 1, the default.
-;;
-;; Normally, the level of the line is taken directly as the length of
-;; its level prefix. This often results in adjacent levels not mapping
-;; to adjacent numbers, which confuses the tree-building portion of the
-;; program, which expects top-level to be 0, first sub-level to be 1,
-;; etc. You can use `level-substring-divisor' or `compute-level' in
-;; SPECS to specify a constant scaling factor or specify a completely
-;; alternative procedure, respectively.
-;;
-;; SPECS is an alist which may contain the following key/value pairs:
-;;
-;; - level-submatch-number NUMBER
-;; - level-substring-divisor NUMBER
-;; - compute-level PROC
-;; - body-submatch-number NUMBER
-;; - extra-fields ((FIELD-1 . SUBMATCH-1) (FIELD-2 . SUBMATCH-2) ...)
-;;
-;; The PROC value associated with key `compute-level' should take a
-;; Scheme match structure (as returned by `regexp-exec') and return a
-;; number, the normalized level for that line. If this is specified,
-;; it takes precedence over other level-computation methods.
-;;
-;; Use `body-submatch-number' if RE specifies the whole body, or if you
-;; want to make use of the extra fields parsing. The `extra-fields'
-;; value is a sub-alist, whose keys name additional fields that are to
-;; be recognized. These fields along with `level' are set as object
-;; properties of the final string ("body") that is consed into the tree.
-;; If a field name ends in "?" the field value is set to be #t if there
-;; is a match and the result is not an empty string, and #f otherwise.
-;;
-;;
-;; Bugs and caveats:
-;;
-;; (1) Only the first file specified on the command line is scanned.
-;; (2) TAB characters at the beginnings of lines are not recognized.
-;; (3) Outlines that "skip" levels signal an error. In other words,
-;; this will fail:
-;;
-;; - a 0
-;; - b 1
-;; - c 3 <-- skipped 2 -- error!
-;; - d 1
-;;
-;;
-;; TODO: Determine what's the right thing to do for skips.
-;; Handle TABs.
-;; Make line format customizable via longopts.
-
-;;; Code:
-
-(define-module (scripts read-text-outline)
- \:export (read-text-outline
- read-text-outline-silently
- make-text-outline-reader)
- \:use-module (ice-9 regex)
- \:autoload (ice-9 rdelim) (read-line)
- \:autoload (ice-9 getopt-long) (getopt-long))
-
-(define %include-in-guild-list #f)
-(define %summary "Convert textual outlines to s-expressions.")
-
-(define (?? symbol)
- (let ((name (symbol->string symbol)))
- (string=? "?" (substring name (1- (string-length name))))))
-
-(define (msub n)
- (lambda (m)
- (match:substring m n)))
-
-(define (??-predicates pair)
- (cons (car pair)
- (if (?? (car pair))
- (lambda (m)
- (not (string=? "" (match:substring m (cdr pair)))))
- (msub (cdr pair)))))
-
-(define (make-line-parser re specs)
- (let* ((rx (let ((fc (substring re 0 1)))
- (make-regexp (if (string=? "^" fc)
- re
- (string-append "^" re)))))
- (check (lambda (key)
- (assq-ref specs key)))
- (level-substring (msub (or (check 'level-submatch-number) 1)))
- (extract-level (cond ((check 'compute-level)
- => (lambda (proc)
- (lambda (m)
- (proc m))))
- ((check 'level-substring-divisor)
- => (lambda (n)
- (lambda (m)
- (/ (string-length (level-substring m))
- n))))
- (else
- (lambda (m)
- (string-length (level-substring m))))))
- (extract-body (cond ((check 'body-submatch-number)
- => msub)
- (else
- (lambda (m) (match:suffix m)))))
- (misc-props! (cond ((check 'extra-fields)
- => (lambda (alist)
- (let ((new (map ??-predicates alist)))
- (lambda (obj m)
- (for-each
- (lambda (pair)
- (set-object-property!
- obj (car pair)
- ((cdr pair) m)))
- new)))))
- (else
- (lambda (obj m) #t)))))
- ;; retval
- (lambda (line)
- (cond ((regexp-exec rx line)
- => (lambda (m)
- (let ((level (extract-level m))
- (body (extract-body m)))
- (set-object-property! body 'level level)
- (misc-props! body m)
- body)))
- (else #f)))))
-
-(define (make-text-outline-reader re specs)
- (let ((parse-line (make-line-parser re specs)))
- ;; retval
- (lambda (port)
- (let* ((all '(start))
- (pchain (list))) ; parents chain
- (let loop ((line (read-line port))
- (prev-level -1) ; how this relates to the first input
- ; level determines whether or not we
- ; start in "sibling" or "child" mode.
- ; in the end, `start' is ignored and
- ; it's much easier to ignore parents
- ; than siblings (sometimes). this is
- ; not to encourage ignorance, however.
- (tp all)) ; tail pointer
- (or (eof-object? line)
- (cond ((parse-line line)
- => (lambda (w)
- (let* ((words (list w))
- (level (object-property w 'level))
- (diff (- level prev-level)))
- (cond
-
- ;; sibling
- ((zero? diff)
- ;; just extend the chain
- (set-cdr! tp words))
-
- ;; child
- ((positive? diff)
- (or (= 1 diff)
- (error "unhandled diff not 1:" diff line))
- ;; parent may be contacted by uncle later (kids
- ;; these days!) so save its level
- (set-object-property! tp 'level prev-level)
- (set! pchain (cons tp pchain))
- ;; "push down" car into hierarchy
- (set-car! tp (cons (car tp) words)))
-
- ;; uncle
- ((negative? diff)
- ;; prune back to where levels match
- (do ((p pchain (cdr p)))
- ((= level (object-property (car p) 'level))
- (set! pchain p)))
- ;; resume at this level
- (set-cdr! (car pchain) words)
- (set! pchain (cdr pchain))))
-
- (loop (read-line port) level words))))
- (else (loop (read-line port) prev-level tp)))))
- (set! all (car all))
- (if (eq? 'start all)
- '() ; wasteland
- (cdr all))))))
-
-(define read-text-outline-silently
- (make-text-outline-reader "(([ ][ ])*)- *"
- '((level-substring-divisor . 2))))
-
-(define (read-text-outline . args)
- (write (read-text-outline-silently (open-file (car args) "r")))
- (newline)
- #t) ; exit val
-
-(define main read-text-outline)
-
-;;; read-text-outline ends here
-;;; scan-api --- Scan and group interpreter and libguile interface elements
-
-;; Copyright (C) 2002, 2006, 2011 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Thien-Thi Nguyen <ttn@gnu.org>
-
-;;; Commentary:
-
-;; Usage: scan-api GUILE SOFILE [GROUPINGS ...]
-;;
-;; Invoke GUILE, an executable guile interpreter, and use nm(1) on SOFILE, a
-;; shared-object library, to determine available interface elements, and
-;; display them to stdout as an alist:
-;;
-;; ((meta ...) (interface ...))
-;;
-;; The meta fields are `GUILE_LOAD_PATH', `LTDL_LIBRARY_PATH', `guile'
-;; `libguileinterface', `sofile' and `groups'. The interface elements are in
-;; turn sub-alists w/ keys `groups' and `scan-data'. Interface elements
-;; initially belong in one of two groups `Scheme' or `C' (but not both --
-;; signal error if that happens).
-;;
-;; Optional GROUPINGS ... are files each containing a single "grouping
-;; definition" alist with each entry of the form:
-;;
-;; (NAME (description "DESCRIPTION") (members SYM...))
-;;
-;; All of the SYM... should be proper subsets of the interface. In addition
-;; to `description' and `members' forms, the entry may optionally include:
-;;
-;; (grok USE-MODULES (lambda (x) CODE))
-;;
-;; where CODE implements a group-membership predicate to be applied to `x', a
-;; symbol. [When evaluated, CODE can assume (use-modules MODULE) has been
-;; executed where MODULE is an element of USE-MODULES, a list. [NOT YET
-;; IMPLEMENTED!]]
-;;
-;; Currently, there are two convenience predicates that operate on `x':
-;; (in-group? x GROUP)
-;; (name-prefix? x PREFIX)
-;;
-;; TODO: Allow for concurrent Scheme/C membership.
-;; Completely separate reporting.
-
-;;; Code:
-
-(define-module (scripts scan-api)
- \:use-module (ice-9 popen)
- \:use-module (ice-9 rdelim)
- \:use-module (ice-9 regex)
- \:export (scan-api))
-
-(define %include-in-guild-list #f)
-(define %summary "Generate an API description for a Guile extension.")
-
-(define put set-object-property!)
-(define get object-property)
-
-(define (add-props object . args)
- (let loop ((args args))
- (if (null? args)
- object ; retval
- (let ((key (car args))
- (value (cadr args)))
- (put object key value)
- (loop (cddr args))))))
-
-(define (scan re command match)
- (let ((rx (make-regexp re))
- (port (open-pipe command OPEN_READ)))
- (let loop ((line (read-line port)))
- (or (eof-object? line)
- (begin
- (cond ((regexp-exec rx line) => match))
- (loop (read-line port)))))))
-
-(define (scan-Scheme! ht guile)
- (scan "^.guile.+: ([^ \t]+)([ \t]+(.+))*$"
- (format #f "~A -c '~S ~S'"
- guile
- '(use-modules (ice-9 session))
- '(apropos "."))
- (lambda (m)
- (let ((x (string->symbol (match:substring m 1))))
- (put x 'Scheme (or (match:substring m 3)
- ""))
- (hashq-set! ht x #t)))))
-
-(define (scan-C! ht sofile)
- (scan "^[0-9a-fA-F]+ ([B-TV-Z]) (.+)$"
- (format #f "nm ~A" sofile)
- (lambda (m)
- (let ((x (string->symbol (match:substring m 2))))
- (put x 'C (string->symbol (match:substring m 1)))
- (and (hashq-get-handle ht x)
- (error "both Scheme and C:" x))
- (hashq-set! ht x #t)))))
-
-(define THIS-MODULE (current-module))
-
-(define (in-group? x group)
- (memq group (get x 'groups)))
-
-(define (name-prefix? x prefix)
- (string-match (string-append "^" prefix) (symbol->string x)))
-
-(define (add-group-name! x name)
- (put x 'groups (cons name (get x 'groups))))
-
-(define (make-grok-proc name form)
- (let* ((predicate? (eval form THIS-MODULE))
- (p (lambda (x)
- (and (predicate? x)
- (add-group-name! x name)))))
- (put p 'name name)
- p))
-
-(define (make-members-proc name members)
- (let ((p (lambda (x)
- (and (memq x members)
- (add-group-name! x name)))))
- (put p 'name name)
- p))
-
-(define (make-grouper files) ; \/^^^o/ . o
- (let ((hook (make-hook 1))) ; /\____\
- (for-each
- (lambda (file)
- (for-each
- (lambda (gdef)
- (let ((name (car gdef))
- (members (assq-ref gdef 'members))
- (grok (assq-ref gdef 'grok)))
- (or members grok
- (error "bad grouping, must have `members' or `grok'"))
- (add-hook! hook
- (if grok
- (add-props (make-grok-proc name (cadr grok))
- 'description
- (assq-ref gdef 'description))
- (make-members-proc name members))
- #t))) ; append
- (read (open-file file OPEN_READ))))
- files)
- hook))
-
-(define (scan-api . args)
- (let ((guile (list-ref args 0))
- (sofile (list-ref args 1))
- (grouper (false-if-exception (make-grouper (cddr args))))
- (ht (make-hash-table 3331)))
- (scan-Scheme! ht guile)
- (scan-C! ht sofile)
- (let ((all (sort (hash-fold (lambda (key value prior-result)
- (add-props
- key
- 'string (symbol->string key)
- 'scan-data (or (get key 'Scheme)
- (get key 'C))
- 'groups (if (get key 'Scheme)
- '(Scheme)
- '(C)))
- (and grouper (run-hook grouper key))
- (cons key prior-result))
- '()
- ht)
- (lambda (a b)
- (string<? (get a 'string)
- (get b 'string))))))
- (format #t ";;; generated by scan-api -- do not edit!\n\n")
- (format #t "(\n")
- (format #t "(meta\n")
- (format #t " (GUILE_LOAD_PATH . ~S)\n"
- (or (getenv "GUILE_LOAD_PATH") ""))
- (format #t " (LTDL_LIBRARY_PATH . ~S)\n"
- (or (getenv "LTDL_LIBRARY_PATH") ""))
- (format #t " (guile . ~S)\n" guile)
- (format #t " (libguileinterface . ~S)\n"
- (let ((i #f))
- (scan "(.+)"
- (format #f "~A -c '(display ~A)'"
- guile
- '(assq-ref %guile-build-info
- 'libguileinterface))
- (lambda (m) (set! i (match:substring m 1))))
- i))
- (format #t " (sofile . ~S)\n" sofile)
- (format #t " ~A\n"
- (cons 'groups (append (if grouper
- (map (lambda (p) (get p 'name))
- (hook->list grouper))
- '())
- '(Scheme C))))
- (format #t ") ;; end of meta\n")
- (format #t "(interface\n")
- (for-each (lambda (x)
- (format #t "(~A ~A (scan-data ~S))\n"
- x
- (cons 'groups (get x 'groups))
- (get x 'scan-data)))
- all)
- (format #t ") ;; end of interface\n")
- (format #t ") ;; eof\n")))
- #t)
-
-(define main scan-api)
-
-;;; scan-api ends here
-;;; snarf-check-and-output-texi --- called by the doc snarfer.
-
-;; Copyright (C) 2001, 2002, 2006, 2011, 2014 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Michael Livshin
-
-;;; Code:
-
-(define-module (scripts snarf-check-and-output-texi)
- \:use-module (ice-9 streams)
- \:use-module (ice-9 match)
- \:export (snarf-check-and-output-texi))
-
-(define %include-in-guild-list #f)
-(define %summary "Transform snarfed .doc files into texinfo documentation.")
-
-;;; why aren't these in some module?
-
-(define-macro (when cond . body)
- `(if ,cond (begin ,@body)))
-
-(define-macro (unless cond . body)
- `(if (not ,cond) (begin ,@body)))
-
-(define *manual-flag* #f)
-
-(define (snarf-check-and-output-texi . flags)
- (if (member "--manual" flags)
- (set! *manual-flag* #t))
- (process-stream (current-input-port)))
-
-(define (process-stream port)
- (let loop ((input (stream-map (match-lambda
- (('id . s)
- (cons 'id (string->symbol s)))
- (('int_dec . s)
- (cons 'int (string->number s)))
- (('int_oct . s)
- (cons 'int (string->number s 8)))
- (('int_hex . s)
- (cons 'int (string->number s 16)))
- ((and x (? symbol?))
- (cons x x))
- ((and x (? string?))
- (cons 'string x))
- (x x))
- (make-stream (lambda (s)
- (let loop ((s s))
- (cond
- ((stream-null? s) #t)
- ((memq (stream-car s) '(eol hash))
- (loop (stream-cdr s)))
- (else (cons (stream-car s) (stream-cdr s))))))
- (port->stream port read)))))
-
- (unless (stream-null? input)
- (let ((token (stream-car input)))
- (if (eq? (car token) 'snarf_cookie)
- (dispatch-top-cookie (stream-cdr input)
- loop)
- (loop (stream-cdr input)))))))
-
-(define (dispatch-top-cookie input cont)
-
- (when (stream-null? input)
- (error 'syntax "premature end of file"))
-
- (let ((token (stream-car input)))
- (cond
- ((eq? (car token) 'brace_open)
- (consume-multiline (stream-cdr input)
- cont))
- (else
- (consume-upto-cookie process-singleline
- input
- cont)))))
-
-(define (consume-upto-cookie process input cont)
- (let loop ((acc '()) (input input))
-
- (when (stream-null? input)
- (error 'syntax "premature end of file in directive context"))
-
- (let ((token (stream-car input)))
- (cond
- ((eq? (car token) 'snarf_cookie)
- (process (reverse! acc))
- (cont (stream-cdr input)))
-
- (else (loop (cons token acc) (stream-cdr input)))))))
-
-(define (consume-multiline input cont)
- (begin-multiline)
-
- (let loop ((input input))
-
- (when (stream-null? input)
- (error 'syntax "premature end of file in multiline context"))
-
- (let ((token (stream-car input)))
- (cond
- ((eq? (car token) 'brace_close)
- (end-multiline)
- (cont (stream-cdr input)))
-
- (else (consume-upto-cookie process-multiline-directive
- input
- loop))))))
-
-(define *file* #f)
-(define *line* #f)
-(define *c-function-name* #f)
-(define *function-name* #f)
-(define *snarf-type* #f)
-(define *args* #f)
-(define *sig* #f)
-(define *docstring* #f)
-
-(define (begin-multiline)
- (set! *file* #f)
- (set! *line* #f)
- (set! *c-function-name* #f)
- (set! *function-name* #f)
- (set! *snarf-type* #f)
- (set! *args* #f)
- (set! *sig* #f)
- (set! *docstring* #f))
-
-(define *primitive-deffnx-signature* "@deffnx {Scheme Procedure} ")
-(define *primitive-deffnx-sig-length* (string-length *primitive-deffnx-signature*))
-
-(define (end-multiline)
- (let* ((req (car *sig*))
- (opt (cadr *sig*))
- (var (caddr *sig*))
- (all (+ req opt var)))
- (if (and (not (eqv? *snarf-type* 'register))
- (not (= (length *args*) all)))
- (error (format #f "~A:~A: ~A's C implementation takes ~A args (should take ~A)"
- *file* *line* *function-name* (length *args*) all)))
- (let ((nice-sig
- (if (eq? *snarf-type* 'register)
- *function-name*
- (with-output-to-string
- (lambda ()
- (format #t "~A" *function-name*)
- (let loop-req ((args *args*) (r 0))
- (if (< r req)
- (begin
- (format #t " ~A" (car args))
- (loop-req (cdr args) (+ 1 r)))
- (let loop-opt ((o 0) (args args) (tail '()))
- (if (< o opt)
- (begin
- (format #t " [~A" (car args))
- (loop-opt (+ 1 o) (cdr args) (cons #\] tail)))
- (begin
- (if (> var 0)
- (format #t " . ~A"
- (car args)))
- (let loop-tail ((tail tail))
- (if (not (null? tail))
- (begin
- (format #t "~A" (car tail))
- (loop-tail (cdr tail))))))))))))))
- (scm-deffnx
- (if (and *manual-flag* (eq? *snarf-type* 'primitive))
- (with-output-to-string
- (lambda ()
- (format #t "@deffnx {C Function} ~A (" *c-function-name*)
- (unless (null? *args*)
- (format #t "~A" (car *args*))
- (let loop ((args (cdr *args*)))
- (unless (null? args)
- (format #t ", ~A" (car args))
- (loop (cdr args)))))
- (format #t ")\n")))
- #f)))
- (format #t "\n ~A\n" *function-name*)
- (format #t "@c snarfed from ~A:~A\n" *file* *line*)
- (format #t "@deffn {Scheme Procedure} ~A\n" nice-sig)
- (let loop ((strings *docstring*) (scm-deffnx scm-deffnx))
- (cond ((null? strings))
- ((or (not scm-deffnx)
- (and (>= (string-length (car strings))
- *primitive-deffnx-sig-length*)
- (string=? (substring (car strings)
- 0 *primitive-deffnx-sig-length*)
- *primitive-deffnx-signature*)))
- (display (car strings))
- (loop (cdr strings) scm-deffnx))
- (else (display scm-deffnx)
- (loop strings #f))))
- (display "\n")
- (display "@end deffn\n"))))
-
-(define (texi-quote s)
- (let rec ((i 0))
- (if (= i (string-length s))
- ""
- (string-append (let ((ss (substring s i (+ i 1))))
- (if (string=? ss "@")
- "@@"
- ss))
- (rec (+ i 1))))))
-
-(define (process-multiline-directive l)
-
- (define do-args
- (match-lambda
-
- (('(paren_close . paren_close))
- '())
-
- (('(comma . comma) rest ...)
- (do-args rest))
-
- (('(id . SCM) ('id . name) rest ...)
- (cons name (do-args rest)))
-
- (x (error (format #f "invalid argument syntax: ~A" (map cdr x))))))
-
- (define do-arglist
- (match-lambda
-
- (('(paren_open . paren_open) '(id . void) '(paren_close . paren_close))
- '())
-
- (('(paren_open . paren_open) rest ...)
- (do-args rest))
-
- (x (error (format #f "invalid arglist syntax: ~A" (map cdr x))))))
-
- (define do-command
- (match-lambda
-
- (('cname ('id . name))
- (set! *c-function-name* (texi-quote (symbol->string name))))
-
- (('fname ('string . name) ...)
- (set! *function-name* (texi-quote (apply string-append name))))
-
- (('type ('id . type))
- (set! *snarf-type* type))
-
- (('type ('int . num))
- (set! *snarf-type* num))
-
- (('location ('string . file) ('int . line))
- (set! *file* file)
- (set! *line* line))
-
- (('arglist rest ...)
- (set! *args* (do-arglist rest)))
-
- (('argsig ('int . req) ('int . opt) ('int . var))
- (set! *sig* (list req opt var)))
-
- (x (error (format #f "unknown doc attribute: ~A" x)))))
-
- (define do-directive
- (match-lambda
-
- ((('id . command) rest ...)
- (do-command (cons command rest)))
-
- ((('string . string) ...)
- (set! *docstring* string))
-
- (x (error (format #f "unknown doc attribute syntax: ~A" x)))))
-
- (do-directive l))
-
-(define (process-singleline l)
-
- (define do-argpos
- (match-lambda
- ((('id . name) ('int . pos) ('int . line))
- (let ((idx (list-index *args* name)))
- (when idx
- (unless (= (+ idx 1) pos)
- (display (format #f "~A:~A: wrong position for argument ~A: ~A (should be ~A)\n"
- *file* line name pos (+ idx 1))
- (current-error-port))))))
- (x #f)))
-
- (define do-command
- (match-lambda
- (('(id . argpos) rest ...)
- (do-argpos rest))
- (x (error (format #f "unknown check: ~A" x)))))
-
- (when *function-name*
- (do-command l)))
-
-(define main snarf-check-and-output-texi)
-;;; snarf-guile-m4-docs --- Parse guile.m4 comments for texi documentation
-
-;; Copyright (C) 2002, 2006, 2011 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Thien-Thi Nguyen <ttn@gnu.org>
-
-;;; Commentary:
-
-;; Usage: snarf-guile-m4-docs FILE
-;;
-;; Grep FILE for comments preceding macro definitions, massage
-;; them into valid texi, and display to stdout. For each comment,
-;; lines preceding "^# Usage:" are discarded.
-;;
-;; TODO: Generalize.
-
-;;; Code:
-
-(define-module (scripts snarf-guile-m4-docs)
- \:use-module (ice-9 rdelim)
- \:export (snarf-guile-m4-docs))
-
-(define %include-in-guild-list #f)
-(define %summary "Snarf out texinfo documentation from .m4 files.")
-
-(define (display-texi lines)
- (display "@deffn {Autoconf Macro}")
- (for-each (lambda (line)
- (display (cond ((and (>= (string-length line) 2)
- (string=? "# " (substring line 0 2)))
- (substring line 2))
- ((string=? "#" (substring line 0 1))
- (substring line 1))
- (else line)))
- (newline))
- lines)
- (display "@end deffn")
- (newline) (newline))
-
-(define (prefix? line sub)
- (false-if-exception
- (string=? sub (substring line 0 (string-length sub)))))
-
-(define (massage-usage line)
- (let loop ((line (string->list line)) (acc '()))
- (if (null? line)
- (list (list->string (reverse acc)))
- (loop (cdr line)
- (cons (case (car line)
- ((#\( #\) #\,) #\space)
- (else (car line)))
- acc)))))
-
-(define (snarf-guile-m4-docs . args)
- (let* ((p (open-file (car args) "r"))
- (next (lambda () (read-line p))))
- (let loop ((line (next)) (acc #f))
- (or (eof-object? line)
- (cond ((prefix? line "# Usage:")
- (loop (next) (massage-usage (substring line 8))))
- ((prefix? line "AC_DEFUN")
- (display-texi (reverse acc))
- (loop (next) #f))
- ((and acc (prefix? line "#"))
- (loop (next) (cons line acc)))
- (else
- (loop (next) #f)))))))
-
-(define main snarf-guile-m4-docs)
-
-;;; snarf-guile-m4-docs ends here
-;;; summarize-guile-TODO --- Display Guile TODO list in various ways
-
-;; Copyright (C) 2002, 2006, 2010, 2011 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Thien-Thi Nguyen <ttn@gnu.org>
-
-;;; Commentary:
-
-;; Usage: summarize-guile-TODO TODOFILE
-;;
-;; The TODOFILE is typically Guile's (see workbook/tasks/README)
-;; presumed to serve as our signal to ourselves (lest we want real
-;; bosses hassling us) wrt to the overt message "items to do" as well as
-;; the messages that can be inferred from its structure.
-;;
-;; This program reads TODOFILE and displays interpretations on its
-;; structure, including registered markers and ownership, in various
-;; ways.
-;;
-;; A primary interest in any task is its parent task. The output
-;; summarization by default lists every item and its parent chain.
-;; Top-level parents are not items. You can use these command-line
-;; options to modify the selection and display (selection criteria
-;; are ANDed together):
-;;
-;; -i, --involved USER -- select USER-involved items
-;; -p, --personal USER -- select USER-responsible items
-;; -t, --todo -- select unfinished items (status "-")
-;; -d, --done -- select finished items (status "+")
-;; -r, --review -- select review items (marker "R")
-;;
-;; -w, --who -- also show who is associated w/ the item
-;; -n, --no-parent -- do not show parent chain
-;;
-;;
-;; Usage from a Scheme program:
-;; (summarize-guile-TODO . args) ; uses first arg only
-;;
-;;
-;; Bugs: (1) Markers are scanned in sequence: D R X N%. This means "XD"
-;; and the like are completely dropped. However, such strings
-;; are unlikely to be used if the markers are chosen to be
-;; somewhat exclusive, which is currently the case for D R X.
-;; N% used w/ these needs to be something like: "D25%" (this
-;; means discussion accounts for 1/4 of the task).
-;;
-;; TODO: Implement more various ways. (Patches welcome.)
-;; Add support for ORing criteria.
-
-;;; Code:
-(debug-enable 'backtrace)
-
-(define-module (scripts summarize-guile-TODO)
- \:use-module (scripts read-text-outline)
- \:use-module (ice-9 getopt-long)
- \:autoload (srfi srfi-13) (string-tokenize) ; string library
- \:autoload (srfi srfi-14) (char-set) ; string library
- \:autoload (ice-9 common-list) (remove-if-not)
- \:export (summarize-guile-TODO))
-
-(define %include-in-guild-list #f)
-(define %summary "A quaint relic of the past.")
-
-(define put set-object-property!)
-(define get object-property)
-
-(define (as-leaf x)
- (cond ((get x 'who)
- => (lambda (who)
- (put x 'who
- (map string->symbol
- (string-tokenize who (char-set #\:)))))))
- (cond ((get x 'pct-done)
- => (lambda (pct-done)
- (put x 'pct-done (string->number pct-done)))))
- x)
-
-(define (hang-by-the-leaves trees)
- (let ((leaves '()))
- (letrec ((hang (lambda (tree parent)
- (if (list? tree)
- (begin
- (put (car tree) 'parent parent)
- (for-each (lambda (child)
- (hang child (car tree)))
- (cdr tree)))
- (begin
- (put tree 'parent parent)
- (set! leaves (cons (as-leaf tree) leaves)))))))
- (for-each (lambda (tree)
- (hang tree #f))
- trees))
- leaves))
-
-(define (read-TODO file)
- (hang-by-the-leaves
- ((make-text-outline-reader
- "(([ ][ ])*)([-+])(D*)(R*)(X*)(([0-9]+)%)* *([^[]*)(\\[(.*)\\])*"
- '((level-substring-divisor . 2)
- (body-submatch-number . 9)
- (extra-fields . ((status . 3)
- (design? . 4)
- (review? . 5)
- (extblock? . 6)
- (pct-done . 8)
- (who . 11)))))
- (open-file file "r"))))
-
-(define (select-items p items)
- (let ((sub '()))
- (cond ((option-ref p 'involved #f)
- => (lambda (u)
- (let ((u (string->symbol u)))
- (set! sub (cons
- (lambda (x)
- (and (get x 'who)
- (memq u (get x 'who))))
- sub))))))
- (cond ((option-ref p 'personal #f)
- => (lambda (u)
- (let ((u (string->symbol u)))
- (set! sub (cons
- (lambda (x)
- (cond ((get x 'who)
- => (lambda (ls)
- (eq? (car (reverse ls))
- u)))
- (else #f)))
- sub))))))
- (for-each (lambda (pair)
- (cond ((option-ref p (car pair) #f)
- (set! sub (cons (cdr pair) sub)))))
- `((todo . ,(lambda (x) (string=? (get x 'status) "-")))
- (done . ,(lambda (x) (string=? (get x 'status) "+")))
- (review . ,(lambda (x) (get x 'review?)))))
- (let loop ((sub (reverse sub)) (items items))
- (if (null? sub)
- (reverse items)
- (loop (cdr sub) (remove-if-not (car sub) items))))))
-
-(define (make-display-item show-who? show-parent?)
- (let ((show-who
- (if show-who?
- (lambda (item)
- (cond ((get item 'who)
- => (lambda (who) (format #f " ~A" who)))
- (else "")))
- (lambda (item) "")))
- (show-parents
- (if show-parent?
- (lambda (item)
- (let loop ((parent (get item 'parent)) (indent 2))
- (and parent
- (begin
- (format #t "under : ~A~A\n"
- (make-string indent #\space)
- parent)
- (loop (get parent 'parent) (+ 2 indent))))))
- (lambda (item) #t))))
- (lambda (item)
- (format #t "status: ~A~A~A~A~A~A\nitem : ~A\n"
- (get item 'status)
- (if (get item 'design?) "D" "")
- (if (get item 'review?) "R" "")
- (if (get item 'extblock?) "X" "")
- (cond ((get item 'pct-done)
- => (lambda (pct-done)
- (format #f " ~A%" pct-done)))
- (else ""))
- (show-who item)
- item)
- (show-parents item))))
-
-(define (display-items p items)
- (let ((display-item (make-display-item (option-ref p 'who #f)
- (not (option-ref p 'no-parent #f))
- )))
- (for-each display-item items)))
-
-(define (summarize-guile-TODO . args)
- (let ((p (getopt-long (cons "summarize-guile-TODO" args)
- '((who (single-char #\w))
- (no-parent (single-char #\n))
- (involved (single-char #\i)
- (value #t))
- (personal (single-char #\p)
- (value #t))
- (todo (single-char #\t))
- (done (single-char #\d))
- (review (single-char #\r))
- ;; Add options here.
- ))))
- (display-items p (select-items p (read-TODO (car (option-ref p '() #f))))))
- #t) ; exit val
-
-(define main summarize-guile-TODO)
-
-;;; summarize-guile-TODO ends here
-;;; use2dot --- Display module dependencies as a DOT specification
-
-;; Copyright (C) 2001, 2006, 2011 Free Software Foundation, Inc.
-;;
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation; either version 3, or
-;; (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this software; see the file COPYING.LESSER. If
-;; not, write to the Free Software Foundation, Inc., 51 Franklin
-;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Thien-Thi Nguyen
-
-;;; Commentary:
-
-;; Usage: use2dot [OPTIONS] [FILE ...]
-;; Display to stdout a DOT specification that describes module dependencies
-;; in FILEs.
-;;
-;; A top-level `use-modules' form or a `:use-module' `define-module'-component
-;; results in a "solid" style edge.
-;;
-;; An `:autoload' `define-module'-component results in a "dotted" style edge
-;; with label "N" indicating that N names are responsible for triggering the
-;; autoload. [The "N" label is not implemented.]
-;;
-;; A top-level `load' or `primitive-load' form results in a a "bold" style
-;; edge to a node named with either the file name if the `load' argument is a
-;; string, or "[computed in FILE]" otherwise.
-;;
-;; Options:
-;; -m, --default-module MOD -- Set MOD as the default module (for top-level
-;; `use-modules' forms that do not follow some
-;; `define-module' form in a file). MOD should be
-;; be a list or `#f', in which case such top-level
-;; `use-modules' forms are effectively ignored.
-;; Default value: `(guile-user)'.
-
-;;; Code:
-
-(define-module (scripts use2dot)
- \:autoload (ice-9 getopt-long) (getopt-long)
- \:use-module ((srfi srfi-13) \:select (string-join))
- \:use-module ((scripts frisk)
- \:select (make-frisker edge-type edge-up edge-down))
- \:export (use2dot))
-
-(define %summary "Print a module's dependencies in graphviz format.")
-
-(define *default-module* '(guile-user))
-
-(define (q s) ; quote
- (format #f "~S" s))
-
-(define (vv pairs) ; => ("var=val" ...)
- (map (lambda (pair)
- (format #f "~A=~A" (car pair) (cdr pair)))
- pairs))
-
-(define (>>header)
- (format #t "digraph use2dot {\n")
- (for-each (lambda (s) (format #t " ~A;\n" s))
- (vv `((label . ,(q "Guile Module Dependencies"))
- ;;(rankdir . LR)
- ;;(size . ,(q "7.5,10"))
- (ratio . fill)
- ;;(nodesep . ,(q "0.05"))
- ))))
-
-(define (>>body edges)
- (for-each
- (lambda (edge)
- (format #t " \"~A\" -> \"~A\"" (edge-down edge) (edge-up edge))
- (cond ((case (edge-type edge)
- ((autoload) '((style . dotted) (fontsize . 5)))
- ((computed) '((style . bold)))
- (else #f))
- => (lambda (etc)
- (format #t " [~A]" (string-join (vv etc) ",")))))
- (format #t ";\n"))
- edges))
-
-(define (>>footer)
- (format #t "}"))
-
-(define (>> edges)
- (>>header)
- (>>body edges)
- (>>footer))
-
-(define (use2dot . args)
- (let* ((parsed-args (getopt-long (cons "use2dot" args) ;;; kludge
- '((default-module
- (single-char #\m) (value #t)))))
- (=m (option-ref parsed-args 'default-module *default-module*))
- (scan (make-frisker `(default-module . ,=m)))
- (files (option-ref parsed-args '() '())))
- (>> (reverse ((scan files) 'edges)))))
-
-(define main use2dot)
-
-;;; use2dot ends here
-;;; srfi-1.scm --- List Library
-
-;; Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2010, 2011 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Some parts from the reference implementation, which is
-;;; Copyright (c) 1998, 1999 by Olin Shivers. You may do as you please with
-;;; this code as long as you do not remove this copyright notice or
-;;; hold me liable for its use.
-
-;;; Author: Martin Grabmueller <mgrabmue@cs.tu-berlin.de>
-;;; Date: 2001-06-06
-
-;;; Commentary:
-
-;; This is an implementation of SRFI-1 (List Library).
-;;
-;; All procedures defined in SRFI-1, which are not already defined in
-;; the Guile core library, are exported. The procedures in this
-;; implementation work, but they have not been tuned for speed or
-;; memory usage.
-;;
-;; This module is fully documented in the Guile Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-1)
- \:export (
-;;; Constructors
- ;; cons <= in the core
- ;; list <= in the core
- xcons
- ;; cons* <= in the core
- ;; make-list <= in the core
- list-tabulate
- list-copy
- circular-list
- ;; iota ; Extended.
-
-;;; Predicates
- proper-list?
- circular-list?
- dotted-list?
- ;; pair? <= in the core
- ;; null? <= in the core
- null-list?
- not-pair?
- list=
-
-;;; Selectors
- ;; car <= in the core
- ;; cdr <= in the core
- ;; caar <= in the core
- ;; cadr <= in the core
- ;; cdar <= in the core
- ;; cddr <= in the core
- ;; caaar <= in the core
- ;; caadr <= in the core
- ;; cadar <= in the core
- ;; caddr <= in the core
- ;; cdaar <= in the core
- ;; cdadr <= in the core
- ;; cddar <= in the core
- ;; cdddr <= in the core
- ;; caaaar <= in the core
- ;; caaadr <= in the core
- ;; caadar <= in the core
- ;; caaddr <= in the core
- ;; cadaar <= in the core
- ;; cadadr <= in the core
- ;; caddar <= in the core
- ;; cadddr <= in the core
- ;; cdaaar <= in the core
- ;; cdaadr <= in the core
- ;; cdadar <= in the core
- ;; cdaddr <= in the core
- ;; cddaar <= in the core
- ;; cddadr <= in the core
- ;; cdddar <= in the core
- ;; cddddr <= in the core
- ;; list-ref <= in the core
- first
- second
- third
- fourth
- fifth
- sixth
- seventh
- eighth
- ninth
- tenth
- car+cdr
- take
- drop
- take-right
- drop-right
- take!
- drop-right!
- split-at
- split-at!
- last
- ;; last-pair <= in the core
-
-;;; Miscelleneous: length, append, concatenate, reverse, zip & count
- ;; length <= in the core
- length+
- ;; append <= in the core
- ;; append! <= in the core
- concatenate
- concatenate!
- ;; reverse <= in the core
- ;; reverse! <= in the core
- append-reverse
- append-reverse!
- zip
- unzip1
- unzip2
- unzip3
- unzip4
- unzip5
- count
-
-;;; Fold, unfold & map
- fold
- fold-right
- pair-fold
- pair-fold-right
- reduce
- reduce-right
- unfold
- unfold-right
- ;; map ; Extended.
- ;; for-each ; Extended.
- append-map
- append-map!
- map!
- ;; map-in-order ; Extended.
- pair-for-each
- filter-map
-
-;;; Filtering & partitioning
- ;; filter <= in the core
- partition
- remove
- ;; filter! <= in the core
- partition!
- remove!
-
-;;; Searching
- find
- find-tail
- take-while
- take-while!
- drop-while
- span
- span!
- break
- break!
- any
- every
- ;; list-index ; Extended.
- ;; member ; Extended.
- ;; memq <= in the core
- ;; memv <= in the core
-
-;;; Deletion
- ;; delete ; Extended.
- ;; delete! ; Extended.
- delete-duplicates
- delete-duplicates!
-
-;;; Association lists
- ;; assoc ; Extended.
- ;; assq <= in the core
- ;; assv <= in the core
- alist-cons
- alist-copy
- alist-delete
- alist-delete!
-
-;;; Set operations on lists
- lset<=
- lset=
- lset-adjoin
- lset-union
- lset-intersection
- lset-difference
- lset-xor
- lset-diff+intersection
- lset-union!
- lset-intersection!
- lset-difference!
- lset-xor!
- lset-diff+intersection!
-
-;;; Primitive side-effects
- ;; set-car! <= in the core
- ;; set-cdr! <= in the core
- )
- \:re-export (cons list cons* make-list pair? null?
- car cdr caar cadr cdar cddr
- caaar caadr cadar caddr cdaar cdadr cddar cdddr
- caaaar caaadr caadar caaddr cadaar cadadr caddar cadddr
- cdaaar cdaadr cdadar cdaddr cddaar cddadr cdddar cddddr
- list-ref last-pair length append append! reverse reverse!
- filter filter! memq memv assq assv set-car! set-cdr!)
- \:replace (iota map for-each map-in-order list-copy list-index member
- delete delete! assoc)
- )
-
-(cond-expand-provide (current-module) '(srfi-1))
-
-;; Load the compiled primitives from the shared library.
-;;
-(load-extension (string-append "libguile-" (effective-version))
- "scm_init_srfi_1")
-
-
-;;; Constructors
-
-(define (xcons d a)
- "Like `cons', but with interchanged arguments. Useful mostly when passed to
-higher-order procedures."
- (cons a d))
-
-(define (wrong-type-arg caller arg)
- (scm-error 'wrong-type-arg (symbol->string caller)
- "Wrong type argument: ~S" (list arg) '()))
-
-(define-syntax-rule (check-arg pred arg caller)
- (if (not (pred arg))
- (wrong-type-arg 'caller arg)))
-
-(define (out-of-range proc arg)
- (scm-error 'out-of-range proc
- "Value out of range: ~A" (list arg) (list arg)))
-
-;; the srfi spec doesn't seem to forbid inexact integers.
-(define (non-negative-integer? x) (and (integer? x) (>= x 0)))
-
-(define (list-tabulate n init-proc)
- "Return an N-element list, where each list element is produced by applying the
-procedure INIT-PROC to the corresponding list index. The order in which
-INIT-PROC is applied to the indices is not specified."
- (check-arg non-negative-integer? n list-tabulate)
- (let lp ((n n) (acc '()))
- (if (<= n 0)
- acc
- (lp (- n 1) (cons (init-proc (- n 1)) acc)))))
-
-(define (circular-list elt1 . elts)
- (set! elts (cons elt1 elts))
- (set-cdr! (last-pair elts) elts)
- elts)
-
-(define* (iota count #\optional (start 0) (step 1))
- (check-arg non-negative-integer? count iota)
- (let lp ((n 0) (acc '()))
- (if (= n count)
- (reverse! acc)
- (lp (+ n 1) (cons (+ start (* n step)) acc)))))
-
-;;; Predicates
-
-(define (proper-list? x)
- (list? x))
-
-(define (circular-list? x)
- (if (not-pair? x)
- #f
- (let lp ((hare (cdr x)) (tortoise x))
- (if (not-pair? hare)
- #f
- (let ((hare (cdr hare)))
- (if (not-pair? hare)
- #f
- (if (eq? hare tortoise)
- #t
- (lp (cdr hare) (cdr tortoise)))))))))
-
-(define (dotted-list? x)
- (cond
- ((null? x) #f)
- ((not-pair? x) #t)
- (else
- (let lp ((hare (cdr x)) (tortoise x))
- (cond
- ((null? hare) #f)
- ((not-pair? hare) #t)
- (else
- (let ((hare (cdr hare)))
- (cond
- ((null? hare) #f)
- ((not-pair? hare) #t)
- ((eq? hare tortoise) #f)
- (else
- (lp (cdr hare) (cdr tortoise)))))))))))
-
-(define (null-list? x)
- (cond
- ((proper-list? x)
- (null? x))
- ((circular-list? x)
- #f)
- (else
- (error "not a proper list in null-list?"))))
-
-(define (not-pair? x)
- "Return #t if X is not a pair, #f otherwise.
-
-This is shorthand notation `(not (pair? X))' and is supposed to be used for
-end-of-list checking in contexts where dotted lists are allowed."
- (not (pair? x)))
-
-(define (list= elt= . rest)
- (define (lists-equal a b)
- (let lp ((a a) (b b))
- (cond ((null? a)
- (null? b))
- ((null? b)
- #f)
- (else
- (and (elt= (car a) (car b))
- (lp (cdr a) (cdr b)))))))
-
- (check-arg procedure? elt= list=)
- (or (null? rest)
- (let lp ((lists rest))
- (or (null? (cdr lists))
- (and (lists-equal (car lists) (cadr lists))
- (lp (cdr lists)))))))
-
-;;; Selectors
-
-(define first car)
-(define second cadr)
-(define third caddr)
-(define fourth cadddr)
-(define (fifth x) (car (cddddr x)))
-(define (sixth x) (cadr (cddddr x)))
-(define (seventh x) (caddr (cddddr x)))
-(define (eighth x) (cadddr (cddddr x)))
-(define (ninth x) (car (cddddr (cddddr x))))
-(define (tenth x) (cadr (cddddr (cddddr x))))
-
-(define (car+cdr x)
- "Return two values, the `car' and the `cdr' of PAIR."
- (values (car x) (cdr x)))
-
-(define take list-head)
-(define drop list-tail)
-
-;;; TAKE-RIGHT and DROP-RIGHT work by getting two pointers into the list,
-;;; off by K, then chasing down the list until the lead pointer falls off
-;;; the end. Note that they diverge for circular lists.
-
-(define (take-right lis k)
- (let lp ((lag lis) (lead (drop lis k)))
- (if (pair? lead)
- (lp (cdr lag) (cdr lead))
- lag)))
-
-(define (drop-right lis k)
- (let lp ((lag lis) (lead (drop lis k)) (result '()))
- (if (pair? lead)
- (lp (cdr lag) (cdr lead) (cons (car lag) result))
- (reverse result))))
-
-(define (take! lst i)
- "Linear-update variant of `take'."
- (if (= i 0)
- '()
- (let ((tail (drop lst (- i 1))))
- (set-cdr! tail '())
- lst)))
-
-(define (drop-right! lst i)
- "Linear-update variant of `drop-right'."
- (let ((tail (drop lst i)))
- (if (null? tail)
- '()
- (let loop ((prev lst)
- (tail (cdr tail)))
- (if (null? tail)
- (if (pair? prev)
- (begin
- (set-cdr! prev '())
- lst)
- lst)
- (loop (cdr prev)
- (cdr tail)))))))
-
-(define (split-at lst i)
- "Return two values, a list of the elements before index I in LST, and
-a list of those after."
- (if (< i 0)
- (out-of-range 'split-at i)
- (let lp ((l lst) (n i) (acc '()))
- (if (<= n 0)
- (values (reverse! acc) l)
- (lp (cdr l) (- n 1) (cons (car l) acc))))))
-
-(define (split-at! lst i)
- "Linear-update variant of `split-at'."
- (cond ((< i 0)
- (out-of-range 'split-at! i))
- ((= i 0)
- (values '() lst))
- (else
- (let lp ((l lst) (n (- i 1)))
- (if (<= n 0)
- (let ((tmp (cdr l)))
- (set-cdr! l '())
- (values lst tmp))
- (lp (cdr l) (- n 1)))))))
-
-(define (last pair)
- "Return the last element of the non-empty, finite list PAIR."
- (car (last-pair pair)))
-
-;;; Miscelleneous: length, append, concatenate, reverse, zip & count
-
-(define (zip clist1 . rest)
- (let lp ((l (cons clist1 rest)) (acc '()))
- (if (any null? l)
- (reverse! acc)
- (lp (map cdr l) (cons (map car l) acc)))))
-
-
-(define (unzip1 l)
- (map first l))
-(define (unzip2 l)
- (values (map first l) (map second l)))
-(define (unzip3 l)
- (values (map first l) (map second l) (map third l)))
-(define (unzip4 l)
- (values (map first l) (map second l) (map third l) (map fourth l)))
-(define (unzip5 l)
- (values (map first l) (map second l) (map third l) (map fourth l)
- (map fifth l)))
-
-;;; Fold, unfold & map
-
-(define (fold kons knil list1 . rest)
- "Apply PROC to the elements of LIST1 ... LISTN to build a result, and return
-that result. See the manual for details."
- (check-arg procedure? kons fold)
- (if (null? rest)
- (let f ((knil knil) (list1 list1))
- (if (null? list1)
- knil
- (f (kons (car list1) knil) (cdr list1))))
- (let f ((knil knil) (lists (cons list1 rest)))
- (if (any null? lists)
- knil
- (let ((cars (map car lists))
- (cdrs (map cdr lists)))
- (f (apply kons (append! cars (list knil))) cdrs))))))
-
-(define (fold-right kons knil clist1 . rest)
- (check-arg procedure? kons fold-right)
- (if (null? rest)
- (let loop ((lst (reverse clist1))
- (result knil))
- (if (null? lst)
- result
- (loop (cdr lst)
- (kons (car lst) result))))
- (let loop ((lists (map reverse (cons clist1 rest)))
- (result knil))
- (if (any1 null? lists)
- result
- (loop (map cdr lists)
- (apply kons (append! (map car lists) (list result))))))))
-
-(define (pair-fold kons knil clist1 . rest)
- (check-arg procedure? kons pair-fold)
- (if (null? rest)
- (let f ((knil knil) (list1 clist1))
- (if (null? list1)
- knil
- (let ((tail (cdr list1)))
- (f (kons list1 knil) tail))))
- (let f ((knil knil) (lists (cons clist1 rest)))
- (if (any null? lists)
- knil
- (let ((tails (map cdr lists)))
- (f (apply kons (append! lists (list knil))) tails))))))
-
-
-(define (pair-fold-right kons knil clist1 . rest)
- (check-arg procedure? kons pair-fold-right)
- (if (null? rest)
- (let f ((list1 clist1))
- (if (null? list1)
- knil
- (kons list1 (f (cdr list1)))))
- (let f ((lists (cons clist1 rest)))
- (if (any null? lists)
- knil
- (apply kons (append! lists (list (f (map cdr lists)))))))))
-
-(define* (unfold p f g seed #\optional (tail-gen (lambda (x) '())))
- (define (reverse+tail lst seed)
- (let loop ((lst lst)
- (result (tail-gen seed)))
- (if (null? lst)
- result
- (loop (cdr lst)
- (cons (car lst) result)))))
-
- (check-arg procedure? p unfold)
- (check-arg procedure? f unfold)
- (check-arg procedure? g unfold)
- (check-arg procedure? tail-gen unfold)
- (let loop ((seed seed)
- (result '()))
- (if (p seed)
- (reverse+tail result seed)
- (loop (g seed)
- (cons (f seed) result)))))
-
-(define* (unfold-right p f g seed #\optional (tail '()))
- (check-arg procedure? p unfold-right)
- (check-arg procedure? f unfold-right)
- (check-arg procedure? g unfold-right)
- (let uf ((seed seed) (lis tail))
- (if (p seed)
- lis
- (uf (g seed) (cons (f seed) lis)))))
-
-(define (reduce f ridentity lst)
- "`reduce' is a variant of `fold', where the first call to F is on two
-elements from LST, rather than one element and a given initial value.
-If LST is empty, RIDENTITY is returned. If LST has just one element
-then that's the return value."
- (check-arg procedure? f reduce)
- (if (null? lst)
- ridentity
- (fold f (car lst) (cdr lst))))
-
-(define (reduce-right f ridentity lst)
- "`reduce-right' is a variant of `fold-right', where the first call to
-F is on two elements from LST, rather than one element and a given
-initial value. If LST is empty, RIDENTITY is returned. If LST
-has just one element then that's the return value."
- (reduce f ridentity (reverse lst)))
-
-(define map
- (case-lambda
- ((f l)
- (check-arg procedure? f map)
- (let map1 ((hare l) (tortoise l) (move? #f) (out '()))
- (if (pair? hare)
- (if move?
- (if (eq? tortoise hare)
- (scm-error 'wrong-type-arg "map" "Circular list: ~S"
- (list l) #f)
- (map1 (cdr hare) (cdr tortoise) #f
- (cons (f (car hare)) out)))
- (map1 (cdr hare) tortoise #t
- (cons (f (car hare)) out)))
- (if (null? hare)
- (reverse! out)
- (scm-error 'wrong-type-arg "map" "Not a list: ~S"
- (list l) #f)))))
-
- ((f l1 . rest)
- (check-arg procedure? f map)
- (let ((len (fold (lambda (ls len)
- (let ((ls-len (length+ ls)))
- (if len
- (if ls-len (min ls-len len) len)
- ls-len)))
- (length+ l1)
- rest)))
- (if (not len)
- (scm-error 'wrong-type-arg "map"
- "Args do not contain a proper (finite) list: ~S"
- (list (cons l1 rest)) #f))
- (let mapn ((l1 l1) (rest rest) (len len) (out '()))
- (if (zero? len)
- (reverse! out)
- (mapn (cdr l1) (map cdr rest) (1- len)
- (cons (apply f (car l1) (map car rest)) out))))))))
-
-(define map-in-order map)
-
-(define for-each
- (case-lambda
- ((f l)
- (check-arg procedure? f for-each)
- (let for-each1 ((hare l) (tortoise l) (move? #f))
- (if (pair? hare)
- (if move?
- (if (eq? tortoise hare)
- (scm-error 'wrong-type-arg "for-each" "Circular list: ~S"
- (list l) #f)
- (begin
- (f (car hare))
- (for-each1 (cdr hare) (cdr tortoise) #f)))
- (begin
- (f (car hare))
- (for-each1 (cdr hare) tortoise #t)))
-
- (if (not (null? hare))
- (scm-error 'wrong-type-arg "for-each" "Not a list: ~S"
- (list l) #f)))))
-
- ((f l1 . rest)
- (check-arg procedure? f for-each)
- (let ((len (fold (lambda (ls len)
- (let ((ls-len (length+ ls)))
- (if len
- (if ls-len (min ls-len len) len)
- ls-len)))
- (length+ l1)
- rest)))
- (if (not len)
- (scm-error 'wrong-type-arg "for-each"
- "Args do not contain a proper (finite) list: ~S"
- (list (cons l1 rest)) #f))
- (let for-eachn ((l1 l1) (rest rest) (len len))
- (if (> len 0)
- (begin
- (apply f (car l1) (map car rest))
- (for-eachn (cdr l1) (map cdr rest) (1- len)))))))))
-
-(define (append-map f clist1 . rest)
- (concatenate (apply map f clist1 rest)))
-
-(define (append-map! f clist1 . rest)
- (concatenate! (apply map f clist1 rest)))
-
-;; OPTIMIZE-ME: Re-use cons cells of list1
-(define map! map)
-
-(define (filter-map proc list1 . rest)
- "Apply PROC to the elements of LIST1... and return a list of the
-results as per SRFI-1 `map', except that any #f results are omitted from
-the list returned."
- (check-arg procedure? proc filter-map)
- (if (null? rest)
- (let lp ((l list1)
- (rl '()))
- (if (null? l)
- (reverse! rl)
- (let ((res (proc (car l))))
- (if res
- (lp (cdr l) (cons res rl))
- (lp (cdr l) rl)))))
- (let lp ((l (cons list1 rest))
- (rl '()))
- (if (any1 null? l)
- (reverse! rl)
- (let ((res (apply proc (map car l))))
- (if res
- (lp (map cdr l) (cons res rl))
- (lp (map cdr l) rl)))))))
-
-(define (pair-for-each f clist1 . rest)
- (check-arg procedure? f pair-for-each)
- (if (null? rest)
- (let lp ((l clist1))
- (if (null? l)
- (if #f #f)
- (begin
- (f l)
- (lp (cdr l)))))
- (let lp ((l (cons clist1 rest)))
- (if (any1 null? l)
- (if #f #f)
- (begin
- (apply f l)
- (lp (map cdr l)))))))
-
-
-;;; Searching
-
-(define (take-while pred ls)
- "Return a new list which is the longest initial prefix of LS whose
-elements all satisfy the predicate PRED."
- (check-arg procedure? pred take-while)
- (cond ((null? ls) '())
- ((not (pred (car ls))) '())
- (else
- (let ((result (list (car ls))))
- (let lp ((ls (cdr ls)) (p result))
- (cond ((null? ls) result)
- ((not (pred (car ls))) result)
- (else
- (set-cdr! p (list (car ls)))
- (lp (cdr ls) (cdr p)))))))))
-
-(define (take-while! pred lst)
- "Linear-update variant of `take-while'."
- (check-arg procedure? pred take-while!)
- (let loop ((prev #f)
- (rest lst))
- (cond ((null? rest)
- lst)
- ((pred (car rest))
- (loop rest (cdr rest)))
- (else
- (if (pair? prev)
- (begin
- (set-cdr! prev '())
- lst)
- '())))))
-
-(define (drop-while pred lst)
- "Drop the longest initial prefix of LST whose elements all satisfy the
-predicate PRED."
- (check-arg procedure? pred drop-while)
- (let loop ((lst lst))
- (cond ((null? lst)
- '())
- ((pred (car lst))
- (loop (cdr lst)))
- (else lst))))
-
-(define (span pred lst)
- "Return two values, the longest initial prefix of LST whose elements
-all satisfy the predicate PRED, and the remainder of LST."
- (check-arg procedure? pred span)
- (let lp ((lst lst) (rl '()))
- (if (and (not (null? lst))
- (pred (car lst)))
- (lp (cdr lst) (cons (car lst) rl))
- (values (reverse! rl) lst))))
-
-(define (span! pred list)
- "Linear-update variant of `span'."
- (check-arg procedure? pred span!)
- (let loop ((prev #f)
- (rest list))
- (cond ((null? rest)
- (values list '()))
- ((pred (car rest))
- (loop rest (cdr rest)))
- (else
- (if (pair? prev)
- (begin
- (set-cdr! prev '())
- (values list rest))
- (values '() list))))))
-
-(define (break pred clist)
- "Return two values, the longest initial prefix of LST whose elements
-all fail the predicate PRED, and the remainder of LST."
- (check-arg procedure? pred break)
- (let lp ((clist clist) (rl '()))
- (if (or (null? clist)
- (pred (car clist)))
- (values (reverse! rl) clist)
- (lp (cdr clist) (cons (car clist) rl)))))
-
-(define (break! pred list)
- "Linear-update variant of `break'."
- (check-arg procedure? pred break!)
- (let loop ((l list)
- (prev #f))
- (cond ((null? l)
- (values list '()))
- ((pred (car l))
- (if (pair? prev)
- (begin
- (set-cdr! prev '())
- (values list l))
- (values '() list)))
- (else
- (loop (cdr l) l)))))
-
-(define (any pred ls . lists)
- (check-arg procedure? pred any)
- (if (null? lists)
- (any1 pred ls)
- (let lp ((lists (cons ls lists)))
- (cond ((any1 null? lists)
- #f)
- ((any1 null? (map cdr lists))
- (apply pred (map car lists)))
- (else
- (or (apply pred (map car lists)) (lp (map cdr lists))))))))
-
-(define (any1 pred ls)
- (let lp ((ls ls))
- (cond ((null? ls)
- #f)
- ((null? (cdr ls))
- (pred (car ls)))
- (else
- (or (pred (car ls)) (lp (cdr ls)))))))
-
-(define (every pred ls . lists)
- (check-arg procedure? pred every)
- (if (null? lists)
- (every1 pred ls)
- (let lp ((lists (cons ls lists)))
- (cond ((any1 null? lists)
- #t)
- ((any1 null? (map cdr lists))
- (apply pred (map car lists)))
- (else
- (and (apply pred (map car lists)) (lp (map cdr lists))))))))
-
-(define (every1 pred ls)
- (let lp ((ls ls))
- (cond ((null? ls)
- #t)
- ((null? (cdr ls))
- (pred (car ls)))
- (else
- (and (pred (car ls)) (lp (cdr ls)))))))
-
-(define (list-index pred clist1 . rest)
- "Return the index of the first set of elements, one from each of
-CLIST1 ... CLISTN, that satisfies PRED."
- (check-arg procedure? pred list-index)
- (if (null? rest)
- (let lp ((l clist1) (i 0))
- (if (null? l)
- #f
- (if (pred (car l))
- i
- (lp (cdr l) (+ i 1)))))
- (let lp ((lists (cons clist1 rest)) (i 0))
- (cond ((any1 null? lists)
- #f)
- ((apply pred (map car lists)) i)
- (else
- (lp (map cdr lists) (+ i 1)))))))
-
-;;; Association lists
-
-(define alist-cons acons)
-
-(define (alist-copy alist)
- "Return a copy of ALIST, copying both the pairs comprising the list
-and those making the associations."
- (let lp ((a alist)
- (rl '()))
- (if (null? a)
- (reverse! rl)
- (lp (cdr a) (alist-cons (caar a) (cdar a) rl)))))
-
-(define* (alist-delete key alist #\optional (k= equal?))
- (check-arg procedure? k= alist-delete)
- (let lp ((a alist) (rl '()))
- (if (null? a)
- (reverse! rl)
- (if (k= key (caar a))
- (lp (cdr a) rl)
- (lp (cdr a) (cons (car a) rl))))))
-
-(define* (alist-delete! key alist #\optional (k= equal?))
- (alist-delete key alist k=)) ; XXX:optimize
-
-;;; Delete / assoc / member
-
-(define* (member x ls #\optional (= equal?))
- (cond
- ;; This might be performance-sensitive, so punt on the check here,
- ;; relying on memq/memv to check that = is a procedure.
- ((eq? = eq?) (memq x ls))
- ((eq? = eqv?) (memv x ls))
- (else
- (check-arg procedure? = member)
- (find-tail (lambda (y) (= x y)) ls))))
-
-;;; Set operations on lists
-
-(define (lset<= = . rest)
- (check-arg procedure? = lset<=)
- (if (null? rest)
- #t
- (let lp ((f (car rest)) (r (cdr rest)))
- (or (null? r)
- (and (every (lambda (el) (member el (car r) =)) f)
- (lp (car r) (cdr r)))))))
-
-(define (lset= = . rest)
- (check-arg procedure? = lset<=)
- (if (null? rest)
- #t
- (let lp ((f (car rest)) (r (cdr rest)))
- (or (null? r)
- (and (every (lambda (el) (member el (car r) =)) f)
- (every (lambda (el) (member el f (lambda (x y) (= y x)))) (car r))
- (lp (car r) (cdr r)))))))
-
-;; It's not quite clear if duplicates among the `rest' elements are meant to
-;; be cast out. The spec says `=' is called as (= lstelem restelem),
-;; suggesting perhaps not, but the reference implementation shows the "list"
-;; at each stage as including those elements already added. The latter
-;; corresponds to what's described for lset-union, so that's what's done.
-;;
-(define (lset-adjoin = list . rest)
- "Add to LIST any of the elements of REST not already in the list.
-These elements are `cons'ed onto the start of LIST (so the return shares
-a common tail with LIST), but the order they're added is unspecified.
-
-The given `=' procedure is used for comparing elements, called
-as `(@var{=} listelem elem)', i.e., the second argument is one of the
-given REST parameters."
- ;; If `=' is `eq?' or `eqv?', users won't be able to tell which arg is
- ;; first, so we can pass the raw procedure through to `member',
- ;; allowing `memq' / `memv' to be selected.
- (define pred
- (if (or (eq? = eq?) (eq? = eqv?))
- =
- (begin
- (check-arg procedure? = lset-adjoin)
- (lambda (x y) (= y x)))))
-
- (let lp ((ans list) (rest rest))
- (if (null? rest)
- ans
- (lp (if (member (car rest) ans pred)
- ans
- (cons (car rest) ans))
- (cdr rest)))))
-
-(define (lset-union = . rest)
- ;; Likewise, allow memq / memv to be used if possible.
- (define pred
- (if (or (eq? = eq?) (eq? = eqv?))
- =
- (begin
- (check-arg procedure? = lset-union)
- (lambda (x y) (= y x)))))
-
- (fold (lambda (lis ans) ; Compute ANS + LIS.
- (cond ((null? lis) ans) ; Don't copy any lists
- ((null? ans) lis) ; if we don't have to.
- ((eq? lis ans) ans)
- (else
- (fold (lambda (elt ans)
- (if (member elt ans pred)
- ans
- (cons elt ans)))
- ans lis))))
- '()
- rest))
-
-(define (lset-intersection = list1 . rest)
- (check-arg procedure? = lset-intersection)
- (let lp ((l list1) (acc '()))
- (if (null? l)
- (reverse! acc)
- (if (every (lambda (ll) (member (car l) ll =)) rest)
- (lp (cdr l) (cons (car l) acc))
- (lp (cdr l) acc)))))
-
-(define (lset-difference = list1 . rest)
- (check-arg procedure? = lset-difference)
- (if (null? rest)
- list1
- (let lp ((l list1) (acc '()))
- (if (null? l)
- (reverse! acc)
- (if (any (lambda (ll) (member (car l) ll =)) rest)
- (lp (cdr l) acc)
- (lp (cdr l) (cons (car l) acc)))))))
-
-;(define (fold kons knil list1 . rest)
-
-(define (lset-xor = . rest)
- (check-arg procedure? = lset-xor)
- (fold (lambda (lst res)
- (let lp ((l lst) (acc '()))
- (if (null? l)
- (let lp0 ((r res) (acc acc))
- (if (null? r)
- (reverse! acc)
- (if (member (car r) lst =)
- (lp0 (cdr r) acc)
- (lp0 (cdr r) (cons (car r) acc)))))
- (if (member (car l) res =)
- (lp (cdr l) acc)
- (lp (cdr l) (cons (car l) acc))))))
- '()
- rest))
-
-(define (lset-diff+intersection = list1 . rest)
- (check-arg procedure? = lset-diff+intersection)
- (let lp ((l list1) (accd '()) (acci '()))
- (if (null? l)
- (values (reverse! accd) (reverse! acci))
- (let ((appears (every (lambda (ll) (member (car l) ll =)) rest)))
- (if appears
- (lp (cdr l) accd (cons (car l) acci))
- (lp (cdr l) (cons (car l) accd) acci))))))
-
-
-(define (lset-union! = . rest)
- (check-arg procedure? = lset-union!)
- (apply lset-union = rest)) ; XXX:optimize
-
-(define (lset-intersection! = list1 . rest)
- (check-arg procedure? = lset-intersection!)
- (apply lset-intersection = list1 rest)) ; XXX:optimize
-
-(define (lset-xor! = . rest)
- (check-arg procedure? = lset-xor!)
- (apply lset-xor = rest)) ; XXX:optimize
-
-(define (lset-diff+intersection! = list1 . rest)
- (check-arg procedure? = lset-diff+intersection!)
- (apply lset-diff+intersection = list1 rest)) ; XXX:optimize
-
-;;; srfi-1.scm ends here
-;;; srfi-10.scm --- Hash-Comma Reader Extension
-
-;; Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Commentary:
-
-;; This module implements the syntax extension #,(), also called
-;; hash-comma, which is defined in SRFI-10.
-;;
-;; The support for SRFI-10 consists of the procedure
-;; `define-reader-ctor' for defining new reader constructors and the
-;; read syntax form
-;;
-;; #,(<ctor> <datum> ...)
-;;
-;; where <ctor> must be a symbol for which a read constructor was
-;; defined previously.
-;;
-;; Example:
-;;
-;; (define-reader-ctor 'file open-input-file)
-;; (define f '#,(file "/etc/passwd"))
-;; (read-line f)
-;; =>
-;; "root:x:0:0:root:/root:/bin/bash"
-;;
-;; Please note the quote before the #,(file ...) expression. This is
-;; necessary because ports are not self-evaluating in Guile.
-;;
-;; This module is fully documented in the Guile Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-10)
- \:use-module (ice-9 rdelim)
- \:export (define-reader-ctor))
-
-(cond-expand-provide (current-module) '(srfi-10))
-
-;; This hash table stores the association between comma-hash tags and
-;; the corresponding constructor procedures.
-;;
-(define reader-ctors (make-hash-table 31))
-
-;; This procedure installs the procedure @var{proc} as the constructor
-;; for the comma-hash tag @var{symbol}.
-;;
-(define (define-reader-ctor symbol proc)
- (hashq-set! reader-ctors symbol proc)
- (if #f #f)) ; Return unspecified value.
-
-;; Retrieve the constructor procedure for the tag @var{symbol} or
-;; throw an error if no such tag is defined.
-;;
-(define (lookup symbol)
- (let ((p (hashq-ref reader-ctors symbol #f)))
- (if (procedure? p)
- p
- (error "unknown hash-comma tag " symbol))))
-
-;; This is the actual reader extension.
-;;
-(define (hash-comma char port)
- (let* ((obj (read port)))
- (if (and (list? obj) (positive? (length obj)) (symbol? (car obj)))
- (let ((p (lookup (car obj))))
- (let ((res (apply p (cdr obj))))
- res))
- (error "syntax error in hash-comma expression"))))
-
-;; Install the hash extension.
-;;
-(read-hash-extend #\, hash-comma)
-
-;;; srfi-10.scm ends here
-;;; srfi-11.scm --- let-values and let*-values
-
-;; Copyright (C) 2000, 2001, 2002, 2004, 2006, 2009 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Commentary:
-
-;; This module exports two syntax forms: let-values and let*-values.
-;;
-;; Sample usage:
-;;
-;; (let-values (((x y . z) (foo a b))
-;; ((p q) (bar c)))
-;; (baz x y z p q))
-;;
-;; This binds `x' and `y' to the first to values returned by `foo',
-;; `z' to the rest of the values from `foo', and `p' and `q' to the
-;; values returned by `bar'. All of these are available to `baz'.
-;;
-;; let*-values : let-values :: let* : let
-;;
-;; This module is fully documented in the Guile Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-11)
- \:export-syntax (let-values let*-values))
-
-(cond-expand-provide (current-module) '(srfi-11))
-
-;;;;;;;;;;;;;;
-;; let-values
-;;
-;; Current approach is to translate
-;;
-;; (let-values (((x y . z) (foo a b))
-;; ((p q) (bar c)))
-;; (baz x y z p q))
-;;
-;; into
-;;
-;; (call-with-values (lambda () (foo a b))
-;; (lambda (<tmp-x> <tmp-y> . <tmp-z>)
-;; (call-with-values (lambda () (bar c))
-;; (lambda (<tmp-p> <tmp-q>)
-;; (let ((x <tmp-x>)
-;; (y <tmp-y>)
-;; (z <tmp-z>)
-;; (p <tmp-p>)
-;; (q <tmp-q>))
-;; (baz x y z p q))))))
-
-;; We could really use quasisyntax here...
-(define-syntax let-values
- (lambda (x)
- (syntax-case x ()
- ((_ ((binds exp)) b0 b1 ...)
- (syntax (call-with-values (lambda () exp)
- (lambda binds b0 b1 ...))))
- ((_ (clause ...) b0 b1 ...)
- (let lp ((clauses (syntax (clause ...)))
- (ids '())
- (tmps '()))
- (if (null? clauses)
- (with-syntax (((id ...) ids)
- ((tmp ...) tmps))
- (syntax (let ((id tmp) ...)
- b0 b1 ...)))
- (syntax-case (car clauses) ()
- (((var ...) exp)
- (with-syntax (((new-tmp ...) (generate-temporaries
- (syntax (var ...))))
- ((id ...) ids)
- ((tmp ...) tmps))
- (with-syntax ((inner (lp (cdr clauses)
- (syntax (var ... id ...))
- (syntax (new-tmp ... tmp ...)))))
- (syntax (call-with-values (lambda () exp)
- (lambda (new-tmp ...) inner))))))
- ((vars exp)
- (with-syntax ((((new-tmp . new-var) ...)
- (let lp ((vars (syntax vars)))
- (syntax-case vars ()
- ((id . rest)
- (acons (syntax id)
- (car
- (generate-temporaries (syntax (id))))
- (lp (syntax rest))))
- (id (acons (syntax id)
- (car
- (generate-temporaries (syntax (id))))
- '())))))
- ((id ...) ids)
- ((tmp ...) tmps))
- (with-syntax ((inner (lp (cdr clauses)
- (syntax (new-var ... id ...))
- (syntax (new-tmp ... tmp ...))))
- (args (let lp ((tmps (syntax (new-tmp ...))))
- (syntax-case tmps ()
- ((id) (syntax id))
- ((id . rest) (cons (syntax id)
- (lp (syntax rest))))))))
- (syntax (call-with-values (lambda () exp)
- (lambda args inner)))))))))))))
-
-;;;;;;;;;;;;;;
-;; let*-values
-;;
-;; Current approach is to translate
-;;
-;; (let*-values (((x y z) (foo a b))
-;; ((p q) (bar c)))
-;; (baz x y z p q))
-;;
-;; into
-;;
-;; (call-with-values (lambda () (foo a b))
-;; (lambda (x y z)
-;; (call-with-values (lambda (bar c))
-;; (lambda (p q)
-;; (baz x y z p q)))))
-
-(define-syntax let*-values
- (syntax-rules ()
- ((let*-values () body ...)
- (let () body ...))
- ((let*-values ((vars-1 binding-1) (vars-2 binding-2) ...) body ...)
- (call-with-values (lambda () binding-1)
- (lambda vars-1
- (let*-values ((vars-2 binding-2) ...)
- body ...))))))
-
-;;; srfi-11.scm ends here
-;;; srfi-111.scm -- SRFI 111 Boxes
-
-;; Copyright (C) 2014 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (srfi srfi-111)
- #\use-module (srfi srfi-9)
- #\use-module (srfi srfi-9 gnu)
- #\export (box box? unbox set-box!))
-
-(cond-expand-provide (current-module) '(srfi-111))
-
-(define-record-type <box>
- (box value)
- box?
- (value unbox set-box!))
-
-(set-record-type-printer! <box>
- (lambda (box port)
- (display "#<box " port)
- (display (number->string (object-address box) 16) port)
- (display " value: ")
- (write (unbox box) port)
- (display ">" port)))
-;;; srfi-13.scm --- String Library
-
-;; Copyright (C) 2001, 2002, 2003, 2004, 2006 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Commentary:
-
-;; This module is fully documented in the Guile Reference Manual.
-;;
-;; All procedures are in the core and are simply reexported here.
-
-;;; Code:
-
-(define-module (srfi srfi-13))
-
-(re-export
-;;; Predicates
- string?
- string-null?
- string-any
- string-every
-
-;;; Constructors
- make-string
- string
- string-tabulate
-
-;;; List/string conversion
- string->list
- list->string
- reverse-list->string
- string-join
-
-;;; Selection
- string-length
- string-ref
- string-copy
- substring/shared
- string-copy!
- string-take string-take-right
- string-drop string-drop-right
- string-pad string-pad-right
- string-trim string-trim-right
- string-trim-both
-
-;;; Modification
- string-set!
- string-fill!
-
-;;; Comparison
- string-compare
- string-compare-ci
- string= string<>
- string< string>
- string<= string>=
- string-ci= string-ci<>
- string-ci< string-ci>
- string-ci<= string-ci>=
- string-hash string-hash-ci
-
-;;; Prefixes/Suffixes
- string-prefix-length
- string-prefix-length-ci
- string-suffix-length
- string-suffix-length-ci
- string-prefix?
- string-prefix-ci?
- string-suffix?
- string-suffix-ci?
-
-;;; Searching
- string-index
- string-index-right
- string-skip string-skip-right
- string-count
- string-contains string-contains-ci
-
-;;; Alphabetic case mapping
- string-upcase
- string-upcase!
- string-downcase
- string-downcase!
- string-titlecase
- string-titlecase!
-
-;;; Reverse/Append
- string-reverse
- string-reverse!
- string-append
- string-append/shared
- string-concatenate
- string-concatenate-reverse
- string-concatenate/shared
- string-concatenate-reverse/shared
-
-;;; Fold/Unfold/Map
- string-map string-map!
- string-fold
- string-fold-right
- string-unfold
- string-unfold-right
- string-for-each
- string-for-each-index
-
-;;; Replicate/Rotate
- xsubstring
- string-xcopy!
-
-;;; Miscellaneous
- string-replace
- string-tokenize
-
-;;; Filtering/Deleting
- string-filter
- string-delete)
-
-(cond-expand-provide (current-module) '(srfi-13))
-
-;;; srfi-13.scm ends here
-;;; srfi-14.scm --- Character-set Library
-
-;; Copyright (C) 2001, 2002, 2004, 2006 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Commentary:
-
-;; This module is fully documented in the Guile Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-14))
-
-(re-export
-;;; General procedures
- char-set?
- char-set=
- char-set<=
- char-set-hash
-
-;;; Iterating over character sets
- char-set-cursor
- char-set-ref
- char-set-cursor-next
- end-of-char-set?
- char-set-fold
- char-set-unfold char-set-unfold!
- char-set-for-each
- char-set-map
-
-;;; Creating character sets
- char-set-copy
- char-set
- list->char-set list->char-set!
- string->char-set string->char-set!
- char-set-filter char-set-filter!
- ucs-range->char-set ucs-range->char-set!
- ->char-set
-
-;;; Querying character sets
- char-set-size
- char-set-count
- char-set->list
- char-set->string
- char-set-contains?
- char-set-every
- char-set-any
-
-;;; Character set algebra
- char-set-adjoin char-set-adjoin!
- char-set-delete char-set-delete!
- char-set-complement
- char-set-union
- char-set-intersection
- char-set-difference
- char-set-xor
- char-set-diff+intersection
- char-set-complement!
- char-set-union!
- char-set-intersection!
- char-set-difference!
- char-set-xor!
- char-set-diff+intersection!
-
-;;; Standard character sets
- char-set:lower-case
- char-set:upper-case
- char-set:title-case
- char-set:letter
- char-set:digit
- char-set:letter+digit
- char-set:graphic
- char-set:printing
- char-set:whitespace
- char-set:iso-control
- char-set:punctuation
- char-set:symbol
- char-set:hex-digit
- char-set:blank
- char-set:ascii
- char-set:empty
- char-set:full)
-
-(cond-expand-provide (current-module) '(srfi-14))
-
-;;; srfi-14.scm ends here
-;;; srfi-16.scm --- case-lambda
-
-;; Copyright (C) 2001, 2002, 2006, 2009, 2014 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Martin Grabmueller
-
-;;; Commentary:
-
-;; Implementation of SRFI-16. `case-lambda' is a syntactic form
-;; which permits writing functions acting different according to the
-;; number of arguments passed.
-;;
-;; The syntax of the `case-lambda' form is defined in the following
-;; EBNF grammar.
-;;
-;; <case-lambda>
-;; --> (case-lambda <case-lambda-clause>)
-;; <case-lambda-clause>
-;; --> (<signature> <definition-or-command>*)
-;; <signature>
-;; --> (<identifier>*)
-;; | (<identifier>* . <identifier>)
-;; | <identifier>
-;;
-;; The value returned by a `case-lambda' form is a procedure which
-;; matches the number of actual arguments against the signatures in
-;; the various clauses, in order. The first matching clause is
-;; selected, the corresponding values from the actual parameter list
-;; are bound to the variable names in the clauses and the body of the
-;; clause is evaluated.
-
-;;; Code:
-
-(define-module (srfi srfi-16)
- #\re-export (case-lambda))
-
-;; Case-lambda is now provided by core psyntax.
-;;; srfi-17.scm --- Generalized set!
-
-;; Copyright (C) 2001, 2002, 2003, 2006 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Matthias Koeppe <mkoeppe@mail.math.uni-magdeburg.de>
-
-;;; Commentary:
-
-;; This is an implementation of SRFI-17: Generalized set!
-;;
-;; It exports the Guile procedure `make-procedure-with-setter' under
-;; the SRFI name `getter-with-setter' and exports the standard
-;; procedures `car', `cdr', ..., `cdddr', `string-ref' and
-;; `vector-ref' as procedures with setters, as required by the SRFI.
-;;
-;; SRFI-17 was heavily criticized during its discussion period but it
-;; was finalized anyway. One issue was its concept of globally
-;; associating setter "properties" with (procedure) values, which is
-;; non-Schemy. For this reason, this implementation chooses not to
-;; provide a way to set the setter of a procedure. In fact, (set!
-;; (setter PROC) SETTER) signals an error. The only way to attach a
-;; setter to a procedure is to create a new object (a "procedure with
-;; setter") via the `getter-with-setter' procedure. This procedure is
-;; also specified in the SRFI. Using it avoids the described
-;; problems.
-;;
-;; This module is fully documented in the Guile Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-17)
- \:export (getter-with-setter)
- \:replace (;; redefined standard procedures
- setter
- car cdr caar cadr cdar cddr caaar caadr cadar caddr cdaar
- cdadr cddar cdddr caaaar caaadr caadar caaddr cadaar cadadr
- caddar cadddr cdaaar cdaadr cdadar cdaddr cddaar cddadr
- cdddar cddddr string-ref vector-ref))
-
-(cond-expand-provide (current-module) '(srfi-17))
-
-;;; Procedures
-
-(define getter-with-setter make-procedure-with-setter)
-
-(define setter
- (getter-with-setter
- (@ (guile) setter)
- (lambda args
- (error "Setting setters is not supported for a good reason."))))
-
-;;; Redefine R5RS procedures to appropriate procedures with setters
-
-(define (compose-setter setter location)
- (lambda (obj value)
- (setter (location obj) value)))
-
-(define car
- (getter-with-setter (@ (guile) car)
- set-car!))
-(define cdr
- (getter-with-setter (@ (guile) cdr)
- set-cdr!))
-
-(define caar
- (getter-with-setter (@ (guile) caar)
- (compose-setter set-car! (@ (guile) car))))
-(define cadr
- (getter-with-setter (@ (guile) cadr)
- (compose-setter set-car! (@ (guile) cdr))))
-(define cdar
- (getter-with-setter (@ (guile) cdar)
- (compose-setter set-cdr! (@ (guile) car))))
-(define cddr
- (getter-with-setter (@ (guile) cddr)
- (compose-setter set-cdr! (@ (guile) cdr))))
-
-(define caaar
- (getter-with-setter (@ (guile) caaar)
- (compose-setter set-car! (@ (guile) caar))))
-(define caadr
- (getter-with-setter (@ (guile) caadr)
- (compose-setter set-car! (@ (guile) cadr))))
-(define cadar
- (getter-with-setter (@ (guile) cadar)
- (compose-setter set-car! (@ (guile) cdar))))
-(define caddr
- (getter-with-setter (@ (guile) caddr)
- (compose-setter set-car! (@ (guile) cddr))))
-(define cdaar
- (getter-with-setter (@ (guile) cdaar)
- (compose-setter set-cdr! (@ (guile) caar))))
-(define cdadr
- (getter-with-setter (@ (guile) cdadr)
- (compose-setter set-cdr! (@ (guile) cadr))))
-(define cddar
- (getter-with-setter (@ (guile) cddar)
- (compose-setter set-cdr! (@ (guile) cdar))))
-(define cdddr
- (getter-with-setter (@ (guile) cdddr)
- (compose-setter set-cdr! (@ (guile) cddr))))
-
-(define caaaar
- (getter-with-setter (@ (guile) caaaar)
- (compose-setter set-car! (@ (guile) caaar))))
-(define caaadr
- (getter-with-setter (@ (guile) caaadr)
- (compose-setter set-car! (@ (guile) caadr))))
-(define caadar
- (getter-with-setter (@ (guile) caadar)
- (compose-setter set-car! (@ (guile) cadar))))
-(define caaddr
- (getter-with-setter (@ (guile) caaddr)
- (compose-setter set-car! (@ (guile) caddr))))
-(define cadaar
- (getter-with-setter (@ (guile) cadaar)
- (compose-setter set-car! (@ (guile) cdaar))))
-(define cadadr
- (getter-with-setter (@ (guile) cadadr)
- (compose-setter set-car! (@ (guile) cdadr))))
-(define caddar
- (getter-with-setter (@ (guile) caddar)
- (compose-setter set-car! (@ (guile) cddar))))
-(define cadddr
- (getter-with-setter (@ (guile) cadddr)
- (compose-setter set-car! (@ (guile) cdddr))))
-(define cdaaar
- (getter-with-setter (@ (guile) cdaaar)
- (compose-setter set-cdr! (@ (guile) caaar))))
-(define cdaadr
- (getter-with-setter (@ (guile) cdaadr)
- (compose-setter set-cdr! (@ (guile) caadr))))
-(define cdadar
- (getter-with-setter (@ (guile) cdadar)
- (compose-setter set-cdr! (@ (guile) cadar))))
-(define cdaddr
- (getter-with-setter (@ (guile) cdaddr)
- (compose-setter set-cdr! (@ (guile) caddr))))
-(define cddaar
- (getter-with-setter (@ (guile) cddaar)
- (compose-setter set-cdr! (@ (guile) cdaar))))
-(define cddadr
- (getter-with-setter (@ (guile) cddadr)
- (compose-setter set-cdr! (@ (guile) cdadr))))
-(define cdddar
- (getter-with-setter (@ (guile) cdddar)
- (compose-setter set-cdr! (@ (guile) cddar))))
-(define cddddr
- (getter-with-setter (@ (guile) cddddr)
- (compose-setter set-cdr! (@ (guile) cdddr))))
-
-(define string-ref
- (getter-with-setter (@ (guile) string-ref)
- string-set!))
-
-(define vector-ref
- (getter-with-setter (@ (guile) vector-ref)
- vector-set!))
-
-;;; srfi-17.scm ends here
-;;; srfi-18.scm --- Multithreading support
-
-;; Copyright (C) 2008, 2009, 2010, 2014 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Julian Graham <julian.graham@aya.yale.edu>
-;;; Date: 2008-04-11
-
-;;; Commentary:
-
-;; This is an implementation of SRFI-18 (Multithreading support).
-;;
-;; All procedures defined in SRFI-18, which are not already defined in
-;; the Guile core library, are exported.
-;;
-;; This module is fully documented in the Guile Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-18)
- \:use-module (srfi srfi-34)
- \:export (
-
-;;; Threads
- ;; current-thread <= in the core
- ;; thread? <= in the core
- make-thread
- thread-name
- thread-specific
- thread-specific-set!
- thread-start!
- thread-yield!
- thread-sleep!
- thread-terminate!
- thread-join!
-
-;;; Mutexes
- ;; mutex? <= in the core
- make-mutex
- mutex-name
- mutex-specific
- mutex-specific-set!
- mutex-state
- mutex-lock!
- mutex-unlock!
-
-;;; Condition variables
- ;; condition-variable? <= in the core
- make-condition-variable
- condition-variable-name
- condition-variable-specific
- condition-variable-specific-set!
- condition-variable-signal!
- condition-variable-broadcast!
- condition-variable-wait!
-
-;;; Time
- current-time
- time?
- time->seconds
- seconds->time
-
- current-exception-handler
- with-exception-handler
- raise
- join-timeout-exception?
- abandoned-mutex-exception?
- terminated-thread-exception?
- uncaught-exception?
- uncaught-exception-reason
- )
- \:re-export (current-thread thread? mutex? condition-variable?)
- \:replace (current-time
- make-thread
- make-mutex
- make-condition-variable
- raise))
-
-(if (not (provided? 'threads))
- (error "SRFI-18 requires Guile with threads support"))
-
-(cond-expand-provide (current-module) '(srfi-18))
-
-(define (check-arg-type pred arg caller)
- (if (pred arg)
- arg
- (scm-error 'wrong-type-arg caller
- "Wrong type argument: ~S" (list arg) '())))
-
-(define abandoned-mutex-exception (list 'abandoned-mutex-exception))
-(define join-timeout-exception (list 'join-timeout-exception))
-(define terminated-thread-exception (list 'terminated-thread-exception))
-(define uncaught-exception (list 'uncaught-exception))
-
-(define object-names (make-weak-key-hash-table))
-(define object-specifics (make-weak-key-hash-table))
-(define thread-start-conds (make-weak-key-hash-table))
-(define thread-exception-handlers (make-weak-key-hash-table))
-
-;; EXCEPTIONS
-
-(define raise (@ (srfi srfi-34) raise))
-(define (initial-handler obj)
- (srfi-18-exception-preserver (cons uncaught-exception obj)))
-
-(define thread->exception (make-object-property))
-
-(define (srfi-18-exception-preserver obj)
- (if (or (terminated-thread-exception? obj)
- (uncaught-exception? obj))
- (set! (thread->exception (current-thread)) obj)))
-
-(define (srfi-18-exception-handler key . args)
-
- ;; SRFI 34 exceptions continue to bubble up no matter who handles them, so
- ;; if one is caught at this level, it has already been taken care of by
- ;; `initial-handler'.
-
- (and (not (eq? key 'srfi-34))
- (srfi-18-exception-preserver (if (null? args)
- (cons uncaught-exception key)
- (cons* uncaught-exception key args)))))
-
-(define (current-handler-stack)
- (let ((ct (current-thread)))
- (or (hashq-ref thread-exception-handlers ct)
- (hashq-set! thread-exception-handlers ct (list initial-handler)))))
-
-(define (with-exception-handler handler thunk)
- (let ((ct (current-thread))
- (hl (current-handler-stack)))
- (check-arg-type procedure? handler "with-exception-handler")
- (check-arg-type thunk? thunk "with-exception-handler")
- (hashq-set! thread-exception-handlers ct (cons handler hl))
- (apply (@ (srfi srfi-34) with-exception-handler)
- (list (lambda (obj)
- (hashq-set! thread-exception-handlers ct hl)
- (handler obj))
- (lambda ()
- (call-with-values thunk
- (lambda res
- (hashq-set! thread-exception-handlers ct hl)
- (apply values res))))))))
-
-(define (current-exception-handler)
- (car (current-handler-stack)))
-
-(define (join-timeout-exception? obj) (eq? obj join-timeout-exception))
-(define (abandoned-mutex-exception? obj) (eq? obj abandoned-mutex-exception))
-(define (uncaught-exception? obj)
- (and (pair? obj) (eq? (car obj) uncaught-exception)))
-(define (uncaught-exception-reason exc)
- (cdr (check-arg-type uncaught-exception? exc "uncaught-exception-reason")))
-(define (terminated-thread-exception? obj)
- (eq? obj terminated-thread-exception))
-
-;; THREADS
-
-;; Create a new thread and prevent it from starting using a condition variable.
-;; Once started, install a top-level exception handler that rethrows any
-;; exceptions wrapped in an uncaught-exception wrapper.
-
-(define make-thread
- (let ((make-cond-wrapper (lambda (thunk lcond lmutex scond smutex)
- (lambda ()
- (lock-mutex lmutex)
- (signal-condition-variable lcond)
- (lock-mutex smutex)
- (unlock-mutex lmutex)
- (wait-condition-variable scond smutex)
- (unlock-mutex smutex)
- (with-exception-handler initial-handler
- thunk)))))
- (lambda (thunk . name)
- (let ((n (and (pair? name) (car name)))
-
- (lm (make-mutex 'launch-mutex))
- (lc (make-condition-variable 'launch-condition-variable))
- (sm (make-mutex 'start-mutex))
- (sc (make-condition-variable 'start-condition-variable)))
-
- (lock-mutex lm)
- (let ((t (call-with-new-thread (make-cond-wrapper thunk lc lm sc sm)
- srfi-18-exception-handler)))
- (hashq-set! thread-start-conds t (cons sm sc))
- (and n (hashq-set! object-names t n))
- (wait-condition-variable lc lm)
- (unlock-mutex lm)
- t)))))
-
-(define (thread-name thread)
- (hashq-ref object-names (check-arg-type thread? thread "thread-name")))
-
-(define (thread-specific thread)
- (hashq-ref object-specifics
- (check-arg-type thread? thread "thread-specific")))
-
-(define (thread-specific-set! thread obj)
- (hashq-set! object-specifics
- (check-arg-type thread? thread "thread-specific-set!")
- obj)
- *unspecified*)
-
-(define (thread-start! thread)
- (let ((x (hashq-ref thread-start-conds
- (check-arg-type thread? thread "thread-start!"))))
- (and x (let ((smutex (car x))
- (scond (cdr x)))
- (hashq-remove! thread-start-conds thread)
- (lock-mutex smutex)
- (signal-condition-variable scond)
- (unlock-mutex smutex)))
- thread))
-
-(define (thread-yield!) (yield) *unspecified*)
-
-(define (thread-sleep! timeout)
- (let* ((ct (time->seconds (current-time)))
- (t (cond ((time? timeout) (- (time->seconds timeout) ct))
- ((number? timeout) (- timeout ct))
- (else (scm-error 'wrong-type-arg "thread-sleep!"
- "Wrong type argument: ~S"
- (list timeout)
- '()))))
- (secs (inexact->exact (truncate t)))
- (usecs (inexact->exact (truncate (* (- t secs) 1000000)))))
- (and (> secs 0) (sleep secs))
- (and (> usecs 0) (usleep usecs))
- *unspecified*))
-
-;; A convenience function for installing exception handlers on SRFI-18
-;; primitives that resume the calling continuation after the handler is
-;; invoked -- this resolves a behavioral incompatibility with Guile's
-;; implementation of SRFI-34, which uses lazy-catch and rethrows handled
-;; exceptions. (SRFI-18, "Primitives and exceptions")
-
-(define (wrap thunk)
- (lambda (continuation)
- (with-exception-handler (lambda (obj)
- ((current-exception-handler) obj)
- (continuation))
- thunk)))
-
-;; A pass-thru to cancel-thread that first installs a handler that throws
-;; terminated-thread exception, as per SRFI-18,
-
-(define (thread-terminate! thread)
- (define (thread-terminate-inner!)
- (let ((current-handler (thread-cleanup thread)))
- (if (thunk? current-handler)
- (set-thread-cleanup! thread
- (lambda ()
- (with-exception-handler initial-handler
- current-handler)
- (srfi-18-exception-preserver
- terminated-thread-exception)))
- (set-thread-cleanup! thread
- (lambda () (srfi-18-exception-preserver
- terminated-thread-exception))))
- (cancel-thread thread)
- *unspecified*))
- (thread-terminate-inner!))
-
-(define (thread-join! thread . args)
- (define thread-join-inner!
- (wrap (lambda ()
- (let ((v (apply join-thread (cons thread args)))
- (e (thread->exception thread)))
- (if (and (= (length args) 1) (not v))
- (raise join-timeout-exception))
- (if e (raise e))
- v))))
- (call/cc thread-join-inner!))
-
-;; MUTEXES
-;; These functions are all pass-thrus to the existing Guile implementations.
-
-(define make-mutex
- (lambda name
- (let ((n (and (pair? name) (car name)))
- (m ((@ (guile) make-mutex)
- 'unchecked-unlock
- 'allow-external-unlock
- 'recursive)))
- (and n (hashq-set! object-names m n)) m)))
-
-(define (mutex-name mutex)
- (hashq-ref object-names (check-arg-type mutex? mutex "mutex-name")))
-
-(define (mutex-specific mutex)
- (hashq-ref object-specifics
- (check-arg-type mutex? mutex "mutex-specific")))
-
-(define (mutex-specific-set! mutex obj)
- (hashq-set! object-specifics
- (check-arg-type mutex? mutex "mutex-specific-set!")
- obj)
- *unspecified*)
-
-(define (mutex-state mutex)
- (let ((owner (mutex-owner mutex)))
- (if owner
- (if (thread-exited? owner) 'abandoned owner)
- (if (> (mutex-level mutex) 0) 'not-owned 'not-abandoned))))
-
-(define (mutex-lock! mutex . args)
- (define mutex-lock-inner!
- (wrap (lambda ()
- (catch 'abandoned-mutex-error
- (lambda () (apply lock-mutex (cons mutex args)))
- (lambda (key . args) (raise abandoned-mutex-exception))))))
- (call/cc mutex-lock-inner!))
-
-(define (mutex-unlock! mutex . args)
- (apply unlock-mutex (cons mutex args)))
-
-;; CONDITION VARIABLES
-;; These functions are all pass-thrus to the existing Guile implementations.
-
-(define make-condition-variable
- (lambda name
- (let ((n (and (pair? name) (car name)))
- (m ((@ (guile) make-condition-variable))))
- (and n (hashq-set! object-names m n)) m)))
-
-(define (condition-variable-name condition-variable)
- (hashq-ref object-names (check-arg-type condition-variable?
- condition-variable
- "condition-variable-name")))
-
-(define (condition-variable-specific condition-variable)
- (hashq-ref object-specifics (check-arg-type condition-variable?
- condition-variable
- "condition-variable-specific")))
-
-(define (condition-variable-specific-set! condition-variable obj)
- (hashq-set! object-specifics
- (check-arg-type condition-variable?
- condition-variable
- "condition-variable-specific-set!")
- obj)
- *unspecified*)
-
-(define (condition-variable-signal! cond)
- (signal-condition-variable cond)
- *unspecified*)
-
-(define (condition-variable-broadcast! cond)
- (broadcast-condition-variable cond)
- *unspecified*)
-
-;; TIME
-
-(define current-time gettimeofday)
-(define (time? obj)
- (and (pair? obj)
- (let ((co (car obj))) (and (integer? co) (>= co 0)))
- (let ((co (cdr obj))) (and (integer? co) (>= co 0)))))
-
-(define (time->seconds time)
- (and (check-arg-type time? time "time->seconds")
- (+ (car time) (/ (cdr time) 1000000))))
-
-(define (seconds->time x)
- (and (check-arg-type number? x "seconds->time")
- (let ((fx (truncate x)))
- (cons (inexact->exact fx)
- (inexact->exact (truncate (* (- x fx) 1000000)))))))
-
-;; srfi-18.scm ends here
-;;; srfi-19.scm --- Time/Date Library
-
-;; Copyright (C) 2001-2003, 2005-2011, 2014, 2016
-;; Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Rob Browning <rlb@cs.utexas.edu>
-;;; Originally from SRFI reference implementation by Will Fitzgerald.
-
-;;; Commentary:
-
-;; This module is fully documented in the Guile Reference Manual.
-
-;;; Code:
-
-;; FIXME: I haven't checked a decent amount of this code for potential
-;; performance improvements, but I suspect that there may be some
-;; substantial ones to be realized, esp. in the later "parsing" half
-;; of the file, by rewriting the code with use of more Guile native
-;; functions that do more work in a "chunk".
-;;
-;; FIXME: mkoeppe: Time zones are treated a little simplistic in
-;; SRFI-19; they are only a numeric offset. Thus, printing time zones
-;; (LOCALE-PRINT-TIME-ZONE) can't be implemented sensibly. The
-;; functions taking an optional TZ-OFFSET should be extended to take a
-;; symbolic time-zone (like "CET"); this string should be stored in
-;; the DATE structure.
-
-(define-module (srfi srfi-19)
- \:use-module (srfi srfi-6)
- \:use-module (srfi srfi-8)
- \:use-module (srfi srfi-9)
- \:autoload (ice-9 rdelim) (read-line)
- \:use-module (ice-9 i18n)
- \:replace (current-time)
- \:export (;; Constants
- time-duration
- time-monotonic
- time-process
- time-tai
- time-thread
- time-utc
- ;; Current time and clock resolution
- current-date
- current-julian-day
- current-modified-julian-day
- time-resolution
- ;; Time object and accessors
- make-time
- time?
- time-type
- time-nanosecond
- time-second
- set-time-type!
- set-time-nanosecond!
- set-time-second!
- copy-time
- ;; Time comparison procedures
- time<=?
- time<?
- time=?
- time>=?
- time>?
- ;; Time arithmetic procedures
- time-difference
- time-difference!
- add-duration
- add-duration!
- subtract-duration
- subtract-duration!
- ;; Date object and accessors
- make-date
- date?
- date-nanosecond
- date-second
- date-minute
- date-hour
- date-day
- date-month
- date-year
- date-zone-offset
- date-year-day
- date-week-day
- date-week-number
- ;; Time/Date/Julian Day/Modified Julian Day converters
- date->julian-day
- date->modified-julian-day
- date->time-monotonic
- date->time-tai
- date->time-utc
- julian-day->date
- julian-day->time-monotonic
- julian-day->time-tai
- julian-day->time-utc
- modified-julian-day->date
- modified-julian-day->time-monotonic
- modified-julian-day->time-tai
- modified-julian-day->time-utc
- time-monotonic->date
- time-monotonic->julian-day
- time-monotonic->modified-julian-day
- time-monotonic->time-tai
- time-monotonic->time-tai!
- time-monotonic->time-utc
- time-monotonic->time-utc!
- time-tai->date
- time-tai->julian-day
- time-tai->modified-julian-day
- time-tai->time-monotonic
- time-tai->time-monotonic!
- time-tai->time-utc
- time-tai->time-utc!
- time-utc->date
- time-utc->julian-day
- time-utc->modified-julian-day
- time-utc->time-monotonic
- time-utc->time-monotonic!
- time-utc->time-tai
- time-utc->time-tai!
- ;; Date to string/string to date converters.
- date->string
- string->date))
-
-(cond-expand-provide (current-module) '(srfi-19))
-
-(define time-tai 'time-tai)
-(define time-utc 'time-utc)
-(define time-monotonic 'time-monotonic)
-(define time-thread 'time-thread)
-(define time-process 'time-process)
-(define time-duration 'time-duration)
-
-;; FIXME: do we want to add gc time?
-;; (define time-gc 'time-gc)
-
-;;-- LOCALE dependent constants
-
-;; See date->string
-(define locale-date-time-format "~a ~b ~d ~H:~M:~S~z ~Y")
-(define locale-short-date-format "~m/~d/~y")
-(define locale-time-format "~H:~M:~S")
-(define iso-8601-date-time-format "~Y-~m-~dT~H:~M:~S~z")
-
-;;-- Miscellaneous Constants.
-;;-- only the tai-epoch-in-jd might need changing if
-;; a different epoch is used.
-
-(define nano 1000000000) ; nanoseconds in a second
-(define sid 86400) ; seconds in a day
-(define sihd 43200) ; seconds in a half day
-(define tai-epoch-in-jd 4881175/2) ; julian day number for 'the epoch'
-
-;; FIXME: should this be something other than misc-error?
-(define (time-error caller type value)
- (if value
- (throw 'misc-error caller "TIME-ERROR type ~A: ~S" (list type value) #f)
- (throw 'misc-error caller "TIME-ERROR type ~A" (list type) #f)))
-
-;; A table of leap seconds
-;; See ftp://maia.usno.navy.mil/ser7/tai-utc.dat
-;; and update as necessary.
-;; this procedures reads the file in the above
-;; format and creates the leap second table
-;; it also calls the almost standard, but not R5 procedures read-line
-;; & open-input-string
-;; ie (set! leap-second-table (read-tai-utc-date "tai-utc.dat"))
-
-(define (read-tai-utc-data filename)
- (define (convert-jd jd)
- (* (- (inexact->exact jd) tai-epoch-in-jd) sid))
- (define (convert-sec sec)
- (inexact->exact sec))
- (let ((port (open-input-file filename))
- (table '()))
- (let loop ((line (read-line port)))
- (if (not (eof-object? line))
- (begin
- (let* ((data (read (open-input-string
- (string-append "(" line ")"))))
- (year (car data))
- (jd (cadddr (cdr data)))
- (secs (cadddr (cdddr data))))
- (if (>= year 1972)
- (set! table (cons
- (cons (convert-jd jd) (convert-sec secs))
- table)))
- (loop (read-line port))))))
- table))
-
-;; each entry is (tai seconds since epoch . # seconds to subtract for utc)
-;; note they go higher to lower, and end in 1972.
-(define leap-second-table
- '((1435708800 . 36)
- (1341100800 . 35)
- (1230768000 . 34)
- (1136073600 . 33)
- (915148800 . 32)
- (867715200 . 31)
- (820454400 . 30)
- (773020800 . 29)
- (741484800 . 28)
- (709948800 . 27)
- (662688000 . 26)
- (631152000 . 25)
- (567993600 . 24)
- (489024000 . 23)
- (425865600 . 22)
- (394329600 . 21)
- (362793600 . 20)
- (315532800 . 19)
- (283996800 . 18)
- (252460800 . 17)
- (220924800 . 16)
- (189302400 . 15)
- (157766400 . 14)
- (126230400 . 13)
- (94694400 . 12)
- (78796800 . 11)
- (63072000 . 10)))
-
-(define (read-leap-second-table filename)
- (set! leap-second-table (read-tai-utc-data filename)))
-
-
-(define (leap-second-delta utc-seconds)
- (letrec ((lsd (lambda (table)
- (cond ((>= utc-seconds (caar table))
- (cdar table))
- (else (lsd (cdr table)))))))
- (if (< utc-seconds (* (- 1972 1970) 365 sid)) 0
- (lsd leap-second-table))))
-
-
-;;; the TIME structure; creates the accessors, too.
-
-(define-record-type time
- (make-time-unnormalized type nanosecond second)
- time?
- (type time-type set-time-type!)
- (nanosecond time-nanosecond set-time-nanosecond!)
- (second time-second set-time-second!))
-
-(define (copy-time time)
- (make-time (time-type time) (time-nanosecond time) (time-second time)))
-
-(define (split-real r)
- (if (integer? r)
- (values (inexact->exact r) 0)
- (let ((l (truncate r)))
- (values (inexact->exact l) (- r l)))))
-
-(define (time-normalize! t)
- (if (>= (abs (time-nanosecond t)) 1000000000)
- (receive (int frac)
- (split-real (time-nanosecond t))
- (set-time-second! t (+ (time-second t)
- (quotient int 1000000000)))
- (set-time-nanosecond! t (+ (remainder int 1000000000)
- frac))))
- (if (and (positive? (time-second t))
- (negative? (time-nanosecond t)))
- (begin
- (set-time-second! t (- (time-second t) 1))
- (set-time-nanosecond! t (+ 1000000000 (time-nanosecond t))))
- (if (and (negative? (time-second t))
- (positive? (time-nanosecond t)))
- (begin
- (set-time-second! t (+ (time-second t) 1))
- (set-time-nanosecond! t (+ 1000000000 (time-nanosecond t))))))
- t)
-
-(define (make-time type nanosecond second)
- (time-normalize! (make-time-unnormalized type nanosecond second)))
-
-;; Helpers
-;; FIXME: finish this and publish it?
-(define (date->broken-down-time date)
- (let ((result (mktime 0)))
- ;; FIXME: What should we do about leap-seconds which may overflow
- ;; set-tm:sec?
- (set-tm:sec result (date-second date))
- (set-tm:min result (date-minute date))
- (set-tm:hour result (date-hour date))
- ;; FIXME: SRFI day ranges from 0-31. (not compatible with set-tm:mday).
- (set-tm:mday result (date-day date))
- (set-tm:mon result (- (date-month date) 1))
- ;; FIXME: need to signal error on range violation.
- (set-tm:year result (+ 1900 (date-year date)))
- (set-tm:isdst result -1)
- (set-tm:gmtoff result (- (date-zone-offset date)))
- result))
-
-;;; current-time
-
-;;; specific time getters.
-
-(define (current-time-utc)
- ;; Resolution is microseconds.
- (let ((tod (gettimeofday)))
- (make-time time-utc (* (cdr tod) 1000) (car tod))))
-
-(define (current-time-tai)
- ;; Resolution is microseconds.
- (let* ((tod (gettimeofday))
- (sec (car tod))
- (usec (cdr tod)))
- (make-time time-tai
- (* usec 1000)
- (+ (car tod) (leap-second-delta sec)))))
-
-;;(define (current-time-ms-time time-type proc)
-;; (let ((current-ms (proc)))
-;; (make-time time-type
-;; (quotient current-ms 10000)
-;; (* (remainder current-ms 1000) 10000))))
-
-;; -- we define it to be the same as TAI.
-;; A different implemation of current-time-montonic
-;; will require rewriting all of the time-monotonic converters,
-;; of course.
-
-(define (current-time-monotonic)
- ;; Resolution is microseconds.
- (current-time-tai))
-
-(define (current-time-thread)
- (time-error 'current-time 'unsupported-clock-type 'time-thread))
-
-(define ns-per-guile-tick (/ 1000000000 internal-time-units-per-second))
-
-(define (current-time-process)
- (let ((run-time (get-internal-run-time)))
- (make-time
- time-process
- (* (remainder run-time internal-time-units-per-second)
- ns-per-guile-tick)
- (quotient run-time internal-time-units-per-second))))
-
-;;(define (current-time-gc)
-;; (current-time-ms-time time-gc current-gc-milliseconds))
-
-(define (current-time . clock-type)
- (let ((clock-type (if (null? clock-type) time-utc (car clock-type))))
- (cond
- ((eq? clock-type time-tai) (current-time-tai))
- ((eq? clock-type time-utc) (current-time-utc))
- ((eq? clock-type time-monotonic) (current-time-monotonic))
- ((eq? clock-type time-thread) (current-time-thread))
- ((eq? clock-type time-process) (current-time-process))
- ;; ((eq? clock-type time-gc) (current-time-gc))
- (else (time-error 'current-time 'invalid-clock-type clock-type)))))
-
-;; -- Time Resolution
-;; This is the resolution of the clock in nanoseconds.
-;; This will be implementation specific.
-
-(define (time-resolution . clock-type)
- (let ((clock-type (if (null? clock-type) time-utc (car clock-type))))
- (case clock-type
- ((time-tai) 1000)
- ((time-utc) 1000)
- ((time-monotonic) 1000)
- ((time-process) ns-per-guile-tick)
- ;; ((eq? clock-type time-thread) 1000)
- ;; ((eq? clock-type time-gc) 10000)
- (else (time-error 'time-resolution 'invalid-clock-type clock-type)))))
-
-;; -- Time comparisons
-
-(define (time=? t1 t2)
- ;; Arrange tests for speed and presume that t1 and t2 are actually times.
- ;; also presume it will be rare to check two times of different types.
- (and (= (time-second t1) (time-second t2))
- (= (time-nanosecond t1) (time-nanosecond t2))
- (eq? (time-type t1) (time-type t2))))
-
-(define (time>? t1 t2)
- (or (> (time-second t1) (time-second t2))
- (and (= (time-second t1) (time-second t2))
- (> (time-nanosecond t1) (time-nanosecond t2)))))
-
-(define (time<? t1 t2)
- (or (< (time-second t1) (time-second t2))
- (and (= (time-second t1) (time-second t2))
- (< (time-nanosecond t1) (time-nanosecond t2)))))
-
-(define (time>=? t1 t2)
- (or (> (time-second t1) (time-second t2))
- (and (= (time-second t1) (time-second t2))
- (>= (time-nanosecond t1) (time-nanosecond t2)))))
-
-(define (time<=? t1 t2)
- (or (< (time-second t1) (time-second t2))
- (and (= (time-second t1) (time-second t2))
- (<= (time-nanosecond t1) (time-nanosecond t2)))))
-
-;; -- Time arithmetic
-
-(define (time-difference! time1 time2)
- (let ((sec-diff (- (time-second time1) (time-second time2)))
- (nsec-diff (- (time-nanosecond time1) (time-nanosecond time2))))
- (set-time-type! time1 time-duration)
- (set-time-second! time1 sec-diff)
- (set-time-nanosecond! time1 nsec-diff)
- (time-normalize! time1)))
-
-(define (time-difference time1 time2)
- (let ((result (copy-time time1)))
- (time-difference! result time2)))
-
-(define (add-duration! t duration)
- (if (not (eq? (time-type duration) time-duration))
- (time-error 'add-duration 'not-duration duration)
- (let ((sec-plus (+ (time-second t) (time-second duration)))
- (nsec-plus (+ (time-nanosecond t) (time-nanosecond duration))))
- (set-time-second! t sec-plus)
- (set-time-nanosecond! t nsec-plus)
- (time-normalize! t))))
-
-(define (add-duration t duration)
- (let ((result (copy-time t)))
- (add-duration! result duration)))
-
-(define (subtract-duration! t duration)
- (if (not (eq? (time-type duration) time-duration))
- (time-error 'add-duration 'not-duration duration)
- (let ((sec-minus (- (time-second t) (time-second duration)))
- (nsec-minus (- (time-nanosecond t) (time-nanosecond duration))))
- (set-time-second! t sec-minus)
- (set-time-nanosecond! t nsec-minus)
- (time-normalize! t))))
-
-(define (subtract-duration time1 duration)
- (let ((result (copy-time time1)))
- (subtract-duration! result duration)))
-
-;; -- Converters between types.
-
-(define (priv:time-tai->time-utc! time-in time-out caller)
- (if (not (eq? (time-type time-in) time-tai))
- (time-error caller 'incompatible-time-types time-in))
- (set-time-type! time-out time-utc)
- (set-time-nanosecond! time-out (time-nanosecond time-in))
- (set-time-second! time-out (- (time-second time-in)
- (leap-second-delta
- (time-second time-in))))
- time-out)
-
-(define (time-tai->time-utc time-in)
- (priv:time-tai->time-utc! time-in (make-time-unnormalized #f #f #f) 'time-tai->time-utc))
-
-
-(define (time-tai->time-utc! time-in)
- (priv:time-tai->time-utc! time-in time-in 'time-tai->time-utc!))
-
-(define (priv:time-utc->time-tai! time-in time-out caller)
- (if (not (eq? (time-type time-in) time-utc))
- (time-error caller 'incompatible-time-types time-in))
- (set-time-type! time-out time-tai)
- (set-time-nanosecond! time-out (time-nanosecond time-in))
- (set-time-second! time-out (+ (time-second time-in)
- (leap-second-delta
- (time-second time-in))))
- time-out)
-
-(define (time-utc->time-tai time-in)
- (priv:time-utc->time-tai! time-in (make-time-unnormalized #f #f #f) 'time-utc->time-tai))
-
-(define (time-utc->time-tai! time-in)
- (priv:time-utc->time-tai! time-in time-in 'time-utc->time-tai!))
-
-;; -- these depend on time-monotonic having the same definition as time-tai!
-(define (time-monotonic->time-utc time-in)
- (if (not (eq? (time-type time-in) time-monotonic))
- (time-error 'time-monotonic->time-utc
- 'incompatible-time-types time-in))
- (let ((ntime (copy-time time-in)))
- (set-time-type! ntime time-tai)
- (priv:time-tai->time-utc! ntime ntime 'time-monotonic->time-utc)))
-
-(define (time-monotonic->time-utc! time-in)
- (if (not (eq? (time-type time-in) time-monotonic))
- (time-error 'time-monotonic->time-utc!
- 'incompatible-time-types time-in))
- (set-time-type! time-in time-tai)
- (priv:time-tai->time-utc! time-in time-in 'time-monotonic->time-utc))
-
-(define (time-monotonic->time-tai time-in)
- (if (not (eq? (time-type time-in) time-monotonic))
- (time-error 'time-monotonic->time-tai
- 'incompatible-time-types time-in))
- (let ((ntime (copy-time time-in)))
- (set-time-type! ntime time-tai)
- ntime))
-
-(define (time-monotonic->time-tai! time-in)
- (if (not (eq? (time-type time-in) time-monotonic))
- (time-error 'time-monotonic->time-tai!
- 'incompatible-time-types time-in))
- (set-time-type! time-in time-tai)
- time-in)
-
-(define (time-utc->time-monotonic time-in)
- (if (not (eq? (time-type time-in) time-utc))
- (time-error 'time-utc->time-monotonic
- 'incompatible-time-types time-in))
- (let ((ntime (priv:time-utc->time-tai! time-in (make-time-unnormalized #f #f #f)
- 'time-utc->time-monotonic)))
- (set-time-type! ntime time-monotonic)
- ntime))
-
-(define (time-utc->time-monotonic! time-in)
- (if (not (eq? (time-type time-in) time-utc))
- (time-error 'time-utc->time-monotonic!
- 'incompatible-time-types time-in))
- (let ((ntime (priv:time-utc->time-tai! time-in time-in
- 'time-utc->time-monotonic!)))
- (set-time-type! ntime time-monotonic)
- ntime))
-
-(define (time-tai->time-monotonic time-in)
- (if (not (eq? (time-type time-in) time-tai))
- (time-error 'time-tai->time-monotonic
- 'incompatible-time-types time-in))
- (let ((ntime (copy-time time-in)))
- (set-time-type! ntime time-monotonic)
- ntime))
-
-(define (time-tai->time-monotonic! time-in)
- (if (not (eq? (time-type time-in) time-tai))
- (time-error 'time-tai->time-monotonic!
- 'incompatible-time-types time-in))
- (set-time-type! time-in time-monotonic)
- time-in)
-
-;; -- Date Structures
-
-;; FIXME: to be really safe, perhaps we should normalize the
-;; seconds/nanoseconds/minutes coming in to make-date...
-
-(define-record-type date
- (make-date nanosecond second minute
- hour day month
- year
- zone-offset)
- date?
- (nanosecond date-nanosecond set-date-nanosecond!)
- (second date-second set-date-second!)
- (minute date-minute set-date-minute!)
- (hour date-hour set-date-hour!)
- (day date-day set-date-day!)
- (month date-month set-date-month!)
- (year date-year set-date-year!)
- (zone-offset date-zone-offset set-date-zone-offset!))
-
-;; gives the julian day which starts at noon.
-(define (encode-julian-day-number day month year)
- (let* ((a (quotient (- 14 month) 12))
- (y (- (+ year 4800) a (if (negative? year) -1 0)))
- (m (- (+ month (* 12 a)) 3)))
- (+ day
- (quotient (+ (* 153 m) 2) 5)
- (* 365 y)
- (quotient y 4)
- (- (quotient y 100))
- (quotient y 400)
- -32045)))
-
-;; gives the seconds/date/month/year
-(define (decode-julian-day-number jdn)
- (let* ((days (inexact->exact (truncate jdn)))
- (a (+ days 32044))
- (b (quotient (+ (* 4 a) 3) 146097))
- (c (- a (quotient (* 146097 b) 4)))
- (d (quotient (+ (* 4 c) 3) 1461))
- (e (- c (quotient (* 1461 d) 4)))
- (m (quotient (+ (* 5 e) 2) 153))
- (y (+ (* 100 b) d -4800 (quotient m 10))))
- (values ; seconds date month year
- (* (- jdn days) sid)
- (+ e (- (quotient (+ (* 153 m) 2) 5)) 1)
- (+ m 3 (* -12 (quotient m 10)))
- (if (>= 0 y) (- y 1) y))))
-
-;; relies on the fact that we named our time zone accessor
-;; differently from MzScheme's....
-;; This should be written to be OS specific.
-
-(define (local-tz-offset utc-time)
- ;; SRFI uses seconds West, but guile (and libc) use seconds East.
- (- (tm:gmtoff (localtime (time-second utc-time)))))
-
-;; special thing -- ignores nanos
-(define (time->julian-day-number seconds tz-offset)
- (+ (/ (+ seconds tz-offset sihd)
- sid)
- tai-epoch-in-jd))
-
-(define (leap-second? second)
- (and (assoc second leap-second-table) #t))
-
-(define (time-utc->date time . tz-offset)
- (if (not (eq? (time-type time) time-utc))
- (time-error 'time->date 'incompatible-time-types time))
- (let* ((offset (if (null? tz-offset)
- (local-tz-offset time)
- (car tz-offset)))
- (leap-second? (leap-second? (+ offset (time-second time))))
- (jdn (time->julian-day-number (if leap-second?
- (- (time-second time) 1)
- (time-second time))
- offset)))
-
- (call-with-values (lambda () (decode-julian-day-number jdn))
- (lambda (secs date month year)
- ;; secs is a real because jdn is a real in Guile;
- ;; but it is conceptionally an integer.
- (let* ((int-secs (inexact->exact (round secs)))
- (hours (quotient int-secs (* 60 60)))
- (rem (remainder int-secs (* 60 60)))
- (minutes (quotient rem 60))
- (seconds (remainder rem 60)))
- (make-date (time-nanosecond time)
- (if leap-second? (+ seconds 1) seconds)
- minutes
- hours
- date
- month
- year
- offset))))))
-
-(define (time-tai->date time . tz-offset)
- (if (not (eq? (time-type time) time-tai))
- (time-error 'time->date 'incompatible-time-types time))
- (let* ((offset (if (null? tz-offset)
- (local-tz-offset (time-tai->time-utc time))
- (car tz-offset)))
- (seconds (- (time-second time)
- (leap-second-delta (time-second time))))
- (leap-second? (leap-second? (+ offset seconds)))
- (jdn (time->julian-day-number (if leap-second?
- (- seconds 1)
- seconds)
- offset)))
- (call-with-values (lambda () (decode-julian-day-number jdn))
- (lambda (secs date month year)
- ;; secs is a real because jdn is a real in Guile;
- ;; but it is conceptionally an integer.
- ;; adjust for leap seconds if necessary ...
- (let* ((int-secs (inexact->exact (round secs)))
- (hours (quotient int-secs (* 60 60)))
- (rem (remainder int-secs (* 60 60)))
- (minutes (quotient rem 60))
- (seconds (remainder rem 60)))
- (make-date (time-nanosecond time)
- (if leap-second? (+ seconds 1) seconds)
- minutes
- hours
- date
- month
- year
- offset))))))
-
-;; this is the same as time-tai->date.
-(define (time-monotonic->date time . tz-offset)
- (if (not (eq? (time-type time) time-monotonic))
- (time-error 'time->date 'incompatible-time-types time))
- (let* ((offset (if (null? tz-offset)
- (local-tz-offset (time-monotonic->time-utc time))
- (car tz-offset)))
- (seconds (- (time-second time)
- (leap-second-delta (time-second time))))
- (leap-second? (leap-second? (+ offset seconds)))
- (jdn (time->julian-day-number (if leap-second?
- (- seconds 1)
- seconds)
- offset)))
- (call-with-values (lambda () (decode-julian-day-number jdn))
- (lambda (secs date month year)
- ;; secs is a real because jdn is a real in Guile;
- ;; but it is conceptionally an integer.
- ;; adjust for leap seconds if necessary ...
- (let* ((int-secs (inexact->exact (round secs)))
- (hours (quotient int-secs (* 60 60)))
- (rem (remainder int-secs (* 60 60)))
- (minutes (quotient rem 60))
- (seconds (remainder rem 60)))
- (make-date (time-nanosecond time)
- (if leap-second? (+ seconds 1) seconds)
- minutes
- hours
- date
- month
- year
- offset))))))
-
-(define (date->time-utc date)
- (let* ((jdays (- (encode-julian-day-number (date-day date)
- (date-month date)
- (date-year date))
- tai-epoch-in-jd))
- ;; jdays is an integer plus 1/2,
- (jdays-1/2 (inexact->exact (- jdays 1/2))))
- (make-time
- time-utc
- (date-nanosecond date)
- (+ (* jdays-1/2 24 60 60)
- (* (date-hour date) 60 60)
- (* (date-minute date) 60)
- (date-second date)
- (- (date-zone-offset date))))))
-
-(define (date->time-tai date)
- (time-utc->time-tai! (date->time-utc date)))
-
-(define (date->time-monotonic date)
- (time-utc->time-monotonic! (date->time-utc date)))
-
-(define (leap-year? year)
- (or (= (modulo year 400) 0)
- (and (= (modulo year 4) 0) (not (= (modulo year 100) 0)))))
-
-;; Map 1-based month number M to number of days in the year before the
-;; start of month M (in a non-leap year).
-(define month-assoc '((1 . 0) (2 . 31) (3 . 59) (4 . 90)
- (5 . 120) (6 . 151) (7 . 181) (8 . 212)
- (9 . 243) (10 . 273) (11 . 304) (12 . 334)))
-
-(define (year-day day month year)
- (let ((days-pr (assoc month month-assoc)))
- (if (not days-pr)
- (time-error 'date-year-day 'invalid-month-specification month))
- (if (and (leap-year? year) (> month 2))
- (+ day (cdr days-pr) 1)
- (+ day (cdr days-pr)))))
-
-(define (date-year-day date)
- (year-day (date-day date) (date-month date) (date-year date)))
-
-;; from calendar faq
-(define (week-day day month year)
- (let* ((a (quotient (- 14 month) 12))
- (y (- year a))
- (m (+ month (* 12 a) -2)))
- (modulo (+ day
- y
- (quotient y 4)
- (- (quotient y 100))
- (quotient y 400)
- (quotient (* 31 m) 12))
- 7)))
-
-(define (date-week-day date)
- (week-day (date-day date) (date-month date) (date-year date)))
-
-(define (days-before-first-week date day-of-week-starting-week)
- (let* ((first-day (make-date 0 0 0 0
- 1
- 1
- (date-year date)
- #f))
- (fdweek-day (date-week-day first-day)))
- (modulo (- day-of-week-starting-week fdweek-day)
- 7)))
-
-;; The "-1" here is a fix for the reference implementation, to make a new
-;; week start on the given day-of-week-starting-week. date-year-day returns
-;; a day starting from 1 for 1st Jan.
-;;
-(define (date-week-number date day-of-week-starting-week)
- (quotient (- (date-year-day date)
- 1
- (days-before-first-week date day-of-week-starting-week))
- 7))
-
-(define (current-date . tz-offset)
- (let ((time (current-time time-utc)))
- (time-utc->date
- time
- (if (null? tz-offset)
- (local-tz-offset time)
- (car tz-offset)))))
-
-;; given a 'two digit' number, find the year within 50 years +/-
-(define (natural-year n)
- (let* ((current-year (date-year (current-date)))
- (current-century (* (quotient current-year 100) 100)))
- (cond
- ((>= n 100) n)
- ((< n 0) n)
- ((<= (- (+ current-century n) current-year) 50) (+ current-century n))
- (else (+ (- current-century 100) n)))))
-
-(define (date->julian-day date)
- (let ((nanosecond (date-nanosecond date))
- (second (date-second date))
- (minute (date-minute date))
- (hour (date-hour date))
- (day (date-day date))
- (month (date-month date))
- (year (date-year date))
- (offset (date-zone-offset date)))
- (+ (encode-julian-day-number day month year)
- (- 1/2)
- (+ (/ (+ (- offset)
- (* hour 60 60)
- (* minute 60)
- second
- (/ nanosecond nano))
- sid)))))
-
-(define (date->modified-julian-day date)
- (- (date->julian-day date)
- 4800001/2))
-
-(define (time-utc->julian-day time)
- (if (not (eq? (time-type time) time-utc))
- (time-error 'time->date 'incompatible-time-types time))
- (+ (/ (+ (time-second time) (/ (time-nanosecond time) nano))
- sid)
- tai-epoch-in-jd))
-
-(define (time-utc->modified-julian-day time)
- (- (time-utc->julian-day time)
- 4800001/2))
-
-(define (time-tai->julian-day time)
- (if (not (eq? (time-type time) time-tai))
- (time-error 'time->date 'incompatible-time-types time))
- (+ (/ (+ (- (time-second time)
- (leap-second-delta (time-second time)))
- (/ (time-nanosecond time) nano))
- sid)
- tai-epoch-in-jd))
-
-(define (time-tai->modified-julian-day time)
- (- (time-tai->julian-day time)
- 4800001/2))
-
-;; this is the same as time-tai->julian-day
-(define (time-monotonic->julian-day time)
- (if (not (eq? (time-type time) time-monotonic))
- (time-error 'time->date 'incompatible-time-types time))
- (+ (/ (+ (- (time-second time)
- (leap-second-delta (time-second time)))
- (/ (time-nanosecond time) nano))
- sid)
- tai-epoch-in-jd))
-
-(define (time-monotonic->modified-julian-day time)
- (- (time-monotonic->julian-day time)
- 4800001/2))
-
-(define (julian-day->time-utc jdn)
- (let ((secs (* sid (- jdn tai-epoch-in-jd))))
- (receive (seconds parts)
- (split-real secs)
- (make-time time-utc
- (* parts nano)
- seconds))))
-
-(define (julian-day->time-tai jdn)
- (time-utc->time-tai! (julian-day->time-utc jdn)))
-
-(define (julian-day->time-monotonic jdn)
- (time-utc->time-monotonic! (julian-day->time-utc jdn)))
-
-(define (julian-day->date jdn . tz-offset)
- (let* ((time (julian-day->time-utc jdn))
- (offset (if (null? tz-offset)
- (local-tz-offset time)
- (car tz-offset))))
- (time-utc->date time offset)))
-
-(define (modified-julian-day->date jdn . tz-offset)
- (apply julian-day->date (+ jdn 4800001/2)
- tz-offset))
-
-(define (modified-julian-day->time-utc jdn)
- (julian-day->time-utc (+ jdn 4800001/2)))
-
-(define (modified-julian-day->time-tai jdn)
- (julian-day->time-tai (+ jdn 4800001/2)))
-
-(define (modified-julian-day->time-monotonic jdn)
- (julian-day->time-monotonic (+ jdn 4800001/2)))
-
-(define (current-julian-day)
- (time-utc->julian-day (current-time time-utc)))
-
-(define (current-modified-julian-day)
- (time-utc->modified-julian-day (current-time time-utc)))
-
-;; returns a string rep. of number N, of minimum LENGTH, padded with
-;; character PAD-WITH. If PAD-WITH is #f, no padding is done, and it's
-;; as if number->string was used. if string is longer than or equal
-;; in length to LENGTH, it's as if number->string was used.
-
-(define (padding n pad-with length)
- (let* ((str (number->string n))
- (str-len (string-length str)))
- (if (or (>= str-len length)
- (not pad-with))
- str
- (string-append (make-string (- length str-len) pad-with) str))))
-
-(define (last-n-digits i n)
- (abs (remainder i (expt 10 n))))
-
-(define (locale-abbr-weekday n) (locale-day-short (+ 1 n)))
-(define (locale-long-weekday n) (locale-day (+ 1 n)))
-(define locale-abbr-month locale-month-short)
-(define locale-long-month locale-month)
-
-(define (date-reverse-lookup needle haystack-ref haystack-len
- same?)
- ;; Lookup NEEDLE (a string) using HAYSTACK-REF (a one argument procedure
- ;; that returns a string corresponding to the given index) by passing it
- ;; indices lower than HAYSTACK-LEN.
- (let loop ((index 1))
- (cond ((> index haystack-len) #f)
- ((same? needle (haystack-ref index))
- index)
- (else (loop (+ index 1))))))
-
-(define (locale-abbr-weekday->index string)
- (date-reverse-lookup string locale-day-short 7 string=?))
-
-(define (locale-long-weekday->index string)
- (date-reverse-lookup string locale-day 7 string=?))
-
-(define (locale-abbr-month->index string)
- (date-reverse-lookup string locale-abbr-month 12 string=?))
-
-(define (locale-long-month->index string)
- (date-reverse-lookup string locale-long-month 12 string=?))
-
-
-;; FIXME: mkoeppe: Put a symbolic time zone in the date structs.
-;; Print it here instead of the numerical offset if available.
-(define (locale-print-time-zone date port)
- (tz-printer (date-zone-offset date) port))
-
-(define (locale-am-string/pm hr)
- (if (> hr 11) (locale-pm-string) (locale-am-string)))
-
-(define (tz-printer offset port)
- (cond
- ((= offset 0) (display "Z" port))
- ((negative? offset) (display "-" port))
- (else (display "+" port)))
- (if (not (= offset 0))
- (let ((hours (abs (quotient offset (* 60 60))))
- (minutes (abs (quotient (remainder offset (* 60 60)) 60))))
- (display (padding hours #\0 2) port)
- (display (padding minutes #\0 2) port))))
-
-;; A table of output formatting directives.
-;; the first time is the format char.
-;; the second is a procedure that takes the date, a padding character
-;; (which might be #f), and the output port.
-;;
-(define directives
- (list
- (cons #\~ (lambda (date pad-with port)
- (display #\~ port)))
- (cons #\a (lambda (date pad-with port)
- (display (locale-abbr-weekday (date-week-day date))
- port)))
- (cons #\A (lambda (date pad-with port)
- (display (locale-long-weekday (date-week-day date))
- port)))
- (cons #\b (lambda (date pad-with port)
- (display (locale-abbr-month (date-month date))
- port)))
- (cons #\B (lambda (date pad-with port)
- (display (locale-long-month (date-month date))
- port)))
- (cons #\c (lambda (date pad-with port)
- (display (date->string date locale-date-time-format) port)))
- (cons #\d (lambda (date pad-with port)
- (display (padding (date-day date)
- #\0 2)
- port)))
- (cons #\D (lambda (date pad-with port)
- (display (date->string date "~m/~d/~y") port)))
- (cons #\e (lambda (date pad-with port)
- (display (padding (date-day date)
- #\Space 2)
- port)))
- (cons #\f (lambda (date pad-with port)
- (if (> (date-nanosecond date)
- nano)
- (display (padding (+ (date-second date) 1)
- pad-with 2)
- port)
- (display (padding (date-second date)
- pad-with 2)
- port))
- (receive (i f)
- (split-real (/
- (date-nanosecond date)
- nano 1.0))
- (let* ((ns (number->string f))
- (le (string-length ns)))
- (if (> le 2)
- (begin
- (display (locale-decimal-point) port)
- (display (substring ns 2 le) port)))))))
- (cons #\h (lambda (date pad-with port)
- (display (date->string date "~b") port)))
- (cons #\H (lambda (date pad-with port)
- (display (padding (date-hour date)
- pad-with 2)
- port)))
- (cons #\I (lambda (date pad-with port)
- (let ((hr (date-hour date)))
- (if (> hr 12)
- (display (padding (- hr 12)
- pad-with 2)
- port)
- (display (padding hr
- pad-with 2)
- port)))))
- (cons #\j (lambda (date pad-with port)
- (display (padding (date-year-day date)
- pad-with 3)
- port)))
- (cons #\k (lambda (date pad-with port)
- (display (padding (date-hour date)
- #\Space 2)
- port)))
- (cons #\l (lambda (date pad-with port)
- (let ((hr (if (> (date-hour date) 12)
- (- (date-hour date) 12) (date-hour date))))
- (display (padding hr #\Space 2)
- port))))
- (cons #\m (lambda (date pad-with port)
- (display (padding (date-month date)
- pad-with 2)
- port)))
- (cons #\M (lambda (date pad-with port)
- (display (padding (date-minute date)
- pad-with 2)
- port)))
- (cons #\n (lambda (date pad-with port)
- (newline port)))
- (cons #\N (lambda (date pad-with port)
- (display (padding (date-nanosecond date)
- pad-with 7)
- port)))
- (cons #\p (lambda (date pad-with port)
- (display (locale-am-string/pm (date-hour date)) port)))
- (cons #\r (lambda (date pad-with port)
- (display (date->string date "~I:~M:~S ~p") port)))
- (cons #\s (lambda (date pad-with port)
- (display (time-second (date->time-utc date)) port)))
- (cons #\S (lambda (date pad-with port)
- (if (> (date-nanosecond date)
- nano)
- (display (padding (+ (date-second date) 1)
- pad-with 2)
- port)
- (display (padding (date-second date)
- pad-with 2)
- port))))
- (cons #\t (lambda (date pad-with port)
- (display #\Tab port)))
- (cons #\T (lambda (date pad-with port)
- (display (date->string date "~H:~M:~S") port)))
- (cons #\U (lambda (date pad-with port)
- (if (> (days-before-first-week date 0) 0)
- (display (padding (+ (date-week-number date 0) 1)
- #\0 2) port)
- (display (padding (date-week-number date 0)
- #\0 2) port))))
- (cons #\V (lambda (date pad-with port)
- (display (padding (date-week-number date 1)
- #\0 2) port)))
- (cons #\w (lambda (date pad-with port)
- (display (date-week-day date) port)))
- (cons #\x (lambda (date pad-with port)
- (display (date->string date locale-short-date-format) port)))
- (cons #\X (lambda (date pad-with port)
- (display (date->string date locale-time-format) port)))
- (cons #\W (lambda (date pad-with port)
- (if (> (days-before-first-week date 1) 0)
- (display (padding (+ (date-week-number date 1) 1)
- #\0 2) port)
- (display (padding (date-week-number date 1)
- #\0 2) port))))
- (cons #\y (lambda (date pad-with port)
- (display (padding (last-n-digits
- (date-year date) 2)
- pad-with
- 2)
- port)))
- (cons #\Y (lambda (date pad-with port)
- (display (date-year date) port)))
- (cons #\z (lambda (date pad-with port)
- (tz-printer (date-zone-offset date) port)))
- (cons #\Z (lambda (date pad-with port)
- (locale-print-time-zone date port)))
- (cons #\1 (lambda (date pad-with port)
- (display (date->string date "~Y-~m-~d") port)))
- (cons #\2 (lambda (date pad-with port)
- (display (date->string date "~H:~M:~S~z") port)))
- (cons #\3 (lambda (date pad-with port)
- (display (date->string date "~H:~M:~S") port)))
- (cons #\4 (lambda (date pad-with port)
- (display (date->string date "~Y-~m-~dT~H:~M:~S~z") port)))
- (cons #\5 (lambda (date pad-with port)
- (display (date->string date "~Y-~m-~dT~H:~M:~S") port)))))
-
-
-(define (get-formatter char)
- (let ((associated (assoc char directives)))
- (if associated (cdr associated) #f)))
-
-(define (date-printer date index format-string str-len port)
- (if (< index str-len)
- (let ((current-char (string-ref format-string index)))
- (if (not (char=? current-char #\~))
- (begin
- (display current-char port)
- (date-printer date (+ index 1) format-string str-len port))
- (if (= (+ index 1) str-len) ; bad format string.
- (time-error 'date-printer 'bad-date-format-string
- format-string)
- (let ((pad-char? (string-ref format-string (+ index 1))))
- (cond
- ((char=? pad-char? #\-)
- (if (= (+ index 2) str-len) ; bad format string.
- (time-error 'date-printer
- 'bad-date-format-string
- format-string)
- (let ((formatter (get-formatter
- (string-ref format-string
- (+ index 2)))))
- (if (not formatter)
- (time-error 'date-printer
- 'bad-date-format-string
- format-string)
- (begin
- (formatter date #f port)
- (date-printer date
- (+ index 3)
- format-string
- str-len
- port))))))
-
- ((char=? pad-char? #\_)
- (if (= (+ index 2) str-len) ; bad format string.
- (time-error 'date-printer
- 'bad-date-format-string
- format-string)
- (let ((formatter (get-formatter
- (string-ref format-string
- (+ index 2)))))
- (if (not formatter)
- (time-error 'date-printer
- 'bad-date-format-string
- format-string)
- (begin
- (formatter date #\Space port)
- (date-printer date
- (+ index 3)
- format-string
- str-len
- port))))))
- (else
- (let ((formatter (get-formatter
- (string-ref format-string
- (+ index 1)))))
- (if (not formatter)
- (time-error 'date-printer
- 'bad-date-format-string
- format-string)
- (begin
- (formatter date #\0 port)
- (date-printer date
- (+ index 2)
- format-string
- str-len
- port))))))))))))
-
-
-(define (date->string date . format-string)
- (let ((str-port (open-output-string))
- (fmt-str (if (null? format-string) "~c" (car format-string))))
- (date-printer date 0 fmt-str (string-length fmt-str) str-port)
- (get-output-string str-port)))
-
-(define (char->int ch)
- (case ch
- ((#\0) 0)
- ((#\1) 1)
- ((#\2) 2)
- ((#\3) 3)
- ((#\4) 4)
- ((#\5) 5)
- ((#\6) 6)
- ((#\7) 7)
- ((#\8) 8)
- ((#\9) 9)
- (else (time-error 'char->int 'bad-date-template-string
- (list "Non-integer character" ch)))))
-
-;; read an integer upto n characters long on port; upto -> #f is any length
-(define (integer-reader upto port)
- (let loop ((accum 0) (nchars 0))
- (let ((ch (peek-char port)))
- (if (or (eof-object? ch)
- (not (char-numeric? ch))
- (and upto (>= nchars upto)))
- accum
- (loop (+ (* accum 10) (char->int (read-char port)))
- (+ nchars 1))))))
-
-(define (make-integer-reader upto)
- (lambda (port)
- (integer-reader upto port)))
-
-;; read *exactly* n characters and convert to integer; could be padded
-(define (integer-reader-exact n port)
- (let ((padding-ok #t))
- (define (accum-int port accum nchars)
- (let ((ch (peek-char port)))
- (cond
- ((>= nchars n) accum)
- ((eof-object? ch)
- (time-error 'string->date 'bad-date-template-string
- "Premature ending to integer read."))
- ((char-numeric? ch)
- (set! padding-ok #f)
- (accum-int port
- (+ (* accum 10) (char->int (read-char port)))
- (+ nchars 1)))
- (padding-ok
- (read-char port) ; consume padding
- (accum-int port accum (+ nchars 1)))
- (else ; padding where it shouldn't be
- (time-error 'string->date 'bad-date-template-string
- "Non-numeric characters in integer read.")))))
- (accum-int port 0 0)))
-
-
-(define (make-integer-exact-reader n)
- (lambda (port)
- (integer-reader-exact n port)))
-
-(define (zone-reader port)
- (let ((offset 0)
- (positive? #f))
- (let ((ch (read-char port)))
- (if (eof-object? ch)
- (time-error 'string->date 'bad-date-template-string
- (list "Invalid time zone +/-" ch)))
- (if (or (char=? ch #\Z) (char=? ch #\z))
- 0
- (begin
- (cond
- ((char=? ch #\+) (set! positive? #t))
- ((char=? ch #\-) (set! positive? #f))
- (else
- (time-error 'string->date 'bad-date-template-string
- (list "Invalid time zone +/-" ch))))
- (let ((ch (read-char port)))
- (if (eof-object? ch)
- (time-error 'string->date 'bad-date-template-string
- (list "Invalid time zone number" ch)))
- (set! offset (* (char->int ch)
- 10 60 60)))
- (let ((ch (read-char port)))
- (if (eof-object? ch)
- (time-error 'string->date 'bad-date-template-string
- (list "Invalid time zone number" ch)))
- (set! offset (+ offset (* (char->int ch)
- 60 60))))
- (let ((ch (read-char port)))
- (if (eof-object? ch)
- (time-error 'string->date 'bad-date-template-string
- (list "Invalid time zone number" ch)))
- (set! offset (+ offset (* (char->int ch)
- 10 60))))
- (let ((ch (read-char port)))
- (if (eof-object? ch)
- (time-error 'string->date 'bad-date-template-string
- (list "Invalid time zone number" ch)))
- (set! offset (+ offset (* (char->int ch)
- 60))))
- (if positive? offset (- offset)))))))
-
-;; looking at a char, read the char string, run thru indexer, return index
-(define (locale-reader port indexer)
-
- (define (read-char-string result)
- (let ((ch (peek-char port)))
- (if (char-alphabetic? ch)
- (read-char-string (cons (read-char port) result))
- (list->string (reverse! result)))))
-
- (let* ((str (read-char-string '()))
- (index (indexer str)))
- (if index index (time-error 'string->date
- 'bad-date-template-string
- (list "Invalid string for " indexer)))))
-
-(define (make-locale-reader indexer)
- (lambda (port)
- (locale-reader port indexer)))
-
-(define (make-char-id-reader char)
- (lambda (port)
- (if (char=? char (read-char port))
- char
- (time-error 'string->date
- 'bad-date-template-string
- "Invalid character match."))))
-
-;; A List of formatted read directives.
-;; Each entry is a list.
-;; 1. the character directive;
-;; a procedure, which takes a character as input & returns
-;; 2. #t as soon as a character on the input port is acceptable
-;; for input,
-;; 3. a port reader procedure that knows how to read the current port
-;; for a value. Its one parameter is the port.
-;; 4. an optional action procedure, that takes the value (from 3.) and
-;; some object (here, always the date) and (probably) side-effects it.
-;; If no action is required, as with ~A, this element may be #f.
-
-(define read-directives
- (let ((ireader4 (make-integer-reader 4))
- (ireader2 (make-integer-reader 2))
- (eireader2 (make-integer-exact-reader 2))
- (locale-reader-abbr-weekday (make-locale-reader
- locale-abbr-weekday->index))
- (locale-reader-long-weekday (make-locale-reader
- locale-long-weekday->index))
- (locale-reader-abbr-month (make-locale-reader
- locale-abbr-month->index))
- (locale-reader-long-month (make-locale-reader
- locale-long-month->index))
- (char-fail (lambda (ch) #t)))
-
- (list
- (list #\~ char-fail (make-char-id-reader #\~) #f)
- (list #\a char-alphabetic? locale-reader-abbr-weekday #f)
- (list #\A char-alphabetic? locale-reader-long-weekday #f)
- (list #\b char-alphabetic? locale-reader-abbr-month
- (lambda (val object)
- (set-date-month! object val)))
- (list #\B char-alphabetic? locale-reader-long-month
- (lambda (val object)
- (set-date-month! object val)))
- (list #\d char-numeric? ireader2 (lambda (val object)
- (set-date-day!
- object val)))
- (list #\e char-fail eireader2 (lambda (val object)
- (set-date-day! object val)))
- (list #\h char-alphabetic? locale-reader-abbr-month
- (lambda (val object)
- (set-date-month! object val)))
- (list #\H char-numeric? ireader2 (lambda (val object)
- (set-date-hour! object val)))
- (list #\k char-fail eireader2 (lambda (val object)
- (set-date-hour! object val)))
- (list #\m char-numeric? ireader2 (lambda (val object)
- (set-date-month! object val)))
- (list #\M char-numeric? ireader2 (lambda (val object)
- (set-date-minute!
- object val)))
- (list #\S char-numeric? ireader2 (lambda (val object)
- (set-date-second! object val)))
- (list #\y char-fail eireader2
- (lambda (val object)
- (set-date-year! object (natural-year val))))
- (list #\Y char-numeric? ireader4 (lambda (val object)
- (set-date-year! object val)))
- (list #\z (lambda (c)
- (or (char=? c #\Z)
- (char=? c #\z)
- (char=? c #\+)
- (char=? c #\-)))
- zone-reader (lambda (val object)
- (set-date-zone-offset! object val))))))
-
-(define (priv:string->date date index format-string str-len port template-string)
- (define (skip-until port skipper)
- (let ((ch (peek-char port)))
- (if (eof-object? ch)
- (time-error 'string->date 'bad-date-format-string template-string)
- (if (not (skipper ch))
- (begin (read-char port) (skip-until port skipper))))))
- (if (< index str-len)
- (let ((current-char (string-ref format-string index)))
- (if (not (char=? current-char #\~))
- (let ((port-char (read-char port)))
- (if (or (eof-object? port-char)
- (not (char=? current-char port-char)))
- (time-error 'string->date
- 'bad-date-format-string template-string))
- (priv:string->date date
- (+ index 1)
- format-string
- str-len
- port
- template-string))
- ;; otherwise, it's an escape, we hope
- (if (> (+ index 1) str-len)
- (time-error 'string->date
- 'bad-date-format-string template-string)
- (let* ((format-char (string-ref format-string (+ index 1)))
- (format-info (assoc format-char read-directives)))
- (if (not format-info)
- (time-error 'string->date
- 'bad-date-format-string template-string)
- (begin
- (let ((skipper (cadr format-info))
- (reader (caddr format-info))
- (actor (cadddr format-info)))
- (skip-until port skipper)
- (let ((val (reader port)))
- (if (eof-object? val)
- (time-error 'string->date
- 'bad-date-format-string
- template-string)
- (if actor (actor val date))))
- (priv:string->date date
- (+ index 2)
- format-string
- str-len
- port
- template-string))))))))))
-
-(define (string->date input-string template-string)
- (define (date-ok? date)
- (and (date-nanosecond date)
- (date-second date)
- (date-minute date)
- (date-hour date)
- (date-day date)
- (date-month date)
- (date-year date)
- (date-zone-offset date)))
- (let ((newdate (make-date 0 0 0 0 #f #f #f #f)))
- (priv:string->date newdate
- 0
- template-string
- (string-length template-string)
- (open-input-string input-string)
- template-string)
- (if (not (date-zone-offset newdate))
- (begin
- ;; this is necessary to get DST right -- as far as we can
- ;; get it right (think of the double/missing hour in the
- ;; night when we are switching between normal time and DST).
- (set-date-zone-offset! newdate
- (local-tz-offset
- (make-time time-utc 0 0)))
- (set-date-zone-offset! newdate
- (local-tz-offset
- (date->time-utc newdate)))))
- (if (date-ok? newdate)
- newdate
- (time-error
- 'string->date
- 'bad-date-format-string
- (list "Incomplete date read. " newdate template-string)))))
-
-;;; srfi-19.scm ends here
-;;; srfi-2.scm --- and-let*
-
-;; Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Commentary:
-
-;; This module is fully documented in the Guile Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-2)
- \:use-module (ice-9 and-let-star)
- \:re-export-syntax (and-let*))
-
-(cond-expand-provide (current-module) '(srfi-2))
-
-;;; srfi-2.scm ends here
-;;; srfi-26.scm --- specializing parameters without currying.
-
-;; Copyright (C) 2002, 2006, 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (srfi srfi-26)
- \:export (cut cute))
-
-(cond-expand-provide (current-module) '(srfi-26))
-
-(define-syntax cut
- (lambda (stx)
- (syntax-case stx ()
- ((cut slot0 slot1+ ...)
- (let loop ((slots #'(slot0 slot1+ ...))
- (params '())
- (args '()))
- (if (null? slots)
- #`(lambda #,(reverse params) #,(reverse args))
- (let ((s (car slots))
- (rest (cdr slots)))
- (with-syntax (((var) (generate-temporaries '(var))))
- (syntax-case s (<> <___>)
- (<>
- (loop rest (cons #'var params) (cons #'var args)))
- (<___>
- (if (pair? rest)
- (error "<___> not on the end of cut expression"))
- #`(lambda #,(append (reverse params) #'var)
- (apply #,@(reverse (cons #'var args)))))
- (else
- (loop rest params (cons s args))))))))))))
-
-(define-syntax cute
- (lambda (stx)
- (syntax-case stx ()
- ((cute slots ...)
- (let loop ((slots #'(slots ...))
- (bindings '())
- (arguments '()))
- (define (process-hole)
- (loop (cdr slots) bindings (cons (car slots) arguments)))
- (if (null? slots)
- #`(let #,bindings
- (cut #,@(reverse arguments)))
- (syntax-case (car slots) (<> <___>)
- (<> (process-hole))
- (<___> (process-hole))
- (expr
- (with-syntax (((t) (generate-temporaries '(t))))
- (loop (cdr slots)
- (cons #'(t expr) bindings)
- (cons #'t arguments)))))))))))
-;;; srfi-27.scm --- Sources of Random Bits
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library. If not, see
-;; <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This module is fully documented in the Guile Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-27)
- #\export (random-integer
- random-real
- default-random-source
- make-random-source
- random-source?
- random-source-state-ref
- random-source-state-set!
- random-source-randomize!
- random-source-pseudo-randomize!
- random-source-make-integers
- random-source-make-reals)
- #\use-module (srfi srfi-9))
-
-(cond-expand-provide (current-module) '(srfi-27))
-
-(define-record-type \:random-source
- (%make-random-source state)
- random-source?
- (state random-source-state set-random-source-state!))
-
-(define (make-random-source)
- (%make-random-source (seed->random-state 0)))
-
-(define (random-source-state-ref s)
- (random-state->datum (random-source-state s)))
-
-(define (random-source-state-set! s state)
- (set-random-source-state! s (datum->random-state state)))
-
-(define (random-source-randomize! s)
- (let ((time (gettimeofday)))
- (set-random-source-state! s (seed->random-state
- (+ (* (car time) 1e6) (cdr time))))))
-
-(define (random-source-pseudo-randomize! s i j)
- (set-random-source-state! s (seed->random-state (i+j->seed i j))))
-
-(define (i+j->seed i j)
- (logior (ash (spread i 2) 1)
- (spread j 2)))
-
-(define (spread n amount)
- (let loop ((result 0) (n n) (shift 0))
- (if (zero? n)
- result
- (loop (logior result
- (ash (logand n 1) shift))
- (ash n -1)
- (+ shift amount)))))
-
-(define (random-source-make-integers s)
- (lambda (n)
- (random n (random-source-state s))))
-
-(define random-source-make-reals
- (case-lambda
- ((s)
- (lambda ()
- (let loop ()
- (let ((x (random:uniform (random-source-state s))))
- (if (zero? x)
- (loop)
- x)))))
- ((s unit)
- (or (and (real? unit) (< 0 unit 1))
- (error "unit must be real between 0 and 1" unit))
- (random-source-make-reals s))))
-
-(define default-random-source (make-random-source))
-(define random-integer (random-source-make-integers default-random-source))
-(define random-real (random-source-make-reals default-random-source))
-;;; srfi-28.scm --- Basic Format Strings
-
-;; Copyright (C) 2014 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Commentary:
-
-;; This module provides a wrapper for simple-format that always outputs
-;; to a string.
-;;
-;; This module is documented in the Guile Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-28)
- #\replace (format))
-
-(define (format message . args)
- (apply simple-format #f message args))
-
-(cond-expand-provide (current-module) '(srfi-28))
-;;; srfi-31.scm --- special form for recursive evaluation
-
-;; Copyright (C) 2004, 2006, 2012 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Original author: Rob Browning <rlb@defaultvalue.org>
-
-(define-module (srfi srfi-31)
- #\export (rec))
-
-(cond-expand-provide (current-module) '(srfi-31))
-
-(define-syntax rec
- (syntax-rules ()
- "Return the given object, defined in a lexical environment where
-NAME is bound to itself."
- ((_ (name . formals) body ...) ; procedure
- (letrec ((name (lambda formals body ...)))
- name))
- ((_ name expr) ; arbitrary object
- (letrec ((name expr))
- name))))
-;;; srfi-34.scm --- Exception handling for programs
-
-;; Copyright (C) 2003, 2006, 2008, 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Neil Jerram <neil@ossau.uklinux.net>
-
-;;; Commentary:
-
-;; This is an implementation of SRFI-34: Exception Handling for
-;; Programs. For documentation please see the SRFI-34 document; this
-;; module is not yet documented at all in the Guile manual.
-
-;;; Code:
-
-(define-module (srfi srfi-34)
- #\export (with-exception-handler)
- #\replace (raise)
- #\export-syntax (guard))
-
-(cond-expand-provide (current-module) '(srfi-34))
-
-(define throw-key 'srfi-34)
-
-(define (with-exception-handler handler thunk)
- "Returns the result(s) of invoking THUNK. HANDLER must be a
-procedure that accepts one argument. It is installed as the current
-exception handler for the dynamic extent (as determined by
-dynamic-wind) of the invocation of THUNK."
- (with-throw-handler throw-key
- thunk
- (lambda (key obj)
- (handler obj))))
-
-(define (raise obj)
- "Invokes the current exception handler on OBJ. The handler is
-called in the dynamic environment of the call to raise, except that
-the current exception handler is that in place for the call to
-with-exception-handler that installed the handler being called. The
-handler's continuation is otherwise unspecified."
- (throw throw-key obj))
-
-(define-syntax guard
- (syntax-rules (else)
- "Syntax: (guard (<var> <clause1> <clause2> ...) <body>)
-Each <clause> should have the same form as a `cond' clause.
-
-Semantics: Evaluating a guard form evaluates <body> with an exception
-handler that binds the raised object to <var> and within the scope of
-that binding evaluates the clauses as if they were the clauses of a
-cond expression. That implicit cond expression is evaluated with the
-continuation and dynamic environment of the guard expression. If
-every <clause>'s <test> evaluates to false and there is no else
-clause, then raise is re-invoked on the raised object within the
-dynamic environment of the original call to raise except that the
-current exception handler is that of the guard expression."
- ((guard (var clause ... (else e e* ...)) body body* ...)
- (catch throw-key
- (lambda () body body* ...)
- (lambda (key var)
- (cond clause ...
- (else e e* ...)))))
- ((guard (var clause clause* ...) body body* ...)
- (catch throw-key
- (lambda () body body* ...)
- (lambda (key var)
- (cond clause clause* ...
- (else (throw key var))))))))
-
-
-;;; (srfi srfi-34) ends here.
-;;; srfi-35.scm --- Conditions -*- coding: utf-8 -*-
-
-;; Copyright (C) 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Ludovic Courtès <ludo@gnu.org>
-
-;;; Commentary:
-
-;; This is an implementation of SRFI-35, "Conditions". Conditions are a
-;; means to convey information about exceptional conditions between parts of
-;; a program.
-
-;;; Code:
-
-(define-module (srfi srfi-35)
- #\use-module (srfi srfi-1)
- #\export (make-condition-type condition-type?
- make-condition condition? condition-has-type? condition-ref
- make-compound-condition extract-condition
- define-condition-type condition
- &condition
- &message message-condition? condition-message
- &serious serious-condition?
- &error error?))
-
-(cond-expand-provide (current-module) '(srfi-35))
-
-
-;;;
-;;; Condition types.
-;;;
-
-(define %condition-type-vtable
- ;; The vtable of all condition types.
- ;; vtable fields: vtable, self, printer
- ;; user fields: id, parent, all-field-names
- (let ((s (make-vtable (string-append standard-vtable-fields "prprpr")
- (lambda (ct port)
- (format port "#<condition-type ~a ~a>"
- (condition-type-id ct)
- (number->string (object-address ct)
- 16))))))
- (set-struct-vtable-name! s 'condition-type)
- s))
-
-(define (%make-condition-type layout id parent all-fields)
- (let ((struct (make-struct %condition-type-vtable 0
- (make-struct-layout layout) ;; layout
- print-condition ;; printer
- id parent all-fields)))
-
- ;; Hack to associate STRUCT with a name, providing a better name for
- ;; GOOPS classes as returned by `class-of' et al.
- (set-struct-vtable-name! struct (cond ((symbol? id) id)
- ((string? id) (string->symbol id))
- (else (string->symbol ""))))
- struct))
-
-(define (condition-type? obj)
- "Return true if OBJ is a condition type."
- (and (struct? obj)
- (eq? (struct-vtable obj)
- %condition-type-vtable)))
-
-(define (condition-type-id ct)
- (and (condition-type? ct)
- (struct-ref ct (+ vtable-offset-user 0))))
-
-(define (condition-type-parent ct)
- (and (condition-type? ct)
- (struct-ref ct (+ vtable-offset-user 1))))
-
-(define (condition-type-all-fields ct)
- (and (condition-type? ct)
- (struct-ref ct (+ vtable-offset-user 2))))
-
-
-(define (struct-layout-for-condition field-names)
- ;; Return a string denoting the layout required to hold the fields listed
- ;; in FIELD-NAMES.
- (let loop ((field-names field-names)
- (layout '("pr")))
- (if (null? field-names)
- (string-concatenate/shared layout)
- (loop (cdr field-names)
- (cons "pr" layout)))))
-
-(define (print-condition c port)
- ;; Print condition C to PORT in a way similar to how records print:
- ;; #<condition TYPE [FIELD: VALUE ...] ADDRESS>.
- (define (field-values)
- (let* ((type (struct-vtable c))
- (strings (fold (lambda (field result)
- (cons (format #f "~A: ~S" field
- (condition-ref c field))
- result))
- '()
- (condition-type-all-fields type))))
- (string-join (reverse strings) " ")))
-
- (format port "#<condition ~a [~a] ~a>"
- (condition-type-id (condition-type c))
- (field-values)
- (number->string (object-address c) 16)))
-
-(define (make-condition-type id parent field-names)
- "Return a new condition type named ID, inheriting from PARENT, and with the
-fields whose names are listed in FIELD-NAMES. FIELD-NAMES must be a list of
-symbols and must not contain names already used by PARENT or one of its
-supertypes."
- (if (symbol? id)
- (if (condition-type? parent)
- (let ((parent-fields (condition-type-all-fields parent)))
- (if (and (every symbol? field-names)
- (null? (lset-intersection eq?
- field-names parent-fields)))
- (let* ((all-fields (append parent-fields field-names))
- (layout (struct-layout-for-condition all-fields)))
- (%make-condition-type layout
- id parent all-fields))
- (error "invalid condition type field names"
- field-names)))
- (error "parent is not a condition type" parent))
- (error "condition type identifier is not a symbol" id)))
-
-(define (make-compound-condition-type id parents)
- ;; Return a compound condition type made of the types listed in PARENTS.
- ;; All fields from PARENTS are kept, even same-named ones, since they are
- ;; needed by `extract-condition'.
- (cond ((null? parents)
- (error "`make-compound-condition-type' passed empty parent list"
- id))
- ((null? (cdr parents))
- (car parents))
- (else
- (let* ((all-fields (append-map condition-type-all-fields
- parents))
- (layout (struct-layout-for-condition all-fields)))
- (%make-condition-type layout
- id
- parents ;; list of parents!
- all-fields)))))
-
-
-;;;
-;;; Conditions.
-;;;
-
-(define (condition? c)
- "Return true if C is a condition."
- (and (struct? c)
- (condition-type? (struct-vtable c))))
-
-(define (condition-type c)
- (and (struct? c)
- (let ((vtable (struct-vtable c)))
- (if (condition-type? vtable)
- vtable
- #f))))
-
-(define (condition-has-type? c type)
- "Return true if condition C has type TYPE."
- (if (and (condition? c) (condition-type? type))
- (let loop ((ct (condition-type c)))
- (or (eq? ct type)
- (and ct
- (let ((parent (condition-type-parent ct)))
- (if (list? parent)
- (any loop parent) ;; compound condition
- (loop (condition-type-parent ct)))))))
- (throw 'wrong-type-arg "condition-has-type?"
- "Wrong type argument")))
-
-(define (condition-ref c field-name)
- "Return the value of the field named FIELD-NAME from condition C."
- (if (condition? c)
- (if (symbol? field-name)
- (let* ((type (condition-type c))
- (fields (condition-type-all-fields type))
- (index (list-index (lambda (name)
- (eq? name field-name))
- fields)))
- (if index
- (struct-ref c index)
- (error "invalid field name" field-name)))
- (error "field name is not a symbol" field-name))
- (throw 'wrong-type-arg "condition-ref"
- "Wrong type argument: ~S" c)))
-
-(define (make-condition-from-values type values)
- (apply make-struct type 0 values))
-
-(define (make-condition type . field+value)
- "Return a new condition of type TYPE with fields initialized as specified
-by FIELD+VALUE, a sequence of field names (symbols) and values."
- (if (condition-type? type)
- (let* ((all-fields (condition-type-all-fields type))
- (inits (fold-right (lambda (field inits)
- (let ((v (memq field field+value)))
- (if (pair? v)
- (cons (cadr v) inits)
- (error "field not specified"
- field))))
- '()
- all-fields)))
- (make-condition-from-values type inits))
- (throw 'wrong-type-arg "make-condition"
- "Wrong type argument: ~S" type)))
-
-(define (make-compound-condition . conditions)
- "Return a new compound condition composed of CONDITIONS."
- (let* ((types (map condition-type conditions))
- (ct (make-compound-condition-type 'compound types))
- (inits (append-map (lambda (c)
- (let ((ct (condition-type c)))
- (map (lambda (f)
- (condition-ref c f))
- (condition-type-all-fields ct))))
- conditions)))
- (make-condition-from-values ct inits)))
-
-(define (extract-condition c type)
- "Return a condition of condition type TYPE with the field values specified
-by C."
-
- (define (first-field-index parents)
- ;; Return the index of the first field of TYPE within C.
- (let loop ((parents parents)
- (index 0))
- (let ((parent (car parents)))
- (cond ((null? parents)
- #f)
- ((eq? parent type)
- index)
- ((pair? parent)
- (or (loop parent index)
- (loop (cdr parents)
- (+ index
- (apply + (map condition-type-all-fields
- parent))))))
- (else
- (let ((shift (length (condition-type-all-fields parent))))
- (loop (cdr parents)
- (+ index shift))))))))
-
- (define (list-fields start-index field-names)
- ;; Return a list of the form `(FIELD-NAME VALUE...)'.
- (let loop ((index start-index)
- (field-names field-names)
- (result '()))
- (if (null? field-names)
- (reverse! result)
- (loop (+ 1 index)
- (cdr field-names)
- (cons* (struct-ref c index)
- (car field-names)
- result)))))
-
- (if (and (condition? c) (condition-type? type))
- (let* ((ct (condition-type c))
- (parent (condition-type-parent ct)))
- (cond ((eq? type ct)
- c)
- ((pair? parent)
- ;; C is a compound condition.
- (let ((field-index (first-field-index parent)))
- ;;(format #t "field-index: ~a ~a~%" field-index
- ;; (list-fields field-index
- ;; (condition-type-all-fields type)))
- (apply make-condition type
- (list-fields field-index
- (condition-type-all-fields type)))))
- (else
- ;; C does not have type TYPE.
- #f)))
- (throw 'wrong-type-arg "extract-condition"
- "Wrong type argument")))
-
-
-;;;
-;;; Syntax.
-;;;
-
-(define-syntax-rule (define-condition-type name parent pred (field-name field-accessor) ...)
- (begin
- (define name
- (make-condition-type 'name parent '(field-name ...)))
- (define (pred c)
- (condition-has-type? c name))
- (define (field-accessor c)
- (condition-ref c 'field-name))
- ...))
-
-(define-syntax-rule (compound-condition (type ...) (field ...))
- ;; Create a compound condition using `make-compound-condition-type'.
- (condition ((make-compound-condition-type '%compound `(,type ...))
- field ...)))
-
-(define-syntax condition-instantiation
- ;; Build the `(make-condition type ...)' call.
- (syntax-rules ()
- ((_ type (out ...))
- (make-condition type out ...))
- ((_ type (out ...) (field-name field-value) rest ...)
- (condition-instantiation type (out ... 'field-name field-value) rest ...))))
-
-(define-syntax condition
- (syntax-rules ()
- ((_ (type field ...))
- (condition-instantiation type () field ...))
- ((_ (type field ...) ...)
- (compound-condition (type ...) (field ... ...)))))
-
-
-;;;
-;;; Standard condition types.
-;;;
-
-(define &condition
- ;; The root condition type.
- (make-struct %condition-type-vtable 0
- (make-struct-layout "")
- (lambda (c port)
- (display "<&condition>"))
- '&condition #f '() '()))
-
-(define-condition-type &message &condition
- message-condition?
- (message condition-message))
-
-(define-condition-type &serious &condition
- serious-condition?)
-
-(define-condition-type &error &serious
- error?)
-
-;;; srfi-35.scm ends here
-;;; srfi-37.scm --- args-fold
-
-;; Copyright (C) 2007, 2008, 2013 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-;;; Commentary:
-;;
-;; To use this module with Guile, use (cdr (program-arguments)) as
-;; the ARGS argument to `args-fold'. Here is a short example:
-;;
-;; (args-fold (cdr (program-arguments))
-;; (let ((display-and-exit-proc
-;; (lambda (msg)
-;; (lambda (opt name arg)
-;; (display msg) (quit) (values)))))
-;; (list (option '(#\v "version") #f #f
-;; (display-and-exit-proc "Foo version 42.0\n"))
-;; (option '(#\h "help") #f #f
-;; (display-and-exit-proc
-;; "Usage: foo scheme-file ..."))))
-;; (lambda (opt name arg)
-;; (error "Unrecognized option `~A'" name))
-;; (lambda (op) (load op) (values)))
-;;
-;;; Code:
-
-
-;;;; Module definition & exports
-(define-module (srfi srfi-37)
- #\use-module (srfi srfi-9)
- #\export (option option-names option-required-arg?
- option-optional-arg? option-processor
- args-fold))
-
-(cond-expand-provide (current-module) '(srfi-37))
-
-;;;; args-fold and periphery procedures
-
-;;; An option as answered by `option'. `names' is a list of
-;;; characters and strings, representing associated short-options and
-;;; long-options respectively that should use this option's
-;;; `processor' in an `args-fold' call.
-;;;
-;;; `required-arg?' and `optional-arg?' are mutually exclusive
-;;; booleans and indicate whether an argument must be or may be
-;;; provided. Besides the obvious, this affects semantics of
-;;; short-options, as short-options with a required or optional
-;;; argument cannot be followed by other short options in the same
-;;; program-arguments string, as they will be interpreted collectively
-;;; as the option's argument.
-;;;
-;;; `processor' is called when this option is encountered. It should
-;;; accept the containing option, the element of `names' (by `equal?')
-;;; encountered, the option's argument (or #f if none), and the seeds
-;;; as variadic arguments, answering the new seeds as values.
-(define-record-type srfi-37:option
- (option names required-arg? optional-arg? processor)
- option?
- (names option-names)
- (required-arg? option-required-arg?)
- (optional-arg? option-optional-arg?)
- (processor option-processor))
-
-(define (error-duplicate-option option-name)
- (scm-error 'program-error "args-fold"
- "Duplicate option name `~A~A'"
- (list (if (char? option-name) #\- "--")
- option-name)
- #f))
-
-(define (build-options-lookup options)
- "Answer an `equal?' Guile hash-table that maps OPTIONS' names back
-to the containing options, signalling an error if a name is
-encountered more than once."
- (let ((lookup (make-hash-table (* 2 (length options)))))
- (for-each
- (lambda (opt)
- (for-each (lambda (name)
- (let ((assoc (hash-create-handle!
- lookup name #f)))
- (if (cdr assoc)
- (error-duplicate-option (car assoc))
- (set-cdr! assoc opt))))
- (option-names opt)))
- options)
- lookup))
-
-(define (args-fold args options unrecognized-option-proc
- operand-proc . seeds)
- "Answer the results of folding SEEDS as multiple values against the
-program-arguments in ARGS, as decided by the OPTIONS'
-`option-processor's, UNRECOGNIZED-OPTION-PROC, and OPERAND-PROC."
- (let ((lookup (build-options-lookup options)))
- ;; I don't like Guile's `error' here
- (define (error msg . args)
- (scm-error 'misc-error "args-fold" msg args #f))
-
- (define (mutate-seeds! procedure . params)
- (set! seeds (call-with-values
- (lambda ()
- (apply procedure (append params seeds)))
- list)))
-
- ;; Clean up the rest of ARGS, assuming they're all operands.
- (define (rest-operands)
- (for-each (lambda (arg) (mutate-seeds! operand-proc arg))
- args)
- (set! args '()))
-
- ;; Call OPT's processor with OPT, NAME, an argument to be decided,
- ;; and the seeds. Depending on OPT's *-arg? specification, get
- ;; the parameter by calling REQ-ARG-PROC or OPT-ARG-PROC thunks;
- ;; if no argument is allowed, call NO-ARG-PROC thunk.
- (define (invoke-option-processor
- opt name req-arg-proc opt-arg-proc no-arg-proc)
- (mutate-seeds!
- (option-processor opt) opt name
- (cond ((option-required-arg? opt) (req-arg-proc))
- ((option-optional-arg? opt) (opt-arg-proc))
- (else (no-arg-proc) #f))))
-
- ;; Compute and answer a short option argument, advancing ARGS as
- ;; necessary, for the short option whose character is at POSITION
- ;; in the current ARG.
- (define (short-option-argument position)
- (cond ((< (1+ position) (string-length (car args)))
- (let ((result (substring (car args) (1+ position))))
- (set! args (cdr args))
- result))
- ((pair? (cdr args))
- (let ((result (cadr args)))
- (set! args (cddr args))
- result))
- ((pair? args)
- (set! args (cdr args))
- #f)
- (else #f)))
-
- ;; Interpret the short-option at index POSITION in (car ARGS),
- ;; followed by the remaining short options in (car ARGS).
- (define (short-option position)
- (if (>= position (string-length (car args)))
- (begin
- (set! args (cdr args))
- (next-arg))
- (let* ((opt-name (string-ref (car args) position))
- (option-here (hash-ref lookup opt-name)))
- (cond ((not option-here)
- (mutate-seeds! unrecognized-option-proc
- (option (list opt-name) #f #f
- unrecognized-option-proc)
- opt-name #f)
- (short-option (1+ position)))
- (else
- (invoke-option-processor
- option-here opt-name
- (lambda ()
- (or (short-option-argument position)
- (error "Missing required argument after `-~A'" opt-name)))
- (lambda ()
- ;; edge case: -xo -zf or -xo -- where opt-name=#\o
- ;; GNU getopt_long resolves these like I do
- (short-option-argument position))
- (lambda () #f))
- (if (not (or (option-required-arg? option-here)
- (option-optional-arg? option-here)))
- (short-option (1+ position))))))))
-
- ;; Process the long option in (car ARGS). We make the
- ;; interesting, possibly non-standard assumption that long option
- ;; names might contain #\=, so keep looking for more #\= in (car
- ;; ARGS) until we find a named option in lookup.
- (define (long-option)
- (let ((arg (car args)))
- (let place-=-after ((start-pos 2))
- (let* ((index (string-index arg #\= start-pos))
- (opt-name (substring arg 2 (or index (string-length arg))))
- (option-here (hash-ref lookup opt-name)))
- (if (not option-here)
- ;; look for a later #\=, unless there can't be one
- (if index
- (place-=-after (1+ index))
- (mutate-seeds!
- unrecognized-option-proc
- (option (list opt-name) #f #f unrecognized-option-proc)
- opt-name #f))
- (invoke-option-processor
- option-here opt-name
- (lambda ()
- (if index
- (substring arg (1+ index))
- (error "Missing required argument after `--~A'" opt-name)))
- (lambda () (and index (substring arg (1+ index))))
- (lambda ()
- (if index
- (error "Extraneous argument after `--~A'" opt-name))))))))
- (set! args (cdr args)))
-
- ;; Process the remaining in ARGS. Basically like calling
- ;; `args-fold', but without having to regenerate `lookup' and the
- ;; funcs above.
- (define (next-arg)
- (if (null? args)
- (apply values seeds)
- (let ((arg (car args)))
- (cond ((or (not (char=? #\- (string-ref arg 0)))
- (= 1 (string-length arg))) ;"-"
- (mutate-seeds! operand-proc arg)
- (set! args (cdr args)))
- ((char=? #\- (string-ref arg 1))
- (if (= 2 (string-length arg)) ;"--"
- (begin (set! args (cdr args)) (rest-operands))
- (long-option)))
- (else (short-option 1)))
- (next-arg))))
-
- (next-arg)))
-
-;;; srfi-37.scm ends here
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-;; Copyright (C) Ray Dillinger 2003. All Rights Reserved.
-;;
-;; Contains code based upon Alex Shinn's public-domain implementation of
-;; `read-with-shared-structure' found in Chicken's SRFI 38 egg.
-
-;; Permission is hereby granted, free of charge, to any person obtaining
-;; a copy of this software and associated documentation files (the
-;; "Software"), to deal in the Software without restriction, including
-;; without limitation the rights to use, copy, modify, merge, publish,
-;; distribute, sublicense, and/or sell copies of the Software, and to
-;; permit persons to whom the Software is furnished to do so, subject to
-;; the following conditions:
-
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-(define-module (srfi srfi-38)
- #\export (write-with-shared-structure
- read-with-shared-structure)
- #\use-module (rnrs bytevectors)
- #\use-module (srfi srfi-8)
- #\use-module (srfi srfi-69)
- #\use-module (system vm trap-state))
-
-(cond-expand-provide (current-module) '(srfi-38))
-
-;; A printer that shows all sharing of substructures. Uses the Common
-;; Lisp print-circle notation: #n# refers to a previous substructure
-;; labeled with #n=. Takes O(n^2) time.
-
-;; Code attributed to Al Petrofsky, modified by Ray Dillinger.
-
-;; Modified in 2010 by Andreas Rottmann to use SRFI 69 hashtables,
-;; making the time O(n), and adding some of Guile's data types to the
-;; `interesting' objects.
-
-(define* (write-with-shared-structure obj
- #\optional
- (outport (current-output-port))
- (optarg #f))
-
- ;; We only track duplicates of pairs, vectors, strings, bytevectors,
- ;; structs (which subsume R6RS and SRFI-9 records), ports and (native)
- ;; hash-tables. We ignore zero-length vectors and strings because
- ;; r5rs doesn't guarantee that eq? treats them sanely (and they aren't
- ;; very interesting anyway).
-
- (define (interesting? obj)
- (or (pair? obj)
- (and (vector? obj) (not (zero? (vector-length obj))))
- (and (string? obj) (not (zero? (string-length obj))))
- (bytevector? obj)
- (struct? obj)
- (port? obj)
- (hash-table? obj)))
-
- ;; (write-obj OBJ STATE):
- ;;
- ;; STATE is a hashtable which has an entry for each interesting part
- ;; of OBJ. The associated value will be:
- ;;
- ;; -- a number if the part has been given one,
- ;; -- #t if the part will need to be assigned a number but has not been yet,
- ;; -- #f if the part will not need a number.
- ;; The entry `counter' in STATE should be the most recently
- ;; assigned number.
- ;;
- ;; Mutates STATE for any parts that had numbers assigned.
- (define (write-obj obj state)
- (define (write-interesting)
- (cond ((pair? obj)
- (display "(" outport)
- (write-obj (car obj) state)
- (let write-cdr ((obj (cdr obj)))
- (cond ((and (pair? obj) (not (hash-table-ref state obj)))
- (display " " outport)
- (write-obj (car obj) state)
- (write-cdr (cdr obj)))
- ((null? obj)
- (display ")" outport))
- (else
- (display " . " outport)
- (write-obj obj state)
- (display ")" outport)))))
- ((vector? obj)
- (display "#(" outport)
- (let ((len (vector-length obj)))
- (write-obj (vector-ref obj 0) state)
- (let write-vec ((i 1))
- (cond ((= i len) (display ")" outport))
- (else (display " " outport)
- (write-obj (vector-ref obj i) state)
- (write-vec (+ i 1)))))))
- ;; else it's a string
- (else (write obj outport))))
- (cond ((interesting? obj)
- (let ((val (hash-table-ref state obj)))
- (cond ((not val) (write-interesting))
- ((number? val)
- (begin (display "#" outport)
- (write val outport)
- (display "#" outport)))
- (else
- (let ((n (+ 1 (hash-table-ref state 'counter))))
- (display "#" outport)
- (write n outport)
- (display "=" outport)
- (hash-table-set! state 'counter n)
- (hash-table-set! state obj n)
- (write-interesting))))))
- (else
- (write obj outport))))
-
- ;; Scan computes the initial value of the hash table, which maps each
- ;; interesting part of the object to #t if it occurs multiple times,
- ;; #f if only once.
- (define (scan obj state)
- (cond ((not (interesting? obj)))
- ((hash-table-exists? state obj)
- (hash-table-set! state obj #t))
- (else
- (hash-table-set! state obj #f)
- (cond ((pair? obj)
- (scan (car obj) state)
- (scan (cdr obj) state))
- ((vector? obj)
- (let ((len (vector-length obj)))
- (do ((i 0 (+ 1 i)))
- ((= i len))
- (scan (vector-ref obj i) state))))))))
-
- (let ((state (make-hash-table eq?)))
- (scan obj state)
- (hash-table-set! state 'counter 0)
- (write-obj obj state)))
-
-;; A reader that understands the output of the above writer. This has
-;; been written by Andreas Rottmann to re-use Guile's built-in reader,
-;; with inspiration from Alex Shinn's public-domain implementation of
-;; `read-with-shared-structure' found in Chicken's SRFI 38 egg.
-
-(define* (read-with-shared-structure #\optional (port (current-input-port)))
- (let ((parts-table (make-hash-table eqv?)))
-
- ;; reads chars that match PRED and returns them as a string.
- (define (read-some-chars pred initial)
- (let iter ((chars initial))
- (let ((c (peek-char port)))
- (if (or (eof-object? c) (not (pred c)))
- (list->string (reverse chars))
- (iter (cons (read-char port) chars))))))
-
- (define (read-hash c port)
- (let* ((n (string->number (read-some-chars char-numeric? (list c))))
- (c (read-char port))
- (thunk (hash-table-ref/default parts-table n #f)))
- (case c
- ((#\=)
- (if thunk
- (error "Double declaration of part " n))
- (let* ((cell (list #f))
- (thunk (lambda () (car cell))))
- (hash-table-set! parts-table n thunk)
- (let ((obj (read port)))
- (set-car! cell obj)
- obj)))
- ((#\#)
- (or thunk
- (error "Use of undeclared part " n)))
- (else
- (error "Malformed shared part specifier")))))
-
- (with-fluid* %read-hash-procedures (fluid-ref %read-hash-procedures)
- (lambda ()
- (for-each (lambda (digit)
- (read-hash-extend digit read-hash))
- '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9))
- (let ((result (read port)))
- (if (< 0 (hash-table-size parts-table))
- (patch! result))
- result)))))
-
-(define (hole? x) (procedure? x))
-(define (fill-hole x) (if (hole? x) (fill-hole (x)) x))
-
-(define (patch! x)
- (cond
- ((pair? x)
- (if (hole? (car x)) (set-car! x (fill-hole (car x))) (patch! (car x)))
- (if (hole? (cdr x)) (set-cdr! x (fill-hole (cdr x))) (patch! (cdr x))))
- ((vector? x)
- (do ((i (- (vector-length x) 1) (- i 1)))
- ((< i 0))
- (let ((elt (vector-ref x i)))
- (if (hole? elt)
- (vector-set! x i (fill-hole elt))
- (patch! elt)))))))
-;;; srfi-39.scm --- Parameter objects
-
-;; Copyright (C) 2004, 2005, 2006, 2008, 2011 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Jose Antonio Ortega Ruiz <jao@gnu.org>
-;;; Date: 2004-05-05
-
-;;; Commentary:
-
-;; This is an implementation of SRFI-39 (Parameter objects).
-;;
-;; The implementation is based on Guile's fluid objects, and is, therefore,
-;; thread-safe (parameters are thread-local).
-;;
-;; In addition to the forms defined in SRFI-39 (`make-parameter',
-;; `parameterize'), a new procedure `with-parameters*' is provided.
-;; This procedures is analogous to `with-fluids*' but taking as first
-;; argument a list of parameter objects instead of a list of fluids.
-;;
-
-;;; Code:
-
-(define-module (srfi srfi-39)
- ;; helper procedure not in srfi-39.
- #\export (with-parameters*)
- #\re-export (make-parameter
- parameterize
- current-input-port current-output-port current-error-port))
-
-(cond-expand-provide (current-module) '(srfi-39))
-
-(define (with-parameters* params values thunk)
- (let more ((params params)
- (values values)
- (fluids '()) ;; fluids from each of PARAMS
- (convs '())) ;; VALUES with conversion proc applied
- (if (null? params)
- (with-fluids* fluids convs thunk)
- (more (cdr params) (cdr values)
- (cons (parameter-fluid (car params)) fluids)
- (cons ((parameter-converter (car params)) (car values)) convs)))))
-;;; srfi-4.scm --- Homogeneous Numeric Vector Datatypes
-
-;; Copyright (C) 2001, 2002, 2004, 2006, 2009, 2010,
-;; 2012, 2014 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Martin Grabmueller <mgrabmue@cs.tu-berlin.de>
-
-;;; Commentary:
-
-;; This module exports the homogeneous numeric vector procedures as
-;; defined in SRFI-4. They are fully documented in the Guile
-;; Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-4)
- #\use-module (rnrs bytevectors)
- #\export (;; Unsigned 8-bit vectors.
- u8vector? make-u8vector u8vector u8vector-length u8vector-ref
- u8vector-set! u8vector->list list->u8vector
-
- ;; Signed 8-bit vectors.
- s8vector? make-s8vector s8vector s8vector-length s8vector-ref
- s8vector-set! s8vector->list list->s8vector
-
- ;; Unsigned 16-bit vectors.
- u16vector? make-u16vector u16vector u16vector-length u16vector-ref
- u16vector-set! u16vector->list list->u16vector
-
- ;; Signed 16-bit vectors.
- s16vector? make-s16vector s16vector s16vector-length s16vector-ref
- s16vector-set! s16vector->list list->s16vector
-
- ;; Unsigned 32-bit vectors.
- u32vector? make-u32vector u32vector u32vector-length u32vector-ref
- u32vector-set! u32vector->list list->u32vector
-
- ;; Signed 32-bit vectors.
- s32vector? make-s32vector s32vector s32vector-length s32vector-ref
- s32vector-set! s32vector->list list->s32vector
-
- ;; Unsigned 64-bit vectors.
- u64vector? make-u64vector u64vector u64vector-length u64vector-ref
- u64vector-set! u64vector->list list->u64vector
-
- ;; Signed 64-bit vectors.
- s64vector? make-s64vector s64vector s64vector-length s64vector-ref
- s64vector-set! s64vector->list list->s64vector
-
- ;; 32-bit floating point vectors.
- f32vector? make-f32vector f32vector f32vector-length f32vector-ref
- f32vector-set! f32vector->list list->f32vector
-
- ;; 64-bit floating point vectors.
- f64vector? make-f64vector f64vector f64vector-length f64vector-ref
- f64vector-set! f64vector->list list->f64vector))
-
-(cond-expand-provide (current-module) '(srfi-4))
-
-;; Need quasisyntax to do this effectively using syntax-case
-(define-macro (define-bytevector-type tag infix size)
- `(begin
- (define (,(symbol-append tag 'vector?) obj)
- (and (bytevector? obj) (eq? (array-type obj) ',tag)))
- (define (,(symbol-append 'make- tag 'vector) len . fill)
- (apply make-srfi-4-vector ',tag len fill))
- (define (,(symbol-append tag 'vector-length) v)
- (let ((len (/ (bytevector-length v) ,size)))
- (if (integer? len)
- len
- (error "fractional length" v ',tag ,size))))
- (define (,(symbol-append tag 'vector) . elts)
- (,(symbol-append 'list-> tag 'vector) elts))
- (define (,(symbol-append 'list-> tag 'vector) elts)
- (let* ((len (length elts))
- (v (,(symbol-append 'make- tag 'vector) len)))
- (let lp ((i 0) (elts elts))
- (if (and (< i len) (pair? elts))
- (begin
- (,(symbol-append tag 'vector-set!) v i (car elts))
- (lp (1+ i) (cdr elts)))
- v))))
- (define (,(symbol-append tag 'vector->list) v)
- (let lp ((i (1- (,(symbol-append tag 'vector-length) v))) (elts '()))
- (if (< i 0)
- elts
- (lp (1- i) (cons (,(symbol-append tag 'vector-ref) v i) elts)))))
- (define (,(symbol-append tag 'vector-ref) v i)
- (,(symbol-append 'bytevector- infix '-ref) v (* i ,size)))
- (define (,(symbol-append tag 'vector-set!) v i x)
- (,(symbol-append 'bytevector- infix '-set!) v (* i ,size) x))
- (define (,(symbol-append tag 'vector-set!) v i x)
- (,(symbol-append 'bytevector- infix '-set!) v (* i ,size) x))))
-
-(define-bytevector-type u8 u8 1)
-(define-bytevector-type s8 s8 1)
-(define-bytevector-type u16 u16-native 2)
-(define-bytevector-type s16 s16-native 2)
-(define-bytevector-type u32 u32-native 4)
-(define-bytevector-type s32 s32-native 4)
-(define-bytevector-type u64 u64-native 8)
-(define-bytevector-type s64 s64-native 8)
-(define-bytevector-type f32 ieee-single-native 4)
-(define-bytevector-type f64 ieee-double-native 8)
-;;; Extensions to SRFI-4
-
-;; Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Commentary:
-
-;; Extensions to SRFI-4. Fully documented in the Guile Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-4 gnu)
- #\use-module (rnrs bytevectors)
- #\use-module (srfi srfi-4)
- #\export (;; Complex numbers with 32- and 64-bit components.
- c32vector? make-c32vector c32vector c32vector-length c32vector-ref
- c32vector-set! c32vector->list list->c32vector
-
- c64vector? make-c64vector c64vector c64vector-length c64vector-ref
- c64vector-set! c64vector->list list->c64vector
-
- make-srfi-4-vector
-
- ;; Somewhat polymorphic conversions.
- any->u8vector any->s8vector any->u16vector any->s16vector
- any->u32vector any->s32vector any->u64vector any->s64vector
- any->f32vector any->f64vector any->c32vector any->c64vector))
-
-
-(define make-srfi-4-vector (@@ (srfi srfi-4) make-srfi-4-vector))
-
-(define (bytevector-c32-native-ref v i)
- (make-rectangular (bytevector-ieee-single-native-ref v i)
- (bytevector-ieee-single-native-ref v (+ i 4))))
-(define (bytevector-c32-native-set! v i x)
- (bytevector-ieee-single-native-set! v i (real-part x))
- (bytevector-ieee-single-native-set! v (+ i 4) (imag-part x)))
-(define (bytevector-c64-native-ref v i)
- (make-rectangular (bytevector-ieee-double-native-ref v i)
- (bytevector-ieee-double-native-ref v (+ i 8))))
-(define (bytevector-c64-native-set! v i x)
- (bytevector-ieee-double-native-set! v i (real-part x))
- (bytevector-ieee-double-native-set! v (+ i 8) (imag-part x)))
-
-((@@ (srfi srfi-4) define-bytevector-type) c32 c32-native 8)
-((@@ (srfi srfi-4) define-bytevector-type) c64 c64-native 16)
-
-(define-macro (define-any->vector . tags)
- `(begin
- ,@(map (lambda (tag)
- `(define (,(symbol-append 'any-> tag 'vector) obj)
- (cond ((,(symbol-append tag 'vector?) obj) obj)
- ((pair? obj) (,(symbol-append 'list-> tag 'vector) obj))
- ((and (array? obj) (eqv? 1 (array-rank obj)))
- (let* ((len (array-length obj))
- (v (,(symbol-append 'make- tag 'vector) len)))
- (let lp ((i 0))
- (if (< i len)
- (begin
- (,(symbol-append tag 'vector-set!)
- v i (array-ref obj i))
- (lp (1+ i)))
- v))))
- (else (scm-error 'wrong-type-arg #f "" '() (list obj))))))
- tags)))
-
-(define-any->vector u8 s8 u16 s16 u32 s32 u64 s64 f32 f64 c32 c64)
-;;; srfi-41.scm -- SRFI 41 streams
-
-;; Copyright (c) 2007 Philip L. Bewig
-;; Copyright (c) 2011, 2012, 2013 Free Software Foundation, Inc.
-
-;; Permission is hereby granted, free of charge, to any person obtaining
-;; a copy of this software and associated documentation files (the
-;; "Software"), to deal in the Software without restriction, including
-;; without limitation the rights to use, copy, modify, merge, publish,
-;; distribute, sublicense, and/or sell copies of the Software, and to
-;; permit persons to whom the Software is furnished to do so, subject to
-;; the following conditions:
-;;
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-;;
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-;; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-;; BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN
-;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN
-;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-(define-module (srfi srfi-41)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-8)
- #\use-module (srfi srfi-9)
- #\use-module (srfi srfi-9 gnu)
- #\use-module (srfi srfi-26)
- #\use-module (ice-9 match)
- #\export (stream-null stream-cons stream? stream-null? stream-pair?
- stream-car stream-cdr stream-lambda define-stream
- list->stream port->stream stream stream->list stream-append
- stream-concat stream-constant stream-drop stream-drop-while
- stream-filter stream-fold stream-for-each stream-from
- stream-iterate stream-length stream-let stream-map
- stream-match stream-of stream-range stream-ref stream-reverse
- stream-scan stream-take stream-take-while stream-unfold
- stream-unfolds stream-zip))
-
-(cond-expand-provide (current-module) '(srfi-41))
-
-;;; Private supporting functions and macros.
-
-(define-syntax-rule (must pred obj func msg args ...)
- (let ((item obj))
- (unless (pred item)
- (throw 'wrong-type-arg func msg (list args ...) (list item)))))
-
-(define-syntax-rule (must-not pred obj func msg args ...)
- (let ((item obj))
- (when (pred item)
- (throw 'wrong-type-arg func msg (list args ...) (list item)))))
-
-(define-syntax-rule (must-every pred objs func msg args ...)
- (let ((flunk (remove pred objs)))
- (unless (null? flunk)
- (throw 'wrong-type-arg func msg (list args ...) flunk))))
-
-(define-syntax-rule (first-value expr)
- (receive (first . _) expr
- first))
-
-(define-syntax-rule (second-value expr)
- (receive (first second . _) expr
- second))
-
-(define-syntax-rule (third-value expr)
- (receive (first second third . _) expr
- third))
-
-(define-syntax define-syntax*
- (syntax-rules ()
- ((_ (name . args) body ...)
- (define-syntax name (lambda* args body ...)))
- ((_ name syntax)
- (define-syntax name syntax))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Here we include a copy of the code of srfi-45.scm (but with renamed
-;; identifiers), in order to create a new promise type that's disjoint
-;; from the promises created by srfi-45. Ideally this should be done
-;; using a 'make-promise-type' macro that instantiates a copy of this
-;; code, but a psyntax bug in Guile 2.0 prevents this from working
-;; properly: <http://bugs.gnu.org/13995>. So for now, we duplicate the
-;; code.
-
-;; Copyright (C) 2010, 2011 Free Software Foundation, Inc.
-;; Copyright (C) 2003 André van Tonder. All Rights Reserved.
-
-;; Permission is hereby granted, free of charge, to any person
-;; obtaining a copy of this software and associated documentation
-;; files (the "Software"), to deal in the Software without
-;; restriction, including without limitation the rights to use, copy,
-;; modify, merge, publish, distribute, sublicense, and/or sell copies
-;; of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-(define-record-type stream-promise (make-stream-promise val) stream-promise?
- (val stream-promise-val stream-promise-val-set!))
-
-(define-record-type stream-value (make-stream-value tag proc) stream-value?
- (tag stream-value-tag stream-value-tag-set!)
- (proc stream-value-proc stream-value-proc-set!))
-
-(define-syntax-rule (stream-lazy exp)
- (make-stream-promise (make-stream-value 'lazy (lambda () exp))))
-
-(define (stream-eager x)
- (make-stream-promise (make-stream-value 'eager x)))
-
-(define-syntax-rule (stream-delay exp)
- (stream-lazy (stream-eager exp)))
-
-(define (stream-force promise)
- (let ((content (stream-promise-val promise)))
- (case (stream-value-tag content)
- ((eager) (stream-value-proc content))
- ((lazy) (let* ((promise* ((stream-value-proc content)))
- (content (stream-promise-val promise)))
- (if (not (eqv? (stream-value-tag content) 'eager))
- (begin (stream-value-tag-set! content
- (stream-value-tag (stream-promise-val promise*)))
- (stream-value-proc-set! content
- (stream-value-proc (stream-promise-val promise*)))
- (stream-promise-val-set! promise* content)))
- (stream-force promise))))))
-
-;;
-;; End of the copy of the code from srfi-45.scm
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;;; Primitive stream functions and macros: (streams primitive)
-
-(define stream? stream-promise?)
-
-(define %stream-null (cons 'stream 'null))
-(define stream-null (stream-eager %stream-null))
-
-(define (stream-null? obj)
- (and (stream-promise? obj)
- (eqv? (stream-force obj) %stream-null)))
-
-(define-record-type stream-pare (make-stream-pare kar kdr) stream-pare?
- (kar stream-kar)
- (kdr stream-kdr))
-
-(define (stream-pair? obj)
- (and (stream-promise? obj) (stream-pare? (stream-force obj))))
-
-(define-syntax-rule (stream-cons obj strm)
- (stream-eager (make-stream-pare (stream-delay obj) (stream-lazy strm))))
-
-(define (stream-car strm)
- (must stream? strm 'stream-car "non-stream")
- (let ((pare (stream-force strm)))
- (must stream-pare? pare 'stream-car "null stream")
- (stream-force (stream-kar pare))))
-
-(define (stream-cdr strm)
- (must stream? strm 'stream-cdr "non-stream")
- (let ((pare (stream-force strm)))
- (must stream-pare? pare 'stream-cdr "null stream")
- (stream-kdr pare)))
-
-(define-syntax-rule (stream-lambda formals body0 body1 ...)
- (lambda formals (stream-lazy (begin body0 body1 ...))))
-
-(define* (stream-promise-visit promise #\key on-eager on-lazy)
- (define content (stream-promise-val promise))
- (case (stream-value-tag content)
- ((eager) (on-eager (stream-value-proc content)))
- ((lazy) (on-lazy (stream-value-proc content)))))
-
-(set-record-type-printer! stream-promise
- (lambda (strm port)
- (display "#<stream" port)
- (let loop ((strm strm))
- (stream-promise-visit strm
- #\on-eager (lambda (pare)
- (cond ((eq? pare %stream-null)
- (write-char #\> port))
- (else
- (write-char #\space port)
- (stream-promise-visit (stream-kar pare)
- #\on-eager (cut write <> port)
- #\on-lazy (lambda (_) (write-char #\? port)))
- (loop (stream-kdr pare)))))
- #\on-lazy (lambda (_) (display " ...>" port))))))
-
-;;; Derived stream functions and macros: (streams derived)
-
-(define-syntax-rule (define-stream (name . formal) body0 body1 ...)
- (define name (stream-lambda formal body0 body1 ...)))
-
-(define-syntax-rule (stream-let tag ((name val) ...) body1 body2 ...)
- ((letrec ((tag (stream-lambda (name ...) body1 body2 ...))) tag) val ...))
-
-(define (list->stream objs)
- (define (list? x)
- (or (proper-list? x) (circular-list? x)))
- (must list? objs 'list->stream "non-list argument")
- (stream-let recur ((objs objs))
- (if (null? objs) stream-null
- (stream-cons (car objs) (recur (cdr objs))))))
-
-(define* (port->stream #\optional (port (current-input-port)))
- (must input-port? port 'port->stream "non-input-port argument")
- (stream-let recur ()
- (let ((c (read-char port)))
- (if (eof-object? c) stream-null
- (stream-cons c (recur))))))
-
-(define-syntax stream
- (syntax-rules ()
- ((_) stream-null)
- ((_ x y ...) (stream-cons x (stream y ...)))))
-
-;; Common helper for the various eager-folding functions, such as
-;; stream-fold, stream-drop, stream->list, stream-length, etc.
-(define-inlinable (stream-fold-aux proc base strm limit)
- (do ((val base (and proc (proc val (stream-car strm))))
- (strm strm (stream-cdr strm))
- (limit limit (and limit (1- limit))))
- ((or (and limit (zero? limit)) (stream-null? strm))
- (values val strm limit))))
-
-(define stream->list
- (case-lambda
- ((strm) (stream->list #f strm))
- ((n strm)
- (must stream? strm 'stream->list "non-stream argument")
- (when n
- (must integer? n 'stream->list "non-integer count")
- (must exact? n 'stream->list "inexact count")
- (must-not negative? n 'stream->list "negative count"))
- (reverse! (first-value (stream-fold-aux xcons '() strm n))))))
-
-(define (stream-append . strms)
- (must-every stream? strms 'stream-append "non-stream argument")
- (stream-let recur ((strms strms))
- (if (null? strms) stream-null
- (let ((strm (car strms)))
- (if (stream-null? strm) (recur (cdr strms))
- (stream-cons (stream-car strm)
- (recur (cons (stream-cdr strm) (cdr strms)))))))))
-
-(define (stream-concat strms)
- (must stream? strms 'stream-concat "non-stream argument")
- (stream-let recur ((strms strms))
- (if (stream-null? strms) stream-null
- (let ((strm (stream-car strms)))
- (must stream? strm 'stream-concat "non-stream object in input stream")
- (if (stream-null? strm) (recur (stream-cdr strms))
- (stream-cons (stream-car strm)
- (recur (stream-cons (stream-cdr strm)
- (stream-cdr strms)))))))))
-
-(define stream-constant
- (case-lambda
- (() stream-null)
- (objs (list->stream (apply circular-list objs)))))
-
-(define-syntax* (stream-do x)
- (define (end x)
- (syntax-case x ()
- (() #'(if #f #f))
- ((result) #'result)
- ((result ...) #'(begin result ...))))
- (define (var-step v s)
- (syntax-case s ()
- (() v)
- ((e) #'e)
- (_ (syntax-violation 'stream-do "bad step expression" x s))))
-
- (syntax-case x ()
- ((_ ((var init . step) ...)
- (test result ...)
- expr ...)
- (with-syntax ((result (end #'(result ...)))
- ((step ...) (map var-step #'(var ...) #'(step ...))))
- #'(stream-let loop ((var init) ...)
- (if test result
- (begin
- expr ...
- (loop step ...))))))))
-
-(define (stream-drop n strm)
- (must integer? n 'stream-drop "non-integer argument")
- (must exact? n 'stream-drop "inexact argument")
- (must-not negative? n 'stream-drop "negative argument")
- (must stream? strm 'stream-drop "non-stream argument")
- (second-value (stream-fold-aux #f #f strm n)))
-
-(define (stream-drop-while pred? strm)
- (must procedure? pred? 'stream-drop-while "non-procedural argument")
- (must stream? strm 'stream-drop-while "non-stream argument")
- (stream-do ((strm strm (stream-cdr strm)))
- ((or (stream-null? strm) (not (pred? (stream-car strm)))) strm)))
-
-(define (stream-filter pred? strm)
- (must procedure? pred? 'stream-filter "non-procedural argument")
- (must stream? strm 'stream-filter "non-stream argument")
- (stream-let recur ((strm strm))
- (cond ((stream-null? strm) stream-null)
- ((pred? (stream-car strm))
- (stream-cons (stream-car strm) (recur (stream-cdr strm))))
- (else (recur (stream-cdr strm))))))
-
-(define (stream-fold proc base strm)
- (must procedure? proc 'stream-fold "non-procedural argument")
- (must stream? strm 'stream-fold "non-stream argument")
- (first-value (stream-fold-aux proc base strm #f)))
-
-(define stream-for-each
- (case-lambda
- ((proc strm)
- (must procedure? proc 'stream-for-each "non-procedural argument")
- (must stream? strm 'stream-for-each "non-stream argument")
- (do ((strm strm (stream-cdr strm)))
- ((stream-null? strm))
- (proc (stream-car strm))))
- ((proc strm . rest)
- (let ((strms (cons strm rest)))
- (must procedure? proc 'stream-for-each "non-procedural argument")
- (must-every stream? strms 'stream-for-each "non-stream argument")
- (do ((strms strms (map stream-cdr strms)))
- ((any stream-null? strms))
- (apply proc (map stream-car strms)))))))
-
-(define* (stream-from first #\optional (step 1))
- (must number? first 'stream-from "non-numeric starting number")
- (must number? step 'stream-from "non-numeric step size")
- (stream-let recur ((first first))
- (stream-cons first (recur (+ first step)))))
-
-(define (stream-iterate proc base)
- (must procedure? proc 'stream-iterate "non-procedural argument")
- (stream-let recur ((base base))
- (stream-cons base (recur (proc base)))))
-
-(define (stream-length strm)
- (must stream? strm 'stream-length "non-stream argument")
- (- -1 (third-value (stream-fold-aux #f #f strm -1))))
-
-(define stream-map
- (case-lambda
- ((proc strm)
- (must procedure? proc 'stream-map "non-procedural argument")
- (must stream? strm 'stream-map "non-stream argument")
- (stream-let recur ((strm strm))
- (if (stream-null? strm) stream-null
- (stream-cons (proc (stream-car strm))
- (recur (stream-cdr strm))))))
- ((proc strm . rest)
- (let ((strms (cons strm rest)))
- (must procedure? proc 'stream-map "non-procedural argument")
- (must-every stream? strms 'stream-map "non-stream argument")
- (stream-let recur ((strms strms))
- (if (any stream-null? strms) stream-null
- (stream-cons (apply proc (map stream-car strms))
- (recur (map stream-cdr strms)))))))))
-
-(define-syntax* (stream-match x)
- (define (make-matcher x)
- (syntax-case x ()
- (() #'(? stream-null?))
- (rest (identifier? #'rest) #'rest)
- ((var . rest) (identifier? #'var)
- (with-syntax ((next (make-matcher #'rest)))
- #'(? (negate stream-null?)
- (= stream-car var)
- (= stream-cdr next))))))
- (define (make-guarded x fail)
- (syntax-case (list x fail) ()
- (((expr) _) #'expr)
- (((guard expr) fail) #'(if guard expr (fail)))))
-
- (syntax-case x ()
- ((_ strm-expr (pat . expr) ...)
- (with-syntax (((fail ...) (generate-temporaries #'(pat ...))))
- (with-syntax (((matcher ...) (map make-matcher #'(pat ...)))
- ((expr ...) (map make-guarded #'(expr ...) #'(fail ...))))
- #'(let ((strm strm-expr))
- (must stream? strm 'stream-match "non-stream argument")
- (match strm (matcher (=> fail) expr) ...)))))))
-
-(define-syntax-rule (stream-of expr rest ...)
- (stream-of-aux expr stream-null rest ...))
-
-(define-syntax stream-of-aux
- (syntax-rules (in is)
- ((_ expr base)
- (stream-cons expr base))
- ((_ expr base (var in stream) rest ...)
- (stream-let recur ((strm stream))
- (if (stream-null? strm) base
- (let ((var (stream-car strm)))
- (stream-of-aux expr (recur (stream-cdr strm)) rest ...)))))
- ((_ expr base (var is exp) rest ...)
- (let ((var exp)) (stream-of-aux expr base rest ...)))
- ((_ expr base pred? rest ...)
- (if pred? (stream-of-aux expr base rest ...) base))))
-
-(define* (stream-range first past #\optional step)
- (must number? first 'stream-range "non-numeric starting number")
- (must number? past 'stream-range "non-numeric ending number")
- (when step
- (must number? step 'stream-range "non-numeric step size"))
- (let* ((step (or step (if (< first past) 1 -1)))
- (lt? (if (< 0 step) < >)))
- (stream-let recur ((first first))
- (if (lt? first past)
- (stream-cons first (recur (+ first step)))
- stream-null))))
-
-(define (stream-ref strm n)
- (must stream? strm 'stream-ref "non-stream argument")
- (must integer? n 'stream-ref "non-integer argument")
- (must exact? n 'stream-ref "inexact argument")
- (must-not negative? n 'stream-ref "negative argument")
- (let ((res (stream-drop n strm)))
- (must-not stream-null? res 'stream-ref "beyond end of stream")
- (stream-car res)))
-
-(define (stream-reverse strm)
- (must stream? strm 'stream-reverse "non-stream argument")
- (stream-do ((strm strm (stream-cdr strm))
- (rev stream-null (stream-cons (stream-car strm) rev)))
- ((stream-null? strm) rev)))
-
-(define (stream-scan proc base strm)
- (must procedure? proc 'stream-scan "non-procedural argument")
- (must stream? strm 'stream-scan "non-stream argument")
- (stream-let recur ((base base) (strm strm))
- (if (stream-null? strm) (stream base)
- (stream-cons base (recur (proc base (stream-car strm))
- (stream-cdr strm))))))
-
-(define (stream-take n strm)
- (must stream? strm 'stream-take "non-stream argument")
- (must integer? n 'stream-take "non-integer argument")
- (must exact? n 'stream-take "inexact argument")
- (must-not negative? n 'stream-take "negative argument")
- (stream-let recur ((n n) (strm strm))
- (if (or (zero? n) (stream-null? strm)) stream-null
- (stream-cons (stream-car strm) (recur (1- n) (stream-cdr strm))))))
-
-(define (stream-take-while pred? strm)
- (must procedure? pred? 'stream-take-while "non-procedural argument")
- (must stream? strm 'stream-take-while "non-stream argument")
- (stream-let recur ((strm strm))
- (cond ((stream-null? strm) stream-null)
- ((pred? (stream-car strm))
- (stream-cons (stream-car strm) (recur (stream-cdr strm))))
- (else stream-null))))
-
-(define (stream-unfold mapper pred? generator base)
- (must procedure? mapper 'stream-unfold "non-procedural mapper")
- (must procedure? pred? 'stream-unfold "non-procedural pred?")
- (must procedure? generator 'stream-unfold "non-procedural generator")
- (stream-let recur ((base base))
- (if (pred? base)
- (stream-cons (mapper base) (recur (generator base)))
- stream-null)))
-
-(define (stream-unfolds gen seed)
- (define-stream (generator-stream seed)
- (receive (next . items) (gen seed)
- (stream-cons (list->vector items) (generator-stream next))))
- (define-stream (make-result-stream genstrm index)
- (define head (vector-ref (stream-car genstrm) index))
- (define-stream (tail) (make-result-stream (stream-cdr genstrm) index))
- (match head
- (() stream-null)
- (#f (tail))
- ((item) (stream-cons item (tail)))
- ((? list? items) (stream-append (list->stream items) (tail)))))
-
- (must procedure? gen 'stream-unfolds "non-procedural argument")
- (let ((genstrm (generator-stream seed)))
- (apply values (list-tabulate (vector-length (stream-car genstrm))
- (cut make-result-stream genstrm <>)))))
-
-(define (stream-zip strm . rest)
- (let ((strms (cons strm rest)))
- (must-every stream? strms 'stream-zip "non-stream argument")
- (stream-let recur ((strms strms))
- (if (any stream-null? strms) stream-null
- (stream-cons (map stream-car strms) (recur (map stream-cdr strms)))))))
-;;; srfi-42.scm --- Eager comprehensions
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library. If not, see
-;; <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This module is not yet documented in the Guile Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-42)
- #\export (\:
- \:-dispatch-ref
- \:-dispatch-set!
- \:char-range
- \:dispatched
- \:do
- \:generator-proc
- \:integers
- \:let
- \:list
- \:parallel
- \:port
- \:range
- \:real-range
- \:string
- \:until
- \:vector
- \:while
- any?-ec
- append-ec
- dispatch-union
- do-ec
- every?-ec
- first-ec
- fold-ec
- fold3-ec
- last-ec
- list-ec
- make-initial-\:-dispatch
- max-ec
- min-ec
- product-ec
- string-append-ec
- string-ec
- sum-ec
- vector-ec
- vector-of-length-ec))
-
-(cond-expand-provide (current-module) '(srfi-42))
-
-(include-from-path "srfi/srfi-42/ec.scm")
-; <PLAINTEXT>
-; Eager Comprehensions in [outer..inner|expr]-Convention
-; ======================================================
-;
-; sebastian.egner@philips.com, Eindhoven, The Netherlands, 26-Dec-2007
-; Scheme R5RS (incl. macros), SRFI-23 (error).
-;
-; Loading the implementation into Scheme48 0.57:
-; ,open srfi-23
-; ,load ec.scm
-;
-; Loading the implementation into PLT/DrScheme 317:
-; ; File > Open ... "ec.scm", click Execute
-;
-; Loading the implementation into SCM 5d7:
-; (require 'macro) (require 'record)
-; (load "ec.scm")
-;
-; Implementation comments:
-; * All local (not exported) identifiers are named ec-<something>.
-; * This implementation focuses on portability, performance,
-; readability, and simplicity roughly in this order. Design
-; decisions related to performance are taken for Scheme48.
-; * Alternative implementations, Comments and Warnings are
-; mentioned after the definition with a heading.
-
-
-; ==========================================================================
-; The fundamental comprehension do-ec
-; ==========================================================================
-;
-; All eager comprehensions are reduced into do-ec and
-; all generators are reduced to :do.
-;
-; We use the following short names for syntactic variables
-; q - qualifier
-; cc - current continuation, thing to call at the end;
-; the CPS is (m (cc ...) arg ...) -> (cc ... expr ...)
-; cmd - an expression being evaluated for its side-effects
-; expr - an expression
-; gen - a generator of an eager comprehension
-; ob - outer binding
-; oc - outer command
-; lb - loop binding
-; ne1? - not-end1? (before the payload)
-; ib - inner binding
-; ic - inner command
-; ne2? - not-end2? (after the payload)
-; ls - loop step
-; etc - more arguments of mixed type
-
-
-; (do-ec q ... cmd)
-; handles nested, if/not/and/or, begin, :let, and calls generator
-; macros in CPS to transform them into fully decorated :do.
-; The code generation for a :do is delegated to do-ec:do.
-
-(define-syntax do-ec
- (syntax-rules (nested if not and or begin \:do let)
-
- ; explicit nesting -> implicit nesting
- ((do-ec (nested q ...) etc ...)
- (do-ec q ... etc ...) )
-
- ; implicit nesting -> fold do-ec
- ((do-ec q1 q2 etc1 etc ...)
- (do-ec q1 (do-ec q2 etc1 etc ...)) )
-
- ; no qualifiers at all -> evaluate cmd once
- ((do-ec cmd)
- (begin cmd (if #f #f)) )
-
-; now (do-ec q cmd) remains
-
- ; filter -> make conditional
- ((do-ec (if test) cmd)
- (if test (do-ec cmd)) )
- ((do-ec (not test) cmd)
- (if (not test) (do-ec cmd)) )
- ((do-ec (and test ...) cmd)
- (if (and test ...) (do-ec cmd)) )
- ((do-ec (or test ...) cmd)
- (if (or test ...) (do-ec cmd)) )
-
- ; begin -> make a sequence
- ((do-ec (begin etc ...) cmd)
- (begin etc ... (do-ec cmd)) )
-
- ; fully decorated :do-generator -> delegate to do-ec:do
- ((do-ec (#\:do olet lbs ne1? ilet ne2? lss) cmd)
- (do-ec:do cmd (#\:do olet lbs ne1? ilet ne2? lss)) )
-
-; anything else -> call generator-macro in CPS; reentry at (*)
-
- ((do-ec (g arg1 arg ...) cmd)
- (g (do-ec:do cmd) arg1 arg ...) )))
-
-
-; (do-ec:do cmd (#\:do olet lbs ne1? ilet ne2? lss))
-; generates code for a single fully decorated :do-generator
-; with cmd as payload, taking care of special cases.
-
-(define-syntax do-ec:do
- (syntax-rules (#\:do let)
-
- ; reentry point (*) -> generate code
- ((do-ec:do cmd
- (#\:do (let obs oc ...)
- lbs
- ne1?
- (let ibs ic ...)
- ne2?
- (ls ...) ))
- (ec-simplify
- (let obs
- oc ...
- (let loop lbs
- (ec-simplify
- (if ne1?
- (ec-simplify
- (let ibs
- ic ...
- cmd
- (ec-simplify
- (if ne2?
- (loop ls ...) )))))))))) ))
-
-
-; (ec-simplify <expression>)
-; generates potentially more efficient code for <expression>.
-; The macro handles if, (begin <command>*), and (let () <command>*)
-; and takes care of special cases.
-
-(define-syntax ec-simplify
- (syntax-rules (if not let begin)
-
-; one- and two-sided if
-
- ; literal <test>
- ((ec-simplify (if #t consequent))
- consequent )
- ((ec-simplify (if #f consequent))
- (if #f #f) )
- ((ec-simplify (if #t consequent alternate))
- consequent )
- ((ec-simplify (if #f consequent alternate))
- alternate )
-
- ; (not (not <test>))
- ((ec-simplify (if (not (not test)) consequent))
- (ec-simplify (if test consequent)) )
- ((ec-simplify (if (not (not test)) consequent alternate))
- (ec-simplify (if test consequent alternate)) )
-
-; (let () <command>*)
-
- ; empty <binding spec>*
- ((ec-simplify (let () command ...))
- (ec-simplify (begin command ...)) )
-
-; begin
-
- ; flatten use helper (ec-simplify 1 done to-do)
- ((ec-simplify (begin command ...))
- (ec-simplify 1 () (command ...)) )
- ((ec-simplify 1 done ((begin to-do1 ...) to-do2 ...))
- (ec-simplify 1 done (to-do1 ... to-do2 ...)) )
- ((ec-simplify 1 (done ...) (to-do1 to-do ...))
- (ec-simplify 1 (done ... to-do1) (to-do ...)) )
-
- ; exit helper
- ((ec-simplify 1 () ())
- (if #f #f) )
- ((ec-simplify 1 (command) ())
- command )
- ((ec-simplify 1 (command1 command ...) ())
- (begin command1 command ...) )
-
-; anything else
-
- ((ec-simplify expression)
- expression )))
-
-
-; ==========================================================================
-; The special generators :do, :let, :parallel, :while, and :until
-; ==========================================================================
-
-(define-syntax \:do
- (syntax-rules ()
-
- ; full decorated -> continue with cc, reentry at (*)
- ((#\:do (cc ...) olet lbs ne1? ilet ne2? lss)
- (cc ... (#\:do olet lbs ne1? ilet ne2? lss)) )
-
- ; short form -> fill in default values
- ((#\:do cc lbs ne1? lss)
- (#\:do cc (let ()) lbs ne1? (let ()) #t lss) )))
-
-
-(define-syntax \:let
- (syntax-rules (index)
- ((\:let cc var (index i) expression)
- (#\:do cc (let ((var expression) (i 0))) () #t (let ()) #f ()) )
- ((\:let cc var expression)
- (#\:do cc (let ((var expression))) () #t (let ()) #f ()) )))
-
-
-(define-syntax \:parallel
- (syntax-rules (#\:do)
- ((\:parallel cc)
- cc )
- ((\:parallel cc (g arg1 arg ...) gen ...)
- (g (\:parallel-1 cc (gen ...)) arg1 arg ...) )))
-
-; (\:parallel-1 cc (to-do ...) result [ next ] )
-; iterates over to-do by converting the first generator into
-; the :do-generator next and merging next into result.
-
-(define-syntax \:parallel-1 ; used as
- (syntax-rules (#\:do let)
-
- ; process next element of to-do, reentry at (**)
- ((\:parallel-1 cc ((g arg1 arg ...) gen ...) result)
- (g (\:parallel-1 cc (gen ...) result) arg1 arg ...) )
-
- ; reentry point (**) -> merge next into result
- ((\:parallel-1
- cc
- gens
- (#\:do (let (ob1 ...) oc1 ...)
- (lb1 ...)
- ne1?1
- (let (ib1 ...) ic1 ...)
- ne2?1
- (ls1 ...) )
- (#\:do (let (ob2 ...) oc2 ...)
- (lb2 ...)
- ne1?2
- (let (ib2 ...) ic2 ...)
- ne2?2
- (ls2 ...) ))
- (\:parallel-1
- cc
- gens
- (#\:do (let (ob1 ... ob2 ...) oc1 ... oc2 ...)
- (lb1 ... lb2 ...)
- (and ne1?1 ne1?2)
- (let (ib1 ... ib2 ...) ic1 ... ic2 ...)
- (and ne2?1 ne2?2)
- (ls1 ... ls2 ...) )))
-
- ; no more gens -> continue with cc, reentry at (*)
- ((\:parallel-1 (cc ...) () result)
- (cc ... result) )))
-
-(define-syntax \:while
- (syntax-rules ()
- ((\:while cc (g arg1 arg ...) test)
- (g (\:while-1 cc test) arg1 arg ...) )))
-
-; (\:while-1 cc test (#\:do ...))
-; modifies the fully decorated :do-generator such that it
-; runs while test is a true value.
-; The original implementation just replaced ne1? by
-; (and ne1? test) as follows:
-;
-; (define-syntax \:while-1
-; (syntax-rules (#\:do)
-; ((\:while-1 cc test (#\:do olet lbs ne1? ilet ne2? lss))
-; (#\:do cc olet lbs (and ne1? test) ilet ne2? lss) )))
-;
-; Bug #1:
-; Unfortunately, this code is wrong because ne1? may depend
-; in the inner bindings introduced in ilet, but ne1? is evaluated
-; outside of the inner bindings. (Refer to the specification of
-; :do to see the structure.)
-; The problem manifests itself (as sunnan@handgranat.org
-; observed, 25-Apr-2005) when the :list-generator is modified:
-;
-; (do-ec (\:while (\:list x '(1 2)) (= x 1)) (display x)).
-;
-; In order to generate proper code, we introduce temporary
-; variables saving the values of the inner bindings. The inner
-; bindings are executed in a new ne1?, which also evaluates ne1?
-; outside the scope of the inner bindings, then the inner commands
-; are executed (possibly changing the variables), and then the
-; values of the inner bindings are saved and (and ne1? test) is
-; returned. In the new ilet, the inner variables are bound and
-; initialized and their values are restored. So we construct:
-;
-; (let (ob .. (ib-tmp #f) ...)
-; oc ...
-; (let loop (lb ...)
-; (if (let (ne1?-value ne1?)
-; (let ((ib-var ib-rhs) ...)
-; ic ...
-; (set! ib-tmp ib-var) ...)
-; (and ne1?-value test))
-; (let ((ib-var ib-tmp) ...)
-; /payload/
-; (if ne2?
-; (loop ls ...) )))))
-;
-; Bug #2:
-; Unfortunately, the above expansion is still incorrect (as Jens-Axel
-; Soegaard pointed out, 4-Jun-2007) because ib-rhs are evaluated even
-; if ne1?-value is #f, indicating that the loop has ended.
-; The problem manifests itself in the following example:
-;
-; (do-ec (\:while (\:list x '(1)) #t) (display x))
-;
-; Which iterates :list beyond exhausting the list '(1).
-;
-; For the fix, we follow Jens-Axel's approach of guarding the evaluation
-; of ib-rhs with a check on ne1?-value.
-
-(define-syntax \:while-1
- (syntax-rules (#\:do let)
- ((\:while-1 cc test (#\:do olet lbs ne1? ilet ne2? lss))
- (\:while-2 cc test () () () (#\:do olet lbs ne1? ilet ne2? lss)))))
-
-(define-syntax \:while-2
- (syntax-rules (#\:do let)
- ((\:while-2 cc
- test
- (ib-let ...)
- (ib-save ...)
- (ib-restore ...)
- (#\:do olet
- lbs
- ne1?
- (let ((ib-var ib-rhs) ib ...) ic ...)
- ne2?
- lss))
- (\:while-2 cc
- test
- (ib-let ... (ib-tmp #f))
- (ib-save ... (ib-var ib-rhs))
- (ib-restore ... (ib-var ib-tmp))
- (#\:do olet
- lbs
- ne1?
- (let (ib ...) ic ... (set! ib-tmp ib-var))
- ne2?
- lss)))
- ((\:while-2 cc
- test
- (ib-let ...)
- (ib-save ...)
- (ib-restore ...)
- (#\:do (let (ob ...) oc ...) lbs ne1? (let () ic ...) ne2? lss))
- (#\:do cc
- (let (ob ... ib-let ...) oc ...)
- lbs
- (let ((ne1?-value ne1?))
- (and ne1?-value
- (let (ib-save ...)
- ic ...
- test)))
- (let (ib-restore ...))
- ne2?
- lss))))
-
-
-(define-syntax \:until
- (syntax-rules ()
- ((\:until cc (g arg1 arg ...) test)
- (g (\:until-1 cc test) arg1 arg ...) )))
-
-(define-syntax \:until-1
- (syntax-rules (#\:do)
- ((\:until-1 cc test (#\:do olet lbs ne1? ilet ne2? lss))
- (#\:do cc olet lbs ne1? ilet (and ne2? (not test)) lss) )))
-
-
-; ==========================================================================
-; The typed generators :list :string :vector etc.
-; ==========================================================================
-
-(define-syntax \:list
- (syntax-rules (index)
- ((\:list cc var (index i) arg ...)
- (\:parallel cc (\:list var arg ...) (\:integers i)) )
- ((\:list cc var arg1 arg2 arg ...)
- (\:list cc var (append arg1 arg2 arg ...)) )
- ((\:list cc var arg)
- (#\:do cc
- (let ())
- ((t arg))
- (not (null? t))
- (let ((var (car t))))
- #t
- ((cdr t)) ))))
-
-
-(define-syntax \:string
- (syntax-rules (index)
- ((\:string cc var (index i) arg)
- (#\:do cc
- (let ((str arg) (len 0))
- (set! len (string-length str)))
- ((i 0))
- (< i len)
- (let ((var (string-ref str i))))
- #t
- ((+ i 1)) ))
- ((\:string cc var (index i) arg1 arg2 arg ...)
- (\:string cc var (index i) (string-append arg1 arg2 arg ...)) )
- ((\:string cc var arg1 arg ...)
- (\:string cc var (index i) arg1 arg ...) )))
-
-; Alternative: An implementation in the style of :vector can also
-; be used for :string. However, it is less interesting as the
-; overhead of string-append is much less than for 'vector-append'.
-
-
-(define-syntax \:vector
- (syntax-rules (index)
- ((\:vector cc var arg)
- (\:vector cc var (index i) arg) )
- ((\:vector cc var (index i) arg)
- (#\:do cc
- (let ((vec arg) (len 0))
- (set! len (vector-length vec)))
- ((i 0))
- (< i len)
- (let ((var (vector-ref vec i))))
- #t
- ((+ i 1)) ))
-
- ((\:vector cc var (index i) arg1 arg2 arg ...)
- (\:parallel cc (\:vector cc var arg1 arg2 arg ...) (\:integers i)) )
- ((\:vector cc var arg1 arg2 arg ...)
- (#\:do cc
- (let ((vec #f)
- (len 0)
- (vecs (ec-:vector-filter (list arg1 arg2 arg ...))) ))
- ((k 0))
- (if (< k len)
- #t
- (if (null? vecs)
- #f
- (begin (set! vec (car vecs))
- (set! vecs (cdr vecs))
- (set! len (vector-length vec))
- (set! k 0)
- #t )))
- (let ((var (vector-ref vec k))))
- #t
- ((+ k 1)) ))))
-
-(define (ec-:vector-filter vecs)
- (if (null? vecs)
- '()
- (if (zero? (vector-length (car vecs)))
- (ec-:vector-filter (cdr vecs))
- (cons (car vecs) (ec-:vector-filter (cdr vecs))) )))
-
-; Alternative: A simpler implementation for :vector uses vector->list
-; append and :list in the multi-argument case. Please refer to the
-; 'design.scm' for more details.
-
-
-(define-syntax \:integers
- (syntax-rules (index)
- ((\:integers cc var (index i))
- (#\:do cc ((var 0) (i 0)) #t ((+ var 1) (+ i 1))) )
- ((\:integers cc var)
- (#\:do cc ((var 0)) #t ((+ var 1))) )))
-
-
-(define-syntax \:range
- (syntax-rules (index)
-
- ; handle index variable and add optional args
- ((\:range cc var (index i) arg1 arg ...)
- (\:parallel cc (\:range var arg1 arg ...) (\:integers i)) )
- ((\:range cc var arg1)
- (\:range cc var 0 arg1 1) )
- ((\:range cc var arg1 arg2)
- (\:range cc var arg1 arg2 1) )
-
-; special cases (partially evaluated by hand from general case)
-
- ((\:range cc var 0 arg2 1)
- (#\:do cc
- (let ((b arg2))
- (if (not (and (integer? b) (exact? b)))
- (error
- "arguments of :range are not exact integer "
- "(use :real-range?)" 0 b 1 )))
- ((var 0))
- (< var b)
- (let ())
- #t
- ((+ var 1)) ))
-
- ((\:range cc var 0 arg2 -1)
- (#\:do cc
- (let ((b arg2))
- (if (not (and (integer? b) (exact? b)))
- (error
- "arguments of :range are not exact integer "
- "(use :real-range?)" 0 b 1 )))
- ((var 0))
- (> var b)
- (let ())
- #t
- ((- var 1)) ))
-
- ((\:range cc var arg1 arg2 1)
- (#\:do cc
- (let ((a arg1) (b arg2))
- (if (not (and (integer? a) (exact? a)
- (integer? b) (exact? b) ))
- (error
- "arguments of :range are not exact integer "
- "(use :real-range?)" a b 1 )) )
- ((var a))
- (< var b)
- (let ())
- #t
- ((+ var 1)) ))
-
- ((\:range cc var arg1 arg2 -1)
- (#\:do cc
- (let ((a arg1) (b arg2) (s -1) (stop 0))
- (if (not (and (integer? a) (exact? a)
- (integer? b) (exact? b) ))
- (error
- "arguments of :range are not exact integer "
- "(use :real-range?)" a b -1 )) )
- ((var a))
- (> var b)
- (let ())
- #t
- ((- var 1)) ))
-
-; the general case
-
- ((\:range cc var arg1 arg2 arg3)
- (#\:do cc
- (let ((a arg1) (b arg2) (s arg3) (stop 0))
- (if (not (and (integer? a) (exact? a)
- (integer? b) (exact? b)
- (integer? s) (exact? s) ))
- (error
- "arguments of :range are not exact integer "
- "(use :real-range?)" a b s ))
- (if (zero? s)
- (error "step size must not be zero in :range") )
- (set! stop (+ a (* (max 0 (ceiling (/ (- b a) s))) s))) )
- ((var a))
- (not (= var stop))
- (let ())
- #t
- ((+ var s)) ))))
-
-; Comment: The macro :range inserts some code to make sure the values
-; are exact integers. This overhead has proven very helpful for
-; saving users from themselves.
-
-
-(define-syntax \:real-range
- (syntax-rules (index)
-
- ; add optional args and index variable
- ((\:real-range cc var arg1)
- (\:real-range cc var (index i) 0 arg1 1) )
- ((\:real-range cc var (index i) arg1)
- (\:real-range cc var (index i) 0 arg1 1) )
- ((\:real-range cc var arg1 arg2)
- (\:real-range cc var (index i) arg1 arg2 1) )
- ((\:real-range cc var (index i) arg1 arg2)
- (\:real-range cc var (index i) arg1 arg2 1) )
- ((\:real-range cc var arg1 arg2 arg3)
- (\:real-range cc var (index i) arg1 arg2 arg3) )
-
- ; the fully qualified case
- ((\:real-range cc var (index i) arg1 arg2 arg3)
- (#\:do cc
- (let ((a arg1) (b arg2) (s arg3) (istop 0))
- (if (not (and (real? a) (real? b) (real? s)))
- (error "arguments of :real-range are not real" a b s) )
- (if (and (exact? a) (or (not (exact? b)) (not (exact? s))))
- (set! a (exact->inexact a)) )
- (set! istop (/ (- b a) s)) )
- ((i 0))
- (< i istop)
- (let ((var (+ a (* s i)))))
- #t
- ((+ i 1)) ))))
-
-; Comment: The macro :real-range adapts the exactness of the start
-; value in case any of the other values is inexact. This is a
-; precaution to avoid (list-ec (\: x 0 3.0) x) => '(0 1.0 2.0).
-
-
-(define-syntax \:char-range
- (syntax-rules (index)
- ((\:char-range cc var (index i) arg1 arg2)
- (\:parallel cc (\:char-range var arg1 arg2) (\:integers i)) )
- ((\:char-range cc var arg1 arg2)
- (#\:do cc
- (let ((imax (char->integer arg2))))
- ((i (char->integer arg1)))
- (<= i imax)
- (let ((var (integer->char i))))
- #t
- ((+ i 1)) ))))
-
-; Warning: There is no R5RS-way to implement the :char-range generator
-; because the integers obtained by char->integer are not necessarily
-; consecutive. We simply assume this anyhow for illustration.
-
-
-(define-syntax \:port
- (syntax-rules (index)
- ((\:port cc var (index i) arg1 arg ...)
- (\:parallel cc (\:port var arg1 arg ...) (\:integers i)) )
- ((\:port cc var arg)
- (\:port cc var arg read) )
- ((\:port cc var arg1 arg2)
- (#\:do cc
- (let ((port arg1) (read-proc arg2)))
- ((var (read-proc port)))
- (not (eof-object? var))
- (let ())
- #t
- ((read-proc port)) ))))
-
-
-; ==========================================================================
-; The typed generator :dispatched and utilities for constructing dispatchers
-; ==========================================================================
-
-(define-syntax \:dispatched
- (syntax-rules (index)
- ((\:dispatched cc var (index i) dispatch arg1 arg ...)
- (\:parallel cc
- (\:integers i)
- (\:dispatched var dispatch arg1 arg ...) ))
- ((\:dispatched cc var dispatch arg1 arg ...)
- (#\:do cc
- (let ((d dispatch)
- (args (list arg1 arg ...))
- (g #f)
- (empty (list #f)) )
- (set! g (d args))
- (if (not (procedure? g))
- (error "unrecognized arguments in dispatching"
- args
- (d '()) )))
- ((var (g empty)))
- (not (eq? var empty))
- (let ())
- #t
- ((g empty)) ))))
-
-; Comment: The unique object empty is created as a newly allocated
-; non-empty list. It is compared using eq? which distinguishes
-; the object from any other object, according to R5RS 6.1.
-
-
-(define-syntax \:generator-proc
- (syntax-rules (#\:do let)
-
- ; call g with a variable, reentry at (**)
- ((\:generator-proc (g arg ...))
- (g (\:generator-proc var) var arg ...) )
-
- ; reentry point (**) -> make the code from a single :do
- ((\:generator-proc
- var
- (#\:do (let obs oc ...)
- ((lv li) ...)
- ne1?
- (let ((i v) ...) ic ...)
- ne2?
- (ls ...)) )
- (ec-simplify
- (let obs
- oc ...
- (let ((lv li) ... (ne2 #t))
- (ec-simplify
- (let ((i #f) ...) ; v not yet valid
- (lambda (empty)
- (if (and ne1? ne2)
- (ec-simplify
- (begin
- (set! i v) ...
- ic ...
- (let ((value var))
- (ec-simplify
- (if ne2?
- (ec-simplify
- (begin (set! lv ls) ...) )
- (set! ne2 #f) ))
- value )))
- empty ))))))))
-
- ; silence warnings of some macro expanders
- ((\:generator-proc var)
- (error "illegal macro call") )))
-
-
-(define (dispatch-union d1 d2)
- (lambda (args)
- (let ((g1 (d1 args)) (g2 (d2 args)))
- (if g1
- (if g2
- (if (null? args)
- (append (if (list? g1) g1 (list g1))
- (if (list? g2) g2 (list g2)) )
- (error "dispatching conflict" args (d1 '()) (d2 '())) )
- g1 )
- (if g2 g2 #f) ))))
-
-
-; ==========================================================================
-; The dispatching generator :
-; ==========================================================================
-
-(define (make-initial-\:-dispatch)
- (lambda (args)
- (case (length args)
- ((0) 'SRFI42)
- ((1) (let ((a1 (car args)))
- (cond
- ((list? a1)
- (\:generator-proc (\:list a1)) )
- ((string? a1)
- (\:generator-proc (\:string a1)) )
- ((vector? a1)
- (\:generator-proc (\:vector a1)) )
- ((and (integer? a1) (exact? a1))
- (\:generator-proc (\:range a1)) )
- ((real? a1)
- (\:generator-proc (\:real-range a1)) )
- ((input-port? a1)
- (\:generator-proc (\:port a1)) )
- (else
- #f ))))
- ((2) (let ((a1 (car args)) (a2 (cadr args)))
- (cond
- ((and (list? a1) (list? a2))
- (\:generator-proc (\:list a1 a2)) )
- ((and (string? a1) (string? a1))
- (\:generator-proc (\:string a1 a2)) )
- ((and (vector? a1) (vector? a2))
- (\:generator-proc (\:vector a1 a2)) )
- ((and (integer? a1) (exact? a1) (integer? a2) (exact? a2))
- (\:generator-proc (\:range a1 a2)) )
- ((and (real? a1) (real? a2))
- (\:generator-proc (\:real-range a1 a2)) )
- ((and (char? a1) (char? a2))
- (\:generator-proc (\:char-range a1 a2)) )
- ((and (input-port? a1) (procedure? a2))
- (\:generator-proc (\:port a1 a2)) )
- (else
- #f ))))
- ((3) (let ((a1 (car args)) (a2 (cadr args)) (a3 (caddr args)))
- (cond
- ((and (list? a1) (list? a2) (list? a3))
- (\:generator-proc (\:list a1 a2 a3)) )
- ((and (string? a1) (string? a1) (string? a3))
- (\:generator-proc (\:string a1 a2 a3)) )
- ((and (vector? a1) (vector? a2) (vector? a3))
- (\:generator-proc (\:vector a1 a2 a3)) )
- ((and (integer? a1) (exact? a1)
- (integer? a2) (exact? a2)
- (integer? a3) (exact? a3))
- (\:generator-proc (\:range a1 a2 a3)) )
- ((and (real? a1) (real? a2) (real? a3))
- (\:generator-proc (\:real-range a1 a2 a3)) )
- (else
- #f ))))
- (else
- (letrec ((every?
- (lambda (pred args)
- (if (null? args)
- #t
- (and (pred (car args))
- (every? pred (cdr args)) )))))
- (cond
- ((every? list? args)
- (\:generator-proc (\:list (apply append args))) )
- ((every? string? args)
- (\:generator-proc (\:string (apply string-append args))) )
- ((every? vector? args)
- (\:generator-proc (\:list (apply append (map vector->list args)))) )
- (else
- #f )))))))
-
-(define \\:-dispatch
- (make-initial-\:-dispatch) )
-
-(define (\\:-dispatch-ref)
- \:-dispatch )
-
-(define (\\:-dispatch-set! dispatch)
- (if (not (procedure? dispatch))
- (error "not a procedure" dispatch) )
- (set! \:-dispatch dispatch) )
-
-(define-syntax \:
- (syntax-rules (index)
- ((\: cc var (index i) arg1 arg ...)
- (\:dispatched cc var (index i) \:-dispatch arg1 arg ...) )
- ((\: cc var arg1 arg ...)
- (\:dispatched cc var \:-dispatch arg1 arg ...) )))
-
-
-; ==========================================================================
-; The utility comprehensions fold-ec, fold3-ec
-; ==========================================================================
-
-(define-syntax fold3-ec
- (syntax-rules (nested)
- ((fold3-ec x0 (nested q1 ...) q etc1 etc2 etc3 etc ...)
- (fold3-ec x0 (nested q1 ... q) etc1 etc2 etc3 etc ...) )
- ((fold3-ec x0 q1 q2 etc1 etc2 etc3 etc ...)
- (fold3-ec x0 (nested q1 q2) etc1 etc2 etc3 etc ...) )
- ((fold3-ec x0 expression f1 f2)
- (fold3-ec x0 (nested) expression f1 f2) )
-
- ((fold3-ec x0 qualifier expression f1 f2)
- (let ((result #f) (empty #t))
- (do-ec qualifier
- (let ((value expression)) ; don't duplicate
- (if empty
- (begin (set! result (f1 value))
- (set! empty #f) )
- (set! result (f2 value result)) )))
- (if empty x0 result) ))))
-
-
-(define-syntax fold-ec
- (syntax-rules (nested)
- ((fold-ec x0 (nested q1 ...) q etc1 etc2 etc ...)
- (fold-ec x0 (nested q1 ... q) etc1 etc2 etc ...) )
- ((fold-ec x0 q1 q2 etc1 etc2 etc ...)
- (fold-ec x0 (nested q1 q2) etc1 etc2 etc ...) )
- ((fold-ec x0 expression f2)
- (fold-ec x0 (nested) expression f2) )
-
- ((fold-ec x0 qualifier expression f2)
- (let ((result x0))
- (do-ec qualifier (set! result (f2 expression result)))
- result ))))
-
-
-; ==========================================================================
-; The comprehensions list-ec string-ec vector-ec etc.
-; ==========================================================================
-
-(define-syntax list-ec
- (syntax-rules ()
- ((list-ec etc1 etc ...)
- (reverse (fold-ec '() etc1 etc ... cons)) )))
-
-; Alternative: Reverse can safely be replaced by reverse! if you have it.
-;
-; Alternative: It is possible to construct the result in the correct order
-; using set-cdr! to add at the tail. This removes the overhead of copying
-; at the end, at the cost of more book-keeping.
-
-
-(define-syntax append-ec
- (syntax-rules ()
- ((append-ec etc1 etc ...)
- (apply append (list-ec etc1 etc ...)) )))
-
-(define-syntax string-ec
- (syntax-rules ()
- ((string-ec etc1 etc ...)
- (list->string (list-ec etc1 etc ...)) )))
-
-; Alternative: For very long strings, the intermediate list may be a
-; problem. A more space-aware implementation collect the characters
-; in an intermediate list and when this list becomes too large it is
-; converted into an intermediate string. At the end, the intermediate
-; strings are concatenated with string-append.
-
-
-(define-syntax string-append-ec
- (syntax-rules ()
- ((string-append-ec etc1 etc ...)
- (apply string-append (list-ec etc1 etc ...)) )))
-
-(define-syntax vector-ec
- (syntax-rules ()
- ((vector-ec etc1 etc ...)
- (list->vector (list-ec etc1 etc ...)) )))
-
-; Comment: A similar approach as for string-ec can be used for vector-ec.
-; However, the space overhead for the intermediate list is much lower
-; than for string-ec and as there is no vector-append, the intermediate
-; vectors must be copied explicitly.
-
-(define-syntax vector-of-length-ec
- (syntax-rules (nested)
- ((vector-of-length-ec k (nested q1 ...) q etc1 etc ...)
- (vector-of-length-ec k (nested q1 ... q) etc1 etc ...) )
- ((vector-of-length-ec k q1 q2 etc1 etc ...)
- (vector-of-length-ec k (nested q1 q2) etc1 etc ...) )
- ((vector-of-length-ec k expression)
- (vector-of-length-ec k (nested) expression) )
-
- ((vector-of-length-ec k qualifier expression)
- (let ((len k))
- (let ((vec (make-vector len))
- (i 0) )
- (do-ec qualifier
- (if (< i len)
- (begin (vector-set! vec i expression)
- (set! i (+ i 1)) )
- (error "vector is too short for the comprehension") ))
- (if (= i len)
- vec
- (error "vector is too long for the comprehension") ))))))
-
-
-(define-syntax sum-ec
- (syntax-rules ()
- ((sum-ec etc1 etc ...)
- (fold-ec (+) etc1 etc ... +) )))
-
-(define-syntax product-ec
- (syntax-rules ()
- ((product-ec etc1 etc ...)
- (fold-ec (*) etc1 etc ... *) )))
-
-(define-syntax min-ec
- (syntax-rules ()
- ((min-ec etc1 etc ...)
- (fold3-ec (min) etc1 etc ... min min) )))
-
-(define-syntax max-ec
- (syntax-rules ()
- ((max-ec etc1 etc ...)
- (fold3-ec (max) etc1 etc ... max max) )))
-
-(define-syntax last-ec
- (syntax-rules (nested)
- ((last-ec default (nested q1 ...) q etc1 etc ...)
- (last-ec default (nested q1 ... q) etc1 etc ...) )
- ((last-ec default q1 q2 etc1 etc ...)
- (last-ec default (nested q1 q2) etc1 etc ...) )
- ((last-ec default expression)
- (last-ec default (nested) expression) )
-
- ((last-ec default qualifier expression)
- (let ((result default))
- (do-ec qualifier (set! result expression))
- result ))))
-
-
-; ==========================================================================
-; The fundamental early-stopping comprehension first-ec
-; ==========================================================================
-
-(define-syntax first-ec
- (syntax-rules (nested)
- ((first-ec default (nested q1 ...) q etc1 etc ...)
- (first-ec default (nested q1 ... q) etc1 etc ...) )
- ((first-ec default q1 q2 etc1 etc ...)
- (first-ec default (nested q1 q2) etc1 etc ...) )
- ((first-ec default expression)
- (first-ec default (nested) expression) )
-
- ((first-ec default qualifier expression)
- (let ((result default) (stop #f))
- (ec-guarded-do-ec
- stop
- (nested qualifier)
- (begin (set! result expression)
- (set! stop #t) ))
- result ))))
-
-; (ec-guarded-do-ec stop (nested q ...) cmd)
-; constructs (do-ec q ... cmd) where the generators gen in q ... are
-; replaced by (\:until gen stop).
-
-(define-syntax ec-guarded-do-ec
- (syntax-rules (nested if not and or begin)
-
- ((ec-guarded-do-ec stop (nested (nested q1 ...) q2 ...) cmd)
- (ec-guarded-do-ec stop (nested q1 ... q2 ...) cmd) )
-
- ((ec-guarded-do-ec stop (nested (if test) q ...) cmd)
- (if test (ec-guarded-do-ec stop (nested q ...) cmd)) )
- ((ec-guarded-do-ec stop (nested (not test) q ...) cmd)
- (if (not test) (ec-guarded-do-ec stop (nested q ...) cmd)) )
- ((ec-guarded-do-ec stop (nested (and test ...) q ...) cmd)
- (if (and test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) )
- ((ec-guarded-do-ec stop (nested (or test ...) q ...) cmd)
- (if (or test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) )
-
- ((ec-guarded-do-ec stop (nested (begin etc ...) q ...) cmd)
- (begin etc ... (ec-guarded-do-ec stop (nested q ...) cmd)) )
-
- ((ec-guarded-do-ec stop (nested gen q ...) cmd)
- (do-ec
- (\:until gen stop)
- (ec-guarded-do-ec stop (nested q ...) cmd) ))
-
- ((ec-guarded-do-ec stop (nested) cmd)
- (do-ec cmd) )))
-
-; Alternative: Instead of modifying the generator with :until, it is
-; possible to use call-with-current-continuation:
-;
-; (define-synatx first-ec
-; ...same as above...
-; ((first-ec default qualifier expression)
-; (call-with-current-continuation
-; (lambda (cc)
-; (do-ec qualifier (cc expression))
-; default ))) ))
-;
-; This is much simpler but not necessarily as efficient.
-
-
-; ==========================================================================
-; The early-stopping comprehensions any?-ec every?-ec
-; ==========================================================================
-
-(define-syntax any?-ec
- (syntax-rules (nested)
- ((any?-ec (nested q1 ...) q etc1 etc ...)
- (any?-ec (nested q1 ... q) etc1 etc ...) )
- ((any?-ec q1 q2 etc1 etc ...)
- (any?-ec (nested q1 q2) etc1 etc ...) )
- ((any?-ec expression)
- (any?-ec (nested) expression) )
-
- ((any?-ec qualifier expression)
- (first-ec #f qualifier (if expression) #t) )))
-
-(define-syntax every?-ec
- (syntax-rules (nested)
- ((every?-ec (nested q1 ...) q etc1 etc ...)
- (every?-ec (nested q1 ... q) etc1 etc ...) )
- ((every?-ec q1 q2 etc1 etc ...)
- (every?-ec (nested q1 q2) etc1 etc ...) )
- ((every?-ec expression)
- (every?-ec (nested) expression) )
-
- ((every?-ec qualifier expression)
- (first-ec #t qualifier (if (not expression)) #f) )))
-
-;;; srfi-43.scm -- SRFI 43 Vector library
-
-;; Copyright (C) 2014 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Mark H Weaver <mhw@netris.org>
-
-(define-module (srfi srfi-43)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-8)
- #\re-export (make-vector vector vector? vector-ref vector-set!
- vector-length)
- #\replace (vector-copy vector-fill! list->vector vector->list)
- #\export (vector-empty? vector= vector-unfold vector-unfold-right
- vector-reverse-copy
- vector-append vector-concatenate
- vector-fold vector-fold-right
- vector-map vector-map!
- vector-for-each vector-count
- vector-index vector-index-right
- vector-skip vector-skip-right
- vector-binary-search
- vector-any vector-every
- vector-swap! vector-reverse!
- vector-copy! vector-reverse-copy!
- reverse-vector->list
- reverse-list->vector))
-
-(cond-expand-provide (current-module) '(srfi-43))
-
-(define (error-from who msg . args)
- (apply error
- (string-append (symbol->string who) ": " msg)
- args))
-
-(define-syntax-rule (assert-nonneg-exact-integer k who)
- (unless (and (exact-integer? k)
- (not (negative? k)))
- (error-from who "expected non-negative exact integer, got" k)))
-
-(define-syntax-rule (assert-procedure f who)
- (unless (procedure? f)
- (error-from who "expected procedure, got" f)))
-
-(define-syntax-rule (assert-vector v who)
- (unless (vector? v)
- (error-from who "expected vector, got" v)))
-
-(define-syntax-rule (assert-valid-index i len who)
- (unless (and (exact-integer? i)
- (<= 0 i len))
- (error-from who "invalid index" i)))
-
-(define-syntax-rule (assert-valid-start start len who)
- (unless (and (exact-integer? start)
- (<= 0 start len))
- (error-from who "invalid start index" start)))
-
-(define-syntax-rule (assert-valid-range start end len who)
- (unless (and (exact-integer? start)
- (exact-integer? end)
- (<= 0 start end len))
- (error-from who "invalid index range" start end)))
-
-(define-syntax-rule (assert-vectors vs who)
- (let loop ((vs vs))
- (unless (null? vs)
- (assert-vector (car vs) who)
- (loop (cdr vs)))))
-
-;; Return the length of the shortest vector in VS.
-;; VS must have at least one element.
-(define (min-length vs)
- (let loop ((vs (cdr vs))
- (result (vector-length (car vs))))
- (if (null? vs)
- result
- (loop (cdr vs) (min result (vector-length (car vs)))))))
-
-;; Return a list of the Ith elements of the vectors in VS.
-(define (vectors-ref vs i)
- (let loop ((vs vs) (xs '()))
- (if (null? vs)
- (reverse! xs)
- (loop (cdr vs) (cons (vector-ref (car vs) i)
- xs)))))
-
-(define vector-unfold
- (case-lambda
- "(vector-unfold f length initial-seed ...) -> vector
-
-The fundamental vector constructor. Create a vector whose length is
-LENGTH and iterates across each index k from 0 up to LENGTH - 1,
-applying F at each iteration to the current index and current seeds, in
-that order, to receive n + 1 values: the element to put in the kth slot
-of the new vector, and n new seeds for the next iteration. It is an
-error for the number of seeds to vary between iterations."
- ((f len)
- (assert-procedure f 'vector-unfold)
- (assert-nonneg-exact-integer len 'vector-unfold)
- (let ((v (make-vector len)))
- (let loop ((i 0))
- (unless (= i len)
- (vector-set! v i (f i))
- (loop (+ i 1))))
- v))
- ((f len seed)
- (assert-procedure f 'vector-unfold)
- (assert-nonneg-exact-integer len 'vector-unfold)
- (let ((v (make-vector len)))
- (let loop ((i 0) (seed seed))
- (unless (= i len)
- (receive (x seed) (f i seed)
- (vector-set! v i x)
- (loop (+ i 1) seed))))
- v))
- ((f len seed1 seed2)
- (assert-procedure f 'vector-unfold)
- (assert-nonneg-exact-integer len 'vector-unfold)
- (let ((v (make-vector len)))
- (let loop ((i 0) (seed1 seed1) (seed2 seed2))
- (unless (= i len)
- (receive (x seed1 seed2) (f i seed1 seed2)
- (vector-set! v i x)
- (loop (+ i 1) seed1 seed2))))
- v))
- ((f len . seeds)
- (assert-procedure f 'vector-unfold)
- (assert-nonneg-exact-integer len 'vector-unfold)
- (let ((v (make-vector len)))
- (let loop ((i 0) (seeds seeds))
- (unless (= i len)
- (receive (x . seeds) (apply f i seeds)
- (vector-set! v i x)
- (loop (+ i 1) seeds))))
- v))))
-
-(define vector-unfold-right
- (case-lambda
- "(vector-unfold-right f length initial-seed ...) -> vector
-
-The fundamental vector constructor. Create a vector whose length is
-LENGTH and iterates across each index k from LENGTH - 1 down to 0,
-applying F at each iteration to the current index and current seeds, in
-that order, to receive n + 1 values: the element to put in the kth slot
-of the new vector, and n new seeds for the next iteration. It is an
-error for the number of seeds to vary between iterations."
- ((f len)
- (assert-procedure f 'vector-unfold-right)
- (assert-nonneg-exact-integer len 'vector-unfold-right)
- (let ((v (make-vector len)))
- (let loop ((i (- len 1)))
- (unless (negative? i)
- (vector-set! v i (f i))
- (loop (- i 1))))
- v))
- ((f len seed)
- (assert-procedure f 'vector-unfold-right)
- (assert-nonneg-exact-integer len 'vector-unfold-right)
- (let ((v (make-vector len)))
- (let loop ((i (- len 1)) (seed seed))
- (unless (negative? i)
- (receive (x seed) (f i seed)
- (vector-set! v i x)
- (loop (- i 1) seed))))
- v))
- ((f len seed1 seed2)
- (assert-procedure f 'vector-unfold-right)
- (assert-nonneg-exact-integer len 'vector-unfold-right)
- (let ((v (make-vector len)))
- (let loop ((i (- len 1)) (seed1 seed1) (seed2 seed2))
- (unless (negative? i)
- (receive (x seed1 seed2) (f i seed1 seed2)
- (vector-set! v i x)
- (loop (- i 1) seed1 seed2))))
- v))
- ((f len . seeds)
- (assert-procedure f 'vector-unfold-right)
- (assert-nonneg-exact-integer len 'vector-unfold-right)
- (let ((v (make-vector len)))
- (let loop ((i (- len 1)) (seeds seeds))
- (unless (negative? i)
- (receive (x . seeds) (apply f i seeds)
- (vector-set! v i x)
- (loop (- i 1) seeds))))
- v))))
-
-(define guile-vector-copy (@ (guile) vector-copy))
-
-;; TODO: Enhance Guile core 'vector-copy' to do this.
-(define vector-copy
- (case-lambda*
- "(vector-copy vec [start [end [fill]]]) -> vector
-
-Allocate a new vector whose length is END - START and fills it with
-elements from vec, taking elements from vec starting at index START
-and stopping at index END. START defaults to 0 and END defaults to
-the value of (vector-length VEC). If END extends beyond the length of
-VEC, the slots in the new vector that obviously cannot be filled by
-elements from VEC are filled with FILL, whose default value is
-unspecified."
- ((v) (guile-vector-copy v))
- ((v start)
- (assert-vector v 'vector-copy)
- (let ((len (vector-length v)))
- (assert-valid-start start len 'vector-copy)
- (let ((result (make-vector (- len start))))
- (vector-move-left! v start len result 0)
- result)))
- ((v start end #\optional (fill *unspecified*))
- (assert-vector v 'vector-copy)
- (let ((len (vector-length v)))
- (unless (and (exact-integer? start)
- (exact-integer? end)
- (<= 0 start end))
- (error-from 'vector-copy "invalid index range" start end))
- (let ((result (make-vector (- end start) fill)))
- (vector-move-left! v start (min end len) result 0)
- result)))))
-
-(define vector-reverse-copy
- (let ()
- (define (%vector-reverse-copy vec start end)
- (let* ((len (- end start))
- (result (make-vector len)))
- (let loop ((i 0) (j (- end 1)))
- (unless (= i len)
- (vector-set! result i (vector-ref vec j))
- (loop (+ i 1) (- j 1))))
- result))
- (case-lambda
- "(vector-reverse-copy vec [start [end]]) -> vector
-
-Allocate a new vector whose length is END - START and fills it with
-elements from vec, taking elements from vec in reverse order starting
-at index START and stopping at index END. START defaults to 0 and END
-defaults to the value of (vector-length VEC)."
- ((vec)
- (assert-vector vec 'vector-reverse-copy)
- (%vector-reverse-copy vec 0 (vector-length vec)))
- ((vec start)
- (assert-vector vec 'vector-reverse-copy)
- (let ((len (vector-length vec)))
- (assert-valid-start start len 'vector-reverse-copy)
- (%vector-reverse-copy vec start len)))
- ((vec start end)
- (assert-vector vec 'vector-reverse-copy)
- (let ((len (vector-length vec)))
- (assert-valid-range start end len 'vector-reverse-copy)
- (%vector-reverse-copy vec start end))))))
-
-(define (%vector-concatenate vs)
- (let* ((result-len (let loop ((vs vs) (len 0))
- (if (null? vs)
- len
- (loop (cdr vs) (+ len (vector-length (car vs)))))))
- (result (make-vector result-len)))
- (let loop ((vs vs) (pos 0))
- (unless (null? vs)
- (let* ((v (car vs))
- (len (vector-length v)))
- (vector-move-left! v 0 len result pos)
- (loop (cdr vs) (+ pos len)))))
- result))
-
-(define vector-append
- (case-lambda
- "(vector-append vec ...) -> vector
-
-Return a newly allocated vector that contains all elements in order
-from the subsequent locations in VEC ..."
- (() (vector))
- ((v)
- (assert-vector v 'vector-append)
- (guile-vector-copy v))
- ((v1 v2)
- (assert-vector v1 'vector-append)
- (assert-vector v2 'vector-append)
- (let ((len1 (vector-length v1))
- (len2 (vector-length v2)))
- (let ((result (make-vector (+ len1 len2))))
- (vector-move-left! v1 0 len1 result 0)
- (vector-move-left! v2 0 len2 result len1)
- result)))
- (vs
- (assert-vectors vs 'vector-append)
- (%vector-concatenate vs))))
-
-(define (vector-concatenate vs)
- "(vector-concatenate list-of-vectors) -> vector
-
-Append each vector in LIST-OF-VECTORS. Equivalent to:
- (apply vector-append LIST-OF-VECTORS)"
- (assert-vectors vs 'vector-concatenate)
- (%vector-concatenate vs))
-
-(define (vector-empty? vec)
- "(vector-empty? vec) -> boolean
-
-Return true if VEC is empty, i.e. its length is 0, and false if not."
- (assert-vector vec 'vector-empty?)
- (zero? (vector-length vec)))
-
-(define vector=
- (let ()
- (define (all-of-length? len vs)
- (or (null? vs)
- (and (= len (vector-length (car vs)))
- (all-of-length? len (cdr vs)))))
- (define (=up-to? i elt=? v1 v2)
- (or (negative? i)
- (let ((x1 (vector-ref v1 i))
- (x2 (vector-ref v2 i)))
- (and (or (eq? x1 x2) (elt=? x1 x2))
- (=up-to? (- i 1) elt=? v1 v2)))))
- (case-lambda
- "(vector= elt=? vec ...) -> boolean
-
-Return true if the vectors VEC ... have equal lengths and equal
-elements according to ELT=?. ELT=? is always applied to two
-arguments. Element comparison must be consistent with eq?, in the
-following sense: if (eq? a b) returns true, then (elt=? a b) must also
-return true. The order in which comparisons are performed is
-unspecified."
- ((elt=?)
- (assert-procedure elt=? 'vector=)
- #t)
- ((elt=? v)
- (assert-procedure elt=? 'vector=)
- (assert-vector v 'vector=)
- #t)
- ((elt=? v1 v2)
- (assert-procedure elt=? 'vector=)
- (assert-vector v1 'vector=)
- (assert-vector v2 'vector=)
- (let ((len (vector-length v1)))
- (and (= len (vector-length v2))
- (=up-to? (- len 1) elt=? v1 v2))))
- ((elt=? v1 . vs)
- (assert-procedure elt=? 'vector=)
- (assert-vector v1 'vector=)
- (assert-vectors vs 'vector=)
- (let ((len (vector-length v1)))
- (and (all-of-length? len vs)
- (let loop ((vs vs))
- (or (null? vs)
- (and (=up-to? (- len 1) elt=? v1 (car vs))
- (loop (cdr vs)))))))))))
-
-(define vector-fold
- (case-lambda
- "(vector-fold kons knil vec1 vec2 ...) -> value
-
-The fundamental vector iterator. KONS is iterated over each index in
-all of the vectors, stopping at the end of the shortest; KONS is
-applied as (KONS i state (vector-ref VEC1 i) (vector-ref VEC2 i) ...)
-where STATE is the current state value, and I is the current index.
-The current state value begins with KNIL, and becomes whatever KONS
-returned at the respective iteration. The iteration is strictly
-left-to-right."
- ((kcons knil v)
- (assert-procedure kcons 'vector-fold)
- (assert-vector v 'vector-fold)
- (let ((len (vector-length v)))
- (let loop ((i 0) (state knil))
- (if (= i len)
- state
- (loop (+ i 1) (kcons i state (vector-ref v i)))))))
- ((kcons knil v1 v2)
- (assert-procedure kcons 'vector-fold)
- (assert-vector v1 'vector-fold)
- (assert-vector v2 'vector-fold)
- (let ((len (min (vector-length v1) (vector-length v2))))
- (let loop ((i 0) (state knil))
- (if (= i len)
- state
- (loop (+ i 1)
- (kcons i state (vector-ref v1 i) (vector-ref v2 i)))))))
- ((kcons knil . vs)
- (assert-procedure kcons 'vector-fold)
- (assert-vectors vs 'vector-fold)
- (let ((len (min-length vs)))
- (let loop ((i 0) (state knil))
- (if (= i len)
- state
- (loop (+ i 1) (apply kcons i state (vectors-ref vs i)))))))))
-
-(define vector-fold-right
- (case-lambda
- "(vector-fold-right kons knil vec1 vec2 ...) -> value
-
-The fundamental vector iterator. KONS is iterated over each index in
-all of the vectors, starting at the end of the shortest; KONS is
-applied as (KONS i state (vector-ref VEC1 i) (vector-ref VEC2 i) ...)
-where STATE is the current state value, and I is the current index.
-The current state value begins with KNIL, and becomes whatever KONS
-returned at the respective iteration. The iteration is strictly
-right-to-left."
- ((kcons knil v)
- (assert-procedure kcons 'vector-fold-right)
- (assert-vector v 'vector-fold-right)
- (let ((len (vector-length v)))
- (let loop ((i (- len 1)) (state knil))
- (if (negative? i)
- state
- (loop (- i 1) (kcons i state (vector-ref v i)))))))
- ((kcons knil v1 v2)
- (assert-procedure kcons 'vector-fold-right)
- (assert-vector v1 'vector-fold-right)
- (assert-vector v2 'vector-fold-right)
- (let ((len (min (vector-length v1) (vector-length v2))))
- (let loop ((i (- len 1)) (state knil))
- (if (negative? i)
- state
- (loop (- i 1)
- (kcons i state (vector-ref v1 i) (vector-ref v2 i)))))))
- ((kcons knil . vs)
- (assert-procedure kcons 'vector-fold-right)
- (assert-vectors vs 'vector-fold-right)
- (let ((len (min-length vs)))
- (let loop ((i (- len 1)) (state knil))
- (if (negative? i)
- state
- (loop (- i 1) (apply kcons i state (vectors-ref vs i)))))))))
-
-(define vector-map
- (case-lambda
- "(vector-map f vec2 vec2 ...) -> vector
-
-Return a new vector of the shortest size of the vector arguments.
-Each element at index i of the new vector is mapped from the old
-vectors by (F i (vector-ref VEC1 i) (vector-ref VEC2 i) ...). The
-dynamic order of application of F is unspecified."
- ((f v)
- (assert-procedure f 'vector-map)
- (assert-vector v 'vector-map)
- (let* ((len (vector-length v))
- (result (make-vector len)))
- (let loop ((i 0))
- (unless (= i len)
- (vector-set! result i (f i (vector-ref v i)))
- (loop (+ i 1))))
- result))
- ((f v1 v2)
- (assert-procedure f 'vector-map)
- (assert-vector v1 'vector-map)
- (assert-vector v2 'vector-map)
- (let* ((len (min (vector-length v1) (vector-length v2)))
- (result (make-vector len)))
- (let loop ((i 0))
- (unless (= i len)
- (vector-set! result i (f i (vector-ref v1 i) (vector-ref v2 i)))
- (loop (+ i 1))))
- result))
- ((f . vs)
- (assert-procedure f 'vector-map)
- (assert-vectors vs 'vector-map)
- (let* ((len (min-length vs))
- (result (make-vector len)))
- (let loop ((i 0))
- (unless (= i len)
- (vector-set! result i (apply f i (vectors-ref vs i)))
- (loop (+ i 1))))
- result))))
-
-(define vector-map!
- (case-lambda
- "(vector-map! f vec2 vec2 ...) -> unspecified
-
-Similar to vector-map, but rather than mapping the new elements into a
-new vector, the new mapped elements are destructively inserted into
-VEC1. The dynamic order of application of F is unspecified."
- ((f v)
- (assert-procedure f 'vector-map!)
- (assert-vector v 'vector-map!)
- (let ((len (vector-length v)))
- (let loop ((i 0))
- (unless (= i len)
- (vector-set! v i (f i (vector-ref v i)))
- (loop (+ i 1))))))
- ((f v1 v2)
- (assert-procedure f 'vector-map!)
- (assert-vector v1 'vector-map!)
- (assert-vector v2 'vector-map!)
- (let ((len (min (vector-length v1) (vector-length v2))))
- (let loop ((i 0))
- (unless (= i len)
- (vector-set! v1 i (f i (vector-ref v1 i) (vector-ref v2 i)))
- (loop (+ i 1))))))
- ((f . vs)
- (assert-procedure f 'vector-map!)
- (assert-vectors vs 'vector-map!)
- (let ((len (min-length vs))
- (v1 (car vs)))
- (let loop ((i 0))
- (unless (= i len)
- (vector-set! v1 i (apply f i (vectors-ref vs i)))
- (loop (+ i 1))))))))
-
-(define vector-for-each
- (case-lambda
- "(vector-for-each f vec1 vec2 ...) -> unspecified
-
-Call (F i VEC1[i] VEC2[i] ...) for each index i less than the length
-of the shortest vector passed. The iteration is strictly
-left-to-right."
- ((f v)
- (assert-procedure f 'vector-for-each)
- (assert-vector v 'vector-for-each)
- (let ((len (vector-length v)))
- (let loop ((i 0))
- (unless (= i len)
- (f i (vector-ref v i))
- (loop (+ i 1))))))
- ((f v1 v2)
- (assert-procedure f 'vector-for-each)
- (assert-vector v1 'vector-for-each)
- (assert-vector v2 'vector-for-each)
- (let ((len (min (vector-length v1)
- (vector-length v2))))
- (let loop ((i 0))
- (unless (= i len)
- (f i (vector-ref v1 i) (vector-ref v2 i))
- (loop (+ i 1))))))
- ((f . vs)
- (assert-procedure f 'vector-for-each)
- (assert-vectors vs 'vector-for-each)
- (let ((len (min-length vs)))
- (let loop ((i 0))
- (unless (= i len)
- (apply f i (vectors-ref vs i))
- (loop (+ i 1))))))))
-
-(define vector-count
- (case-lambda
- "(vector-count pred? vec1 vec2 ...) -> exact nonnegative integer
-
-Count the number of indices i for which (PRED? VEC1[i] VEC2[i] ...)
-returns true, where i is less than the length of the shortest vector
-passed."
- ((pred? v)
- (assert-procedure pred? 'vector-count)
- (assert-vector v 'vector-count)
- (let ((len (vector-length v)))
- (let loop ((i 0) (count 0))
- (cond ((= i len) count)
- ((pred? i (vector-ref v i))
- (loop (+ i 1) (+ count 1)))
- (else
- (loop (+ i 1) count))))))
- ((pred? v1 v2)
- (assert-procedure pred? 'vector-count)
- (assert-vector v1 'vector-count)
- (assert-vector v2 'vector-count)
- (let ((len (min (vector-length v1)
- (vector-length v2))))
- (let loop ((i 0) (count 0))
- (cond ((= i len) count)
- ((pred? i (vector-ref v1 i) (vector-ref v2 i))
- (loop (+ i 1) (+ count 1)))
- (else
- (loop (+ i 1) count))))))
- ((pred? . vs)
- (assert-procedure pred? 'vector-count)
- (assert-vectors vs 'vector-count)
- (let ((len (min-length vs)))
- (let loop ((i 0) (count 0))
- (cond ((= i len) count)
- ((apply pred? i (vectors-ref vs i))
- (loop (+ i 1) (+ count 1)))
- (else
- (loop (+ i 1) count))))))))
-
-(define vector-index
- (case-lambda
- "(vector-index pred? vec1 vec2 ...) -> exact nonnegative integer or #f
-
-Find and return the index of the first elements in VEC1 VEC2 ... that
-satisfy PRED?. If no matching element is found by the end of the
-shortest vector, return #f."
- ((pred? v)
- (assert-procedure pred? 'vector-index)
- (assert-vector v 'vector-index)
- (let ((len (vector-length v)))
- (let loop ((i 0))
- (and (< i len)
- (if (pred? (vector-ref v i))
- i
- (loop (+ i 1)))))))
- ((pred? v1 v2)
- (assert-procedure pred? 'vector-index)
- (assert-vector v1 'vector-index)
- (assert-vector v2 'vector-index)
- (let ((len (min (vector-length v1)
- (vector-length v2))))
- (let loop ((i 0))
- (and (< i len)
- (if (pred? (vector-ref v1 i)
- (vector-ref v2 i))
- i
- (loop (+ i 1)))))))
- ((pred? . vs)
- (assert-procedure pred? 'vector-index)
- (assert-vectors vs 'vector-index)
- (let ((len (min-length vs)))
- (let loop ((i 0))
- (and (< i len)
- (if (apply pred? (vectors-ref vs i))
- i
- (loop (+ i 1)))))))))
-
-(define vector-index-right
- (case-lambda
- "(vector-index-right pred? vec1 vec2 ...) -> exact nonnegative integer or #f
-
-Find and return the index of the last elements in VEC1 VEC2 ... that
-satisfy PRED?, searching from right-to-left. If no matching element
-is found before the end of the shortest vector, return #f."
- ((pred? v)
- (assert-procedure pred? 'vector-index-right)
- (assert-vector v 'vector-index-right)
- (let ((len (vector-length v)))
- (let loop ((i (- len 1)))
- (and (>= i 0)
- (if (pred? (vector-ref v i))
- i
- (loop (- i 1)))))))
- ((pred? v1 v2)
- (assert-procedure pred? 'vector-index-right)
- (assert-vector v1 'vector-index-right)
- (assert-vector v2 'vector-index-right)
- (let ((len (min (vector-length v1)
- (vector-length v2))))
- (let loop ((i (- len 1)))
- (and (>= i 0)
- (if (pred? (vector-ref v1 i)
- (vector-ref v2 i))
- i
- (loop (- i 1)))))))
- ((pred? . vs)
- (assert-procedure pred? 'vector-index-right)
- (assert-vectors vs 'vector-index-right)
- (let ((len (min-length vs)))
- (let loop ((i (- len 1)))
- (and (>= i 0)
- (if (apply pred? (vectors-ref vs i))
- i
- (loop (- i 1)))))))))
-
-(define vector-skip
- (case-lambda
- "(vector-skip pred? vec1 vec2 ...) -> exact nonnegative integer or #f
-
-Find and return the index of the first elements in VEC1 VEC2 ... that
-do not satisfy PRED?. If no matching element is found by the end of
-the shortest vector, return #f."
- ((pred? v)
- (assert-procedure pred? 'vector-skip)
- (assert-vector v 'vector-skip)
- (let ((len (vector-length v)))
- (let loop ((i 0))
- (and (< i len)
- (if (pred? (vector-ref v i))
- (loop (+ i 1))
- i)))))
- ((pred? v1 v2)
- (assert-procedure pred? 'vector-skip)
- (assert-vector v1 'vector-skip)
- (assert-vector v2 'vector-skip)
- (let ((len (min (vector-length v1)
- (vector-length v2))))
- (let loop ((i 0))
- (and (< i len)
- (if (pred? (vector-ref v1 i)
- (vector-ref v2 i))
- (loop (+ i 1))
- i)))))
- ((pred? . vs)
- (assert-procedure pred? 'vector-skip)
- (assert-vectors vs 'vector-skip)
- (let ((len (min-length vs)))
- (let loop ((i 0))
- (and (< i len)
- (if (apply pred? (vectors-ref vs i))
- (loop (+ i 1))
- i)))))))
-
-(define vector-skip-right
- (case-lambda
- "(vector-skip-right pred? vec1 vec2 ...) -> exact nonnegative integer or #f
-
-Find and return the index of the last elements in VEC1 VEC2 ... that
-do not satisfy PRED?, searching from right-to-left. If no matching
-element is found before the end of the shortest vector, return #f."
- ((pred? v)
- (assert-procedure pred? 'vector-skip-right)
- (assert-vector v 'vector-skip-right)
- (let ((len (vector-length v)))
- (let loop ((i (- len 1)))
- (and (not (negative? i))
- (if (pred? (vector-ref v i))
- (loop (- i 1))
- i)))))
- ((pred? v1 v2)
- (assert-procedure pred? 'vector-skip-right)
- (assert-vector v1 'vector-skip-right)
- (assert-vector v2 'vector-skip-right)
- (let ((len (min (vector-length v1)
- (vector-length v2))))
- (let loop ((i (- len 1)))
- (and (not (negative? i))
- (if (pred? (vector-ref v1 i)
- (vector-ref v2 i))
- (loop (- i 1))
- i)))))
- ((pred? . vs)
- (assert-procedure pred? 'vector-skip-right)
- (assert-vectors vs 'vector-skip-right)
- (let ((len (min-length vs)))
- (let loop ((i (- len 1)))
- (and (not (negative? i))
- (if (apply pred? (vectors-ref vs i))
- (loop (- i 1))
- i)))))))
-
-(define vector-binary-search
- (let ()
- (define (%vector-binary-search vec value cmp start end)
- (let loop ((lo start) (hi end))
- (and (< lo hi)
- (let* ((i (quotient (+ lo hi) 2))
- (x (vector-ref vec i))
- (c (cmp x value)))
- (cond ((zero? c) i)
- ((positive? c) (loop lo i))
- ((negative? c) (loop (+ i 1) hi)))))))
- (case-lambda
- "(vector-binary-search vec value cmp [start [end]]) -> exact nonnegative integer or #f
-
-Find and return an index of VEC between START and END whose value is
-VALUE using a binary search. If no matching element is found, return
-#f. The default START is 0 and the default END is the length of VEC.
-CMP must be a procedure of two arguments such that (CMP A B) returns
-a negative integer if A < B, a positive integer if A > B, or zero if
-A = B. The elements of VEC must be sorted in non-decreasing order
-according to CMP."
- ((vec value cmp)
- (assert-vector vec 'vector-binary-search)
- (assert-procedure cmp 'vector-binary-search)
- (%vector-binary-search vec value cmp 0 (vector-length vec)))
-
- ((vec value cmp start)
- (assert-vector vec 'vector-binary-search)
- (let ((len (vector-length vec)))
- (assert-valid-start start len 'vector-binary-search)
- (%vector-binary-search vec value cmp start len)))
-
- ((vec value cmp start end)
- (assert-vector vec 'vector-binary-search)
- (let ((len (vector-length vec)))
- (assert-valid-range start end len 'vector-binary-search)
- (%vector-binary-search vec value cmp start end))))))
-
-(define vector-any
- (case-lambda
- "(vector-any pred? vec1 vec2 ...) -> value or #f
-
-Find the first parallel set of elements from VEC1 VEC2 ... for which
-PRED? returns a true value. If such a parallel set of elements
-exists, vector-any returns the value that PRED? returned for that set
-of elements. The iteration is strictly left-to-right."
- ((pred? v)
- (assert-procedure pred? 'vector-any)
- (assert-vector v 'vector-any)
- (let ((len (vector-length v)))
- (let loop ((i 0))
- (and (< i len)
- (or (pred? (vector-ref v i))
- (loop (+ i 1)))))))
- ((pred? v1 v2)
- (assert-procedure pred? 'vector-any)
- (assert-vector v1 'vector-any)
- (assert-vector v2 'vector-any)
- (let ((len (min (vector-length v1)
- (vector-length v2))))
- (let loop ((i 0))
- (and (< i len)
- (or (pred? (vector-ref v1 i)
- (vector-ref v2 i))
- (loop (+ i 1)))))))
- ((pred? . vs)
- (assert-procedure pred? 'vector-any)
- (assert-vectors vs 'vector-any)
- (let ((len (min-length vs)))
- (let loop ((i 0))
- (and (< i len)
- (or (apply pred? (vectors-ref vs i))
- (loop (+ i 1)))))))))
-
-(define vector-every
- (case-lambda
- "(vector-every pred? vec1 vec2 ...) -> value or #f
-
-If, for every index i less than the length of the shortest vector
-argument, the set of elements VEC1[i] VEC2[i] ... satisfies PRED?,
-vector-every returns the value that PRED? returned for the last set of
-elements, at the last index of the shortest vector. The iteration is
-strictly left-to-right."
- ((pred? v)
- (assert-procedure pred? 'vector-every)
- (assert-vector v 'vector-every)
- (let ((len (vector-length v)))
- (or (zero? len)
- (let loop ((i 0))
- (let ((val (pred? (vector-ref v i)))
- (next-i (+ i 1)))
- (if (or (not val) (= next-i len))
- val
- (loop next-i)))))))
- ((pred? v1 v2)
- (assert-procedure pred? 'vector-every)
- (assert-vector v1 'vector-every)
- (assert-vector v2 'vector-every)
- (let ((len (min (vector-length v1)
- (vector-length v2))))
- (or (zero? len)
- (let loop ((i 0))
- (let ((val (pred? (vector-ref v1 i)
- (vector-ref v2 i)))
- (next-i (+ i 1)))
- (if (or (not val) (= next-i len))
- val
- (loop next-i)))))))
- ((pred? . vs)
- (assert-procedure pred? 'vector-every)
- (assert-vectors vs 'vector-every)
- (let ((len (min-length vs)))
- (or (zero? len)
- (let loop ((i 0))
- (let ((val (apply pred? (vectors-ref vs i)))
- (next-i (+ i 1)))
- (if (or (not val) (= next-i len))
- val
- (loop next-i)))))))))
-
-(define (vector-swap! vec i j)
- "(vector-swap! vec i j) -> unspecified
-
-Swap the values of the locations in VEC at I and J."
- (assert-vector vec 'vector-swap!)
- (let ((len (vector-length vec)))
- (assert-valid-index i len 'vector-swap!)
- (assert-valid-index j len 'vector-swap!)
- (let ((tmp (vector-ref vec i)))
- (vector-set! vec i (vector-ref vec j))
- (vector-set! vec j tmp))))
-
-;; TODO: Enhance Guile core 'vector-fill!' to do this.
-(define vector-fill!
- (let ()
- (define guile-vector-fill!
- (@ (guile) vector-fill!))
- (define (%vector-fill! vec fill start end)
- (let loop ((i start))
- (when (< i end)
- (vector-set! vec i fill)
- (loop (+ i 1)))))
- (case-lambda
- "(vector-fill! vec fill [start [end]]) -> unspecified
-
-Assign the value of every location in VEC between START and END to
-FILL. START defaults to 0 and END defaults to the length of VEC."
- ((vec fill)
- (guile-vector-fill! vec fill))
- ((vec fill start)
- (assert-vector vec 'vector-fill!)
- (let ((len (vector-length vec)))
- (assert-valid-start start len 'vector-fill!)
- (%vector-fill! vec fill start len)))
- ((vec fill start end)
- (assert-vector vec 'vector-fill!)
- (let ((len (vector-length vec)))
- (assert-valid-range start end len 'vector-fill!)
- (%vector-fill! vec fill start end))))))
-
-(define (%vector-reverse! vec start end)
- (let loop ((i start) (j (- end 1)))
- (when (< i j)
- (let ((tmp (vector-ref vec i)))
- (vector-set! vec i (vector-ref vec j))
- (vector-set! vec j tmp)
- (loop (+ i 1) (- j 1))))))
-
-(define vector-reverse!
- (case-lambda
- "(vector-reverse! vec [start [end]]) -> unspecified
-
-Destructively reverse the contents of VEC between START and END.
-START defaults to 0 and END defaults to the length of VEC."
- ((vec)
- (assert-vector vec 'vector-reverse!)
- (%vector-reverse! vec 0 (vector-length vec)))
- ((vec start)
- (assert-vector vec 'vector-reverse!)
- (let ((len (vector-length vec)))
- (assert-valid-start start len 'vector-reverse!)
- (%vector-reverse! vec start len)))
- ((vec start end)
- (assert-vector vec 'vector-reverse!)
- (let ((len (vector-length vec)))
- (assert-valid-range start end len 'vector-reverse!)
- (%vector-reverse! vec start end)))))
-
-(define-syntax-rule (define-vector-copier! copy! docstring inner-proc)
- (define copy!
- (let ((%copy! inner-proc))
- (case-lambda
- docstring
- ((target tstart source)
- (assert-vector target 'copy!)
- (assert-vector source 'copy!)
- (let ((tlen (vector-length target))
- (slen (vector-length source)))
- (assert-valid-start tstart tlen 'copy!)
- (unless (>= tlen (+ tstart slen))
- (error-from 'copy! "would write past end of target"))
- (%copy! target tstart source 0 slen)))
-
- ((target tstart source sstart)
- (assert-vector target 'copy!)
- (assert-vector source 'copy!)
- (let ((tlen (vector-length target))
- (slen (vector-length source)))
- (assert-valid-start tstart tlen 'copy!)
- (assert-valid-start sstart slen 'copy!)
- (unless (>= tlen (+ tstart (- slen sstart)))
- (error-from 'copy! "would write past end of target"))
- (%copy! target tstart source sstart slen)))
-
- ((target tstart source sstart send)
- (assert-vector target 'copy!)
- (assert-vector source 'copy!)
- (let ((tlen (vector-length target))
- (slen (vector-length source)))
- (assert-valid-start tstart tlen 'copy!)
- (assert-valid-range sstart send slen 'copy!)
- (unless (>= tlen (+ tstart (- send sstart)))
- (error-from 'copy! "would write past end of target"))
- (%copy! target tstart source sstart send)))))))
-
-(define-vector-copier! vector-copy!
- "(vector-copy! target tstart source [sstart [send]]) -> unspecified
-
-Copy a block of elements from SOURCE to TARGET, both of which must be
-vectors, starting in TARGET at TSTART and starting in SOURCE at
-SSTART, ending when SEND - SSTART elements have been copied. It is an
-error for TARGET to have a length less than TSTART + (SEND - SSTART).
-SSTART defaults to 0 and SEND defaults to the length of SOURCE."
- (lambda (target tstart source sstart send)
- (if (< tstart sstart)
- (vector-move-left! source sstart send target tstart)
- (vector-move-right! source sstart send target tstart))))
-
-(define-vector-copier! vector-reverse-copy!
- "(vector-reverse-copy! target tstart source [sstart [send]]) -> unspecified
-
-Like vector-copy!, but copy the elements in the reverse order. It is
-an error if TARGET and SOURCE are identical vectors and the TARGET and
-SOURCE ranges overlap; however, if TSTART = SSTART,
-vector-reverse-copy! behaves as (vector-reverse! TARGET TSTART SEND)
-would."
- (lambda (target tstart source sstart send)
- (if (and (eq? target source) (= tstart sstart))
- (%vector-reverse! target sstart send)
- (let loop ((i tstart) (j (- send 1)))
- (when (>= j sstart)
- (vector-set! target i (vector-ref source j))
- (loop (+ i 1) (- j 1)))))))
-
-(define vector->list
- (let ()
- (define (%vector->list vec start end)
- (let loop ((i (- end 1))
- (result '()))
- (if (< i start)
- result
- (loop (- i 1) (cons (vector-ref vec i) result)))))
- (case-lambda
- "(vector->list vec [start [end]]) -> proper-list
-
-Return a newly allocated list containing the elements in VEC between
-START and END. START defaults to 0 and END defaults to the length of
-VEC."
- ((vec)
- (assert-vector vec 'vector->list)
- (%vector->list vec 0 (vector-length vec)))
- ((vec start)
- (assert-vector vec 'vector->list)
- (let ((len (vector-length vec)))
- (assert-valid-start start len 'vector->list)
- (%vector->list vec start len)))
- ((vec start end)
- (assert-vector vec 'vector->list)
- (let ((len (vector-length vec)))
- (assert-valid-range start end len 'vector->list)
- (%vector->list vec start end))))))
-
-(define reverse-vector->list
- (let ()
- (define (%reverse-vector->list vec start end)
- (let loop ((i start)
- (result '()))
- (if (>= i end)
- result
- (loop (+ i 1) (cons (vector-ref vec i) result)))))
- (case-lambda
- "(reverse-vector->list vec [start [end]]) -> proper-list
-
-Return a newly allocated list containing the elements in VEC between
-START and END in reverse order. START defaults to 0 and END defaults
-to the length of VEC."
- ((vec)
- (assert-vector vec 'reverse-vector->list)
- (%reverse-vector->list vec 0 (vector-length vec)))
- ((vec start)
- (assert-vector vec 'reverse-vector->list)
- (let ((len (vector-length vec)))
- (assert-valid-start start len 'reverse-vector->list)
- (%reverse-vector->list vec start len)))
- ((vec start end)
- (assert-vector vec 'reverse-vector->list)
- (let ((len (vector-length vec)))
- (assert-valid-range start end len 'reverse-vector->list)
- (%reverse-vector->list vec start end))))))
-
-;; TODO: change to use 'case-lambda' and improve error checking.
-(define* (list->vector lst #\optional (start 0) (end (length lst)))
- "(list->vector proper-list [start [end]]) -> vector
-
-Return a newly allocated vector of the elements from PROPER-LIST with
-indices between START and END. START defaults to 0 and END defaults
-to the length of PROPER-LIST."
- (let* ((len (- end start))
- (result (make-vector len)))
- (let loop ((i 0) (lst (drop lst start)))
- (if (= i len)
- result
- (begin (vector-set! result i (car lst))
- (loop (+ i 1) (cdr lst)))))))
-
-;; TODO: change to use 'case-lambda' and improve error checking.
-(define* (reverse-list->vector lst #\optional (start 0) (end (length lst)))
- "(reverse-list->vector proper-list [start [end]]) -> vector
-
-Return a newly allocated vector of the elements from PROPER-LIST with
-indices between START and END, in reverse order. START defaults to 0
-and END defaults to the length of PROPER-LIST."
- (let* ((len (- end start))
- (result (make-vector len)))
- (let loop ((i (- len 1)) (lst (drop lst start)))
- (if (negative? i)
- result
- (begin (vector-set! result i (car lst))
- (loop (- i 1) (cdr lst)))))))
-;;; srfi-45.scm -- Primitives for Expressing Iterative Lazy Algorithms
-
-;; Copyright (C) 2010, 2011, 2013 Free Software Foundation, Inc.
-;; Copyright (C) 2003 André van Tonder. All Rights Reserved.
-
-;; Permission is hereby granted, free of charge, to any person
-;; obtaining a copy of this software and associated documentation
-;; files (the "Software"), to deal in the Software without
-;; restriction, including without limitation the rights to use, copy,
-;; modify, merge, publish, distribute, sublicense, and/or sell copies
-;; of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-;;; Commentary:
-
-;; This is the code of the reference implementation of SRFI-45, modified
-;; to use SRFI-9 and to add 'promise?' to the list of exports.
-
-;; This module is documented in the Guile Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-45)
- #\export (delay
- lazy
- force
- eager
- promise?)
- #\replace (delay force promise?)
- #\use-module (srfi srfi-9)
- #\use-module (srfi srfi-9 gnu))
-
-(cond-expand-provide (current-module) '(srfi-45))
-
-(define-record-type promise (make-promise val) promise?
- (val promise-val promise-val-set!))
-
-(define-record-type value (make-value tag proc) value?
- (tag value-tag value-tag-set!)
- (proc value-proc value-proc-set!))
-
-(define-syntax-rule (lazy exp)
- (make-promise (make-value 'lazy (lambda () exp))))
-
-(define (eager x)
- (make-promise (make-value 'eager x)))
-
-(define-syntax-rule (delay exp)
- (lazy (eager exp)))
-
-(define (force promise)
- (let ((content (promise-val promise)))
- (case (value-tag content)
- ((eager) (value-proc content))
- ((lazy) (let* ((promise* ((value-proc content)))
- (content (promise-val promise))) ; *
- (if (not (eqv? (value-tag content) 'eager)) ; *
- (begin (value-tag-set! content
- (value-tag (promise-val promise*)))
- (value-proc-set! content
- (value-proc (promise-val promise*)))
- (promise-val-set! promise* content)))
- (force promise))))))
-
-;; (*) These two lines re-fetch and check the original promise in case
-;; the first line of the let* caused it to be forced. For an example
-;; where this happens, see reentrancy test 3 below.
-
-(define* (promise-visit promise #\key on-eager on-lazy)
- (define content (promise-val promise))
- (case (value-tag content)
- ((eager) (on-eager (value-proc content)))
- ((lazy) (on-lazy (value-proc content)))))
-
-(set-record-type-printer! promise
- (lambda (promise port)
- (promise-visit promise
- #\on-eager (lambda (value)
- (format port "#<promise = ~s>" value))
- #\on-lazy (lambda (proc)
- (format port "#<promise => ~s>" proc)))))
-;;; srfi-6.scm --- Basic String Ports
-
-;; Copyright (C) 2001, 2002, 2003, 2006, 2012 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Commentary:
-
-;; This module is fully documented in the Guile Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-6)
- #\replace (open-input-string open-output-string)
- #\re-export (get-output-string))
-
-;; SRFI-6 says nothing about encodings, and assumes that any character
-;; or string can be written to a string port. Thus, make all SRFI-6
-;; string ports Unicode capable. See <http://bugs.gnu.org/11197>.
-
-(define (open-input-string s)
- (with-fluids ((%default-port-encoding "UTF-8"))
- ((@ (guile) open-input-string) s)))
-
-(define (open-output-string)
- (with-fluids ((%default-port-encoding "UTF-8"))
- ((@ (guile) open-output-string))))
-
-(cond-expand-provide (current-module) '(srfi-6))
-
-;;; srfi-6.scm ends here
-;;; srfi-60.scm --- Integers as Bits
-
-;; Copyright (C) 2005, 2006, 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (srfi srfi-60)
- #\export (bitwise-and
- bitwise-ior
- bitwise-xor
- bitwise-not
- any-bits-set?
- bitwise-if bitwise-merge
- log2-binary-factors first-set-bit
- bit-set?
- copy-bit
- bit-field
- copy-bit-field
- arithmetic-shift
- rotate-bit-field
- reverse-bit-field
- integer->list
- list->integer
- booleans->integer)
- #\replace (bit-count)
- #\re-export (logand
- logior
- logxor
- integer-length
- logtest
- logcount
- logbit?
- ash))
-
-(load-extension (string-append "libguile-" (effective-version))
- "scm_init_srfi_60")
-
-(define bitwise-and logand)
-(define bitwise-ior logior)
-(define bitwise-xor logxor)
-(define bitwise-not lognot)
-(define any-bits-set? logtest)
-(define bit-count logcount)
-
-(define (bitwise-if mask n0 n1)
- (logior (logand mask n0)
- (logand (lognot mask) n1)))
-(define bitwise-merge bitwise-if)
-
-(define first-set-bit log2-binary-factors)
-(define bit-set? logbit?)
-(define bit-field bit-extract)
-
-(define (copy-bit-field n newbits start end)
- (logxor n (ash (logxor (bit-extract n start end) ;; cancel old
- (bit-extract newbits 0 (- end start))) ;; insert new
- start)))
-
-(define arithmetic-shift ash)
-
-(cond-expand-provide (current-module) '(srfi-60))
-;;; srfi-64.scm -- SRFI 64 - A Scheme API for test suites.
-
-;; Copyright (C) 2014 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (srfi srfi-64)
- #\export
- (test-begin
- test-end test-assert test-eqv test-eq test-equal
- test-approximate test-assert test-error test-apply test-with-runner
- test-match-nth test-match-all test-match-any test-match-name
- test-skip test-expect-fail test-read-eval-string
- test-runner-group-path test-group test-group-with-cleanup
- test-result-ref test-result-set! test-result-clear test-result-remove
- test-result-kind test-passed?
- test-log-to-file
- test-runner? test-runner-reset test-runner-null
- test-runner-simple test-runner-current test-runner-factory test-runner-get
- test-runner-create test-runner-test-name
- test-runner-pass-count test-runner-pass-count!
- test-runner-fail-count test-runner-fail-count!
- test-runner-xpass-count test-runner-xpass-count!
- test-runner-xfail-count test-runner-xfail-count!
- test-runner-skip-count test-runner-skip-count!
- test-runner-group-stack test-runner-group-stack!
- test-runner-on-test-begin test-runner-on-test-begin!
- test-runner-on-test-end test-runner-on-test-end!
- test-runner-on-group-begin test-runner-on-group-begin!
- test-runner-on-group-end test-runner-on-group-end!
- test-runner-on-final test-runner-on-final!
- test-runner-on-bad-count test-runner-on-bad-count!
- test-runner-on-bad-end-name test-runner-on-bad-end-name!
- test-result-alist test-result-alist!
- test-runner-aux-value test-runner-aux-value!
- test-on-group-begin-simple test-on-group-end-simple
- test-on-bad-count-simple test-on-bad-end-name-simple
- test-on-final-simple test-on-test-end-simple
- test-on-final-simple))
-
-(cond-expand-provide (current-module) '(srfi-64))
-
-(include-from-path "srfi/srfi-64/testing.scm")
-;; Copyright (c) 2005, 2006, 2007, 2012, 2013 Per Bothner
-;; Added "full" support for Chicken, Gauche, Guile and SISC.
-;; Alex Shinn, Copyright (c) 2005.
-;; Modified for Scheme Spheres by Álvaro Castro-Castilla, Copyright (c) 2012.
-;; Support for Guile 2 by Mark H Weaver <mhw@netris.org>, Copyright (c) 2014.
-;;
-;; Permission is hereby granted, free of charge, to any person
-;; obtaining a copy of this software and associated documentation
-;; files (the "Software"), to deal in the Software without
-;; restriction, including without limitation the rights to use, copy,
-;; modify, merge, publish, distribute, sublicense, and/or sell copies
-;; of the Software, and to permit persons to whom the Software is
-;; furnished to do so, subject to the following conditions:
-;;
-;; The above copyright notice and this permission notice shall be
-;; included in all copies or substantial portions of the Software.
-;;
-;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-;; SOFTWARE.
-
-(cond-expand
- (chicken
- (require-extension syntax-case))
- (guile-2
- (use-modules (srfi srfi-9)
- ;; In 2.0.9, srfi-34 and srfi-35 are not well integrated
- ;; with either Guile's native exceptions or R6RS exceptions.
- ;;(srfi srfi-34) (srfi srfi-35)
- (srfi srfi-39)))
- (guile
- (use-modules (ice-9 syncase) (srfi srfi-9)
- ;;(srfi srfi-34) (srfi srfi-35) - not in Guile 1.6.7
- (srfi srfi-39)))
- (sisc
- (require-extension (srfi 9 34 35 39)))
- (kawa
- (module-compile-options warn-undefined-variable\: #t
- warn-invoke-unknown-method\: #t)
- (provide 'srfi-64)
- (provide 'testing)
- (require 'srfi-34)
- (require 'srfi-35))
- (else ()
- ))
-
-(cond-expand
- (kawa
- (define-syntax %test-export
- (syntax-rules ()
- ((%test-export test-begin . other-names)
- (module-export %test-begin . other-names)))))
- (else
- (define-syntax %test-export
- (syntax-rules ()
- ((%test-export . names) (if #f #f))))))
-
-;; List of exported names
-(%test-export
- test-begin ;; must be listed first, since in Kawa (at least) it is "magic".
- test-end test-assert test-eqv test-eq test-equal
- test-approximate test-assert test-error test-apply test-with-runner
- test-match-nth test-match-all test-match-any test-match-name
- test-skip test-expect-fail test-read-eval-string
- test-runner-group-path test-group test-group-with-cleanup
- test-result-ref test-result-set! test-result-clear test-result-remove
- test-result-kind test-passed?
- test-log-to-file
- ; Misc test-runner functions
- test-runner? test-runner-reset test-runner-null
- test-runner-simple test-runner-current test-runner-factory test-runner-get
- test-runner-create test-runner-test-name
- ;; test-runner field setter and getter functions - see %test-record-define:
- test-runner-pass-count test-runner-pass-count!
- test-runner-fail-count test-runner-fail-count!
- test-runner-xpass-count test-runner-xpass-count!
- test-runner-xfail-count test-runner-xfail-count!
- test-runner-skip-count test-runner-skip-count!
- test-runner-group-stack test-runner-group-stack!
- test-runner-on-test-begin test-runner-on-test-begin!
- test-runner-on-test-end test-runner-on-test-end!
- test-runner-on-group-begin test-runner-on-group-begin!
- test-runner-on-group-end test-runner-on-group-end!
- test-runner-on-final test-runner-on-final!
- test-runner-on-bad-count test-runner-on-bad-count!
- test-runner-on-bad-end-name test-runner-on-bad-end-name!
- test-result-alist test-result-alist!
- test-runner-aux-value test-runner-aux-value!
- ;; default/simple call-back functions, used in default test-runner,
- ;; but can be called to construct more complex ones.
- test-on-group-begin-simple test-on-group-end-simple
- test-on-bad-count-simple test-on-bad-end-name-simple
- test-on-final-simple test-on-test-end-simple
- test-on-final-simple)
-
-(cond-expand
- (srfi-9
- (define-syntax %test-record-define
- (syntax-rules ()
- ((%test-record-define alloc runner? (name index setter getter) ...)
- (define-record-type test-runner
- (alloc)
- runner?
- (name setter getter) ...)))))
- (else
- (define %test-runner-cookie (list "test-runner"))
- (define-syntax %test-record-define
- (syntax-rules ()
- ((%test-record-define alloc runner? (name index getter setter) ...)
- (begin
- (define (runner? obj)
- (and (vector? obj)
- (> (vector-length obj) 1)
- (eq (vector-ref obj 0) %test-runner-cookie)))
- (define (alloc)
- (let ((runner (make-vector 23)))
- (vector-set! runner 0 %test-runner-cookie)
- runner))
- (begin
- (define (getter runner)
- (vector-ref runner index)) ...)
- (begin
- (define (setter runner value)
- (vector-set! runner index value)) ...)))))))
-
-(%test-record-define
- %test-runner-alloc test-runner?
- ;; Cumulate count of all tests that have passed and were expected to.
- (pass-count 1 test-runner-pass-count test-runner-pass-count!)
- (fail-count 2 test-runner-fail-count test-runner-fail-count!)
- (xpass-count 3 test-runner-xpass-count test-runner-xpass-count!)
- (xfail-count 4 test-runner-xfail-count test-runner-xfail-count!)
- (skip-count 5 test-runner-skip-count test-runner-skip-count!)
- (skip-list 6 %test-runner-skip-list %test-runner-skip-list!)
- (fail-list 7 %test-runner-fail-list %test-runner-fail-list!)
- ;; Normally #t, except when in a test-apply.
- (run-list 8 %test-runner-run-list %test-runner-run-list!)
- (skip-save 9 %test-runner-skip-save %test-runner-skip-save!)
- (fail-save 10 %test-runner-fail-save %test-runner-fail-save!)
- (group-stack 11 test-runner-group-stack test-runner-group-stack!)
- (on-test-begin 12 test-runner-on-test-begin test-runner-on-test-begin!)
- (on-test-end 13 test-runner-on-test-end test-runner-on-test-end!)
- ;; Call-back when entering a group. Takes (runner suite-name count).
- (on-group-begin 14 test-runner-on-group-begin test-runner-on-group-begin!)
- ;; Call-back when leaving a group.
- (on-group-end 15 test-runner-on-group-end test-runner-on-group-end!)
- ;; Call-back when leaving the outermost group.
- (on-final 16 test-runner-on-final test-runner-on-final!)
- ;; Call-back when expected number of tests was wrong.
- (on-bad-count 17 test-runner-on-bad-count test-runner-on-bad-count!)
- ;; Call-back when name in test=end doesn't match test-begin.
- (on-bad-end-name 18 test-runner-on-bad-end-name test-runner-on-bad-end-name!)
- ;; Cumulate count of all tests that have been done.
- (total-count 19 %test-runner-total-count %test-runner-total-count!)
- ;; Stack (list) of (count-at-start . expected-count):
- (count-list 20 %test-runner-count-list %test-runner-count-list!)
- (result-alist 21 test-result-alist test-result-alist!)
- ;; Field can be used by test-runner for any purpose.
- ;; test-runner-simple uses it for a log file.
- (aux-value 22 test-runner-aux-value test-runner-aux-value!)
-)
-
-(define (test-runner-reset runner)
- (test-result-alist! runner '())
- (test-runner-pass-count! runner 0)
- (test-runner-fail-count! runner 0)
- (test-runner-xpass-count! runner 0)
- (test-runner-xfail-count! runner 0)
- (test-runner-skip-count! runner 0)
- (%test-runner-total-count! runner 0)
- (%test-runner-count-list! runner '())
- (%test-runner-run-list! runner #t)
- (%test-runner-skip-list! runner '())
- (%test-runner-fail-list! runner '())
- (%test-runner-skip-save! runner '())
- (%test-runner-fail-save! runner '())
- (test-runner-group-stack! runner '()))
-
-(define (test-runner-group-path runner)
- (reverse (test-runner-group-stack runner)))
-
-(define (%test-null-callback runner) #f)
-
-(define (test-runner-null)
- (let ((runner (%test-runner-alloc)))
- (test-runner-reset runner)
- (test-runner-on-group-begin! runner (lambda (runner name count) #f))
- (test-runner-on-group-end! runner %test-null-callback)
- (test-runner-on-final! runner %test-null-callback)
- (test-runner-on-test-begin! runner %test-null-callback)
- (test-runner-on-test-end! runner %test-null-callback)
- (test-runner-on-bad-count! runner (lambda (runner count expected) #f))
- (test-runner-on-bad-end-name! runner (lambda (runner begin end) #f))
- runner))
-
-;; Not part of the specification. FIXME
-;; Controls whether a log file is generated.
-(define test-log-to-file #t)
-
-(define (test-runner-simple)
- (let ((runner (%test-runner-alloc)))
- (test-runner-reset runner)
- (test-runner-on-group-begin! runner test-on-group-begin-simple)
- (test-runner-on-group-end! runner test-on-group-end-simple)
- (test-runner-on-final! runner test-on-final-simple)
- (test-runner-on-test-begin! runner test-on-test-begin-simple)
- (test-runner-on-test-end! runner test-on-test-end-simple)
- (test-runner-on-bad-count! runner test-on-bad-count-simple)
- (test-runner-on-bad-end-name! runner test-on-bad-end-name-simple)
- runner))
-
-(cond-expand
- (srfi-39
- (define test-runner-current (make-parameter #f))
- (define test-runner-factory (make-parameter test-runner-simple)))
- (else
- (define %test-runner-current #f)
- (define-syntax test-runner-current
- (syntax-rules ()
- ((test-runner-current)
- %test-runner-current)
- ((test-runner-current runner)
- (set! %test-runner-current runner))))
- (define %test-runner-factory test-runner-simple)
- (define-syntax test-runner-factory
- (syntax-rules ()
- ((test-runner-factory)
- %test-runner-factory)
- ((test-runner-factory runner)
- (set! %test-runner-factory runner))))))
-
-;; A safer wrapper to test-runner-current.
-(define (test-runner-get)
- (let ((r (test-runner-current)))
- (if (not r)
- (cond-expand
- (srfi-23 (error "test-runner not initialized - test-begin missing?"))
- (else #t)))
- r))
-
-(define (%test-specifier-matches spec runner)
- (spec runner))
-
-(define (test-runner-create)
- ((test-runner-factory)))
-
-(define (%test-any-specifier-matches list runner)
- (let ((result #f))
- (let loop ((l list))
- (cond ((null? l) result)
- (else
- (if (%test-specifier-matches (car l) runner)
- (set! result #t))
- (loop (cdr l)))))))
-
-;; Returns #f, #t, or 'xfail.
-(define (%test-should-execute runner)
- (let ((run (%test-runner-run-list runner)))
- (cond ((or
- (not (or (eqv? run #t)
- (%test-any-specifier-matches run runner)))
- (%test-any-specifier-matches
- (%test-runner-skip-list runner)
- runner))
- (test-result-set! runner 'result-kind 'skip)
- #f)
- ((%test-any-specifier-matches
- (%test-runner-fail-list runner)
- runner)
- (test-result-set! runner 'result-kind 'xfail)
- 'xfail)
- (else #t))))
-
-(define (%test-begin suite-name count)
- (if (not (test-runner-current))
- (test-runner-current (test-runner-create)))
- (let ((runner (test-runner-current)))
- ((test-runner-on-group-begin runner) runner suite-name count)
- (%test-runner-skip-save! runner
- (cons (%test-runner-skip-list runner)
- (%test-runner-skip-save runner)))
- (%test-runner-fail-save! runner
- (cons (%test-runner-fail-list runner)
- (%test-runner-fail-save runner)))
- (%test-runner-count-list! runner
- (cons (cons (%test-runner-total-count runner)
- count)
- (%test-runner-count-list runner)))
- (test-runner-group-stack! runner (cons suite-name
- (test-runner-group-stack runner)))))
-(cond-expand
- (kawa
- ;; Kawa has test-begin built in, implemented as:
- ;; (begin
- ;; (cond-expand (srfi-64 #!void) (else (require 'srfi-64)))
- ;; (%test-begin suite-name [count]))
- ;; This puts test-begin but only test-begin in the default environment.,
- ;; which makes normal test suites loadable without non-portable commands.
- )
- (else
- (define-syntax test-begin
- (syntax-rules ()
- ((test-begin suite-name)
- (%test-begin suite-name #f))
- ((test-begin suite-name count)
- (%test-begin suite-name count))))))
-
-(define (test-on-group-begin-simple runner suite-name count)
- (if (null? (test-runner-group-stack runner))
- (begin
- (display "%%%% Starting test ")
- (display suite-name)
- (if test-log-to-file
- (let* ((log-file-name
- (if (string? test-log-to-file) test-log-to-file
- (string-append suite-name ".log")))
- (log-file
- (cond-expand (mzscheme
- (open-output-file log-file-name 'truncate/replace))
- (else (open-output-file log-file-name)))))
- (display "%%%% Starting test " log-file)
- (display suite-name log-file)
- (newline log-file)
- (test-runner-aux-value! runner log-file)
- (display " (Writing full log to \"")
- (display log-file-name)
- (display "\")")))
- (newline)))
- (let ((log (test-runner-aux-value runner)))
- (if (output-port? log)
- (begin
- (display "Group begin: " log)
- (display suite-name log)
- (newline log))))
- #f)
-
-(define (test-on-group-end-simple runner)
- (let ((log (test-runner-aux-value runner)))
- (if (output-port? log)
- (begin
- (display "Group end: " log)
- (display (car (test-runner-group-stack runner)) log)
- (newline log))))
- #f)
-
-(define (%test-on-bad-count-write runner count expected-count port)
- (display "*** Total number of tests was " port)
- (display count port)
- (display " but should be " port)
- (display expected-count port)
- (display ". ***" port)
- (newline port)
- (display "*** Discrepancy indicates testsuite error or exceptions. ***" port)
- (newline port))
-
-(define (test-on-bad-count-simple runner count expected-count)
- (%test-on-bad-count-write runner count expected-count (current-output-port))
- (let ((log (test-runner-aux-value runner)))
- (if (output-port? log)
- (%test-on-bad-count-write runner count expected-count log))))
-
-(define (test-on-bad-end-name-simple runner begin-name end-name)
- (let ((msg (string-append (%test-format-line runner) "test-end " begin-name
- " does not match test-begin " end-name)))
- (cond-expand
- (srfi-23 (error msg))
- (else (display msg) (newline)))))
-
-
-(define (%test-final-report1 value label port)
- (if (> value 0)
- (begin
- (display label port)
- (display value port)
- (newline port))))
-
-(define (%test-final-report-simple runner port)
- (%test-final-report1 (test-runner-pass-count runner)
- "# of expected passes " port)
- (%test-final-report1 (test-runner-xfail-count runner)
- "# of expected failures " port)
- (%test-final-report1 (test-runner-xpass-count runner)
- "# of unexpected successes " port)
- (%test-final-report1 (test-runner-fail-count runner)
- "# of unexpected failures " port)
- (%test-final-report1 (test-runner-skip-count runner)
- "# of skipped tests " port))
-
-(define (test-on-final-simple runner)
- (%test-final-report-simple runner (current-output-port))
- (let ((log (test-runner-aux-value runner)))
- (if (output-port? log)
- (%test-final-report-simple runner log))))
-
-(define (%test-format-line runner)
- (let* ((line-info (test-result-alist runner))
- (source-file (assq 'source-file line-info))
- (source-line (assq 'source-line line-info))
- (file (if source-file (cdr source-file) "")))
- (if source-line
- (string-append file ":"
- (number->string (cdr source-line)) ": ")
- "")))
-
-(define (%test-end suite-name line-info)
- (let* ((r (test-runner-get))
- (groups (test-runner-group-stack r))
- (line (%test-format-line r)))
- (test-result-alist! r line-info)
- (if (null? groups)
- (let ((msg (string-append line "test-end not in a group")))
- (cond-expand
- (srfi-23 (error msg))
- (else (display msg) (newline)))))
- (if (and suite-name (not (equal? suite-name (car groups))))
- ((test-runner-on-bad-end-name r) r suite-name (car groups)))
- (let* ((count-list (%test-runner-count-list r))
- (expected-count (cdar count-list))
- (saved-count (caar count-list))
- (group-count (- (%test-runner-total-count r) saved-count)))
- (if (and expected-count
- (not (= expected-count group-count)))
- ((test-runner-on-bad-count r) r group-count expected-count))
- ((test-runner-on-group-end r) r)
- (test-runner-group-stack! r (cdr (test-runner-group-stack r)))
- (%test-runner-skip-list! r (car (%test-runner-skip-save r)))
- (%test-runner-skip-save! r (cdr (%test-runner-skip-save r)))
- (%test-runner-fail-list! r (car (%test-runner-fail-save r)))
- (%test-runner-fail-save! r (cdr (%test-runner-fail-save r)))
- (%test-runner-count-list! r (cdr count-list))
- (if (null? (test-runner-group-stack r))
- ((test-runner-on-final r) r)))))
-
-(define-syntax test-group
- (syntax-rules ()
- ((test-group suite-name . body)
- (let ((r (test-runner-current)))
- ;; Ideally should also set line-number, if available.
- (test-result-alist! r (list (cons 'test-name suite-name)))
- (if (%test-should-execute r)
- (dynamic-wind
- (lambda () (test-begin suite-name))
- (lambda () . body)
- (lambda () (test-end suite-name))))))))
-
-(define-syntax test-group-with-cleanup
- (syntax-rules ()
- ((test-group-with-cleanup suite-name form cleanup-form)
- (test-group suite-name
- (dynamic-wind
- (lambda () #f)
- (lambda () form)
- (lambda () cleanup-form))))
- ((test-group-with-cleanup suite-name cleanup-form)
- (test-group-with-cleanup suite-name #f cleanup-form))
- ((test-group-with-cleanup suite-name form1 form2 form3 . rest)
- (test-group-with-cleanup suite-name (begin form1 form2) form3 . rest))))
-
-(define (test-on-test-begin-simple runner)
- (let ((log (test-runner-aux-value runner)))
- (if (output-port? log)
- (let* ((results (test-result-alist runner))
- (source-file (assq 'source-file results))
- (source-line (assq 'source-line results))
- (source-form (assq 'source-form results))
- (test-name (assq 'test-name results)))
- (display "Test begin:" log)
- (newline log)
- (if test-name (%test-write-result1 test-name log))
- (if source-file (%test-write-result1 source-file log))
- (if source-line (%test-write-result1 source-line log))
- (if source-form (%test-write-result1 source-form log))))))
-
-(define-syntax test-result-ref
- (syntax-rules ()
- ((test-result-ref runner pname)
- (test-result-ref runner pname #f))
- ((test-result-ref runner pname default)
- (let ((p (assq pname (test-result-alist runner))))
- (if p (cdr p) default)))))
-
-(define (test-on-test-end-simple runner)
- (let ((log (test-runner-aux-value runner))
- (kind (test-result-ref runner 'result-kind)))
- (if (memq kind '(fail xpass))
- (let* ((results (test-result-alist runner))
- (source-file (assq 'source-file results))
- (source-line (assq 'source-line results))
- (test-name (assq 'test-name results)))
- (if (or source-file source-line)
- (begin
- (if source-file (display (cdr source-file)))
- (display ":")
- (if source-line (display (cdr source-line)))
- (display ": ")))
- (display (if (eq? kind 'xpass) "XPASS" "FAIL"))
- (if test-name
- (begin
- (display " ")
- (display (cdr test-name))))
- (newline)))
- (if (output-port? log)
- (begin
- (display "Test end:" log)
- (newline log)
- (let loop ((list (test-result-alist runner)))
- (if (pair? list)
- (let ((pair (car list)))
- ;; Write out properties not written out by on-test-begin.
- (if (not (memq (car pair)
- '(test-name source-file source-line source-form)))
- (%test-write-result1 pair log))
- (loop (cdr list)))))))))
-
-(define (%test-write-result1 pair port)
- (display " " port)
- (display (car pair) port)
- (display ": " port)
- (write (cdr pair) port)
- (newline port))
-
-(define (test-result-set! runner pname value)
- (let* ((alist (test-result-alist runner))
- (p (assq pname alist)))
- (if p
- (set-cdr! p value)
- (test-result-alist! runner (cons (cons pname value) alist)))))
-
-(define (test-result-clear runner)
- (test-result-alist! runner '()))
-
-(define (test-result-remove runner pname)
- (let* ((alist (test-result-alist runner))
- (p (assq pname alist)))
- (if p
- (test-result-alist! runner
- (let loop ((r alist))
- (if (eq? r p) (cdr r)
- (cons (car r) (loop (cdr r)))))))))
-
-(define (test-result-kind . rest)
- (let ((runner (if (pair? rest) (car rest) (test-runner-current))))
- (test-result-ref runner 'result-kind)))
-
-(define (test-passed? . rest)
- (let ((runner (if (pair? rest) (car rest) (test-runner-get))))
- (memq (test-result-ref runner 'result-kind) '(pass xpass))))
-
-(define (%test-report-result)
- (let* ((r (test-runner-get))
- (result-kind (test-result-kind r)))
- (case result-kind
- ((pass)
- (test-runner-pass-count! r (+ 1 (test-runner-pass-count r))))
- ((fail)
- (test-runner-fail-count! r (+ 1 (test-runner-fail-count r))))
- ((xpass)
- (test-runner-xpass-count! r (+ 1 (test-runner-xpass-count r))))
- ((xfail)
- (test-runner-xfail-count! r (+ 1 (test-runner-xfail-count r))))
- (else
- (test-runner-skip-count! r (+ 1 (test-runner-skip-count r)))))
- (%test-runner-total-count! r (+ 1 (%test-runner-total-count r)))
- ((test-runner-on-test-end r) r)))
-
-(cond-expand
- (guile
- (define-syntax %test-evaluate-with-catch
- (syntax-rules ()
- ((%test-evaluate-with-catch test-expression)
- (catch #t
- (lambda () test-expression)
- (lambda (key . args)
- (test-result-set! (test-runner-current) 'actual-error
- (cons key args))
- #f))))))
- (kawa
- (define-syntax %test-evaluate-with-catch
- (syntax-rules ()
- ((%test-evaluate-with-catch test-expression)
- (try-catch test-expression
- (ex <java.lang.Throwable>
- (test-result-set! (test-runner-current) 'actual-error ex)
- #f))))))
- (srfi-34
- (define-syntax %test-evaluate-with-catch
- (syntax-rules ()
- ((%test-evaluate-with-catch test-expression)
- (guard (err (else #f)) test-expression)))))
- (chicken
- (define-syntax %test-evaluate-with-catch
- (syntax-rules ()
- ((%test-evaluate-with-catch test-expression)
- (condition-case test-expression (ex () #f))))))
- (else
- (define-syntax %test-evaluate-with-catch
- (syntax-rules ()
- ((%test-evaluate-with-catch test-expression)
- test-expression)))))
-
-(cond-expand
- ((or kawa mzscheme)
- (cond-expand
- (mzscheme
- (define-for-syntax (%test-syntax-file form)
- (let ((source (syntax-source form)))
- (cond ((string? source) file)
- ((path? source) (path->string source))
- (else #f)))))
- (kawa
- (define (%test-syntax-file form)
- (syntax-source form))))
- (define (%test-source-line2 form)
- (let* ((line (syntax-line form))
- (file (%test-syntax-file form))
- (line-pair (if line (list (cons 'source-line line)) '())))
- (cons (cons 'source-form (syntax-object->datum form))
- (if file (cons (cons 'source-file file) line-pair) line-pair)))))
- (guile-2
- (define (%test-source-line2 form)
- (let* ((src-props (syntax-source form))
- (file (and src-props (assq-ref src-props 'filename)))
- (line (and src-props (assq-ref src-props 'line)))
- (file-alist (if file
- `((source-file . ,file))
- '()))
- (line-alist (if line
- `((source-line . ,(+ line 1)))
- '())))
- (datum->syntax (syntax here)
- `((source-form . ,(syntax->datum form))
- ,@file-alist
- ,@line-alist)))))
- (else
- (define (%test-source-line2 form)
- '())))
-
-(define (%test-on-test-begin r)
- (%test-should-execute r)
- ((test-runner-on-test-begin r) r)
- (not (eq? 'skip (test-result-ref r 'result-kind))))
-
-(define (%test-on-test-end r result)
- (test-result-set! r 'result-kind
- (if (eq? (test-result-ref r 'result-kind) 'xfail)
- (if result 'xpass 'xfail)
- (if result 'pass 'fail))))
-
-(define (test-runner-test-name runner)
- (test-result-ref runner 'test-name ""))
-
-(define-syntax %test-comp2body
- (syntax-rules ()
- ((%test-comp2body r comp expected expr)
- (let ()
- (if (%test-on-test-begin r)
- (let ((exp expected))
- (test-result-set! r 'expected-value exp)
- (let ((res (%test-evaluate-with-catch expr)))
- (test-result-set! r 'actual-value res)
- (%test-on-test-end r (comp exp res)))))
- (%test-report-result)))))
-
-(define (%test-approximate= error)
- (lambda (value expected)
- (let ((rval (real-part value))
- (ival (imag-part value))
- (rexp (real-part expected))
- (iexp (imag-part expected)))
- (and (>= rval (- rexp error))
- (>= ival (- iexp error))
- (<= rval (+ rexp error))
- (<= ival (+ iexp error))))))
-
-(define-syntax %test-comp1body
- (syntax-rules ()
- ((%test-comp1body r expr)
- (let ()
- (if (%test-on-test-begin r)
- (let ()
- (let ((res (%test-evaluate-with-catch expr)))
- (test-result-set! r 'actual-value res)
- (%test-on-test-end r res))))
- (%test-report-result)))))
-
-(cond-expand
- ((or kawa mzscheme guile-2)
- ;; Should be made to work for any Scheme with syntax-case
- ;; However, I haven't gotten the quoting working. FIXME.
- (define-syntax test-end
- (lambda (x)
- (syntax-case (list x (list (syntax quote) (%test-source-line2 x))) ()
- (((mac suite-name) line)
- (syntax
- (%test-end suite-name line)))
- (((mac) line)
- (syntax
- (%test-end #f line))))))
- (define-syntax test-assert
- (lambda (x)
- (syntax-case (list x (list (syntax quote) (%test-source-line2 x))) ()
- (((mac tname expr) line)
- (syntax
- (let* ((r (test-runner-get))
- (name tname))
- (test-result-alist! r (cons (cons 'test-name tname) line))
- (%test-comp1body r expr))))
- (((mac expr) line)
- (syntax
- (let* ((r (test-runner-get)))
- (test-result-alist! r line)
- (%test-comp1body r expr)))))))
- (define (%test-comp2 comp x)
- (syntax-case (list x (list (syntax quote) (%test-source-line2 x)) comp) ()
- (((mac tname expected expr) line comp)
- (syntax
- (let* ((r (test-runner-get))
- (name tname))
- (test-result-alist! r (cons (cons 'test-name tname) line))
- (%test-comp2body r comp expected expr))))
- (((mac expected expr) line comp)
- (syntax
- (let* ((r (test-runner-get)))
- (test-result-alist! r line)
- (%test-comp2body r comp expected expr))))))
- (define-syntax test-eqv
- (lambda (x) (%test-comp2 (syntax eqv?) x)))
- (define-syntax test-eq
- (lambda (x) (%test-comp2 (syntax eq?) x)))
- (define-syntax test-equal
- (lambda (x) (%test-comp2 (syntax equal?) x)))
- (define-syntax test-approximate ;; FIXME - needed for non-Kawa
- (lambda (x)
- (syntax-case (list x (list (syntax quote) (%test-source-line2 x))) ()
- (((mac tname expected expr error) line)
- (syntax
- (let* ((r (test-runner-get))
- (name tname))
- (test-result-alist! r (cons (cons 'test-name tname) line))
- (%test-comp2body r (%test-approximate= error) expected expr))))
- (((mac expected expr error) line)
- (syntax
- (let* ((r (test-runner-get)))
- (test-result-alist! r line)
- (%test-comp2body r (%test-approximate= error) expected expr))))))))
- (else
- (define-syntax test-end
- (syntax-rules ()
- ((test-end)
- (%test-end #f '()))
- ((test-end suite-name)
- (%test-end suite-name '()))))
- (define-syntax test-assert
- (syntax-rules ()
- ((test-assert tname test-expression)
- (let* ((r (test-runner-get))
- (name tname))
- (test-result-alist! r '((test-name . tname)))
- (%test-comp1body r test-expression)))
- ((test-assert test-expression)
- (let* ((r (test-runner-get)))
- (test-result-alist! r '())
- (%test-comp1body r test-expression)))))
- (define-syntax %test-comp2
- (syntax-rules ()
- ((%test-comp2 comp tname expected expr)
- (let* ((r (test-runner-get))
- (name tname))
- (test-result-alist! r (list (cons 'test-name tname)))
- (%test-comp2body r comp expected expr)))
- ((%test-comp2 comp expected expr)
- (let* ((r (test-runner-get)))
- (test-result-alist! r '())
- (%test-comp2body r comp expected expr)))))
- (define-syntax test-equal
- (syntax-rules ()
- ((test-equal . rest)
- (%test-comp2 equal? . rest))))
- (define-syntax test-eqv
- (syntax-rules ()
- ((test-eqv . rest)
- (%test-comp2 eqv? . rest))))
- (define-syntax test-eq
- (syntax-rules ()
- ((test-eq . rest)
- (%test-comp2 eq? . rest))))
- (define-syntax test-approximate
- (syntax-rules ()
- ((test-approximate tname expected expr error)
- (%test-comp2 (%test-approximate= error) tname expected expr))
- ((test-approximate expected expr error)
- (%test-comp2 (%test-approximate= error) expected expr))))))
-
-(cond-expand
- (guile
- (define-syntax %test-error
- (syntax-rules ()
- ((%test-error r etype expr)
- (cond ((%test-on-test-begin r)
- (let ((et etype))
- (test-result-set! r 'expected-error et)
- (%test-on-test-end r
- (catch #t
- (lambda ()
- (test-result-set! r 'actual-value expr)
- #f)
- (lambda (key . args)
- ;; TODO: decide how to specify expected
- ;; error types for Guile.
- (test-result-set! r 'actual-error
- (cons key args))
- #t)))
- (%test-report-result))))))))
- (mzscheme
- (define-syntax %test-error
- (syntax-rules ()
- ((%test-error r etype expr)
- (%test-comp1body r (with-handlers (((lambda (h) #t) (lambda (h) #t)))
- (let ()
- (test-result-set! r 'actual-value expr)
- #f)))))))
- (chicken
- (define-syntax %test-error
- (syntax-rules ()
- ((%test-error r etype expr)
- (%test-comp1body r (condition-case expr (ex () #t)))))))
- (kawa
- (define-syntax %test-error
- (syntax-rules ()
- ((%test-error r #t expr)
- (cond ((%test-on-test-begin r)
- (test-result-set! r 'expected-error #t)
- (%test-on-test-end r
- (try-catch
- (let ()
- (test-result-set! r 'actual-value expr)
- #f)
- (ex <java.lang.Throwable>
- (test-result-set! r 'actual-error ex)
- #t)))
- (%test-report-result))))
- ((%test-error r etype expr)
- (if (%test-on-test-begin r)
- (let ((et etype))
- (test-result-set! r 'expected-error et)
- (%test-on-test-end r
- (try-catch
- (let ()
- (test-result-set! r 'actual-value expr)
- #f)
- (ex <java.lang.Throwable>
- (test-result-set! r 'actual-error ex)
- (cond ((and (instance? et <gnu.bytecode.ClassType>)
- (gnu.bytecode.ClassType:isSubclass et <java.lang.Throwable>))
- (instance? ex et))
- (else #t)))))
- (%test-report-result)))))))
- ((and srfi-34 srfi-35)
- (define-syntax %test-error
- (syntax-rules ()
- ((%test-error r etype expr)
- (%test-comp1body r (guard (ex ((condition-type? etype)
- (and (condition? ex) (condition-has-type? ex etype)))
- ((procedure? etype)
- (etype ex))
- ((equal? etype #t)
- #t)
- (else #t))
- expr #f))))))
- (srfi-34
- (define-syntax %test-error
- (syntax-rules ()
- ((%test-error r etype expr)
- (%test-comp1body r (guard (ex (else #t)) expr #f))))))
- (else
- (define-syntax %test-error
- (syntax-rules ()
- ((%test-error r etype expr)
- (begin
- ((test-runner-on-test-begin r) r)
- (test-result-set! r 'result-kind 'skip)
- (%test-report-result)))))))
-
-(cond-expand
- ((or kawa mzscheme guile-2)
-
- (define-syntax test-error
- (lambda (x)
- (syntax-case (list x (list (syntax quote) (%test-source-line2 x))) ()
- (((mac tname etype expr) line)
- (syntax
- (let* ((r (test-runner-get))
- (name tname))
- (test-result-alist! r (cons (cons 'test-name tname) line))
- (%test-error r etype expr))))
- (((mac etype expr) line)
- (syntax
- (let* ((r (test-runner-get)))
- (test-result-alist! r line)
- (%test-error r etype expr))))
- (((mac expr) line)
- (syntax
- (let* ((r (test-runner-get)))
- (test-result-alist! r line)
- (%test-error r #t expr))))))))
- (else
- (define-syntax test-error
- (syntax-rules ()
- ((test-error name etype expr)
- (let ((r (test-runner-get)))
- (test-result-alist! r `((test-name . ,name)))
- (%test-error r etype expr)))
- ((test-error etype expr)
- (let ((r (test-runner-get)))
- (test-result-alist! r '())
- (%test-error r etype expr)))
- ((test-error expr)
- (let ((r (test-runner-get)))
- (test-result-alist! r '())
- (%test-error r #t expr)))))))
-
-(define (test-apply first . rest)
- (if (test-runner? first)
- (test-with-runner first (apply test-apply rest))
- (let ((r (test-runner-current)))
- (if r
- (let ((run-list (%test-runner-run-list r)))
- (cond ((null? rest)
- (%test-runner-run-list! r (reverse run-list))
- (first)) ;; actually apply procedure thunk
- (else
- (%test-runner-run-list!
- r
- (if (eq? run-list #t) (list first) (cons first run-list)))
- (apply test-apply rest)
- (%test-runner-run-list! r run-list))))
- (let ((r (test-runner-create)))
- (test-with-runner r (apply test-apply first rest))
- ((test-runner-on-final r) r))))))
-
-(define-syntax test-with-runner
- (syntax-rules ()
- ((test-with-runner runner form ...)
- (let ((saved-runner (test-runner-current)))
- (dynamic-wind
- (lambda () (test-runner-current runner))
- (lambda () form ...)
- (lambda () (test-runner-current saved-runner)))))))
-
-;;; Predicates
-
-(define (%test-match-nth n count)
- (let ((i 0))
- (lambda (runner)
- (set! i (+ i 1))
- (and (>= i n) (< i (+ n count))))))
-
-(define-syntax test-match-nth
- (syntax-rules ()
- ((test-match-nth n)
- (test-match-nth n 1))
- ((test-match-nth n count)
- (%test-match-nth n count))))
-
-(define (%test-match-all . pred-list)
- (lambda (runner)
- (let ((result #t))
- (let loop ((l pred-list))
- (if (null? l)
- result
- (begin
- (if (not ((car l) runner))
- (set! result #f))
- (loop (cdr l))))))))
-
-(define-syntax test-match-all
- (syntax-rules ()
- ((test-match-all pred ...)
- (%test-match-all (%test-as-specifier pred) ...))))
-
-(define (%test-match-any . pred-list)
- (lambda (runner)
- (let ((result #f))
- (let loop ((l pred-list))
- (if (null? l)
- result
- (begin
- (if ((car l) runner)
- (set! result #t))
- (loop (cdr l))))))))
-
-(define-syntax test-match-any
- (syntax-rules ()
- ((test-match-any pred ...)
- (%test-match-any (%test-as-specifier pred) ...))))
-
-;; Coerce to a predicate function:
-(define (%test-as-specifier specifier)
- (cond ((procedure? specifier) specifier)
- ((integer? specifier) (test-match-nth 1 specifier))
- ((string? specifier) (test-match-name specifier))
- (else
- (error "not a valid test specifier"))))
-
-(define-syntax test-skip
- (syntax-rules ()
- ((test-skip pred ...)
- (let ((runner (test-runner-get)))
- (%test-runner-skip-list! runner
- (cons (test-match-all (%test-as-specifier pred) ...)
- (%test-runner-skip-list runner)))))))
-
-(define-syntax test-expect-fail
- (syntax-rules ()
- ((test-expect-fail pred ...)
- (let ((runner (test-runner-get)))
- (%test-runner-fail-list! runner
- (cons (test-match-all (%test-as-specifier pred) ...)
- (%test-runner-fail-list runner)))))))
-
-(define (test-match-name name)
- (lambda (runner)
- (equal? name (test-runner-test-name runner))))
-
-(define (test-read-eval-string string)
- (let* ((port (open-input-string string))
- (form (read port)))
- (if (eof-object? (read-char port))
- (cond-expand
- (guile (eval form (current-module)))
- (else (eval form)))
- (cond-expand
- (srfi-23 (error "(not at eof)"))
- (else "error")))))
-
-;;; srfi-67.scm --- Compare Procedures
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library. If not, see
-;; <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This module is not yet documented in the Guile Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-67)
- #\export (</<=?
- </<?
- <=/<=?
- <=/<?
- <=?
- <?
- =?
- >/>=?
- >/>?
- >=/>=?
- >=/>?
- >=?
- >?
- boolean-compare
- chain<=?
- chain<?
- chain=?
- chain>=?
- chain>?
- char-compare
- char-compare-ci
- compare-by<
- compare-by<=
- compare-by=/<
- compare-by=/>
- compare-by>
- compare-by>=
- complex-compare
- cond-compare
- debug-compare
- default-compare
- if-not=?
- if3
- if<=?
- if<?
- if=?
- if>=?
- if>?
- integer-compare
- kth-largest
- list-compare
- list-compare-as-vector
- max-compare
- min-compare
- not=?
- number-compare
- pair-compare
- pair-compare-car
- pair-compare-cdr
- pairwise-not=?
- rational-compare
- real-compare
- refine-compare
- select-compare
- symbol-compare
- vector-compare
- vector-compare-as-list)
- #\replace (string-compare string-compare-ci)
- #\use-module (srfi srfi-27))
-
-(cond-expand-provide (current-module) '(srfi-67))
-
-(include-from-path "srfi/srfi-67/compare.scm")
-; Copyright (c) 2011 Free Software Foundation, Inc.
-; Copyright (c) 2005 Sebastian Egner and Jens Axel S{\o}gaard.
-;
-; Permission is hereby granted, free of charge, to any person obtaining
-; a copy of this software and associated documentation files (the
-; ``Software''), to deal in the Software without restriction, including
-; without limitation the rights to use, copy, modify, merge, publish,
-; distribute, sublicense, and/or sell copies of the Software, and to
-; permit persons to whom the Software is furnished to do so, subject to
-; the following conditions:
-;
-; The above copyright notice and this permission notice shall be
-; included in all copies or substantial portions of the Software.
-;
-; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
-; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-;
-; -----------------------------------------------------------------------
-;
-; Compare procedures SRFI (reference implementation)
-; Sebastian.Egner@philips.com, Jensaxel@soegaard.net
-; history of this file:
-; SE, 14-Oct-2004: first version
-; SE, 18-Oct-2004: 1st redesign: axioms for 'compare function'
-; SE, 29-Oct-2004: 2nd redesign: higher order reverse/map/refine/unite
-; SE, 2-Nov-2004: 3rd redesign: macros cond/refine-compare replace h.o.f's
-; SE, 10-Nov-2004: (im,re) replaced by (re,im) in complex-compare
-; SE, 11-Nov-2004: case-compare by case (not by cond); select-compare added
-; SE, 12-Jan-2005: pair-compare-cdr
-; SE, 15-Feb-2005: stricter typing for compare-<type>; pairwise-not=?
-; SE, 16-Feb-2005: case-compare -> if-compare -> if3; <? </<? chain<? etc.
-; JS, 24-Feb-2005: selection-compare added
-; SE, 25-Feb-2005: selection-compare -> kth-largest modified; if<? etc.
-; JS, 28-Feb-2005: kth-largest modified - is "stable" now
-; SE, 28-Feb-2005: simplified pairwise-not=?/kth-largest; min/max debugged
-; SE, 07-Apr-2005: compare-based type checks made explicit
-; SE, 18-Apr-2005: added (rel? compare) and eq?-test
-; SE, 16-May-2005: naming convention changed; compare-by< etc. optional x y
-
-; =============================================================================
-
-; Reference Implementation
-; ========================
-;
-; in R5RS (including hygienic macros)
-; + SRFI-16 (case-lambda)
-; + SRFI-23 (error)
-; + SRFI-27 (random-integer)
-
-; Implementation remarks:
-; * In general, the emphasis of this implementation is on correctness
-; and portability, not on efficiency.
-; * Variable arity procedures are expressed in terms of case-lambda
-; in the hope that this will produce efficient code for the case
-; where the arity is statically known at the call site.
-; * In procedures that are required to type-check their arguments,
-; we use (compare x x) for executing extra checks. This relies on
-; the assumption that eq? is used to catch this case quickly.
-; * Care has been taken to reference comparison procedures of R5RS
-; only at the time the operations here are being defined. This
-; makes it possible to redefine these operations, if need be.
-; * For the sake of efficiency, some inlining has been done by hand.
-; This is mainly expressed by macros producing defines.
-; * Identifiers of the form compare:<something> are private.
-;
-; Hints for low-level implementation:
-; * The basis of this SRFI are the atomic compare procedures,
-; i.e. boolean-compare, char-compare, etc. and the conditionals
-; if3, if=?, if<? etc., and default-compare. These should make
-; optimal use of the available type information.
-; * For the sake of speed, the reference implementation does not
-; use a LET to save the comparison value c for the ERROR call.
-; This can be fixed in a low-level implementation at no cost.
-; * Type-checks based on (compare x x) are made explicit by the
-; expression (compare:check result compare x ...).
-; * Eq? should can used to speed up built-in compare procedures,
-; but it can only be used after type-checking at least one of
-; the arguments.
-
-(define (compare:checked result compare . args)
- (for-each (lambda (x) (compare x x)) args)
- result)
-
-
-; 3-sided conditional
-
-(define-syntax-rule (if3 c less equal greater)
- (case c
- ((-1) less)
- (( 0) equal)
- (( 1) greater)
- (else (error "comparison value not in {-1,0,1}"))))
-
-
-; 2-sided conditionals for comparisons
-
-(define-syntax compare:if-rel?
- (syntax-rules ()
- ((compare:if-rel? c-cases a-cases c consequence)
- (compare:if-rel? c-cases a-cases c consequence (if #f #f)))
- ((compare:if-rel? c-cases a-cases c consequence alternate)
- (case c
- (c-cases consequence)
- (a-cases alternate)
- (else (error "comparison value not in {-1,0,1}"))))))
-
-(define-syntax-rule (if=? arg ...)
- (compare:if-rel? (0) (-1 1) arg ...))
-
-(define-syntax-rule (if<? arg ...)
- (compare:if-rel? (-1) (0 1) arg ...))
-
-(define-syntax-rule (if>? arg ...)
- (compare:if-rel? (1) (-1 0) arg ...))
-
-(define-syntax-rule (if<=? arg ...)
- (compare:if-rel? (-1 0) (1) arg ...))
-
-(define-syntax-rule (if>=? arg ...)
- (compare:if-rel? (0 1) (-1) arg ...))
-
-(define-syntax-rule (if-not=? arg ...)
- (compare:if-rel? (-1 1) (0) arg ...))
-
-
-; predicates from compare procedures
-
-(define-syntax-rule (compare:define-rel? rel? if-rel?)
- (define rel?
- (case-lambda
- (() (lambda (x y) (if-rel? (default-compare x y) #t #f)))
- ((compare) (lambda (x y) (if-rel? (compare x y) #t #f)))
- ((x y) (if-rel? (default-compare x y) #t #f))
- ((compare x y)
- (if (procedure? compare)
- (if-rel? (compare x y) #t #f)
- (error "not a procedure (Did you mean rel/rel??): " compare))))))
-
-(compare:define-rel? =? if=?)
-(compare:define-rel? <? if<?)
-(compare:define-rel? >? if>?)
-(compare:define-rel? <=? if<=?)
-(compare:define-rel? >=? if>=?)
-(compare:define-rel? not=? if-not=?)
-
-
-; chains of length 3
-
-(define-syntax-rule (compare:define-rel1/rel2? rel1/rel2? if-rel1? if-rel2?)
- (define rel1/rel2?
- (case-lambda
- (()
- (lambda (x y z)
- (if-rel1? (default-compare x y)
- (if-rel2? (default-compare y z) #t #f)
- (compare:checked #f default-compare z))))
- ((compare)
- (lambda (x y z)
- (if-rel1? (compare x y)
- (if-rel2? (compare y z) #t #f)
- (compare:checked #f compare z))))
- ((x y z)
- (if-rel1? (default-compare x y)
- (if-rel2? (default-compare y z) #t #f)
- (compare:checked #f default-compare z)))
- ((compare x y z)
- (if-rel1? (compare x y)
- (if-rel2? (compare y z) #t #f)
- (compare:checked #f compare z))))))
-
-(compare:define-rel1/rel2? </<? if<? if<?)
-(compare:define-rel1/rel2? </<=? if<? if<=?)
-(compare:define-rel1/rel2? <=/<? if<=? if<?)
-(compare:define-rel1/rel2? <=/<=? if<=? if<=?)
-(compare:define-rel1/rel2? >/>? if>? if>?)
-(compare:define-rel1/rel2? >/>=? if>? if>=?)
-(compare:define-rel1/rel2? >=/>? if>=? if>?)
-(compare:define-rel1/rel2? >=/>=? if>=? if>=?)
-
-
-; chains of arbitrary length
-
-(define-syntax-rule (compare:define-chain-rel? chain-rel? if-rel?)
- (define chain-rel?
- (case-lambda
- ((compare)
- #t)
- ((compare x1)
- (compare:checked #t compare x1))
- ((compare x1 x2)
- (if-rel? (compare x1 x2) #t #f))
- ((compare x1 x2 x3)
- (if-rel? (compare x1 x2)
- (if-rel? (compare x2 x3) #t #f)
- (compare:checked #f compare x3)))
- ((compare x1 x2 . x3+)
- (if-rel? (compare x1 x2)
- (let chain? ((head x2) (tail x3+))
- (if (null? tail)
- #t
- (if-rel? (compare head (car tail))
- (chain? (car tail) (cdr tail))
- (apply compare:checked #f
- compare (cdr tail)))))
- (apply compare:checked #f compare x3+))))))
-
-(compare:define-chain-rel? chain=? if=?)
-(compare:define-chain-rel? chain<? if<?)
-(compare:define-chain-rel? chain>? if>?)
-(compare:define-chain-rel? chain<=? if<=?)
-(compare:define-chain-rel? chain>=? if>=?)
-
-
-; pairwise inequality
-
-(define pairwise-not=?
- (let ((= =) (<= <=))
- (case-lambda
- ((compare)
- #t)
- ((compare x1)
- (compare:checked #t compare x1))
- ((compare x1 x2)
- (if-not=? (compare x1 x2) #t #f))
- ((compare x1 x2 x3)
- (if-not=? (compare x1 x2)
- (if-not=? (compare x2 x3)
- (if-not=? (compare x1 x3) #t #f)
- #f)
- (compare:checked #f compare x3)))
- ((compare . x1+)
- (let unequal? ((x x1+) (n (length x1+)) (unchecked? #t))
- (if (< n 2)
- (if (and unchecked? (= n 1))
- (compare:checked #t compare (car x))
- #t)
- (let* ((i-pivot (random-integer n))
- (x-pivot (list-ref x i-pivot)))
- (let split ((i 0) (x x) (x< '()) (x> '()))
- (if (null? x)
- (and (unequal? x< (length x<) #f)
- (unequal? x> (length x>) #f))
- (if (= i i-pivot)
- (split (+ i 1) (cdr x) x< x>)
- (if3 (compare (car x) x-pivot)
- (split (+ i 1) (cdr x) (cons (car x) x<) x>)
- (if unchecked?
- (apply compare:checked #f compare (cdr x))
- #f)
- (split (+ i 1) (cdr x) x< (cons (car x) x>)))))))))))))
-
-
-; min/max
-
-(define min-compare
- (case-lambda
- ((compare x1)
- (compare:checked x1 compare x1))
- ((compare x1 x2)
- (if<=? (compare x1 x2) x1 x2))
- ((compare x1 x2 x3)
- (if<=? (compare x1 x2)
- (if<=? (compare x1 x3) x1 x3)
- (if<=? (compare x2 x3) x2 x3)))
- ((compare x1 x2 x3 x4)
- (if<=? (compare x1 x2)
- (if<=? (compare x1 x3)
- (if<=? (compare x1 x4) x1 x4)
- (if<=? (compare x3 x4) x3 x4))
- (if<=? (compare x2 x3)
- (if<=? (compare x2 x4) x2 x4)
- (if<=? (compare x3 x4) x3 x4))))
- ((compare x1 x2 . x3+)
- (let min ((xmin (if<=? (compare x1 x2) x1 x2)) (xs x3+))
- (if (null? xs)
- xmin
- (min (if<=? (compare xmin (car xs)) xmin (car xs))
- (cdr xs)))))))
-
-(define max-compare
- (case-lambda
- ((compare x1)
- (compare:checked x1 compare x1))
- ((compare x1 x2)
- (if>=? (compare x1 x2) x1 x2))
- ((compare x1 x2 x3)
- (if>=? (compare x1 x2)
- (if>=? (compare x1 x3) x1 x3)
- (if>=? (compare x2 x3) x2 x3)))
- ((compare x1 x2 x3 x4)
- (if>=? (compare x1 x2)
- (if>=? (compare x1 x3)
- (if>=? (compare x1 x4) x1 x4)
- (if>=? (compare x3 x4) x3 x4))
- (if>=? (compare x2 x3)
- (if>=? (compare x2 x4) x2 x4)
- (if>=? (compare x3 x4) x3 x4))))
- ((compare x1 x2 . x3+)
- (let max ((xmax (if>=? (compare x1 x2) x1 x2)) (xs x3+))
- (if (null? xs)
- xmax
- (max (if>=? (compare xmax (car xs)) xmax (car xs))
- (cdr xs)))))))
-
-
-; kth-largest
-
-(define kth-largest
- (let ((= =) (< <))
- (case-lambda
- ((compare k x0)
- (case (modulo k 1)
- ((0) (compare:checked x0 compare x0))
- (else (error "bad index" k))))
- ((compare k x0 x1)
- (case (modulo k 2)
- ((0) (if<=? (compare x0 x1) x0 x1))
- ((1) (if<=? (compare x0 x1) x1 x0))
- (else (error "bad index" k))))
- ((compare k x0 x1 x2)
- (case (modulo k 3)
- ((0) (if<=? (compare x0 x1)
- (if<=? (compare x0 x2) x0 x2)
- (if<=? (compare x1 x2) x1 x2)))
- ((1) (if3 (compare x0 x1)
- (if<=? (compare x1 x2)
- x1
- (if<=? (compare x0 x2) x2 x0))
- (if<=? (compare x0 x2) x1 x0)
- (if<=? (compare x0 x2)
- x0
- (if<=? (compare x1 x2) x2 x1))))
- ((2) (if<=? (compare x0 x1)
- (if<=? (compare x1 x2) x2 x1)
- (if<=? (compare x0 x2) x2 x0)))
- (else (error "bad index" k))))
- ((compare k x0 . x1+) ; |x1+| >= 1
- (if (not (and (integer? k) (exact? k)))
- (error "bad index" k))
- (let ((n (+ 1 (length x1+))))
- (let kth ((k (modulo k n))
- (n n) ; = |x|
- (rev #t) ; are x<, x=, x> reversed?
- (x (cons x0 x1+)))
- (let ((pivot (list-ref x (random-integer n))))
- (let split ((x x) (x< '()) (n< 0) (x= '()) (n= 0) (x> '()) (n> 0))
- (if (null? x)
- (cond
- ((< k n<)
- (kth k n< (not rev) x<))
- ((< k (+ n< n=))
- (if rev
- (list-ref x= (- (- n= 1) (- k n<)))
- (list-ref x= (- k n<))))
- (else
- (kth (- k (+ n< n=)) n> (not rev) x>)))
- (if3 (compare (car x) pivot)
- (split (cdr x) (cons (car x) x<) (+ n< 1) x= n= x> n>)
- (split (cdr x) x< n< (cons (car x) x=) (+ n= 1) x> n>)
- (split (cdr x) x< n< x= n= (cons (car x) x>) (+ n> 1))))))))))))
-
-
-; compare functions from predicates
-
-(define compare-by<
- (case-lambda
- ((lt) (lambda (x y) (if (lt x y) -1 (if (lt y x) 1 0))))
- ((lt x y) (if (lt x y) -1 (if (lt y x) 1 0)))))
-
-(define compare-by>
- (case-lambda
- ((gt) (lambda (x y) (if (gt x y) 1 (if (gt y x) -1 0))))
- ((gt x y) (if (gt x y) 1 (if (gt y x) -1 0)))))
-
-(define compare-by<=
- (case-lambda
- ((le) (lambda (x y) (if (le x y) (if (le y x) 0 -1) 1)))
- ((le x y) (if (le x y) (if (le y x) 0 -1) 1))))
-
-(define compare-by>=
- (case-lambda
- ((ge) (lambda (x y) (if (ge x y) (if (ge y x) 0 1) -1)))
- ((ge x y) (if (ge x y) (if (ge y x) 0 1) -1))))
-
-(define compare-by=/<
- (case-lambda
- ((eq lt) (lambda (x y) (if (eq x y) 0 (if (lt x y) -1 1))))
- ((eq lt x y) (if (eq x y) 0 (if (lt x y) -1 1)))))
-
-(define compare-by=/>
- (case-lambda
- ((eq gt) (lambda (x y) (if (eq x y) 0 (if (gt x y) 1 -1))))
- ((eq gt x y) (if (eq x y) 0 (if (gt x y) 1 -1)))))
-
-; refine and extend construction
-
-(define-syntax refine-compare
- (syntax-rules ()
- ((refine-compare)
- 0)
- ((refine-compare c1)
- c1)
- ((refine-compare c1 c2 cs ...)
- (if3 c1 -1 (refine-compare c2 cs ...) 1))))
-
-(define-syntax select-compare
- (syntax-rules (else)
- ((select-compare x y clause ...)
- (let ((x-val x) (y-val y))
- (select-compare (x-val y-val clause ...))))
- ; used internally: (select-compare (x y clause ...))
- ((select-compare (x y))
- 0)
- ((select-compare (x y (else c ...)))
- (refine-compare c ...))
- ((select-compare (x y (t? c ...) clause ...))
- (let ((t?-val t?))
- (let ((tx (t?-val x)) (ty (t?-val y)))
- (if tx
- (if ty (refine-compare c ...) -1)
- (if ty 1 (select-compare (x y clause ...)))))))))
-
-(define-syntax cond-compare
- (syntax-rules (else)
- ((cond-compare)
- 0)
- ((cond-compare (else cs ...))
- (refine-compare cs ...))
- ((cond-compare ((tx ty) cs ...) clause ...)
- (let ((tx-val tx) (ty-val ty))
- (if tx-val
- (if ty-val (refine-compare cs ...) -1)
- (if ty-val 1 (cond-compare clause ...)))))))
-
-
-; R5RS atomic types
-
-(define-syntax compare:type-check
- (syntax-rules ()
- ((compare:type-check type? type-name x)
- (if (not (type? x))
- (error (string-append "not " type-name ":") x)))
- ((compare:type-check type? type-name x y)
- (begin (compare:type-check type? type-name x)
- (compare:type-check type? type-name y)))))
-
-(define-syntax-rule (compare:define-by=/< compare = < type? type-name)
- (define compare
- (let ((= =) (< <))
- (lambda (x y)
- (if (type? x)
- (if (eq? x y)
- 0
- (if (type? y)
- (if (= x y) 0 (if (< x y) -1 1))
- (error (string-append "not " type-name ":") y)))
- (error (string-append "not " type-name ":") x))))))
-
-(define (boolean-compare x y)
- (compare:type-check boolean? "boolean" x y)
- (if x (if y 0 1) (if y -1 0)))
-
-(compare:define-by=/< char-compare char=? char<? char? "char")
-
-(compare:define-by=/< char-compare-ci char-ci=? char-ci<? char? "char")
-
-(compare:define-by=/< string-compare string=? string<? string? "string")
-
-(compare:define-by=/< string-compare-ci string-ci=? string-ci<? string? "string")
-
-(define (symbol-compare x y)
- (compare:type-check symbol? "symbol" x y)
- (string-compare (symbol->string x) (symbol->string y)))
-
-(compare:define-by=/< integer-compare = < integer? "integer")
-
-(compare:define-by=/< rational-compare = < rational? "rational")
-
-(compare:define-by=/< real-compare = < real? "real")
-
-(define (complex-compare x y)
- (compare:type-check complex? "complex" x y)
- (if (and (real? x) (real? y))
- (real-compare x y)
- (refine-compare (real-compare (real-part x) (real-part y))
- (real-compare (imag-part x) (imag-part y)))))
-
-(define (number-compare x y)
- (compare:type-check number? "number" x y)
- (complex-compare x y))
-
-
-; R5RS compound data structures: dotted pair, list, vector
-
-(define (pair-compare-car compare)
- (lambda (x y)
- (compare (car x) (car y))))
-
-(define (pair-compare-cdr compare)
- (lambda (x y)
- (compare (cdr x) (cdr y))))
-
-(define pair-compare
- (case-lambda
-
- ; dotted pair
- ((pair-compare-car pair-compare-cdr x y)
- (refine-compare (pair-compare-car (car x) (car y))
- (pair-compare-cdr (cdr x) (cdr y))))
-
- ; possibly improper lists
- ((compare x y)
- (cond-compare
- (((null? x) (null? y)) 0)
- (((pair? x) (pair? y)) (compare (car x) (car y))
- (pair-compare compare (cdr x) (cdr y)))
- (else (compare x y))))
-
- ; for convenience
- ((x y)
- (pair-compare default-compare x y))))
-
-(define list-compare
- (case-lambda
- ((compare x y empty? head tail)
- (cond-compare
- (((empty? x) (empty? y)) 0)
- (else (compare (head x) (head y))
- (list-compare compare (tail x) (tail y) empty? head tail))))
-
- ; for convenience
- (( x y empty? head tail)
- (list-compare default-compare x y empty? head tail))
- ((compare x y )
- (list-compare compare x y null? car cdr))
- (( x y )
- (list-compare default-compare x y null? car cdr))))
-
-(define list-compare-as-vector
- (case-lambda
- ((compare x y empty? head tail)
- (refine-compare
- (let compare-length ((x x) (y y))
- (cond-compare
- (((empty? x) (empty? y)) 0)
- (else (compare-length (tail x) (tail y)))))
- (list-compare compare x y empty? head tail)))
-
- ; for convenience
- (( x y empty? head tail)
- (list-compare-as-vector default-compare x y empty? head tail))
- ((compare x y )
- (list-compare-as-vector compare x y null? car cdr))
- (( x y )
- (list-compare-as-vector default-compare x y null? car cdr))))
-
-(define vector-compare
- (let ((= =))
- (case-lambda
- ((compare x y size ref)
- (let ((n (size x)) (m (size y)))
- (refine-compare
- (integer-compare n m)
- (let compare-rest ((i 0)) ; compare x[i..n-1] y[i..n-1]
- (if (= i n)
- 0
- (refine-compare (compare (ref x i) (ref y i))
- (compare-rest (+ i 1))))))))
-
- ; for convenience
- (( x y size ref)
- (vector-compare default-compare x y size ref))
- ((compare x y )
- (vector-compare compare x y vector-length vector-ref))
- (( x y )
- (vector-compare default-compare x y vector-length vector-ref)))))
-
-(define vector-compare-as-list
- (let ((= =))
- (case-lambda
- ((compare x y size ref)
- (let ((nx (size x)) (ny (size y)))
- (let ((n (min nx ny)))
- (let compare-rest ((i 0)) ; compare x[i..n-1] y[i..n-1]
- (if (= i n)
- (integer-compare nx ny)
- (refine-compare (compare (ref x i) (ref y i))
- (compare-rest (+ i 1))))))))
-
- ; for convenience
- (( x y size ref)
- (vector-compare-as-list default-compare x y size ref))
- ((compare x y )
- (vector-compare-as-list compare x y vector-length vector-ref))
- (( x y )
- (vector-compare-as-list default-compare x y vector-length vector-ref)))))
-
-
-; default compare
-
-(define (default-compare x y)
- (select-compare
- x y
- (null? 0)
- (pair? (default-compare (car x) (car y))
- (default-compare (cdr x) (cdr y)))
- (boolean? (boolean-compare x y))
- (char? (char-compare x y))
- (string? (string-compare x y))
- (symbol? (symbol-compare x y))
- (number? (number-compare x y))
- (vector? (vector-compare default-compare x y))
- (else (error "unrecognized type in default-compare" x y))))
-
-; Note that we pass default-compare to compare-{pair,vector} explictly.
-; This makes sure recursion proceeds with this default-compare, which
-; need not be the one in the lexical scope of compare-{pair,vector}.
-
-
-; debug compare
-
-(define (debug-compare c)
-
- (define (checked-value c x y)
- (let ((c-xy (c x y)))
- (if (or (eqv? c-xy -1) (eqv? c-xy 0) (eqv? c-xy 1))
- c-xy
- (error "compare value not in {-1,0,1}" c-xy (list c x y)))))
-
- (define (random-boolean)
- (zero? (random-integer 2)))
-
- (define q ; (u v w) such that u <= v, v <= w, and not u <= w
- '#(
- ;x < y x = y x > y [x < z]
- 0 0 0 ; y < z
- 0 (z y x) (z y x) ; y = z
- 0 (z y x) (z y x) ; y > z
-
- ;x < y x = y x > y [x = z]
- (y z x) (z x y) 0 ; y < z
- (y z x) 0 (x z y) ; y = z
- 0 (y x z) (x z y) ; y > z
-
- ;x < y x = y x > y [x > z]
- (x y z) (x y z) 0 ; y < z
- (x y z) (x y z) 0 ; y = z
- 0 0 0 ; y > z
- ))
-
- (let ((z? #f) (z #f)) ; stored element from previous call
- (lambda (x y)
- (let ((c-xx (checked-value c x x))
- (c-yy (checked-value c y y))
- (c-xy (checked-value c x y))
- (c-yx (checked-value c y x)))
- (if (not (zero? c-xx))
- (error "compare error: not reflexive" c x))
- (if (not (zero? c-yy))
- (error "compare error: not reflexive" c y))
- (if (not (zero? (+ c-xy c-yx)))
- (error "compare error: not anti-symmetric" c x y))
- (if z?
- (let ((c-xz (checked-value c x z))
- (c-zx (checked-value c z x))
- (c-yz (checked-value c y z))
- (c-zy (checked-value c z y)))
- (if (not (zero? (+ c-xz c-zx)))
- (error "compare error: not anti-symmetric" c x z))
- (if (not (zero? (+ c-yz c-zy)))
- (error "compare error: not anti-symmetric" c y z))
- (let ((ijk (vector-ref q (+ c-xy (* 3 c-yz) (* 9 c-xz) 13))))
- (if (list? ijk)
- (apply error
- "compare error: not transitive"
- c
- (map (lambda (i) (case i ((x) x) ((y) y) ((z) z)))
- ijk)))))
- (set! z? #t))
- (set! z (if (random-boolean) x y)) ; randomized testing
- c-xy))))
-;;; srfi-69.scm --- Basic hash tables
-
-;; Copyright (C) 2007 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;;; Commentary:
-
-;; My `hash' is compatible with core `hash', so I replace it.
-;; However, my `hash-table?' and `make-hash-table' are different, so
-;; importing this module will warn about them. If you don't rename my
-;; imports, you shouldn't use both my hash tables and Guile's hash
-;; tables in the same module.
-;;
-;; SRFI-13 `string-hash' and `string-hash-ci' have more arguments, but
-;; are compatible with my `string-hash' and `string-ci-hash', and are
-;; furthermore primitive in Guile, so I use them as my own.
-;;
-;; I also have the extension of allowing hash functions that require a
-;; second argument to be used as the `hash-table-hash-function', and use
-;; these in defaults to avoid an indirection in the hashx functions. The
-;; only deviation this causes is:
-;;
-;; ((hash-table-hash-function (make-hash-table)) obj)
-;; error> Wrong number of arguments to #<primitive-procedure hash>
-;;
-;; I don't think that SRFI 69 actually specifies that I *can't* do this,
-;; because it only implies the signature of a hash function by way of the
-;; named, exported hash functions. However, if this matters enough I can
-;; add a private derivation of hash-function to the srfi-69:hash-table
-;; record type, like associator is to equivalence-function.
-;;
-;; Also, outside of the issue of how weak keys and values are referenced
-;; outside the table, I always interpret key equivalence to be that of
-;; the `hash-table-equivalence-function'. For example, given the
-;; requirement that `alist->hash-table' give earlier associations
-;; priority, what should these answer?
-;;
-;; (hash-table-keys
-;; (alist->hash-table '(("xY" . 1) ("Xy" . 2)) string-ci=?))
-;;
-;; (let ((ht (make-hash-table string-ci=?)))
-;; (hash-table-set! ht "xY" 2)
-;; (hash-table-set! ht "Xy" 1)
-;; (hash-table-keys ht))
-;;
-;; My interpretation is that they can answer either ("Xy") or ("xY"),
-;; where `hash-table-values' will of course always answer (1), because
-;; the keys are the same according to the equivalence function. In this
-;; implementation, both answer ("xY"). However, I don't guarantee that
-;; this won't change in the future.
-
-;;; Code:
-
-;;;; Module definition & exports
-
-(define-module (srfi srfi-69)
- #\use-module (srfi srfi-1) ;alist-cons,second&c,assoc
- #\use-module (srfi srfi-9)
- #\use-module (srfi srfi-13) ;string-hash,string-hash-ci
- #\use-module (ice-9 optargs)
- #\export (;; Type constructors & predicate
- make-hash-table hash-table? alist->hash-table
- ;; Reflective queries
- hash-table-equivalence-function hash-table-hash-function
- ;; Dealing with single elements
- hash-table-ref hash-table-ref/default hash-table-set!
- hash-table-delete! hash-table-exists? hash-table-update!
- hash-table-update!/default
- ;; Dealing with the whole contents
- hash-table-size hash-table-keys hash-table-values
- hash-table-walk hash-table-fold hash-table->alist
- hash-table-copy hash-table-merge!
- ;; Hashing
- string-ci-hash hash-by-identity)
- #\re-export (string-hash)
- #\replace (hash make-hash-table hash-table?))
-
-(cond-expand-provide (current-module) '(srfi-69))
-
-;;;; Internal helper macros
-
-;; Define these first, so the compiler will pick them up.
-
-;; I am a macro only for efficiency, to avoid varargs/apply.
-(define-macro (hashx-invoke hashx-proc ht-var . args)
- "Invoke HASHX-PROC, a `hashx-*' procedure taking a hash-function,
-assoc-function, and the hash-table as first args."
- `(,hashx-proc (hash-table-hash-function ,ht-var)
- (ht-associator ,ht-var)
- (ht-real-table ,ht-var)
- . ,args))
-
-(define-macro (with-hashx-values bindings ht-var . body-forms)
- "Bind BINDINGS to the hash-function, associator, and real-table of
-HT-VAR, while evaluating BODY-FORMS."
- `(let ((,(first bindings) (hash-table-hash-function ,ht-var))
- (,(second bindings) (ht-associator ,ht-var))
- (,(third bindings) (ht-real-table ,ht-var)))
- . ,body-forms))
-
-
-;;;; Hashing
-
-;;; The largest fixnum is in `most-positive-fixnum' in module (guile),
-;;; though not documented anywhere but libguile/numbers.c.
-
-(define (caller-with-default-size hash-fn)
- "Answer a function that makes `most-positive-fixnum' the default
-second argument to HASH-FN, a 2-arg procedure."
- (lambda* (obj #\optional (size most-positive-fixnum))
- (hash-fn obj size)))
-
-(define hash (caller-with-default-size (@ (guile) hash)))
-
-(define string-ci-hash string-hash-ci)
-
-(define hash-by-identity (caller-with-default-size hashq))
-
-;;;; Reflective queries, construction, predicate
-
-(define-record-type srfi-69:hash-table
- (make-srfi-69-hash-table real-table associator size weakness
- equivalence-function hash-function)
- hash-table?
- (real-table ht-real-table)
- (associator ht-associator)
- ;; required for O(1) by SRFI-69. It really makes a mess of things,
- ;; and I'd like to compute it in O(n) and memoize it because it
- ;; doesn't seem terribly useful, but SRFI-69 is final.
- (size ht-size ht-size!)
- ;; required for `hash-table-copy'
- (weakness ht-weakness)
- ;; used only to implement hash-table-equivalence-function; I don't
- ;; use it internally other than for `ht-associator'.
- (equivalence-function hash-table-equivalence-function)
- (hash-function hash-table-hash-function))
-
-(define (guess-hash-function equal-proc)
- "Guess a hash function for EQUAL-PROC, falling back on `hash', as
-specified in SRFI-69 for `make-hash-table'."
- (cond ((eq? equal? equal-proc) (@ (guile) hash)) ;shortcut most common case
- ((eq? eq? equal-proc) hashq)
- ((eq? eqv? equal-proc) hashv)
- ((eq? string=? equal-proc) string-hash)
- ((eq? string-ci=? equal-proc) string-ci-hash)
- (else (@ (guile) hash))))
-
-(define (without-keyword-args rest-list)
- "Answer REST-LIST with all keywords removed along with items that
-follow them."
- (let lp ((acc '()) (rest-list rest-list))
- (cond ((null? rest-list) (reverse! acc))
- ((keyword? (first rest-list))
- (lp acc (cddr rest-list)))
- (else (lp (cons (first rest-list) acc) (cdr rest-list))))))
-
-(define (guile-ht-ctor weakness)
- "Answer the Guile HT constructor for the given WEAKNESS."
- (case weakness
- ((#f) (@ (guile) make-hash-table))
- ((key) make-weak-key-hash-table)
- ((value) make-weak-value-hash-table)
- ((key-or-value) make-doubly-weak-hash-table)
- (else (error "Invalid weak hash table type" weakness))))
-
-(define (equivalence-proc->associator equal-proc)
- "Answer an `assoc'-like procedure that compares the argument key to
-alist keys with EQUAL-PROC."
- (cond ((or (eq? equal? equal-proc)
- (eq? string=? equal-proc)) (@ (guile) assoc))
- ((eq? eq? equal-proc) assq)
- ((eq? eqv? equal-proc) assv)
- (else (lambda (item alist)
- (assoc item alist equal-proc)))))
-
-(define* (make-hash-table
- #\optional (equal-proc equal?)
- (hash-proc (guess-hash-function equal-proc))
- #\key (weak #f) #\rest guile-opts)
- "Answer a new hash table using EQUAL-PROC as the comparison
-function, and HASH-PROC as the hash function. See the reference
-manual for specifics, of which there are many."
- (make-srfi-69-hash-table
- (apply (guile-ht-ctor weak) (without-keyword-args guile-opts))
- (equivalence-proc->associator equal-proc)
- 0 weak equal-proc hash-proc))
-
-(define (alist->hash-table alist . mht-args)
- "Convert ALIST to a hash table created with MHT-ARGS."
- (let* ((result (apply make-hash-table mht-args))
- (size (ht-size result)))
- (with-hashx-values (hash-proc associator real-table) result
- (for-each (lambda (pair)
- (let ((handle (hashx-get-handle hash-proc associator
- real-table (car pair))))
- (cond ((not handle)
- (set! size (1+ size))
- (hashx-set! hash-proc associator real-table
- (car pair) (cdr pair))))))
- alist))
- (ht-size! result size)
- result))
-
-;;;; Accessing table items
-
-;; We use this to denote missing or unspecified values to avoid
-;; possible collision with *unspecified*.
-(define ht-unspecified (cons *unspecified* "ht-value"))
-
-(define (hash-table-ref ht key . default-thunk-lst)
- "Lookup KEY in HT and answer the value, invoke DEFAULT-THUNK if KEY
-isn't present, or signal an error if DEFAULT-THUNK isn't provided."
- (let ((result (hashx-invoke hashx-ref ht key ht-unspecified)))
- (if (eq? ht-unspecified result)
- (if (pair? default-thunk-lst)
- ((first default-thunk-lst))
- (error "Key not in table" key ht))
- result)))
-
-(define (hash-table-ref/default ht key default)
- "Lookup KEY in HT and answer the value. Answer DEFAULT if KEY isn't
-present."
- (hashx-invoke hashx-ref ht key default))
-
-(define (hash-table-set! ht key new-value)
- "Set KEY to NEW-VALUE in HT."
- (let ((handle (hashx-invoke hashx-create-handle! ht key ht-unspecified)))
- (if (eq? ht-unspecified (cdr handle))
- (ht-size! ht (1+ (ht-size ht))))
- (set-cdr! handle new-value))
- *unspecified*)
-
-(define (hash-table-delete! ht key)
- "Remove KEY's association in HT."
- (with-hashx-values (h a real-ht) ht
- (if (hashx-get-handle h a real-ht key)
- (begin
- (ht-size! ht (1- (ht-size ht)))
- (hashx-remove! h a real-ht key))))
- *unspecified*)
-
-(define (hash-table-exists? ht key)
- "Return whether KEY is a key in HT."
- (and (hashx-invoke hashx-get-handle ht key) #t))
-
-;;; `hashx-set!' duplicates the hash lookup, but we use it anyway to
-;;; avoid creating a handle in case DEFAULT-THUNK exits
-;;; `hash-table-update!' non-locally.
-(define (hash-table-update! ht key modifier . default-thunk-lst)
- "Modify HT's value at KEY by passing its value to MODIFIER and
-setting it to the result thereof. Invoke DEFAULT-THUNK for the old
-value if KEY isn't in HT, or signal an error if DEFAULT-THUNK is not
-provided."
- (with-hashx-values (hash-proc associator real-table) ht
- (let ((handle (hashx-get-handle hash-proc associator real-table key)))
- (cond (handle
- (set-cdr! handle (modifier (cdr handle))))
- (else
- (hashx-set! hash-proc associator real-table key
- (if (pair? default-thunk-lst)
- (modifier ((car default-thunk-lst)))
- (error "Key not in table" key ht)))
- (ht-size! ht (1+ (ht-size ht)))))))
- *unspecified*)
-
-(define (hash-table-update!/default ht key modifier default)
- "Modify HT's value at KEY by passing its old value, or DEFAULT if it
-doesn't have one, to MODIFIER, and setting it to the result thereof."
- (hash-table-update! ht key modifier (lambda () default)))
-
-;;;; Accessing whole tables
-
-(define (hash-table-size ht)
- "Return the number of associations in HT. This is guaranteed O(1)
-for tables where #:weak was #f or not specified at creation time."
- (if (ht-weakness ht)
- (hash-table-fold ht (lambda (k v ans) (1+ ans)) 0)
- (ht-size ht)))
-
-(define (hash-table-keys ht)
- "Return a list of the keys in HT."
- (hash-table-fold ht (lambda (k v lst) (cons k lst)) '()))
-
-(define (hash-table-values ht)
- "Return a list of the values in HT."
- (hash-table-fold ht (lambda (k v lst) (cons v lst)) '()))
-
-(define (hash-table-walk ht proc)
- "Call PROC with each key and value as two arguments."
- (hash-table-fold ht (lambda (k v unspec)
- (call-with-values (lambda () (proc k v))
- (lambda vals unspec)))
- *unspecified*))
-
-(define (hash-table-fold ht f knil)
- "Invoke (F KEY VAL PREV) for each KEY and VAL in HT, where PREV is
-the result of the previous invocation, using KNIL as the first PREV.
-Answer the final F result."
- (hash-fold f knil (ht-real-table ht)))
-
-(define (hash-table->alist ht)
- "Return an alist for HT."
- (hash-table-fold ht alist-cons '()))
-
-(define (hash-table-copy ht)
- "Answer a copy of HT."
- (with-hashx-values (h a real-ht) ht
- (let* ((size (hash-table-size ht)) (weak (ht-weakness ht))
- (new-real-ht ((guile-ht-ctor weak) size)))
- (hash-fold (lambda (k v ign) (hashx-set! h a new-real-ht k v))
- #f real-ht)
- (make-srfi-69-hash-table ;real,assoc,size,weak,equiv,h
- new-real-ht a size weak
- (hash-table-equivalence-function ht) h))))
-
-(define (hash-table-merge! ht other-ht)
- "Add all key/value pairs from OTHER-HT to HT, overriding HT's
-mappings where present. Return HT."
- (hash-table-fold
- ht (lambda (k v ign) (hash-table-set! ht k v)) #f)
- ht)
-
-;;; srfi-69.scm ends here
-;;; srfi-8.scm --- receive
-
-;; Copyright (C) 2000, 2001, 2002, 2006 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Commentary:
-
-;; This module is fully documented in the Guile Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-8)
- \:use-module (ice-9 receive)
- \:re-export-syntax (receive))
-
-(cond-expand-provide (current-module) '(srfi-8))
-
-;;; srfi-8.scm ends here
-;;; srfi-88.scm --- Keyword Objects -*- coding: utf-8 -*-
-
-;; Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Ludovic Courtès <ludo@gnu.org>
-
-;;; Commentary:
-
-;; This is a convenience module providing SRFI-88 "keyword object". All it
-;; does is setup the right reader option and export keyword-related
-;; convenience procedures.
-
-;;; Code:
-
-(define-module (srfi srfi-88)
- #\re-export (keyword?)
- #\export (keyword->string string->keyword))
-
-(cond-expand-provide (current-module) '(srfi-88))
-
-
-;; Change the keyword syntax both at compile time and run time; the latter is
-;; useful at the REPL.
-(eval-when (expand load eval)
- (read-set! keywords 'postfix))
-
-(define (keyword->string k)
- "Return the name of @var{k} as a string."
- (symbol->string (keyword->symbol k)))
-
-(define (string->keyword s)
- "Return the keyword object whose name is @var{s}."
- (symbol->keyword (string->symbol s)))
-
-;;; Local Variables:
-;;; coding: latin-1
-;;; End:
-
-;;; srfi-88.scm ends here
-;;; srfi-9.scm --- define-record-type
-
-;; Copyright (C) 2001, 2002, 2006, 2009, 2010, 2011, 2012,
-;; 2013 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Commentary:
-
-;; This module exports the syntactic form `define-record-type', which
-;; is the means for creating record types defined in SRFI-9.
-;;
-;; The syntax of a record type definition is:
-;;
-;; <record type definition>
-;; -> (define-record-type <type name>
-;; (<constructor name> <field tag> ...)
-;; <predicate name>
-;; <field spec> ...)
-;;
-;; <field spec> -> (<field tag> <getter name>)
-;; -> (<field tag> <getter name> <setter name>)
-;;
-;; <field tag> -> <identifier>
-;; <... name> -> <identifier>
-;;
-;; Usage example:
-;;
-;; guile> (use-modules (srfi srfi-9))
-;; guile> (define-record-type :foo (make-foo x) foo?
-;; (x get-x) (y get-y set-y!))
-;; guile> (define f (make-foo 1))
-;; guile> f
-;; #<:foo x: 1 y: #f>
-;; guile> (get-x f)
-;; 1
-;; guile> (set-y! f 2)
-;; 2
-;; guile> (get-y f)
-;; 2
-;; guile> f
-;; #<:foo x: 1 y: 2>
-;; guile> (foo? f)
-;; #t
-;; guile> (foo? 1)
-;; #f
-
-;;; Code:
-
-(define-module (srfi srfi-9)
- #\use-module (srfi srfi-1)
- #\use-module (system base ck)
- #\export (define-record-type))
-
-(cond-expand-provide (current-module) '(srfi-9))
-
-;; Roll our own instead of using the public `define-inlinable'. This is
-;; because the public one has a different `make-procedure-name', so
-;; using it would require users to recompile code that uses SRFI-9. See
-;; <http://lists.gnu.org/archive/html/guile-devel/2011-04/msg00111.html>.
-;;
-
-(define-syntax-rule (define-inlinable (name formals ...) body ...)
- (define-tagged-inlinable () (name formals ...) body ...))
-
-;; 'define-tagged-inlinable' has an additional feature: it stores a map
-;; of keys to values that can be retrieved at expansion time. This is
-;; currently used to retrieve the rtd id, field index, and record copier
-;; macro for an arbitrary getter.
-
-(define-syntax-rule (%%on-error err) err)
-
-(define %%type #f) ; a private syntax literal
-(define-syntax getter-type
- (syntax-rules (quote)
- ((_ s 'getter 'err)
- (getter (%%on-error err) %%type s))))
-
-(define %%index #f) ; a private syntax literal
-(define-syntax getter-index
- (syntax-rules (quote)
- ((_ s 'getter 'err)
- (getter (%%on-error err) %%index s))))
-
-(define %%copier #f) ; a private syntax literal
-(define-syntax getter-copier
- (syntax-rules (quote)
- ((_ s 'getter 'err)
- (getter (%%on-error err) %%copier s))))
-
-(define-syntax define-tagged-inlinable
- (lambda (x)
- (define (make-procedure-name name)
- (datum->syntax name
- (symbol-append '% (syntax->datum name)
- '-procedure)))
-
- (syntax-case x ()
- ((_ ((key value) ...) (name formals ...) body ...)
- (identifier? #'name)
- (with-syntax ((proc-name (make-procedure-name #'name))
- ((args ...) (generate-temporaries #'(formals ...))))
- #`(begin
- (define (proc-name formals ...)
- body ...)
- (define-syntax name
- (lambda (x)
- (syntax-case x (%%on-error key ...)
- ((_ (%%on-error err) key s) #'(ck s 'value)) ...
- ((_ args ...)
- #'((lambda (formals ...)
- body ...)
- args ...))
- ((_ a (... ...))
- (syntax-violation 'name "Wrong number of arguments" x))
- (_
- (identifier? x)
- #'proc-name))))))))))
-
-(define (default-record-printer s p)
- (display "#<" p)
- (display (record-type-name (record-type-descriptor s)) p)
- (let loop ((fields (record-type-fields (record-type-descriptor s)))
- (off 0))
- (cond
- ((not (null? fields))
- (display " " p)
- (display (car fields) p)
- (display ": " p)
- (write (struct-ref s off) p)
- (loop (cdr fields) (+ 1 off)))))
- (display ">" p))
-
-(define (throw-bad-struct s who)
- (throw 'wrong-type-arg who
- "Wrong type argument: ~S" (list s)
- (list s)))
-
-(define (make-copier-id type-name)
- (datum->syntax type-name
- (symbol-append '%% (syntax->datum type-name)
- '-set-fields)))
-
-(define-syntax %%set-fields
- (lambda (x)
- (syntax-case x ()
- ((_ type-name (getter-id ...) check? s (getter expr) ...)
- (every identifier? #'(getter ...))
- (let ((copier-name (syntax->datum (make-copier-id #'type-name)))
- (getter+exprs #'((getter expr) ...)))
- (define (lookup id default-expr)
- (let ((results
- (filter (lambda (g+e)
- (free-identifier=? id (car g+e)))
- getter+exprs)))
- (case (length results)
- ((0) default-expr)
- ((1) (cadar results))
- (else (syntax-violation
- copier-name "duplicate getter" x id)))))
- (for-each (lambda (id)
- (or (find (lambda (getter-id)
- (free-identifier=? id getter-id))
- #'(getter-id ...))
- (syntax-violation
- copier-name "unknown getter" x id)))
- #'(getter ...))
- (with-syntax ((unsafe-expr
- #`(make-struct
- type-name 0
- #,@(map (lambda (getter index)
- (lookup getter #`(struct-ref s #,index)))
- #'(getter-id ...)
- (iota (length #'(getter-id ...)))))))
- (if (syntax->datum #'check?)
- #`(if (eq? (struct-vtable s) type-name)
- unsafe-expr
- (throw-bad-struct
- s '#,(datum->syntax #'here copier-name)))
- #'unsafe-expr)))))))
-
-(define-syntax %define-record-type
- (lambda (x)
- (define (field-identifiers field-specs)
- (map (lambda (field-spec)
- (syntax-case field-spec ()
- ((name getter) #'name)
- ((name getter setter) #'name)))
- field-specs))
-
- (define (getter-identifiers field-specs)
- (map (lambda (field-spec)
- (syntax-case field-spec ()
- ((name getter) #'getter)
- ((name getter setter) #'getter)))
- field-specs))
-
- (define (constructor form type-name constructor-spec field-names)
- (syntax-case constructor-spec ()
- ((ctor field ...)
- (every identifier? #'(field ...))
- (let ((ctor-args (map (lambda (field)
- (let ((name (syntax->datum field)))
- (or (memq name field-names)
- (syntax-violation
- (syntax-case form ()
- ((macro . args)
- (syntax->datum #'macro)))
- "unknown field in constructor spec"
- form field))
- (cons name field)))
- #'(field ...))))
- #`(define-inlinable #,constructor-spec
- (make-struct #,type-name 0
- #,@(map (lambda (name)
- (assq-ref ctor-args name))
- field-names)))))))
-
- (define (getters type-name getter-ids copier-id)
- (map (lambda (getter index)
- #`(define-tagged-inlinable
- ((%%type #,type-name)
- (%%index #,index)
- (%%copier #,copier-id))
- (#,getter s)
- (if (eq? (struct-vtable s) #,type-name)
- (struct-ref s #,index)
- (throw-bad-struct s '#,getter))))
- getter-ids
- (iota (length getter-ids))))
-
- (define (copier type-name getter-ids copier-id)
- #`(define-syntax-rule
- (#,copier-id check? s (getter expr) (... ...))
- (%%set-fields #,type-name #,getter-ids
- check? s (getter expr) (... ...))))
-
- (define (setters type-name field-specs)
- (filter-map (lambda (field-spec index)
- (syntax-case field-spec ()
- ((name getter) #f)
- ((name getter setter)
- #`(define-inlinable (setter s val)
- (if (eq? (struct-vtable s) #,type-name)
- (struct-set! s #,index val)
- (throw-bad-struct s 'setter))))))
- field-specs
- (iota (length field-specs))))
-
- (define (functional-setters copier-id field-specs)
- (filter-map (lambda (field-spec index)
- (syntax-case field-spec ()
- ((name getter) #f)
- ((name getter setter)
- #`(define-inlinable (setter s val)
- (#,copier-id #t s (getter val))))))
- field-specs
- (iota (length field-specs))))
-
- (define (record-layout immutable? count)
- (let ((desc (if immutable? "pr" "pw")))
- (string-concatenate (make-list count desc))))
-
- (syntax-case x ()
- ((_ immutable? form type-name constructor-spec predicate-name
- field-spec ...)
- (let ()
- (define (syntax-error message subform)
- (syntax-violation (syntax-case #'form ()
- ((macro . args) (syntax->datum #'macro)))
- message #'form subform))
- (and (boolean? (syntax->datum #'immutable?))
- (or (identifier? #'type-name)
- (syntax-error "expected type name" #'type-name))
- (syntax-case #'constructor-spec ()
- ((ctor args ...)
- (every identifier? #'(ctor args ...))
- #t)
- (_ (syntax-error "invalid constructor spec"
- #'constructor-spec)))
- (or (identifier? #'predicate-name)
- (syntax-error "expected predicate name" #'predicate-name))
- (every (lambda (spec)
- (syntax-case spec ()
- ((field getter) #t)
- ((field getter setter) #t)
- (_ (syntax-error "invalid field spec" spec))))
- #'(field-spec ...))))
- (let* ((field-ids (field-identifiers #'(field-spec ...)))
- (getter-ids (getter-identifiers #'(field-spec ...)))
- (field-count (length field-ids))
- (immutable? (syntax->datum #'immutable?))
- (layout (record-layout immutable? field-count))
- (field-names (map syntax->datum field-ids))
- (ctor-name (syntax-case #'constructor-spec ()
- ((ctor args ...) #'ctor)))
- (copier-id (make-copier-id #'type-name)))
- #`(begin
- #,(constructor #'form #'type-name #'constructor-spec field-names)
-
- (define type-name
- (let ((rtd (make-struct/no-tail
- record-type-vtable
- '#,(datum->syntax #'here (make-struct-layout layout))
- default-record-printer
- 'type-name
- '#,field-ids)))
- (set-struct-vtable-name! rtd 'type-name)
- (struct-set! rtd (+ 2 vtable-offset-user) #,ctor-name)
- rtd))
-
- (define-inlinable (predicate-name obj)
- (and (struct? obj)
- (eq? (struct-vtable obj) type-name)))
-
- #,@(getters #'type-name getter-ids copier-id)
- #,(copier #'type-name getter-ids copier-id)
- #,@(if immutable?
- (functional-setters copier-id #'(field-spec ...))
- (setters #'type-name #'(field-spec ...))))))
- ((_ immutable? form . rest)
- (syntax-violation
- (syntax-case #'form ()
- ((macro . args) (syntax->datum #'macro)))
- "invalid record definition syntax"
- #'form)))))
-
-(define-syntax-rule (define-record-type name ctor pred fields ...)
- (%define-record-type #f (define-record-type name ctor pred fields ...)
- name ctor pred fields ...))
-
-;;; srfi-9.scm ends here
-;;; Extensions to SRFI-9
-
-;; Copyright (C) 2010, 2012 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Commentary:
-
-;; Extensions to SRFI-9. Fully documented in the Guile Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-9 gnu)
- #\use-module (srfi srfi-1)
- #\use-module (system base ck)
- #\export (set-record-type-printer!
- define-immutable-record-type
- set-field
- set-fields))
-
-(define (set-record-type-printer! type proc)
- "Set PROC as the custom printer for TYPE."
- (struct-set! type vtable-index-printer proc))
-
-(define-syntax-rule (define-immutable-record-type name ctor pred fields ...)
- ((@@ (srfi srfi-9) %define-record-type)
- #t (define-immutable-record-type name ctor pred fields ...)
- name ctor pred fields ...))
-
-(define-syntax-rule (set-field s (getter ...) expr)
- (%set-fields #t (set-field s (getter ...) expr) ()
- s ((getter ...) expr)))
-
-(define-syntax-rule (set-fields s . rest)
- (%set-fields #t (set-fields s . rest) ()
- s . rest))
-
-;;
-;; collate-set-field-specs is a helper for %set-fields
-;; thats combines all specs with the same head together.
-;;
-;; For example:
-;;
-;; SPECS: (((a b c) expr1)
-;; ((a d) expr2)
-;; ((b c) expr3)
-;; ((c) expr4))
-;;
-;; RESULT: ((a ((b c) expr1)
-;; ((d) expr2))
-;; (b ((c) expr3))
-;; (c (() expr4)))
-;;
-(define (collate-set-field-specs specs)
- (define (insert head tail expr result)
- (cond ((find (lambda (tree)
- (free-identifier=? head (car tree)))
- result)
- => (lambda (tree)
- `((,head (,tail ,expr)
- ,@(cdr tree))
- ,@(delq tree result))))
- (else `((,head (,tail ,expr))
- ,@result))))
- (with-syntax (((((head . tail) expr) ...) specs))
- (fold insert '() #'(head ...) #'(tail ...) #'(expr ...))))
-
-(define-syntax unknown-getter
- (lambda (x)
- (syntax-case x ()
- ((_ orig-form getter)
- (syntax-violation 'set-fields "unknown getter" #'orig-form #'getter)))))
-
-(define-syntax c-list
- (lambda (x)
- (syntax-case x (quote)
- ((_ s 'v ...)
- #'(ck s '(v ...))))))
-
-(define-syntax c-same-type-check
- (lambda (x)
- (syntax-case x (quote)
- ((_ s 'orig-form '(path ...)
- '(getter0 getter ...)
- '(type0 type ...)
- 'on-success)
- (every (lambda (t g)
- (or (free-identifier=? t #'type0)
- (syntax-violation
- 'set-fields
- (format #f
- "\\
-field paths ~a and ~a require one object to belong to two different record types (~a and ~a)"
- (syntax->datum #`(path ... #,g))
- (syntax->datum #'(path ... getter0))
- (syntax->datum t)
- (syntax->datum #'type0))
- #'orig-form)))
- #'(type ...)
- #'(getter ...))
- #'(ck s 'on-success)))))
-
-(define-syntax %set-fields
- (lambda (x)
- (with-syntax ((getter-type #'(@@ (srfi srfi-9) getter-type))
- (getter-index #'(@@ (srfi srfi-9) getter-index))
- (getter-copier #'(@@ (srfi srfi-9) getter-copier)))
- (syntax-case x ()
- ((_ check? orig-form (path-so-far ...)
- s)
- #'s)
- ((_ check? orig-form (path-so-far ...)
- s (() e))
- #'e)
- ((_ check? orig-form (path-so-far ...)
- struct-expr ((head . tail) expr) ...)
- (let ((collated-specs (collate-set-field-specs
- #'(((head . tail) expr) ...))))
- (with-syntax (((getter0 getter ...)
- (map car collated-specs)))
- (with-syntax ((err #'(unknown-getter
- orig-form getter0)))
- #`(ck
- ()
- (c-same-type-check
- 'orig-form
- '(path-so-far ...)
- '(getter0 getter ...)
- (c-list (getter-type 'getter0 'err)
- (getter-type 'getter 'err) ...)
- '(let ((s struct-expr))
- ((ck () (getter-copier 'getter0 'err))
- check?
- s
- #,@(map (lambda (spec)
- (with-syntax (((head (tail expr) ...) spec))
- (with-syntax ((err #'(unknown-getter
- orig-form head)))
- #'(head (%set-fields
- check?
- orig-form
- (path-so-far ... head)
- (struct-ref s (ck () (getter-index
- 'head 'err)))
- (tail expr) ...)))))
- collated-specs)))))))))
- ((_ check? orig-form (path-so-far ...)
- s (() e) (() e*) ...)
- (syntax-violation 'set-fields "duplicate field path"
- #'orig-form #'(path-so-far ...)))
- ((_ check? orig-form (path-so-far ...)
- s ((getter ...) expr) ...)
- (syntax-violation 'set-fields "one field path is a prefix of another"
- #'orig-form #'(path-so-far ...)))
- ((_ check? orig-form . rest)
- (syntax-violation 'set-fields "invalid syntax" #'orig-form))))))
-;;; srfi-98.scm --- An interface to access environment variables
-
-;; Copyright (C) 2009 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Author: Julian Graham <julian.graham@aya.yale.edu>
-;;; Date: 2009-05-26
-
-;;; Commentary:
-
-;; This is an implementation of SRFI-98 (An interface to access environment
-;; variables).
-;;
-;; This module is fully documented in the Guile Reference Manual.
-
-;;; Code:
-
-(define-module (srfi srfi-98)
- \:use-module (srfi srfi-1)
- \:export (get-environment-variable
- get-environment-variables))
-
-(cond-expand-provide (current-module) '(srfi-98))
-
-(define get-environment-variable getenv)
-(define (get-environment-variables)
- (define (string->alist-entry str)
- (let ((pvt (string-index str #\=))
- (len (string-length str)))
- (and pvt (cons (substring str 0 pvt) (substring str (+ pvt 1) len)))))
- (filter-map string->alist-entry (environ)))
-;;;; (statprof) -- a statistical profiler for Guile
-;;;; -*-scheme-*-
-;;;;
-;;;; Copyright (C) 2009, 2010, 2011, 2015 Free Software Foundation, Inc.
-;;;; Copyright (C) 2004, 2009 Andy Wingo <wingo at pobox dot com>
-;;;; Copyright (C) 2001 Rob Browning <rlb at defaultvalue dot org>
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-;;; Commentary:
-;;
-;;@code{(statprof)} is intended to be a fairly simple
-;;statistical profiler for guile. It is in the early stages yet, so
-;;consider its output still suspect, and please report any bugs to
-;;@email{guile-devel at gnu.org}, or to me directly at @email{rlb at
-;;defaultvalue.org}.
-;;
-;;A simple use of statprof would look like this:
-;;
-;;@example
-;; (statprof-reset 0 50000 #t)
-;; (statprof-start)
-;; (do-something)
-;; (statprof-stop)
-;; (statprof-display)
-;;@end example
-;;
-;;This would reset statprof, clearing all accumulated statistics, then
-;;start profiling, run some code, stop profiling, and finally display a
-;;gprof flat-style table of statistics which will look something like
-;;this:
-;;
-;;@example
-;; % cumulative self self total
-;; time seconds seconds calls ms/call ms/call name
-;; 35.29 0.23 0.23 2002 0.11 0.11 -
-;; 23.53 0.15 0.15 2001 0.08 0.08 positive?
-;; 23.53 0.15 0.15 2000 0.08 0.08 +
-;; 11.76 0.23 0.08 2000 0.04 0.11 do-nothing
-;; 5.88 0.64 0.04 2001 0.02 0.32 loop
-;; 0.00 0.15 0.00 1 0.00 150.59 do-something
-;; ...
-;;@end example
-;;
-;;All of the numerical data with the exception of the calls column is
-;;statistically approximate. In the following column descriptions, and
-;;in all of statprof, "time" refers to execution time (both user and
-;;system), not wall clock time.
-;;
-;;@table @asis
-;;@item % time
-;;The percent of the time spent inside the procedure itself
-;;(not counting children).
-;;@item cumulative seconds
-;;The total number of seconds spent in the procedure, including
-;;children.
-;;@item self seconds
-;;The total number of seconds spent in the procedure itself (not counting
-;;children).
-;;@item calls
-;;The total number of times the procedure was called.
-;;@item self ms/call
-;;The average time taken by the procedure itself on each call, in ms.
-;;@item total ms/call
-;;The average time taken by each call to the procedure, including time
-;;spent in child functions.
-;;@item name
-;;The name of the procedure.
-;;@end table
-;;
-;;The profiler uses @code{eq?} and the procedure object itself to
-;;identify the procedures, so it won't confuse different procedures with
-;;the same name. They will show up as two different rows in the output.
-;;
-;;Right now the profiler is quite simplistic. I cannot provide
-;;call-graphs or other higher level information. What you see in the
-;;table is pretty much all there is. Patches are welcome :-)
-;;
-;;@section Implementation notes
-;;
-;;The profiler works by setting the unix profiling signal
-;;@code{ITIMER_PROF} to go off after the interval you define in the call
-;;to @code{statprof-reset}. When the signal fires, a sampling routine is
-;;run which looks at the current procedure that's executing, and then
-;;crawls up the stack, and for each procedure encountered, increments
-;;that procedure's sample count. Note that if a procedure is encountered
-;;multiple times on a given stack, it is only counted once. After the
-;;sampling is complete, the profiler resets profiling timer to fire
-;;again after the appropriate interval.
-;;
-;;Meanwhile, the profiler keeps track, via @code{get-internal-run-time},
-;;how much CPU time (system and user -- which is also what
-;;@code{ITIMER_PROF} tracks), has elapsed while code has been executing
-;;within a statprof-start/stop block.
-;;
-;;The profiler also tries to avoid counting or timing its own code as
-;;much as possible.
-;;
-;;; Code:
-
-;; When you add new features, please also add tests to ./tests/ if you
-;; have time, and then add the new files to ./run-tests. Also, if
-;; anyone's bored, there are a lot of existing API bits that don't
-;; have tests yet.
-
-;; TODO
-;;
-;; Check about profiling C functions -- does profiling primitives work?
-;; Also look into stealing code from qprof so we can sample the C stack
-;; Call graphs?
-
-(define-module (statprof)
- #\use-module (srfi srfi-1)
- #\autoload (ice-9 format) (format)
- #\use-module (system vm vm)
- #\use-module (system vm frame)
- #\use-module (system vm program)
- #\export (statprof-active?
- statprof-start
- statprof-stop
- statprof-reset
-
- statprof-accumulated-time
- statprof-sample-count
- statprof-fold-call-data
- statprof-proc-call-data
- statprof-call-data-name
- statprof-call-data-calls
- statprof-call-data-cum-samples
- statprof-call-data-self-samples
- statprof-call-data->stats
-
- statprof-stats-proc-name
- statprof-stats-%-time-in-proc
- statprof-stats-cum-secs-in-proc
- statprof-stats-self-secs-in-proc
- statprof-stats-calls
- statprof-stats-self-secs-per-call
- statprof-stats-cum-secs-per-call
-
- statprof-display
- statprof-display-anomolies
-
- statprof-fetch-stacks
- statprof-fetch-call-tree
-
- statprof
- with-statprof
-
- gcprof))
-
-
-;; This profiler tracks two numbers for every function called while
-;; it's active. It tracks the total number of calls, and the number
-;; of times the function was active when the sampler fired.
-;;
-;; Globally the profiler tracks the total time elapsed and the number
-;; of times the sampler was fired.
-;;
-;; Right now, this profiler is not per-thread and is not thread safe.
-
-(define accumulated-time #f) ; total so far.
-(define last-start-time #f) ; start-time when timer is active.
-(define sample-count #f) ; total count of sampler calls.
-(define sampling-frequency #f) ; in (seconds . microseconds)
-(define remaining-prof-time #f) ; time remaining when prof suspended.
-(define profile-level 0) ; for user start/stop nesting.
-(define %count-calls? #t) ; whether to catch apply-frame.
-(define gc-time-taken 0) ; gc time between statprof-start and
- ; statprof-stop.
-(define record-full-stacks? #f) ; if #t, stash away the stacks
- ; for later analysis.
-(define stacks '())
-
-;; procedure-data will be a hash where the key is the function object
-;; itself and the value is the data. The data will be a vector like
-;; this: #(name call-count cum-sample-count self-sample-count)
-(define procedure-data #f)
-
-;; If you change the call-data data structure, you need to also change
-;; sample-uncount-frame.
-(define (make-call-data proc call-count cum-sample-count self-sample-count)
- (vector proc call-count cum-sample-count self-sample-count))
-(define (call-data-proc cd) (vector-ref cd 0))
-(define (call-data-name cd) (procedure-name (call-data-proc cd)))
-(define (call-data-printable cd)
- (or (call-data-name cd)
- (with-output-to-string (lambda () (write (call-data-proc cd))))))
-(define (call-data-call-count cd) (vector-ref cd 1))
-(define (call-data-cum-sample-count cd) (vector-ref cd 2))
-(define (call-data-self-sample-count cd) (vector-ref cd 3))
-
-(define (inc-call-data-call-count! cd)
- (vector-set! cd 1 (1+ (vector-ref cd 1))))
-(define (inc-call-data-cum-sample-count! cd)
- (vector-set! cd 2 (1+ (vector-ref cd 2))))
-(define (inc-call-data-self-sample-count! cd)
- (vector-set! cd 3 (1+ (vector-ref cd 3))))
-
-(define-macro (accumulate-time stop-time)
- `(set! accumulated-time
- (+ accumulated-time 0.0 (- ,stop-time last-start-time))))
-
-(define (get-call-data proc)
- (let ((k (if (or (not (program? proc))
- (zero? (program-num-free-variables proc)))
- proc
- (program-objcode proc))))
- (or (hashq-ref procedure-data k)
- (let ((call-data (make-call-data proc 0 0 0)))
- (hashq-set! procedure-data k call-data)
- call-data))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; SIGPROF handler
-
-(define (sample-stack-procs stack)
- (let ((stacklen (stack-length stack))
- (hit-count-call? #f))
-
- (if record-full-stacks?
- (set! stacks (cons stack stacks)))
-
- (set! sample-count (+ sample-count 1))
- ;; Now accumulate stats for the whole stack.
- (let loop ((frame (stack-ref stack 0))
- (procs-seen (make-hash-table 13))
- (self #f))
- (cond
- ((not frame)
- (hash-fold
- (lambda (proc val accum)
- (inc-call-data-cum-sample-count!
- (get-call-data proc)))
- #f
- procs-seen)
- (and=> (and=> self get-call-data)
- inc-call-data-self-sample-count!))
- ((frame-procedure frame)
- => (lambda (proc)
- (cond
- ((eq? proc count-call)
- ;; We're not supposed to be sampling count-call and
- ;; its sub-functions, so loop again with a clean
- ;; slate.
- (set! hit-count-call? #t)
- (loop (frame-previous frame) (make-hash-table 13) #f))
- (else
- (hashq-set! procs-seen proc #t)
- (loop (frame-previous frame)
- procs-seen
- (or self proc))))))
- (else
- (loop (frame-previous frame) procs-seen self))))
- hit-count-call?))
-
-(define inside-profiler? #f)
-
-(define (profile-signal-handler sig)
- (set! inside-profiler? #t)
-
- ;; FIXME: with-statprof should be able to set an outer frame for the
- ;; stack cut
- (if (positive? profile-level)
- (let* ((stop-time (get-internal-run-time))
- ;; cut down to the signal handler. note that this will only
- ;; work if statprof.scm is compiled; otherwise we get
- ;; `eval' on the stack instead, because if it's not
- ;; compiled, profile-signal-handler is a thunk that
- ;; tail-calls eval. perhaps we should always compile the
- ;; signal handler instead...
- (stack (or (make-stack #t profile-signal-handler)
- (pk 'what! (make-stack #t))))
- (inside-apply-trap? (sample-stack-procs stack)))
-
- (if (not inside-apply-trap?)
- (begin
- ;; disabling here is just a little more efficient, but
- ;; not necessary given inside-profiler?. We can't just
- ;; disable unconditionally at the top of this function
- ;; and eliminate inside-profiler? because it seems to
- ;; confuse guile wrt re-enabling the trap when
- ;; count-call finishes.
- (if %count-calls?
- (set-vm-trace-level! (the-vm)
- (1- (vm-trace-level (the-vm)))))
- (accumulate-time stop-time)))
-
- (setitimer ITIMER_PROF
- 0 0
- (car sampling-frequency)
- (cdr sampling-frequency))
-
- (if (not inside-apply-trap?)
- (begin
- (set! last-start-time (get-internal-run-time))
- (if %count-calls?
- (set-vm-trace-level! (the-vm)
- (1+ (vm-trace-level (the-vm)))))))))
-
- (set! inside-profiler? #f))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Count total calls.
-
-(define (count-call frame)
- (if (not inside-profiler?)
- (begin
- (accumulate-time (get-internal-run-time))
-
- (and=> (frame-procedure frame)
- (lambda (proc)
- (inc-call-data-call-count!
- (get-call-data proc))))
-
- (set! last-start-time (get-internal-run-time)))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define (statprof-active?)
- "Returns @code{#t} if @code{statprof-start} has been called more times
-than @code{statprof-stop}, @code{#f} otherwise."
- (positive? profile-level))
-
-;; Do not call this from statprof internal functions -- user only.
-(define (statprof-start)
- "Start the profiler.@code{}"
- ;; After some head-scratching, I don't *think* I need to mask/unmask
- ;; signals here, but if I'm wrong, please let me know.
- (set! profile-level (+ profile-level 1))
- (if (= profile-level 1)
- (let* ((rpt remaining-prof-time)
- (use-rpt? (and rpt
- (or (positive? (car rpt))
- (positive? (cdr rpt))))))
- (set! remaining-prof-time #f)
- (set! last-start-time (get-internal-run-time))
- (set! gc-time-taken
- (cdr (assq 'gc-time-taken (gc-stats))))
- (if use-rpt?
- (setitimer ITIMER_PROF 0 0 (car rpt) (cdr rpt))
- (setitimer ITIMER_PROF
- 0 0
- (car sampling-frequency)
- (cdr sampling-frequency)))
- (if %count-calls?
- (add-hook! (vm-apply-hook (the-vm)) count-call))
- (set-vm-trace-level! (the-vm) (1+ (vm-trace-level (the-vm))))
- #t)))
-
-;; Do not call this from statprof internal functions -- user only.
-(define (statprof-stop)
- "Stop the profiler.@code{}"
- ;; After some head-scratching, I don't *think* I need to mask/unmask
- ;; signals here, but if I'm wrong, please let me know.
- (set! profile-level (- profile-level 1))
- (if (zero? profile-level)
- (begin
- (set! gc-time-taken
- (- (cdr (assq 'gc-time-taken (gc-stats))) gc-time-taken))
- (set-vm-trace-level! (the-vm) (1- (vm-trace-level (the-vm))))
- (if %count-calls?
- (remove-hook! (vm-apply-hook (the-vm)) count-call))
- ;; I believe that we need to do this before getting the time
- ;; (unless we want to make things even more complicated).
- (set! remaining-prof-time (setitimer ITIMER_PROF 0 0 0 0))
- (accumulate-time (get-internal-run-time))
- (set! last-start-time #f))))
-
-(define* (statprof-reset sample-seconds sample-microseconds count-calls?
- #\optional full-stacks?)
- "Reset the statprof sampler interval to @var{sample-seconds} and
-@var{sample-microseconds}. If @var{count-calls?} is true, arrange to
-instrument procedure calls as well as collecting statistical profiling
-data. If @var{full-stacks?} is true, collect all sampled stacks into a
-list for later analysis.
-
-Enables traps and debugging as necessary."
- (if (positive? profile-level)
- (error "Can't reset profiler while profiler is running."))
- (set! %count-calls? count-calls?)
- (set! accumulated-time 0)
- (set! last-start-time #f)
- (set! sample-count 0)
- (set! sampling-frequency (cons sample-seconds sample-microseconds))
- (set! remaining-prof-time #f)
- (set! procedure-data (make-hash-table 131))
- (set! record-full-stacks? full-stacks?)
- (set! stacks '())
- (sigaction SIGPROF profile-signal-handler)
- #t)
-
-(define (statprof-fold-call-data proc init)
- "Fold @var{proc} over the call-data accumulated by statprof. Cannot be
-called while statprof is active. @var{proc} should take two arguments,
-@code{(@var{call-data} @var{prior-result})}.
-
-Note that a given proc-name may appear multiple times, but if it does,
-it represents different functions with the same name."
- (if (positive? profile-level)
- (error "Can't call statprof-fold-called while profiler is running."))
-
- (hash-fold
- (lambda (key value prior-result)
- (proc value prior-result))
- init
- procedure-data))
-
-(define (statprof-proc-call-data proc)
- "Returns the call-data associated with @var{proc}, or @code{#f} if
-none is available."
- (if (positive? profile-level)
- (error "Can't call statprof-fold-called while profiler is running."))
-
- (hashq-ref procedure-data proc))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Stats
-
-(define (statprof-call-data->stats call-data)
- "Returns an object of type @code{statprof-stats}."
- ;; returns (vector proc-name
- ;; %-time-in-proc
- ;; cum-seconds-in-proc
- ;; self-seconds-in-proc
- ;; num-calls
- ;; self-secs-per-call
- ;; total-secs-per-call)
-
- (let* ((proc-name (call-data-printable call-data))
- (self-samples (call-data-self-sample-count call-data))
- (cum-samples (call-data-cum-sample-count call-data))
- (all-samples (statprof-sample-count))
- (secs-per-sample (/ (statprof-accumulated-time)
- (statprof-sample-count)))
- (num-calls (and %count-calls? (statprof-call-data-calls call-data))))
-
- (vector proc-name
- (* (/ self-samples all-samples) 100.0)
- (* cum-samples secs-per-sample 1.0)
- (* self-samples secs-per-sample 1.0)
- num-calls
- (and num-calls ;; maybe we only sampled in children
- (if (zero? self-samples) 0.0
- (/ (* self-samples secs-per-sample) 1.0 num-calls)))
- (and num-calls ;; cum-samples must be positive
- (/ (* cum-samples secs-per-sample)
- 1.0
- ;; num-calls might be 0 if we entered statprof during the
- ;; dynamic extent of the call
- (max num-calls 1))))))
-
-(define (statprof-stats-proc-name stats) (vector-ref stats 0))
-(define (statprof-stats-%-time-in-proc stats) (vector-ref stats 1))
-(define (statprof-stats-cum-secs-in-proc stats) (vector-ref stats 2))
-(define (statprof-stats-self-secs-in-proc stats) (vector-ref stats 3))
-(define (statprof-stats-calls stats) (vector-ref stats 4))
-(define (statprof-stats-self-secs-per-call stats) (vector-ref stats 5))
-(define (statprof-stats-cum-secs-per-call stats) (vector-ref stats 6))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(define (stats-sorter x y)
- (let ((diff (- (statprof-stats-self-secs-in-proc x)
- (statprof-stats-self-secs-in-proc y))))
- (positive?
- (if (= diff 0)
- (- (statprof-stats-cum-secs-in-proc x)
- (statprof-stats-cum-secs-in-proc y))
- diff))))
-
-(define (statprof-display . port)
- "Displays a gprof-like summary of the statistics collected. Unless an
-optional @var{port} argument is passed, uses the current output port."
- (if (null? port) (set! port (current-output-port)))
-
- (cond
- ((zero? (statprof-sample-count))
- (format port "No samples recorded.\n"))
- (else
- (let* ((stats-list (statprof-fold-call-data
- (lambda (data prior-value)
- (cons (statprof-call-data->stats data)
- prior-value))
- '()))
- (sorted-stats (sort stats-list stats-sorter)))
-
- (define (display-stats-line stats)
- (if %count-calls?
- (format port "~6,2f ~9,2f ~9,2f ~7d ~8,2f ~8,2f "
- (statprof-stats-%-time-in-proc stats)
- (statprof-stats-cum-secs-in-proc stats)
- (statprof-stats-self-secs-in-proc stats)
- (statprof-stats-calls stats)
- (* 1000 (statprof-stats-self-secs-per-call stats))
- (* 1000 (statprof-stats-cum-secs-per-call stats)))
- (format port "~6,2f ~9,2f ~9,2f "
- (statprof-stats-%-time-in-proc stats)
- (statprof-stats-cum-secs-in-proc stats)
- (statprof-stats-self-secs-in-proc stats)))
- (display (statprof-stats-proc-name stats) port)
- (newline port))
-
- (if %count-calls?
- (begin
- (format port "~5a ~10a ~7a ~8a ~8a ~8a ~8@a\n"
- "% " "cumulative" "self" "" "self" "total" "")
- (format port "~5a ~9a ~8a ~8a ~8a ~8a ~8@a\n"
- "time" "seconds" "seconds" "calls" "ms/call" "ms/call" "name"))
- (begin
- (format port "~5a ~10a ~7a ~8@a\n"
- "%" "cumulative" "self" "")
- (format port "~5a ~10a ~7a ~8@a\n"
- "time" "seconds" "seconds" "name")))
-
- (for-each display-stats-line sorted-stats)
-
- (display "---\n" port)
- (simple-format #t "Sample count: ~A\n" (statprof-sample-count))
- (simple-format #t "Total time: ~A seconds (~A seconds in GC)\n"
- (statprof-accumulated-time)
- (/ gc-time-taken 1.0 internal-time-units-per-second))))))
-
-(define (statprof-display-anomolies)
- "A sanity check that attempts to detect anomolies in statprof's
-statistics.@code{}"
- (statprof-fold-call-data
- (lambda (data prior-value)
- (if (and %count-calls?
- (zero? (call-data-call-count data))
- (positive? (call-data-cum-sample-count data)))
- (simple-format #t
- "==[~A ~A ~A]\n"
- (call-data-name data)
- (call-data-call-count data)
- (call-data-cum-sample-count data))))
- #f)
- (simple-format #t "Total time: ~A\n" (statprof-accumulated-time))
- (simple-format #t "Sample count: ~A\n" (statprof-sample-count)))
-
-(define (statprof-accumulated-time)
- "Returns the time accumulated during the last statprof run.@code{}"
- (if (positive? profile-level)
- (error "Can't get accumulated time while profiler is running."))
- (/ accumulated-time internal-time-units-per-second))
-
-(define (statprof-sample-count)
- "Returns the number of samples taken during the last statprof run.@code{}"
- (if (positive? profile-level)
- (error "Can't get accumulated time while profiler is running."))
- sample-count)
-
-(define statprof-call-data-name call-data-name)
-(define statprof-call-data-calls call-data-call-count)
-(define statprof-call-data-cum-samples call-data-cum-sample-count)
-(define statprof-call-data-self-samples call-data-self-sample-count)
-
-(define (statprof-fetch-stacks)
- "Returns a list of stacks, as they were captured since the last call
-to @code{statprof-reset}.
-
-Note that stacks are only collected if the @var{full-stacks?} argument
-to @code{statprof-reset} is true."
- stacks)
-
-(define procedure=?
- (lambda (a b)
- (cond
- ((eq? a b))
- ((and (program? a) (program? b))
- (eq? (program-objcode a) (program-objcode b)))
- (else
- #f))))
-
-;; tree ::= (car n . tree*)
-
-(define (lists->trees lists equal?)
- (let lp ((in lists) (n-terminal 0) (tails '()))
- (cond
- ((null? in)
- (let ((trees (map (lambda (tail)
- (cons (car tail)
- (lists->trees (cdr tail) equal?)))
- tails)))
- (cons (apply + n-terminal (map cadr trees))
- (sort trees
- (lambda (a b) (> (cadr a) (cadr b)))))))
- ((null? (car in))
- (lp (cdr in) (1+ n-terminal) tails))
- ((find (lambda (x) (equal? (car x) (caar in)))
- tails)
- => (lambda (tail)
- (lp (cdr in)
- n-terminal
- (assq-set! tails
- (car tail)
- (cons (cdar in) (cdr tail))))))
- (else
- (lp (cdr in)
- n-terminal
- (acons (caar in) (list (cdar in)) tails))))))
-
-(define (stack->procedures stack)
- (filter identity
- (unfold-right (lambda (x) (not x))
- frame-procedure
- frame-previous
- (stack-ref stack 0))))
-
-(define (statprof-fetch-call-tree)
- "Return a call tree for the previous statprof run.
-
-The return value is a list of nodes, each of which is of the type:
-@code
- node ::= (@var{proc} @var{count} . @var{nodes})
-@end code"
- (cons #t (lists->trees (map stack->procedures stacks) procedure=?)))
-
-(define* (statprof thunk #\key (loop 1) (hz 100) (count-calls? #f)
- (full-stacks? #f))
- "Profile the execution of @var{thunk}, and return its return values.
-
-The stack will be sampled @var{hz} times per second, and the thunk
-itself will be called @var{loop} times.
-
-If @var{count-calls?} is true, all procedure calls will be recorded. This
-operation is somewhat expensive.
-
-If @var{full-stacks?} is true, at each sample, statprof will store away the
-whole call tree, for later analysis. Use @code{statprof-fetch-stacks} or
-@code{statprof-fetch-call-tree} to retrieve the last-stored stacks."
- (dynamic-wind
- (lambda ()
- (statprof-reset (inexact->exact (floor (/ 1 hz)))
- (inexact->exact (* 1e6 (- (/ 1 hz)
- (floor (/ 1 hz)))))
- count-calls?
- full-stacks?)
- (statprof-start))
- (lambda ()
- (let lp ((i loop)
- (result '()))
- (if (zero? i)
- (apply values result)
- (call-with-values thunk
- (lambda result
- (lp (1- i) result))))))
- (lambda ()
- (statprof-stop)
- (statprof-display)
- (set! procedure-data #f))))
-
-(define-macro (with-statprof . args)
- "Profile the expressions in the body, and return the body's return values.
-
-Keyword arguments:
-
-@table @code
-@item #:loop
-Execute the body @var{loop} number of times, or @code{#f} for no looping
-
-default: @code{#f}
-@item #:hz
-Sampling rate
-
-default: @code{20}
-@item #:count-calls?
-Whether to instrument each function call (expensive)
-
-default: @code{#f}
-@item #:full-stacks?
-Whether to collect away all sampled stacks into a list
-
-default: @code{#f}
-@end table"
- (define (kw-arg-ref kw args def)
- (cond
- ((null? args) (error "Invalid macro body"))
- ((keyword? (car args))
- (if (eq? (car args) kw)
- (cadr args)
- (kw-arg-ref kw (cddr args) def)))
- ((eq? kw #f def) ;; asking for the body
- args)
- (else def))) ;; kw not found
- `((@ (statprof) statprof)
- (lambda () ,@(kw-arg-ref #f args #f))
- #\loop ,(kw-arg-ref #\loop args 1)
- #\hz ,(kw-arg-ref #\hz args 100)
- #\count-calls? ,(kw-arg-ref #\count-calls? args #f)
- #\full-stacks? ,(kw-arg-ref #\full-stacks? args #f)))
-
-(define* (gcprof thunk #\key (loop 1) (full-stacks? #f))
- "Do an allocation profile of the execution of @var{thunk}.
-
-The stack will be sampled soon after every garbage collection, yielding
-an approximate idea of what is causing allocation in your program.
-
-Since GC does not occur very frequently, you may need to use the
-@var{loop} parameter, to cause @var{thunk} to be called @var{loop}
-times.
-
-If @var{full-stacks?} is true, at each sample, statprof will store away the
-whole call tree, for later analysis. Use @code{statprof-fetch-stacks} or
-@code{statprof-fetch-call-tree} to retrieve the last-stored stacks."
-
- (define (reset)
- (if (positive? profile-level)
- (error "Can't reset profiler while profiler is running."))
- (set! accumulated-time 0)
- (set! last-start-time #f)
- (set! sample-count 0)
- (set! %count-calls? #f)
- (set! procedure-data (make-hash-table 131))
- (set! record-full-stacks? full-stacks?)
- (set! stacks '()))
-
- (define (gc-callback)
- (cond
- (inside-profiler?)
- (else
- (set! inside-profiler? #t)
-
- ;; FIXME: should be able to set an outer frame for the stack cut
- (let ((stop-time (get-internal-run-time))
- ;; Cut down to gc-callback, and then one before (the
- ;; after-gc async). See the note in profile-signal-handler
- ;; also.
- (stack (or (make-stack #t gc-callback 0 1)
- (pk 'what! (make-stack #t)))))
- (sample-stack-procs stack)
- (accumulate-time stop-time)
- (set! last-start-time (get-internal-run-time)))
-
- (set! inside-profiler? #f))))
-
- (define (start)
- (set! profile-level (+ profile-level 1))
- (if (= profile-level 1)
- (begin
- (set! remaining-prof-time #f)
- (set! last-start-time (get-internal-run-time))
- (set! gc-time-taken (cdr (assq 'gc-time-taken (gc-stats))))
- (add-hook! after-gc-hook gc-callback)
- (set-vm-trace-level! (the-vm) (1+ (vm-trace-level (the-vm))))
- #t)))
-
- (define (stop)
- (set! profile-level (- profile-level 1))
- (if (zero? profile-level)
- (begin
- (set! gc-time-taken
- (- (cdr (assq 'gc-time-taken (gc-stats))) gc-time-taken))
- (remove-hook! after-gc-hook gc-callback)
- (accumulate-time (get-internal-run-time))
- (set! last-start-time #f))))
-
- (dynamic-wind
- (lambda ()
- (reset)
- (start))
- (lambda ()
- (let lp ((i loop))
- (if (not (zero? i))
- (begin
- (thunk)
- (lp (1- i))))))
- (lambda ()
- (stop)
- (statprof-display)
- (set! procedure-data #f))))
-;;;; (sxml apply-templates) -- xslt-like transformation for sxml
-;;;;
-;;;; Copyright (C) 2009 Free Software Foundation, Inc.
-;;;; Copyright 2004 by Andy Wingo <wingo at pobox dot com>.
-;;;; Written 2003 by Oleg Kiselyov <oleg at pobox dot com> as apply-templates.scm.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary:
-;;
-;; Pre-order traversal of a tree and creation of a new tree:
-;;
-;;@smallexample
-;; apply-templates:: tree x <templates> -> <new-tree>
-;;@end smallexample
-;; where
-;;@smallexample
-;; <templates> ::= (<template> ...)
-;; <template> ::= (<node-test> <node-test> ... <node-test> . <handler>)
-;; <node-test> ::= an argument to node-typeof? above
-;; <handler> ::= <tree> -> <new-tree>
-;;@end smallexample
-;;
-;; This procedure does a @emph{normal}, pre-order traversal of an SXML
-;; tree. It walks the tree, checking at each node against the list of
-;; matching templates.
-;;
-;; If the match is found (which must be unique, i.e., unambiguous), the
-;; corresponding handler is invoked and given the current node as an
-;; argument. The result from the handler, which must be a @code{<tree>},
-;; takes place of the current node in the resulting tree.
-;;
-;; The name of the function is not accidental: it resembles rather
-;; closely an @code{apply-templates} function of XSLT.
-;;
-;;; Code:
-
-(define-module (sxml apply-templates)
- #\use-module (sxml ssax)
- #\use-module ((sxml xpath) \:hide (filter))
-
- #\export (apply-templates))
-
-(define (apply-templates tree templates)
-
- ; Filter the list of templates. If a template does not
- ; contradict the given node (that is, its head matches
- ; the type of the node), chop off the head and keep the
- ; rest as the result. All contradicting templates are removed.
- (define (filter-templates node templates)
- (cond
- ((null? templates) templates)
- ((not (pair? (car templates))) ; A good template must be a list
- (filter-templates node (cdr templates)))
- (((node-typeof? (caar templates)) node)
- (cons (cdar templates) (filter-templates node (cdr templates))))
- (else
- (filter-templates node (cdr templates)))))
-
- ; Here <templates> ::= [<template> | <handler>]
- ; If there is a <handler> in the above list, it must
- ; be only one. If found, return it; otherwise, return #f
- (define (find-handler templates)
- (and (pair? templates)
- (cond
- ((procedure? (car templates))
- (if (find-handler (cdr templates))
- (error "ambiguous template match"))
- (car templates))
- (else (find-handler (cdr templates))))))
-
- (let loop ((tree tree) (active-templates '()))
- ;(cout "active-templates: " active-templates nl "tree: " tree nl)
- (if (nodeset? tree)
- (map-union (lambda (a-tree) (loop a-tree active-templates)) tree)
- (let ((still-active-templates
- (append
- (filter-templates tree active-templates)
- (filter-templates tree templates))))
- (cond
- ;((null? still-active-templates) '())
- ((find-handler still-active-templates) =>
- (lambda (handler) (handler tree)))
- ((not (pair? tree)) '())
- (else
- (loop (cdr tree) still-active-templates)))))))
-
-;;; arch-tag: 88cd87de-8825-4ab3-9721-cf99694fb787
-;;; templates.scm ends here
-;;;; (sxml fold) -- transformation of sxml via fold operations
-;;;;
-;;;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
-;;;; Written 2007 by Andy Wingo <wingo at pobox dot com>.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary:
-;;
-;; @code{(sxml fold)} defines a number of variants of the @dfn{fold}
-;; algorithm for use in transforming SXML trees. Additionally it defines
-;; the layout operator, @code{fold-layout}, which might be described as
-;; a context-passing variant of SSAX's @code{pre-post-order}.
-;;
-;;; Code:
-
-(define-module (sxml fold)
- #\use-module (srfi srfi-1)
- #\export (foldt
- foldts
- foldts*
- fold-values
- foldts*-values
- fold-layout))
-
-(define (atom? x)
- (not (pair? x)))
-
-(define (foldt fup fhere tree)
- "The standard multithreaded tree fold.
-
-@var{fup} is of type [a] -> a. @var{fhere} is of type object -> a.
-"
- (if (atom? tree)
- (fhere tree)
- (fup (map (lambda (kid)
- (foldt fup fhere kid))
- tree))))
-
-(define (foldts fdown fup fhere seed tree)
- "The single-threaded tree fold originally defined in SSAX.
-@xref{sxml ssax,,(sxml ssax)}, for more information."
- (if (atom? tree)
- (fhere seed tree)
- (fup seed
- (fold (lambda (kid kseed)
- (foldts fdown fup fhere kseed kid))
- (fdown seed tree)
- tree)
- tree)))
-
-(define (foldts* fdown fup fhere seed tree)
- "A variant of @ref{sxml fold foldts,,foldts} that allows pre-order
-tree rewrites. Originally defined in Andy Wingo's 2007 paper,
-@emph{Applications of fold to XML transformation}."
- (if (atom? tree)
- (fhere seed tree)
- (call-with-values
- (lambda () (fdown seed tree))
- (lambda (kseed tree)
- (fup seed
- (fold (lambda (kid kseed)
- (foldts* fdown fup fhere
- kseed kid))
- kseed
- tree)
- tree)))))
-
-(define (fold-values proc list . seeds)
- "A variant of @ref{SRFI-1 Fold and Map, fold} that allows multi-valued
-seeds. Note that the order of the arguments differs from that of
-@code{fold}."
- (if (null? list)
- (apply values seeds)
- (call-with-values
- (lambda () (apply proc (car list) seeds))
- (lambda seeds
- (apply fold-values proc (cdr list) seeds)))))
-
-(define (foldts*-values fdown fup fhere tree . seeds)
- "A variant of @ref{sxml fold foldts*,,foldts*} that allows
-multi-valued seeds. Originally defined in Andy Wingo's 2007 paper,
-@emph{Applications of fold to XML transformation}."
- (if (atom? tree)
- (apply fhere tree seeds)
- (call-with-values
- (lambda () (apply fdown tree seeds))
- (lambda (tree . kseeds)
- (call-with-values
- (lambda ()
- (apply fold-values
- (lambda (tree . seeds)
- (apply foldts*-values
- fdown fup fhere tree seeds))
- tree kseeds))
- (lambda kseeds
- (apply fup tree (append seeds kseeds))))))))
-
-(define (assq-ref alist key default)
- (cond ((assq key alist) => cdr)
- (else default)))
-
-(define (fold-layout tree bindings params layout stylesheet)
- "A traversal combinator in the spirit of SSAX's @ref{sxml transform
-pre-post-order,,pre-post-order}.
-
-@code{fold-layout} was originally presented in Andy Wingo's 2007 paper,
-@emph{Applications of fold to XML transformation}.
-
-@example
-bindings := (<binding>...)
-binding := (<tag> <bandler-pair>...)
- | (*default* . <post-handler>)
- | (*text* . <text-handler>)
-tag := <symbol>
-handler-pair := (pre-layout . <pre-layout-handler>)
- | (post . <post-handler>)
- | (bindings . <bindings>)
- | (pre . <pre-handler>)
- | (macro . <macro-handler>)
-@end example
-
-@table @var
-@item pre-layout-handler
-A function of three arguments:
-
-@table @var
-@item kids
-the kids of the current node, before traversal
-@item params
-the params of the current node
-@item layout
-the layout coming into this node
-@end table
-
-@var{pre-layout-handler} is expected to use this information to return a
-layout to pass to the kids. The default implementation returns the
-layout given in the arguments.
-
-@item post-handler
-A function of five arguments:
-@table @var
-@item tag
-the current tag being processed
-@item params
-the params of the current node
-@item layout
-the layout coming into the current node, before any kids were processed
-@item klayout
-the layout after processing all of the children
-@item kids
-the already-processed child nodes
-@end table
-
-@var{post-handler} should return two values, the layout to pass to the
-next node and the final tree.
-
-@item text-handler
-@var{text-handler} is a function of three arguments:
-@table @var
-@item text
-the string
-@item params
-the current params
-@item layout
-the current layout
-@end table
-
-@var{text-handler} should return two values, the layout to pass to the
-next node and the value to which the string should transform.
-@end table
-"
- (define (err . args)
- (error "no binding available" args))
- (define (fdown tree bindings pcont params layout ret)
- (define (fdown-helper new-bindings new-layout cont)
- (let ((cont-with-tag (lambda args
- (apply cont (car tree) args)))
- (bindings (if new-bindings
- (append new-bindings bindings)
- bindings))
- (style-params (assq-ref stylesheet (car tree) '())))
- (cond
- ((null? (cdr tree))
- (values
- '() bindings cont-with-tag (cons style-params params) new-layout '()))
- ((and (pair? (cadr tree)) (eq? (caadr tree) '@))
- (let ((params (cons (append (cdadr tree) style-params) params)))
- (values
- (cddr tree) bindings cont-with-tag params new-layout '())))
- (else
- (values
- (cdr tree) bindings cont-with-tag (cons style-params params) new-layout '())))))
- (define (no-bindings)
- (fdown-helper #f layout (assq-ref bindings '*default* err)))
- (define (macro macro-handler)
- (fdown (apply macro-handler tree)
- bindings pcont params layout ret))
- (define (pre pre-handler)
- (values '() bindings
- (lambda (params layout old-layout kids)
- (values layout (reverse kids)))
- params layout (apply pre-handler tree)))
- (define (have-bindings tag-bindings)
- (fdown-helper
- (assq-ref tag-bindings 'bindings #f)
- ((assq-ref tag-bindings 'pre-layout
- (lambda (tag params layout)
- layout))
- tree params layout)
- (assq-ref tag-bindings 'post
- (assq-ref bindings '*default* err))))
- (let ((tag-bindings (assq-ref bindings (car tree) #f)))
- (cond
- ((not tag-bindings) (no-bindings))
- ((assq-ref tag-bindings 'macro #f) => macro)
- ((assq-ref tag-bindings 'pre #f) => pre)
- (else (have-bindings tag-bindings)))))
- (define (fup tree bindings cont params layout ret
- kbindings kcont kparams klayout kret)
- (call-with-values
- (lambda ()
- (kcont kparams layout klayout (reverse kret)))
- (lambda (klayout kret)
- (values bindings cont params klayout (cons kret ret)))))
- (define (fhere tree bindings cont params layout ret)
- (call-with-values
- (lambda ()
- ((assq-ref bindings '*text* err) tree params layout))
- (lambda (tlayout tret)
- (values bindings cont params tlayout (cons tret ret)))))
- (call-with-values
- (lambda ()
- (foldts*-values
- fdown fup fhere tree bindings #f (cons params '()) layout '()))
- (lambda (bindings cont params layout ret)
- (values (car ret) layout))))
-;;; -*- mode: scheme; coding: utf-8; -*-
-;;;
-;;; Copyright (C) 2010, 2011 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or modify it
-;;; under the terms of the GNU Lesser General Public License as published by
-;;; the Free Software Foundation; either version 3 of the License, or (at
-;;; your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful, but
-;;; WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
-;;; General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public License
-;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-(define-module (sxml match)
- #\export (sxml-match
- sxml-match-let
- sxml-match-let*)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-11)
- #\use-module (ice-9 control))
-
-
-;;; Commentary:
-;;;
-;;; This module provides an SXML pattern matcher, written by Jim Bender. This
-;;; allows application code to match on SXML nodes and attributes without having
-;;; to deal with the details of s-expression matching, without worrying about
-;;; the order of attributes, etc.
-;;;
-;;; It is fully documented in the Guile Reference Manual.
-;;;
-;;; Code:
-
-
-
-;;;
-;;; PLT compatibility layer.
-;;;
-
-(define-syntax-rule (syntax-object->datum stx)
- (syntax->datum stx))
-
-(define-syntax-rule (void)
- *unspecified*)
-
-(define (raise-syntax-error x msg obj sub)
- (throw 'sxml-match-error x msg obj sub))
-
-(define-syntax module
- (syntax-rules (provide require)
- ((_ name lang (provide p_ ...) (require r_ ...)
- body ...)
- (begin body ...))))
-
-
-;;;
-;;; Include upstream source file.
-;;;
-
-;; This file was taken from
-;; <http://planet.plt-scheme.org/package-source/jim/sxml-match.plt/1/1/> on
-;; 2010-05-24. It was written by Jim Bender <benderjg2@aol.com> and released
-;; under the MIT/X11 license
-;; <http://www.gnu.org/licenses/license-list.html#X11License>.
-;;
-;; Modified the `sxml-match1' macro to allow multiple-value returns (upstream
-;; was notified.)
-
-(include-from-path "sxml/sxml-match.ss")
-
-;;; match.scm ends here
-;;;; (sxml simple) -- a simple interface to the SSAX parser
-;;;;
-;;;; Copyright (C) 2009, 2010, 2013 Free Software Foundation, Inc.
-;;;; Modified 2004 by Andy Wingo <wingo at pobox dot com>.
-;;;; Originally written by Oleg Kiselyov <oleg at pobox dot com> as SXML-to-HTML.scm.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary:
-;;
-;;A simple interface to XML parsing and serialization.
-;;
-;;; Code:
-
-(define-module (sxml simple)
- #\use-module (sxml ssax input-parse)
- #\use-module (sxml ssax)
- #\use-module (sxml transform)
- #\use-module (ice-9 match)
- #\use-module (srfi srfi-13)
- #\export (xml->sxml sxml->xml sxml->string))
-
-;; Helpers from upstream/SSAX.scm.
-;;
-
-; ssax:reverse-collect-str LIST-OF-FRAGS -> LIST-OF-FRAGS
-; given the list of fragments (some of which are text strings)
-; reverse the list and concatenate adjacent text strings.
-; We can prove from the general case below that if LIST-OF-FRAGS
-; has zero or one element, the result of the procedure is equal?
-; to its argument. This fact justifies the shortcut evaluation below.
-(define (ssax:reverse-collect-str fragments)
- (cond
- ((null? fragments) '()) ; a shortcut
- ((null? (cdr fragments)) fragments) ; see the comment above
- (else
- (let loop ((fragments fragments) (result '()) (strs '()))
- (cond
- ((null? fragments)
- (if (null? strs) result
- (cons (string-concatenate/shared strs) result)))
- ((string? (car fragments))
- (loop (cdr fragments) result (cons (car fragments) strs)))
- (else
- (loop (cdr fragments)
- (cons
- (car fragments)
- (if (null? strs) result
- (cons (string-concatenate/shared strs) result)))
- '())))))))
-
-(define (read-internal-doctype-as-string port)
- (string-concatenate/shared
- (let loop ()
- (let ((fragment
- (next-token '() '(#\]) "reading internal DOCTYPE" port)))
- (if (eqv? #\> (peek-next-char port))
- (begin
- (read-char port)
- (cons fragment '()))
- (cons* fragment "]" (loop)))))))
-
-;; Ideas for the future for this interface:
-;;
-;; * Allow doctypes to provide parsed entities
-;;
-;; * Allow validation (the ELEMENTS value from the DOCTYPE handler
-;; below)
-;;
-;; * Parse internal DTDs
-;;
-;; * Parse external DTDs
-;;
-(define* (xml->sxml #\optional (string-or-port (current-input-port)) #\key
- (namespaces '())
- (declare-namespaces? #t)
- (trim-whitespace? #f)
- (entities '())
- (default-entity-handler #f)
- (doctype-handler #f))
- "Use SSAX to parse an XML document into SXML. Takes one optional
-argument, @var{string-or-port}, which defaults to the current input
-port."
- ;; NAMESPACES: alist of PREFIX -> URI. Specifies the symbol prefix
- ;; that the user wants on elements of a given namespace in the
- ;; resulting SXML, regardless of the abbreviated namespaces defined in
- ;; the document by xmlns attributes. If DECLARE-NAMESPACES? is true,
- ;; these namespaces are treated as if they were declared in the DTD.
-
- ;; ENTITIES: alist of SYMBOL -> STRING.
-
- ;; NAMESPACES: list of (DOC-PREFIX . (USER-PREFIX . URI)).
- ;; A DOC-PREFIX of #f indicates that it comes from the user.
- ;; Otherwise, prefixes are symbols.
- (define (munge-namespaces namespaces)
- (map (lambda (el)
- (match el
- ((prefix . uri-string)
- (cons* (and declare-namespaces? prefix)
- prefix
- (ssax:uri-string->symbol uri-string)))))
- namespaces))
-
- (define (user-namespaces)
- (munge-namespaces namespaces))
-
- (define (user-entities)
- (if (and default-entity-handler
- (not (assq '*DEFAULT* entities)))
- (acons '*DEFAULT* default-entity-handler entities)
- entities))
-
- (define (name->sxml name)
- (match name
- ((prefix . local-part)
- (symbol-append prefix (string->symbol ":") local-part))
- (_ name)))
-
- (define (doctype-continuation seed)
- (lambda* (#\key (entities '()) (namespaces '()))
- (values #f
- (append entities (user-entities))
- (append (munge-namespaces namespaces) (user-namespaces))
- seed)))
-
- ;; The SEED in this parser is the SXML: initialized to '() at each new
- ;; level by the fdown handlers; built in reverse by the fhere parsers;
- ;; and reverse-collected by the fup handlers.
- (define parser
- (ssax:make-parser
- NEW-LEVEL-SEED ; fdown
- (lambda (elem-gi attributes namespaces expected-content seed)
- '())
-
- FINISH-ELEMENT ; fup
- (lambda (elem-gi attributes namespaces parent-seed seed)
- (let ((seed (if trim-whitespace?
- (ssax:reverse-collect-str-drop-ws seed)
- (ssax:reverse-collect-str seed)))
- (attrs (attlist-fold
- (lambda (attr accum)
- (cons (list (name->sxml (car attr)) (cdr attr))
- accum))
- '() attributes)))
- (acons (name->sxml elem-gi)
- (if (null? attrs)
- seed
- (cons (cons '@ attrs) seed))
- parent-seed)))
-
- CHAR-DATA-HANDLER ; fhere
- (lambda (string1 string2 seed)
- (if (string-null? string2)
- (cons string1 seed)
- (cons* string2 string1 seed)))
-
- DOCTYPE
- ;; -> ELEMS ENTITIES NAMESPACES SEED
- ;;
- ;; ELEMS is for validation and currently unused.
- ;;
- ;; ENTITIES is an alist of parsed entities (symbol -> string).
- ;;
- ;; NAMESPACES is as above.
- ;;
- ;; SEED builds up the content.
- (lambda (port docname systemid internal-subset? seed)
- (call-with-values
- (lambda ()
- (cond
- (doctype-handler
- (doctype-handler docname systemid
- (and internal-subset?
- (read-internal-doctype-as-string port))))
- (else
- (when internal-subset?
- (ssax:skip-internal-dtd port))
- (values))))
- (doctype-continuation seed)))
-
- UNDECL-ROOT
- ;; This is like the DOCTYPE handler, but for documents that do not
- ;; have a <!DOCTYPE!> entry.
- (lambda (elem-gi seed)
- (call-with-values
- (lambda ()
- (if doctype-handler
- (doctype-handler #f #f #f)
- (values)))
- (doctype-continuation seed)))
-
- PI
- ((*DEFAULT*
- . (lambda (port pi-tag seed)
- (cons
- (list '*PI* pi-tag (ssax:read-pi-body-as-string port))
- seed))))))
-
- (let* ((port (if (string? string-or-port)
- (open-input-string string-or-port)
- string-or-port))
- (elements (reverse (parser port '()))))
- `(*TOP* ,@elements)))
-
-(define check-name
- (let ((*good-cache* (make-hash-table)))
- (lambda (name)
- (if (not (hashq-ref *good-cache* name))
- (let* ((str (symbol->string name))
- (i (string-index str #\:))
- (head (or (and i (substring str 0 i)) str))
- (tail (and i (substring str (1+ i)))))
- (and i (string-index (substring str (1+ i)) #\:)
- (error "Invalid QName: more than one colon" name))
- (for-each
- (lambda (s)
- (and s
- (or (char-alphabetic? (string-ref s 0))
- (eq? (string-ref s 0) #\_)
- (error "Invalid name starting character" s name))
- (string-for-each
- (lambda (c)
- (or (char-alphabetic? c) (string-index "0123456789.-_" c)
- (error "Invalid name character" c s name)))
- s)))
- (list head tail))
- (hashq-set! *good-cache* name #t))))))
-
-;; The following two functions serialize tags and attributes. They are
-;; being used in the node handlers for the post-order function, see
-;; below.
-
-(define (attribute-value->xml value port)
- (cond
- ((pair? value)
- (attribute-value->xml (car value) port)
- (attribute-value->xml (cdr value) port))
- ((null? value)
- *unspecified*)
- ((string? value)
- (string->escaped-xml value port))
- ((procedure? value)
- (with-output-to-port port value))
- (else
- (string->escaped-xml
- (call-with-output-string (lambda (port) (display value port)))
- port))))
-
-(define (attribute->xml attr value port)
- (check-name attr)
- (display attr port)
- (display "=\"" port)
- (attribute-value->xml value port)
- (display #\" port))
-
-(define (element->xml tag attrs body port)
- (check-name tag)
- (display #\< port)
- (display tag port)
- (if attrs
- (let lp ((attrs attrs))
- (if (pair? attrs)
- (let ((attr (car attrs)))
- (display #\space port)
- (if (pair? attr)
- (attribute->xml (car attr) (cdr attr) port)
- (error "bad attribute" tag attr))
- (lp (cdr attrs)))
- (if (not (null? attrs))
- (error "bad attributes" tag attrs)))))
- (if (pair? body)
- (begin
- (display #\> port)
- (let lp ((body body))
- (cond
- ((pair? body)
- (sxml->xml (car body) port)
- (lp (cdr body)))
- ((null? body)
- (display "</" port)
- (display tag port)
- (display ">" port))
- (else
- (error "bad element body" tag body)))))
- (display " />" port)))
-
-;; FIXME: ensure name is valid
-(define (entity->xml name port)
- (display #\& port)
- (display name port)
- (display #\; port))
-
-;; FIXME: ensure tag and str are valid
-(define (pi->xml tag str port)
- (display "<?" port)
- (display tag port)
- (display #\space port)
- (display str port)
- (display "?>" port))
-
-(define* (sxml->xml tree #\optional (port (current-output-port)))
- "Serialize the sxml tree @var{tree} as XML. The output will be written
-to the current output port, unless the optional argument @var{port} is
-present."
- (cond
- ((pair? tree)
- (if (symbol? (car tree))
- ;; An element.
- (let ((tag (car tree)))
- (case tag
- ((*TOP*)
- (sxml->xml (cdr tree) port))
- ((*ENTITY*)
- (if (and (list? (cdr tree)) (= (length (cdr tree)) 1))
- (entity->xml (cadr tree) port)
- (error "bad *ENTITY* args" (cdr tree))))
- ((*PI*)
- (if (and (list? (cdr tree)) (= (length (cdr tree)) 2))
- (pi->xml (cadr tree) (caddr tree) port)
- (error "bad *PI* args" (cdr tree))))
- (else
- (let* ((elems (cdr tree))
- (attrs (and (pair? elems) (pair? (car elems))
- (eq? '@ (caar elems))
- (cdar elems))))
- (element->xml tag attrs (if attrs (cdr elems) elems) port)))))
- ;; A nodelist.
- (for-each (lambda (x) (sxml->xml x port)) tree)))
- ((string? tree)
- (string->escaped-xml tree port))
- ((null? tree) *unspecified*)
- ((not tree) *unspecified*)
- ((eqv? tree #t) *unspecified*)
- ((procedure? tree)
- (with-output-to-port port tree))
- (else
- (string->escaped-xml
- (call-with-output-string (lambda (port) (display tree port)))
- port))))
-
-(define (sxml->string sxml)
- "Detag an sxml tree @var{sxml} into a string. Does not perform any
-formatting."
- (string-concatenate-reverse
- (foldts
- (lambda (seed tree) ; fdown
- '())
- (lambda (seed kid-seed tree) ; fup
- (append! kid-seed seed))
- (lambda (seed tree) ; fhere
- (if (string? tree) (cons tree seed) seed))
- '()
- sxml)))
-
-(define (make-char-quotator char-encoding)
- (let ((bad-chars (list->char-set (map car char-encoding))))
-
- ;; Check to see if str contains one of the characters in charset,
- ;; from the position i onward. If so, return that character's index.
- ;; otherwise, return #f
- (define (index-cset str i charset)
- (string-index str charset i))
-
- ;; The body of the function
- (lambda (str port)
- (let ((bad-pos (index-cset str 0 bad-chars)))
- (if (not bad-pos)
- (display str port) ; str had all good chars
- (let loop ((from 0) (to bad-pos))
- (cond
- ((>= from (string-length str)) *unspecified*)
- ((not to)
- (display (substring str from (string-length str)) port))
- (else
- (let ((quoted-char
- (cdr (assv (string-ref str to) char-encoding)))
- (new-to
- (index-cset str (+ 1 to) bad-chars)))
- (if (< from to)
- (display (substring str from to) port))
- (display quoted-char port)
- (loop (1+ to) new-to))))))))))
-
-;; Given a string, check to make sure it does not contain characters
-;; such as '<' or '&' that require encoding. Return either the original
-;; string, or a list of string fragments with special characters
-;; replaced by appropriate character entities.
-
-(define string->escaped-xml
- (make-char-quotator
- '((#\< . "&lt;") (#\> . "&gt;") (#\& . "&amp;") (#\" . "&quot;"))))
-
-;;; arch-tag: 9c853b25-d82f-42ef-a959-ae26fdc7d1ac
-;;; simple.scm ends here
-
-;;;; (sxml ssax) -- the SSAX parser
-;;;;
-;;;; Copyright (C) 2009, 2010,2012,2013 Free Software Foundation, Inc.
-;;;; Modified 2004 by Andy Wingo <wingo at pobox dot com>.
-;;;; Written 2001,2002,2003,2004 by Oleg Kiselyov <oleg at pobox dot com> as SSAX.scm.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary:
-;;
-;@subheading Functional XML parsing framework
-;@subsubheading SAX/DOM and SXML parsers with support for XML Namespaces and validation
-;
-; This is a package of low-to-high level lexing and parsing procedures
-; that can be combined to yield a SAX, a DOM, a validating parser, or
-; a parser intended for a particular document type. The procedures in
-; the package can be used separately to tokenize or parse various
-; pieces of XML documents. The package supports XML Namespaces,
-; internal and external parsed entities, user-controlled handling of
-; whitespace, and validation. This module therefore is intended to be
-; a framework, a set of "Lego blocks" you can use to build a parser
-; following any discipline and performing validation to any degree. As
-; an example of the parser construction, this file includes a
-; semi-validating SXML parser.
-
-; The present XML framework has a "sequential" feel of SAX yet a
-; "functional style" of DOM. Like a SAX parser, the framework scans the
-; document only once and permits incremental processing. An application
-; that handles document elements in order can run as efficiently as
-; possible. @emph{Unlike} a SAX parser, the framework does not require
-; an application register stateful callbacks and surrender control to
-; the parser. Rather, it is the application that can drive the framework
-; -- calling its functions to get the current lexical or syntax element.
-; These functions do not maintain or mutate any state save the input
-; port. Therefore, the framework permits parsing of XML in a pure
-; functional style, with the input port being a monad (or a linear,
-; read-once parameter).
-
-; Besides the @var{port}, there is another monad -- @var{seed}. Most of
-; the middle- and high-level parsers are single-threaded through the
-; @var{seed}. The functions of this framework do not process or affect
-; the @var{seed} in any way: they simply pass it around as an instance
-; of an opaque datatype. User functions, on the other hand, can use the
-; seed to maintain user's state, to accumulate parsing results, etc. A
-; user can freely mix his own functions with those of the framework. On
-; the other hand, the user may wish to instantiate a high-level parser:
-; @code{SSAX:make-elem-parser} or @code{SSAX:make-parser}. In the latter
-; case, the user must provide functions of specific signatures, which
-; are called at predictable moments during the parsing: to handle
-; character data, element data, or processing instructions (PI). The
-; functions are always given the @var{seed}, among other parameters, and
-; must return the new @var{seed}.
-
-; From a functional point of view, XML parsing is a combined
-; pre-post-order traversal of a "tree" that is the XML document
-; itself. This down-and-up traversal tells the user about an element
-; when its start tag is encountered. The user is notified about the
-; element once more, after all element's children have been
-; handled. The process of XML parsing therefore is a fold over the
-; raw XML document. Unlike a fold over trees defined in [1], the
-; parser is necessarily single-threaded -- obviously as elements
-; in a text XML document are laid down sequentially. The parser
-; therefore is a tree fold that has been transformed to accept an
-; accumulating parameter [1,2].
-
-; Formally, the denotational semantics of the parser can be expressed
-; as
-;@smallexample
-; parser:: (Start-tag -> Seed -> Seed) ->
-; (Start-tag -> Seed -> Seed -> Seed) ->
-; (Char-Data -> Seed -> Seed) ->
-; XML-text-fragment -> Seed -> Seed
-; parser fdown fup fchar "<elem attrs> content </elem>" seed
-; = fup "<elem attrs>" seed
-; (parser fdown fup fchar "content" (fdown "<elem attrs>" seed))
-;
-; parser fdown fup fchar "char-data content" seed
-; = parser fdown fup fchar "content" (fchar "char-data" seed)
-;
-; parser fdown fup fchar "elem-content content" seed
-; = parser fdown fup fchar "content" (
-; parser fdown fup fchar "elem-content" seed)
-;@end smallexample
-
-; Compare the last two equations with the left fold
-;@smallexample
-; fold-left kons elem:list seed = fold-left kons list (kons elem seed)
-;@end smallexample
-
-; The real parser created by @code{SSAX:make-parser} is slightly more
-; complicated, to account for processing instructions, entity
-; references, namespaces, processing of document type declaration, etc.
-
-
-; The XML standard document referred to in this module is
-; @uref{http://www.w3.org/TR/1998/REC-xml-19980210.html}
-;
-; The present file also defines a procedure that parses the text of an
-; XML document or of a separate element into SXML, an S-expression-based
-; model of an XML Information Set. SXML is also an Abstract Syntax Tree
-; of an XML document. SXML is similar but not identical to DOM; SXML is
-; particularly suitable for Scheme-based XML/HTML authoring, SXPath
-; queries, and tree transformations. See SXML.html for more details.
-; SXML is a term implementation of evaluation of the XML document [3].
-; The other implementation is context-passing.
-
-; The present frameworks fully supports the XML Namespaces Recommendation:
-; @uref{http://www.w3.org/TR/REC-xml-names/}
-; Other links:
-;@table @asis
-;@item [1]
-; Jeremy Gibbons, Geraint Jones, "The Under-appreciated Unfold,"
-; Proc. ICFP'98, 1998, pp. 273-279.
-;@item [2]
-; Richard S. Bird, The promotion and accumulation strategies in
-; transformational programming, ACM Trans. Progr. Lang. Systems,
-; 6(4):487-504, October 1984.
-;@item [3]
-; Ralf Hinze, "Deriving Backtracking Monad Transformers,"
-; Functional Pearl. Proc ICFP'00, pp. 186-197.
-;@end table
-;;
-;;; Code:
-
-(define-module (sxml ssax)
- #\use-module (sxml ssax input-parse)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-13)
-
- #\export (current-ssax-error-port
- with-ssax-error-to-port
- xml-token? xml-token-kind xml-token-head
- make-empty-attlist attlist-add
- attlist-null?
- attlist-remove-top
- attlist->alist attlist-fold
- define-parsed-entity!
- reset-parsed-entity-definitions!
- ssax:uri-string->symbol
- ssax:skip-internal-dtd
- ssax:read-pi-body-as-string
- ssax:reverse-collect-str-drop-ws
- ssax:read-markup-token
- ssax:read-cdata-body
- ssax:read-char-ref
- ssax:read-attributes
- ssax:complete-start-tag
- ssax:read-external-id
- ssax:read-char-data
- ssax:xml->sxml
- ssax:make-parser
- ssax:make-pi-parser
- ssax:make-elem-parser))
-
-(define (parser-error port message . rest)
- (apply throw 'parser-error port message rest))
-(define ascii->char integer->char)
-(define char->ascii char->integer)
-
-(define current-ssax-error-port
- (make-parameter (current-error-port)))
-
-(define *current-ssax-error-port*
- (parameter-fluid current-ssax-error-port))
-
-(define (with-ssax-error-to-port port thunk)
- (parameterize ((current-ssax-error-port port))
- (thunk)))
-
-(define (ssax:warn port . args)
- (with-output-to-port (current-ssax-error-port)
- (lambda ()
- (display ";;; SSAX warning: ")
- (for-each display args)
- (newline))))
-
-(define (ucscode->string codepoint)
- (string (integer->char codepoint)))
-
-(define char-newline #\newline)
-(define char-return #\return)
-(define char-tab #\tab)
-(define nl "\n")
-
-;; This isn't a great API, but a more proper fix will involve hacking
-;; SSAX.
-(define (reset-parsed-entity-definitions!)
- "Restore the set of parsed entity definitions to its initial state."
- (set! ssax:predefined-parsed-entities
- '((amp . "&")
- (lt . "<")
- (gt . ">")
- (apos . "'")
- (quot . "\""))))
-
-(define (define-parsed-entity! entity str)
- "Define a new parsed entity. @var{entity} should be a symbol.
-
-Instances of &@var{entity}; in XML text will be replaced with the
-string @var{str}, which will then be parsed."
- (set! ssax:predefined-parsed-entities
- (acons entity str ssax:predefined-parsed-entities)))
-
-;; Execute a sequence of forms and return the result of the _first_ one.
-;; Like PROG1 in Lisp. Typically used to evaluate one or more forms with
-;; side effects and return a value that must be computed before some or
-;; all of the side effects happen.
-(define-syntax begin0
- (syntax-rules ()
- ((begin0 form form1 ... )
- (let ((val form)) form1 ... val))))
-
-; Like let* but allowing for multiple-value bindings
-(define-syntax let*-values
- (syntax-rules ()
- ((let*-values () . bodies) (begin . bodies))
- ((let*-values (((var) initializer) . rest) . bodies)
- (let ((var initializer)) ; a single var optimization
- (let*-values rest . bodies)))
- ((let*-values ((vars initializer) . rest) . bodies)
- (call-with-values (lambda () initializer) ; the most generic case
- (lambda vars (let*-values rest . bodies))))))
-
-;; needed for some dumb reason
-(define inc 1+)
-(define dec 1-)
-
-(define-syntax include-from-path/filtered
- (lambda (x)
- (define (read-filtered accept-list file)
- (with-input-from-file (%search-load-path file)
- (lambda ()
- (let loop ((sexp (read)) (out '()))
- (cond
- ((eof-object? sexp) (reverse out))
- ((and (pair? sexp) (memq (car sexp) accept-list))
- (loop (read) (cons sexp out)))
- (else
- (loop (read) out)))))))
- (syntax-case x ()
- ((_ accept-list file)
- (with-syntax (((exp ...) (datum->syntax
- x
- (read-filtered
- (syntax->datum #'accept-list)
- (syntax->datum #'file)))))
- #'(begin exp ...))))))
-
-(include-from-path "sxml/upstream/assert.scm")
-(include-from-path/filtered
- (define define-syntax ssax:define-labeled-arg-macro)
- "sxml/upstream/SSAX.scm")
-;;;; (sxml ssax input-parse) -- a simple lexer
-;;;;
-;;;; Copyright (C) 2009 Free Software Foundation, Inc.
-;;;; Modified 2004 by Andy Wingo <wingo at pobox dot com>.
-;;;; Written 2003 by Oleg Kiselyov <oleg at pobox dot com> as input-parse.scm.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary:
-;;
-;; A simple lexer.
-;;
-;; The procedures in this module surprisingly often suffice to parse an
-;; input stream. They either skip, or build and return tokens, according
-;; to inclusion or delimiting semantics. The list of characters to
-;; expect, include, or to break at may vary from one invocation of a
-;; function to another. This allows the functions to easily parse even
-;; context-sensitive languages.
-;;
-;; EOF is generally frowned on, and thrown up upon if encountered.
-;; Exceptions are mentioned specifically. The list of expected
-;; characters (characters to skip until, or break-characters) may
-;; include an EOF "character", which is to be coded as the symbol,
-;; @code{*eof*}.
-;;
-;; The input stream to parse is specified as a @dfn{port}, which is
-;; usually the last (and optional) argument. It defaults to the current
-;; input port if omitted.
-;;
-;; If the parser encounters an error, it will throw an exception to the
-;; key @code{parser-error}. The arguments will be of the form
-;; @code{(@var{port} @var{message} @var{specialising-msg}*)}.
-;;
-;; The first argument is a port, which typically points to the offending
-;; character or its neighborhood. You can then use @code{port-column}
-;; and @code{port-line} to query the current position. @var{message} is
-;; the description of the error. Other arguments supply more details
-;; about the problem.
-;;
-;;; Code:
-
-(define-module (sxml ssax input-parse)
- #\use-module (ice-9 rdelim)
- #\export (peek-next-char
- assert-curr-char
- skip-until
- skip-while
- next-token
- next-token-of
- read-text-line
- read-string
- find-string-from-port?))
-
-(define ascii->char integer->char)
-(define char->ascii char->integer)
-(define char-newline #\newline)
-(define char-return #\return)
-(define inc 1+)
-(define dec 1-)
-
-;; rewrite oleg's define-opt into define* style
-(define-macro (define-opt bindings body . body-rest)
- (let* ((rev-bindings (reverse bindings))
- (opt-bindings
- (and (pair? rev-bindings) (pair? (car rev-bindings))
- (eq? 'optional (caar rev-bindings))
- (cdar rev-bindings))))
- (if opt-bindings
- `(define* ,(append (reverse (cons #\optional (cdr rev-bindings)))
- opt-bindings)
- ,body ,@body-rest)
- `(define* ,bindings ,body ,@body-rest))))
-
-(define (parser-error port message . rest)
- (apply throw 'parser-error port message rest))
-
-(include-from-path "sxml/upstream/input-parse.scm")
-
-;; This version for guile is quite speedy, due to read-delimited (which
-;; is implemented in C).
-(define-opt (next-token prefix-skipped-chars break-chars
- (optional (comment "") (port (current-input-port))) )
- (let ((delims (list->string (delete '*eof* break-chars))))
- (if (eof-object? (if (null? prefix-skipped-chars)
- (peek-char port)
- (skip-while prefix-skipped-chars port)))
- (if (memq '*eof* break-chars)
- ""
- (parser-error port "EOF while reading a token " comment))
- (let ((token (read-delimited delims port 'peek)))
- (if (and (eof-object? (peek-char port))
- (not (memq '*eof* break-chars)))
- (parser-error port "EOF while reading a token " comment)
- token)))))
-
-(define-opt (read-text-line (optional (port (current-input-port))) )
- (read-line port))
-
-;; Written 1995, 1996 by Oleg Kiselyov (oleg@acm.org)
-;; Modified 1996, 1997, 1998, 2001 by A. Jaffer (agj@alum.mit.edu)
-;; Modified 2003 by Steve VanDevender (stevev@hexadecimal.uoregon.edu)
-;; Modified 2004 Andy Wingo <wingo at pobox dot com>
-;; This function is from SLIB's strsrch.scm, and is in the public domain.
-(define (find-string-from-port? str <input-port> . max-no-char)
- "Looks for @var{str} in @var{<input-port>}, optionally within the
-first @var{max-no-char} characters."
- (set! max-no-char (if (null? max-no-char) #f (car max-no-char)))
- (letrec
- ((no-chars-read 0)
- (peeked? #f)
- (my-peek-char ; Return a peeked char or #f
- (lambda () (and (or (not (number? max-no-char))
- (< no-chars-read max-no-char))
- (let ((c (peek-char <input-port>)))
- (cond (peeked? c)
- ((eof-object? c) #f)
- ((procedure? max-no-char)
- (set! peeked? #t)
- (if (max-no-char c) #f c))
- ((eqv? max-no-char c) #f)
- (else c))))))
- (next-char (lambda () (set! peeked? #f) (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)))
- (and c
- (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)))
-;;;; (sxml transform) -- pre- and post-order sxml transformation
-;;;;
-;;;; Copyright (C) 2009 Free Software Foundation, Inc.
-;;;; Modified 2004 by Andy Wingo <wingo at pobox dot com>.
-;;;; Written 2003 by Oleg Kiselyov <oleg at pobox dot com> as SXML-tree-trans.scm.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary:
-;;
-;;@heading SXML expression tree transformers
-;
-;@subheading Pre-Post-order traversal of a tree and creation of a new tree
-;@smallexample
-;pre-post-order:: <tree> x <bindings> -> <new-tree>
-;@end smallexample
-; where
-;@smallexample
-; <bindings> ::= (<binding> ...)
-; <binding> ::= (<trigger-symbol> *preorder* . <handler>) |
-; (<trigger-symbol> *macro* . <handler>) |
-; (<trigger-symbol> <new-bindings> . <handler>) |
-; (<trigger-symbol> . <handler>)
-; <trigger-symbol> ::= XMLname | *text* | *default*
-; <handler> :: <trigger-symbol> x [<tree>] -> <new-tree>
-;@end smallexample
-;
-; The pre-post-order function visits the nodes and nodelists
-; pre-post-order (depth-first). For each @code{<Node>} of the form
-; @code{(@var{name} <Node> ...)}, it looks up an association with the
-; given @var{name} among its @var{<bindings>}. If failed,
-; @code{pre-post-order} tries to locate a @code{*default*} binding. It's
-; an error if the latter attempt fails as well. Having found a binding,
-; the @code{pre-post-order} function first checks to see if the binding
-; is of the form
-;@smallexample
-; (<trigger-symbol> *preorder* . <handler>)
-;@end smallexample
-;
-; If it is, the handler is 'applied' to the current node. Otherwise, the
-; pre-post-order function first calls itself recursively for each child
-; of the current node, with @var{<new-bindings>} prepended to the
-; @var{<bindings>} in effect. The result of these calls is passed to the
-; @var{<handler>} (along with the head of the current @var{<Node>}). To
-; be more precise, the handler is _applied_ to the head of the current
-; node and its processed children. The result of the handler, which
-; should also be a @code{<tree>}, replaces the current @var{<Node>}. If
-; the current @var{<Node>} is a text string or other atom, a special
-; binding with a symbol @code{*text*} is looked up.
-;
-; A binding can also be of a form
-;@smallexample
-; (<trigger-symbol> *macro* . <handler>)
-;@end smallexample
-; This is equivalent to @code{*preorder*} described above. However, the
-; result is re-processed again, with the current stylesheet.
-;;
-;;; Code:
-
-(define-module (sxml transform)
- #\export (SRV:send-reply
- foldts
- post-order
- pre-post-order
- replace-range))
-
-;; Upstream version:
-; $Id: SXML-tree-trans.scm,v 1.8 2003/04/24 19:39:53 oleg Exp oleg $
-
-; Like let* but allowing for multiple-value bindings
-(define-macro (let*-values bindings . body)
- (if (null? bindings) (cons 'begin body)
- (apply
- (lambda (vars initializer)
- (let ((cont
- (cons 'let*-values
- (cons (cdr bindings) body))))
- (cond
- ((not (pair? vars)) ; regular let case, a single var
- `(let ((,vars ,initializer)) ,cont))
- ((null? (cdr vars)) ; single var, see the prev case
- `(let ((,(car vars) ,initializer)) ,cont))
- (else ; the most generic case
- `(call-with-values (lambda () ,initializer)
- (lambda ,vars ,cont))))))
- (car bindings))))
-
-(define (SRV:send-reply . fragments)
- "Output the @var{fragments} to the current output port.
-
-The fragments are a list of strings, characters, numbers, thunks,
-@code{#f}, @code{#t} -- and other fragments. The function traverses the
-tree depth-first, writes out strings and characters, executes thunks,
-and ignores @code{#f} and @code{'()}. The function returns @code{#t} if
-anything was written at all; otherwise the result is @code{#f} If
-@code{#t} occurs among the fragments, it is not written out but causes
-the result of @code{SRV:send-reply} to be @code{#t}."
- (let loop ((fragments fragments) (result #f))
- (cond
- ((null? fragments) result)
- ((not (car fragments)) (loop (cdr fragments) result))
- ((null? (car fragments)) (loop (cdr fragments) result))
- ((eq? #t (car fragments)) (loop (cdr fragments) #t))
- ((pair? (car fragments))
- (loop (cdr fragments) (loop (car fragments) result)))
- ((procedure? (car fragments))
- ((car fragments))
- (loop (cdr fragments) #t))
- (else
- (display (car fragments))
- (loop (cdr fragments) #t)))))
-
-
-
-;------------------------------------------------------------------------
-; Traversal of an SXML tree or a grove:
-; a <Node> or a <Nodelist>
-;
-; A <Node> and a <Nodelist> are mutually-recursive datatypes that
-; underlie the SXML tree:
-; <Node> ::= (name . <Nodelist>) | "text string"
-; An (ordered) set of nodes is just a list of the constituent nodes:
-; <Nodelist> ::= (<Node> ...)
-; Nodelists, and Nodes other than text strings are both lists. A
-; <Nodelist> however is either an empty list, or a list whose head is
-; not a symbol (an atom in general). A symbol at the head of a node is
-; either an XML name (in which case it's a tag of an XML element), or
-; an administrative name such as '@'.
-; See SXPath.scm and SSAX.scm for more information on SXML.
-
-
-;; see the commentary for docs
-(define (pre-post-order tree bindings)
- (let* ((default-binding (assq '*default* bindings))
- (text-binding (or (assq '*text* bindings) default-binding))
- (text-handler ; Cache default and text bindings
- (and text-binding
- (if (procedure? (cdr text-binding))
- (cdr text-binding) (cddr text-binding)))))
- (let loop ((tree tree))
- (cond
- ((null? tree) '())
- ((not (pair? tree))
- (let ((trigger '*text*))
- (if text-handler (text-handler trigger tree)
- (error "Unknown binding for " trigger " and no default"))))
- ((not (symbol? (car tree))) (map loop tree)) ; tree is a nodelist
- (else ; tree is an SXML node
- (let* ((trigger (car tree))
- (binding (or (assq trigger bindings) default-binding)))
- (cond
- ((not binding)
- (error "Unknown binding for " trigger " and no default"))
- ((not (pair? (cdr binding))) ; must be a procedure: handler
- (apply (cdr binding) trigger (map loop (cdr tree))))
- ((eq? '*preorder* (cadr binding))
- (apply (cddr binding) tree))
- ((eq? '*macro* (cadr binding))
- (loop (apply (cddr binding) tree)))
- (else ; (cadr binding) is a local binding
- (apply (cddr binding) trigger
- (pre-post-order (cdr tree) (append (cadr binding) bindings)))
- ))))))))
-
-; post-order is a strict subset of pre-post-order without *preorder*
-; (let alone *macro*) traversals.
-; Now pre-post-order is actually faster than the old post-order.
-; The function post-order is deprecated and is aliased below for
-; backward compatibility.
-(define post-order pre-post-order)
-
-;------------------------------------------------------------------------
-; Extended tree fold
-; tree = atom | (node-name tree ...)
-;
-; foldts fdown fup fhere seed (Leaf str) = fhere seed str
-; foldts fdown fup fhere seed (Nd kids) =
-; fup seed $ foldl (foldts fdown fup fhere) (fdown seed) kids
-
-; procedure fhere: seed -> atom -> seed
-; procedure fdown: seed -> node -> seed
-; procedure fup: parent-seed -> last-kid-seed -> node -> seed
-; foldts returns the final seed
-
-(define (foldts fdown fup fhere seed tree)
- (cond
- ((null? tree) seed)
- ((not (pair? tree)) ; An atom
- (fhere seed tree))
- (else
- (let loop ((kid-seed (fdown seed tree)) (kids (cdr tree)))
- (if (null? kids)
- (fup seed kid-seed tree)
- (loop (foldts fdown fup fhere kid-seed (car kids))
- (cdr kids)))))))
-
-;------------------------------------------------------------------------
-; Traverse a forest depth-first and cut/replace ranges of nodes.
-;
-; The nodes that define a range don't have to have the same immediate
-; parent, don't have to be on the same level, and the end node of a
-; range doesn't even have to exist. A replace-range procedure removes
-; nodes from the beginning node of the range up to (but not including)
-; the end node of the range. In addition, the beginning node of the
-; range can be replaced by a node or a list of nodes. The range of
-; nodes is cut while depth-first traversing the forest. If all
-; branches of the node are cut a node is cut as well. The procedure
-; can cut several non-overlapping ranges from a forest.
-
-; replace-range:: BEG-PRED x END-PRED x FOREST -> FOREST
-; where
-; type FOREST = (NODE ...)
-; type NODE = Atom | (Name . FOREST) | FOREST
-;
-; The range of nodes is specified by two predicates, beg-pred and end-pred.
-; beg-pred:: NODE -> #f | FOREST
-; end-pred:: NODE -> #f | FOREST
-; The beg-pred predicate decides on the beginning of the range. The node
-; for which the predicate yields non-#f marks the beginning of the range
-; The non-#f value of the predicate replaces the node. The value can be a
-; list of nodes. The replace-range procedure then traverses the tree and skips
-; all the nodes, until the end-pred yields non-#f. The value of the end-pred
-; replaces the end-range node. The new end node and its brothers will be
-; re-scanned.
-; The predicates are evaluated pre-order. We do not descend into a node that
-; is marked as the beginning of the range.
-
-(define (replace-range beg-pred end-pred forest)
-
- ; loop forest keep? new-forest
- ; forest is the forest to traverse
- ; new-forest accumulates the nodes we will keep, in the reverse
- ; order
- ; If keep? is #t, keep the curr node if atomic. If the node is not atomic,
- ; traverse its children and keep those that are not in the skip range.
- ; If keep? is #f, skip the current node if atomic. Otherwise,
- ; traverse its children. If all children are skipped, skip the node
- ; as well.
-
- (define (loop forest keep? new-forest)
- (if (null? forest) (values (reverse new-forest) keep?)
- (let ((node (car forest)))
- (if keep?
- (cond ; accumulate mode
- ((beg-pred node) => ; see if the node starts the skip range
- (lambda (repl-branches) ; if so, skip/replace the node
- (loop (cdr forest) #f
- (append (reverse repl-branches) new-forest))))
- ((not (pair? node)) ; it's an atom, keep it
- (loop (cdr forest) keep? (cons node new-forest)))
- (else
- (let*-values
- (((node?) (symbol? (car node))) ; or is it a nodelist?
- ((new-kids keep?) ; traverse its children
- (loop (if node? (cdr node) node) #t '())))
- (loop (cdr forest) keep?
- (cons
- (if node? (cons (car node) new-kids) new-kids)
- new-forest)))))
- ; skip mode
- (cond
- ((end-pred node) => ; end the skip range
- (lambda (repl-branches) ; repl-branches will be re-scanned
- (loop (append repl-branches (cdr forest)) #t
- new-forest)))
- ((not (pair? node)) ; it's an atom, skip it
- (loop (cdr forest) keep? new-forest))
- (else
- (let*-values
- (((node?) (symbol? (car node))) ; or is it a nodelist?
- ((new-kids keep?) ; traverse its children
- (loop (if node? (cdr node) node) #f '())))
- (loop (cdr forest) keep?
- (if (or keep? (pair? new-kids))
- (cons
- (if node? (cons (car node) new-kids) new-kids)
- new-forest)
- new-forest) ; if all kids are skipped
- )))))))) ; skip the node too
-
- (let*-values (((new-forest keep?) (loop forest #t '())))
- new-forest))
-
-;;; arch-tag: 6c814f4b-38f7-42c1-b8ef-ce3447edefc7
-;;; transform.scm ends here
-; Functional XML parsing framework: SAX/DOM and SXML parsers
-; with support for XML Namespaces and validation
-;
-; This is a package of low-to-high level lexing and parsing procedures
-; that can be combined to yield a SAX, a DOM, a validating parsers, or
-; a parser intended for a particular document type. The procedures in
-; the package can be used separately to tokenize or parse various
-; pieces of XML documents. The package supports XML Namespaces,
-; internal and external parsed entities, user-controlled handling of
-; whitespace, and validation. This module therefore is intended to be
-; a framework, a set of "Lego blocks" you can use to build a parser
-; following any discipline and performing validation to any degree. As
-; an example of the parser construction, this file includes a
-; semi-validating SXML parser.
-
-; The present XML framework has a "sequential" feel of SAX yet a
-; "functional style" of DOM. Like a SAX parser, the framework scans
-; the document only once and permits incremental processing. An
-; application that handles document elements in order can run as
-; efficiently as possible. _Unlike_ a SAX parser, the framework does
-; not require an application register stateful callbacks and surrender
-; control to the parser. Rather, it is the application that can drive
-; the framework -- calling its functions to get the current lexical or
-; syntax element. These functions do not maintain or mutate any state
-; save the input port. Therefore, the framework permits parsing of XML
-; in a pure functional style, with the input port being a monad (or a
-; linear, read-once parameter).
-
-; Besides the PORT, there is another monad -- SEED. Most of the
-; middle- and high-level parsers are single-threaded through the
-; seed. The functions of this framework do not process or affect the
-; SEED in any way: they simply pass it around as an instance of an
-; opaque datatype. User functions, on the other hand, can use the
-; seed to maintain user's state, to accumulate parsing results, etc. A
-; user can freely mix his own functions with those of the
-; framework. On the other hand, the user may wish to instantiate a
-; high-level parser: ssax:make-elem-parser or ssax:make-parser. In
-; the latter case, the user must provide functions of specific
-; signatures, which are called at predictable moments during the
-; parsing: to handle character data, element data, or processing
-; instructions (PI). The functions are always given the SEED, among
-; other parameters, and must return the new SEED.
-
-; From a functional point of view, XML parsing is a combined
-; pre-post-order traversal of a "tree" that is the XML document
-; itself. This down-and-up traversal tells the user about an element
-; when its start tag is encountered. The user is notified about the
-; element once more, after all element's children have been
-; handled. The process of XML parsing therefore is a fold over the
-; raw XML document. Unlike a fold over trees defined in [1], the
-; parser is necessarily single-threaded -- obviously as elements
-; in a text XML document are laid down sequentially. The parser
-; therefore is a tree fold that has been transformed to accept an
-; accumulating parameter [1,2].
-
-; Formally, the denotational semantics of the parser can be expressed
-; as
-; parser:: (Start-tag -> Seed -> Seed) ->
-; (Start-tag -> Seed -> Seed -> Seed) ->
-; (Char-Data -> Seed -> Seed) ->
-; XML-text-fragment -> Seed -> Seed
-; parser fdown fup fchar "<elem attrs> content </elem>" seed
-; = fup "<elem attrs>" seed
-; (parser fdown fup fchar "content" (fdown "<elem attrs>" seed))
-;
-; parser fdown fup fchar "char-data content" seed
-; = parser fdown fup fchar "content" (fchar "char-data" seed)
-;
-; parser fdown fup fchar "elem-content content" seed
-; = parser fdown fup fchar "content" (
-; parser fdown fup fchar "elem-content" seed)
-
-; Compare the last two equations with the left fold
-; fold-left kons elem:list seed = fold-left kons list (kons elem seed)
-
-; The real parser created my ssax:make-parser is slightly more complicated,
-; to account for processing instructions, entity references, namespaces,
-; processing of document type declaration, etc.
-
-
-; The XML standard document referred to in this module is
-; http://www.w3.org/TR/1998/REC-xml-19980210.html
-;
-; The present file also defines a procedure that parses the text of an
-; XML document or of a separate element into SXML, an
-; S-expression-based model of an XML Information Set. SXML is also an
-; Abstract Syntax Tree of an XML document. SXML is similar
-; but not identical to DOM; SXML is particularly suitable for
-; Scheme-based XML/HTML authoring, SXPath queries, and tree
-; transformations. See SXML.html for more details.
-; SXML is a term implementation of evaluation of the XML document [3].
-; The other implementation is context-passing.
-
-; The present frameworks fully supports the XML Namespaces Recommendation:
-; http://www.w3.org/TR/REC-xml-names/
-; Other links:
-; [1] Jeremy Gibbons, Geraint Jones, "The Under-appreciated Unfold,"
-; Proc. ICFP'98, 1998, pp. 273-279.
-; [2] Richard S. Bird, The promotion and accumulation strategies in
-; transformational programming, ACM Trans. Progr. Lang. Systems,
-; 6(4):487-504, October 1984.
-; [3] Ralf Hinze, "Deriving Backtracking Monad Transformers,"
-; Functional Pearl. Proc ICFP'00, pp. 186-197.
-
-; IMPORT
-; parser-error ssax:warn, see Handling of errors, below
-; functions declared in files util.scm, input-parse.scm and look-for-str.scm
-; char-encoding.scm for various platform-specific character-encoding functions.
-; From SRFI-13: string-concatenate/shared and string-concatenate-reverse/shared
-; If a particular implementation lacks SRFI-13 support, please
-; include the file srfi-13-local.scm
-
-; Handling of errors
-; This package relies on a function parser-error, which must be defined
-; by a user of the package. The function has the following signature:
-; parser-error PORT MESSAGE SPECIALISING-MSG*
-; Many procedures of this package call 'parser-error' whenever a
-; parsing, well-formedness or validation error is encountered. The
-; first argument is a port, which typically points to the offending
-; character or its neighborhood. Most of the Scheme systems let the
-; user query a PORT for the current position. The MESSAGE argument
-; indicates a failed XML production or a failed XML constraint. The
-; latter is referred to by its anchor name in the XML Recommendation
-; or XML Namespaces Recommendation. The parsing library (e.g.,
-; next-token, assert-curr-char) invoke 'parser-error' as well, in
-; exactly the same way. See input-parse.scm for more details.
-; See
-; http://pair.com/lisovsky/download/parse-error.scm
-; for an excellent example of such a redefined parser-error function.
-;
-; In addition, the present code invokes a function ssax:warn
-; ssax:warn PORT MESSAGE SPECIALISING-MSG*
-; to notify the user about warnings that are NOT errors but still
-; may alert the user.
-;
-; Again, parser-error and ssax:warn are supposed to be defined by the
-; user. However, if a run-test macro below is set to include
-; self-tests, this present code does provide the definitions for these
-; functions to allow tests to run.
-
-; Misc notes
-; It seems it is highly desirable to separate tests out in a dedicated
-; file.
-;
-; Jim Bender wrote on Mon, 9 Sep 2002 20:03:42 EDT on the SSAX-SXML
-; mailing list (message A fine-grained "lego")
-; The task was to record precise source location information, as PLT
-; does with its current XML parser. That parser records the start and
-; end location (filepos, line#, column#) for pi, elements, attributes,
-; chuncks of "pcdata".
-; As suggested above, though, in some cases I needed to be able force
-; open an interface that did not yet exist. For instance, I added an
-; "end-char-data-hook", which would be called at the end of char-data
-; fragment. This returns a function of type (seed -> seed) which is
-; invoked on the current seed only if read-char-data has indeed reached
-; the end of a block of char data (after reading a new token.
-; But the deepest interface that I needed to expose was that of reading
-; attributes. In the official distribution, this is not even a separate
-; function. Instead, it is embedded within SSAX:read-attributes. This
-; required some small re-structuring as well.
-; This definitely will not be to everyone's taste (nor needed by most).
-; Certainly, the existing make-parser interface addresses most custom
-; needs. And likely 80-90 lines of a "link specification" to create a
-; parser from many tiny little lego blocks may please only a few, while
-; appalling others.
-; The code is available at http://celtic.benderweb.net/ssax-lego.plt or
-; http://celtic.benderweb.net/ssax-lego.tar.gz
-; In the examples directory, I provide:
-; - a unit version of the make-parser interface,
-; - a simple SXML parser using that interface,
-; - an SXML parser which directly uses the "new lego",
-; - a pseudo-SXML parser, which records source location information
-; - and lastly a parser which returns the structures used in PLT's xml
-; collection, with source location information
-
-; $Id: SSAX.scm,v 5.1 2004/07/07 16:02:30 sperber Exp $
-;^^^^^^^^^
-
-
- ; See the Makefile in the ../tests directory
- ; (in particular, the rule vSSAX) for an example of how
- ; to run this code on various Scheme systems.
- ; See SSAX examples for many samples of using this code,
- ; again, on a variety of Scheme systems.
- ; See http://ssax.sf.net/
-
-
-; The following macro runs built-in test cases -- or does not run,
-; depending on which of the two cases below you commented out
-; Case 1: no tests:
-;(define-macro run-test (lambda body '(begin #f)))
-;(define-syntax run-test (syntax-rules () ((run-test . args) (begin #f))))
-
-; Case 2: with tests.
-; The following macro could've been defined just as
-; (define-macro run-test (lambda body `(begin (display "\n-->Test\n") ,@body)))
-;
-; Instead, it's more involved, to make up for case-insensitivity of
-; symbols on some Scheme systems. In Gambit, symbols are case
-; sensitive: (eq? 'A 'a) is #f and (eq? 'Aa (string->symbol "Aa")) is
-; #t. On some systems, symbols are case-insensitive and just the
-; opposite is true. Therefore, we introduce a notation '"ASymbol" (a
-; quoted string) that stands for a case-_sensitive_ ASymbol -- on any
-; R5RS Scheme system. This notation is valid only within the body of
-; run-test.
-; The notation is implemented by scanning the run-test's
-; body and replacing every occurrence of (quote "str") with the result
-; of (string->symbol "str"). We can do such a replacement at macro-expand
-; time (rather than at run time).
-
-; Here's the previous version of run-test, implemented as a low-level
-; macro.
-; (define-macro run-test
-; (lambda body
-; (define (re-write body)
-; (cond
-; ((vector? body)
-; (list->vector (re-write (vector->list body))))
-; ((not (pair? body)) body)
-; ((and (eq? 'quote (car body)) (pair? (cdr body))
-; (string? (cadr body)))
-; (string->symbol (cadr body)))
-; (else (cons (re-write (car body)) (re-write (cdr body))))))
-; (cons 'begin (re-write body))))
-;
-; For portability, it is re-written as syntax-rules. The syntax-rules
-; version is less powerful: for example, it can't handle
-; (case x (('"Foo") (do-on-Foo))) whereas the low-level macro
-; could correctly place a case-sensitive symbol at the right place.
-; We also do not scan vectors (because we don't use them here).
-; Twice-deep quasiquotes aren't handled either.
-; Still, the syntax-rules version satisfies our immediate needs.
-; Incidentally, I originally didn't believe that the macro below
-; was at all possible.
-;
-; The macro is written in a continuation-passing style. A continuation
-; typically has the following structure: (k-head ! . args)
-; When the continuation is invoked, we expand into
-; (k-head <computed-result> . arg). That is, the dedicated symbol !
-; is the placeholder for the result.
-;
-; It seems that the most modular way to write the run-test macro would
-; be the following
-;
-; (define-syntax run-test
-; (syntax-rules ()
-; ((run-test . ?body)
-; (letrec-syntax
-; ((scan-exp ; (scan-exp body k)
-; (syntax-rules (quote quasiquote !)
-; ((scan-exp (quote (hd . tl)) k)
-; (scan-lit-lst (hd . tl) (do-wrap ! quasiquote k)))
-; ((scan-exp (quote x) (k-head ! . args))
-; (k-head
-; (if (string? (quote x)) (string->symbol (quote x)) (quote x))
-; . args))
-; ((scan-exp (hd . tl) k)
-; (scan-exp hd (do-tl ! scan-exp tl k)))
-; ((scan-exp x (k-head ! . args))
-; (k-head x . args))))
-; (do-tl
-; (syntax-rules (!)
-; ((do-tl processed-hd fn () (k-head ! . args))
-; (k-head (processed-hd) . args))
-; ((do-tl processed-hd fn old-tl k)
-; (fn old-tl (do-cons ! processed-hd k)))))
-; ...
-; (do-finish
-; (syntax-rules ()
-; ((do-finish (new-body)) new-body)
-; ((do-finish new-body) (begin . new-body))))
-; ...
-; (scan-exp ?body (do-finish !))
-; ))))
-;
-; Alas, that doesn't work on all systems. We hit yet another dark
-; corner of the R5RS macros. The reason is that run-test is used in
-; the code below to introduce definitions. For example:
-; (run-test
-; (define (ssax:warn port msg . other-msg)
-; (apply cerr (cons* nl "Warning: " msg other-msg)))
-; )
-; This code expands to
-; (begin
-; (define (ssax:warn port msg . other-msg) ...))
-; so the definition gets spliced in into the top level. Right?
-; Well, On Petite Chez Scheme it is so. However, many other systems
-; don't like this approach. The reason is that the invocation of
-; (run-test (define (ssax:warn port msg . other-msg) ...))
-; first expands into
-; (letrec-syntax (...)
-; (scan-exp ((define (ssax:warn port msg . other-msg) ...)) ...))
-; because of the presence of (letrec-syntax ...), the begin form that
-; is generated eventually is no longer at the top level! The begin
-; form in Scheme is an overloading of two distinct forms: top-level
-; begin and the other begin. The forms have different rules: for example,
-; (begin (define x 1)) is OK for a top-level begin but not OK for
-; the other begin. Some Scheme systems see the that the macro
-; (run-test ...) expands into (letrec-syntax ...) and decide right there
-; that any further (begin ...) forms are NOT top-level begin forms.
-; The only way out is to make sure all our macros are top-level.
-; The best approach <sigh> seems to be to make run-test one huge
-; top-level macro.
-
-
-(define-syntax run-test
- (syntax-rules (define)
- ((run-test "scan-exp" (define vars body))
- (define vars (run-test "scan-exp" body)))
- ((run-test "scan-exp" ?body)
- (letrec-syntax
- ((scan-exp ; (scan-exp body k)
- (syntax-rules (quote quasiquote !)
- ((scan-exp '() (k-head ! . args))
- (k-head '() . args))
- ((scan-exp (quote (hd . tl)) k)
- (scan-lit-lst (hd . tl) (do-wrap ! quasiquote k)))
- ((scan-exp (quasiquote (hd . tl)) k)
- (scan-lit-lst (hd . tl) (do-wrap ! quasiquote k)))
- ((scan-exp (quote x) (k-head ! . args))
- (k-head
- (if (string? (quote x)) (string->symbol (quote x)) (quote x))
- . args))
- ((scan-exp (hd . tl) k)
- (scan-exp hd (do-tl ! scan-exp tl k)))
- ((scan-exp x (k-head ! . args))
- (k-head x . args))))
- (do-tl
- (syntax-rules (!)
- ((do-tl processed-hd fn () (k-head ! . args))
- (k-head (processed-hd) . args))
- ((do-tl processed-hd fn old-tl k)
- (fn old-tl (do-cons ! processed-hd k)))))
- (do-cons
- (syntax-rules (!)
- ((do-cons processed-tl processed-hd (k-head ! . args))
- (k-head (processed-hd . processed-tl) . args))))
- (do-wrap
- (syntax-rules (!)
- ((do-wrap val fn (k-head ! . args))
- (k-head (fn val) . args))))
- (do-finish
- (syntax-rules ()
- ((do-finish new-body) new-body)))
-
- (scan-lit-lst ; scan literal list
- (syntax-rules (quote unquote unquote-splicing !)
- ((scan-lit-lst '() (k-head ! . args))
- (k-head '() . args))
- ((scan-lit-lst (quote (hd . tl)) k)
- (do-tl quote scan-lit-lst ((hd . tl)) k))
- ((scan-lit-lst (unquote x) k)
- (scan-exp x (do-wrap ! unquote k)))
- ((scan-lit-lst (unquote-splicing x) k)
- (scan-exp x (do-wrap ! unquote-splicing k)))
- ((scan-lit-lst (quote x) (k-head ! . args))
- (k-head
- ,(if (string? (quote x)) (string->symbol (quote x)) (quote x))
- . args))
- ((scan-lit-lst (hd . tl) k)
- (scan-lit-lst hd (do-tl ! scan-lit-lst tl k)))
- ((scan-lit-lst x (k-head ! . args))
- (k-head x . args))))
- )
- (scan-exp ?body (do-finish !))))
- ((run-test body ...)
- (begin
- (run-test "scan-exp" body) ...))
-))
-
-;========================================================================
-; Data Types
-
-; TAG-KIND
-; a symbol 'START, 'END, 'PI, 'DECL, 'COMMENT, 'CDSECT
-; or 'ENTITY-REF that identifies a markup token
-
-; UNRES-NAME
-; a name (called GI in the XML Recommendation) as given in an xml
-; document for a markup token: start-tag, PI target, attribute name.
-; If a GI is an NCName, UNRES-NAME is this NCName converted into
-; a Scheme symbol. If a GI is a QName, UNRES-NAME is a pair of
-; symbols: (PREFIX . LOCALPART)
-
-; RES-NAME
-; An expanded name, a resolved version of an UNRES-NAME.
-; For an element or an attribute name with a non-empty namespace URI,
-; RES-NAME is a pair of symbols, (URI-SYMB . LOCALPART).
-; Otherwise, it's a single symbol.
-
-; ELEM-CONTENT-MODEL
-; A symbol:
-; ANY - anything goes, expect an END tag.
-; EMPTY-TAG - no content, and no END-tag is coming
-; EMPTY - no content, expect the END-tag as the next token
-; PCDATA - expect character data only, and no children elements
-; MIXED
-; ELEM-CONTENT
-
-; URI-SYMB
-; A symbol representing a namespace URI -- or other symbol chosen
-; by the user to represent URI. In the former case,
-; URI-SYMB is created by %-quoting of bad URI characters and
-; converting the resulting string into a symbol.
-
-; NAMESPACES
-; A list representing namespaces in effect. An element of the list
-; has one of the following forms:
-; (PREFIX URI-SYMB . URI-SYMB) or
-; (PREFIX USER-PREFIX . URI-SYMB)
-; USER-PREFIX is a symbol chosen by the user
-; to represent the URI.
-; (#f USER-PREFIX . URI-SYMB)
-; Specification of the user-chosen prefix and a URI-SYMBOL.
-; (*DEFAULT* USER-PREFIX . URI-SYMB)
-; Declaration of the default namespace
-; (*DEFAULT* #f . #f)
-; Un-declaration of the default namespace. This notation
-; represents overriding of the previous declaration
-; A NAMESPACES list may contain several elements for the same PREFIX.
-; The one closest to the beginning of the list takes effect.
-
-; ATTLIST
-; An ordered collection of (NAME . VALUE) pairs, where NAME is
-; a RES-NAME or an UNRES-NAME. The collection is an ADT
-
-; STR-HANDLER
-; A procedure of three arguments: STRING1 STRING2 SEED
-; returning a new SEED
-; The procedure is supposed to handle a chunk of character data
-; STRING1 followed by a chunk of character data STRING2.
-; STRING2 is a short string, often "\n" and even ""
-
-; ENTITIES
-; An assoc list of pairs:
-; (named-entity-name . named-entity-body)
-; where named-entity-name is a symbol under which the entity was
-; declared, named-entity-body is either a string, or
-; (for an external entity) a thunk that will return an
-; input port (from which the entity can be read).
-; named-entity-body may also be #f. This is an indication that a
-; named-entity-name is currently being expanded. A reference to
-; this named-entity-name will be an error: violation of the
-; WFC nonrecursion.
-;
-; As an extension to the original SSAX, Guile allows a
-; named-entity-name of *DEFAULT* to indicate a fallback procedure,
-; called as (FALLBACK PORT NAME). The procedure should return a
-; string.
-
-; XML-TOKEN -- a record
-
-; In Gambit, you can use the following declaration:
-; (define-structure xml-token kind head)
-; The following declaration is "standard" as it follows SRFI-9:
-;;(define-record-type xml-token (make-xml-token kind head) xml-token?
-;; (kind xml-token-kind)
-;; (head xml-token-head) )
-; No field mutators are declared as SSAX is a pure functional parser
-;
-; But to make the code more portable, we define xml-token simply as
-; a pair. It suffices for us. Furthermore, xml-token-kind and xml-token-head
-; can be defined as simple procedures. However, they are declared as
-; macros below for efficiency.
-
-(define (make-xml-token kind head) (cons kind head))
-(define xml-token? pair?)
-(define-syntax xml-token-kind
- (syntax-rules () ((xml-token-kind token) (car token))))
-(define-syntax xml-token-head
- (syntax-rules () ((xml-token-head token) (cdr token))))
-
-; (define-macro xml-token-kind (lambda (token) `(car ,token)))
-; (define-macro xml-token-head (lambda (token) `(cdr ,token)))
-
-; This record represents a markup, which is, according to the XML
-; Recommendation, "takes the form of start-tags, end-tags, empty-element tags,
-; entity references, character references, comments, CDATA section delimiters,
-; document type declarations, and processing instructions."
-;
-; kind -- a TAG-KIND
-; head -- an UNRES-NAME. For xml-tokens of kinds 'COMMENT and
-; 'CDSECT, the head is #f
-;
-; For example,
-; <P> => kind='START, head='P
-; </P> => kind='END, head='P
-; <BR/> => kind='EMPTY-EL, head='BR
-; <!DOCTYPE OMF ...> => kind='DECL, head='DOCTYPE
-; <?xml version="1.0"?> => kind='PI, head='xml
-; &my-ent; => kind = 'ENTITY-REF, head='my-ent
-;
-; Character references are not represented by xml-tokens as these references
-; are transparently resolved into the corresponding characters.
-;
-
-
-
-; XML-DECL -- a record
-
-; The following is Gambit-specific, see below for a portable declaration
-;(define-structure xml-decl elems entities notations)
-
-; The record represents a datatype of an XML document: the list of
-; declared elements and their attributes, declared notations, list of
-; replacement strings or loading procedures for parsed general
-; entities, etc. Normally an xml-decl record is created from a DTD or
-; an XML Schema, although it can be created and filled in in many other
-; ways (e.g., loaded from a file).
-;
-; elems: an (assoc) list of decl-elem or #f. The latter instructs
-; the parser to do no validation of elements and attributes.
-;
-; decl-elem: declaration of one element:
-; (elem-name elem-content decl-attrs)
-; elem-name is an UNRES-NAME for the element.
-; elem-content is an ELEM-CONTENT-MODEL.
-; decl-attrs is an ATTLIST, of (ATTR-NAME . VALUE) associations
-; !!!This element can declare a user procedure to handle parsing of an
-; element (e.g., to do a custom validation, or to build a hash of
-; IDs as they're encountered).
-;
-; decl-attr: an element of an ATTLIST, declaration of one attribute
-; (attr-name content-type use-type default-value)
-; attr-name is an UNRES-NAME for the declared attribute
-; content-type is a symbol: CDATA, NMTOKEN, NMTOKENS, ...
-; or a list of strings for the enumerated type.
-; use-type is a symbol: REQUIRED, IMPLIED, FIXED
-; default-value is a string for the default value, or #f if not given.
-;
-;
-
-; see a function make-empty-xml-decl to make a XML declaration entry
-; suitable for a non-validating parsing.
-
-
-;-------------------------
-; Utilities
-
-; ssax:warn PORT MESSAGE SPECIALISING-MSG*
-; to notify the user about warnings that are NOT errors but still
-; may alert the user.
-; Result is unspecified.
-; We need to define the function to allow the self-tests to run.
-; Normally the definition of ssax:warn is to be provided by the user.
-(run-test
- (define (ssax:warn port msg . other-msg)
- (apply cerr (cons* nl "Warning: " msg other-msg)))
-)
-
-
-; parser-error PORT MESSAGE SPECIALISING-MSG*
-; to let the user know of a syntax error or a violation of a
-; well-formedness or validation constraint.
-; Result is unspecified.
-; We need to define the function to allow the self-tests to run.
-; Normally the definition of parser-error is to be provided by the user.
-(run-test
- (define (parser-error port msg . specializing-msgs)
- (apply error (cons msg specializing-msgs)))
-)
-
-; The following is a function that is often used in validation tests,
-; to make sure that the computed result matches the expected one.
-; This function is a standard equal? predicate with one exception.
-; On Scheme systems where (string->symbol "A") and a symbol A
-; are the same, equal_? is precisely equal?
-; On other Scheme systems, we compare symbols disregarding their case.
-; Since this function is used only in tests, we don't have to
-; strive to make it efficient.
-(run-test
- (define (equal_? e1 e2)
- (if (eq? 'A (string->symbol "A")) (equal? e1 e2)
- (cond
- ((symbol? e1)
- (and (symbol? e2)
- (string-ci=? (symbol->string e1) (symbol->string e2))))
- ((pair? e1)
- (and (pair? e2)
- (equal_? (car e1) (car e2)) (equal_? (cdr e1) (cdr e2))))
- ((vector? e1)
- (and (vector? e2) (equal_? (vector->list e1) (vector->list e2))))
- (else
- (equal? e1 e2)))))
-)
-
-; The following function, which is often used in validation tests,
-; lets us conveniently enter newline, CR and tab characters in a character
-; string.
-; unesc-string: ESC-STRING -> STRING
-; where ESC-STRING is a character string that may contain
-; %n -- for #\newline
-; %r -- for #\return
-; %t -- for #\tab
-; %% -- for #\%
-;
-; The result of unesc-string is a character string with all %-combinations
-; above replaced with their character equivalents
-
-(run-test
- (define (unesc-string str)
- (call-with-input-string str
- (lambda (port)
- (let loop ((frags '()))
- (let* ((token (next-token '() '(#\% *eof*) "unesc-string" port))
- (cterm (read-char port))
- (frags (cons token frags)))
- (if (eof-object? cterm) (string-concatenate-reverse/shared frags)
- (let ((cchar (read-char port))) ; char after #\%
- (if (eof-object? cchar)
- (error "unexpected EOF after reading % in unesc-string:" str)
- (loop
- (cons
- (case cchar
- ((#\n) (string #\newline))
- ((#\r) (string char-return))
- ((#\t) (string char-tab))
- ((#\%) "%")
- (else (error "bad %-char in unesc-string:" cchar)))
- frags))))))))))
-)
-
-
-; Test if a string is made of only whitespace
-; An empty string is considered made of whitespace as well
-(define (string-whitespace? str)
- (let ((len (string-length str)))
- (cond
- ((zero? len) #t)
- ((= 1 len) (char-whitespace? (string-ref str 0)))
- ((= 2 len) (and (char-whitespace? (string-ref str 0))
- (char-whitespace? (string-ref str 1))))
- (else
- (let loop ((i 0))
- (or (>= i len)
- (and (char-whitespace? (string-ref str i))
- (loop (inc i)))))))))
-
-; Find val in alist
-; Return (values found-el remaining-alist) or
-; (values #f alist)
-
-(define (assq-values val alist)
- (let loop ((alist alist) (scanned '()))
- (cond
- ((null? alist) (values #f scanned))
- ((equal? val (caar alist))
- (values (car alist) (append scanned (cdr alist))))
- (else
- (loop (cdr alist) (cons (car alist) scanned))))))
-
-; From SRFI-1
-(define (fold-right kons knil lis1)
- (let recur ((lis lis1))
- (if (null? lis) knil
- (let ((head (car lis)))
- (kons head (recur (cdr lis)))))))
-
-; Left fold combinator for a single list
-(define (fold kons knil lis1)
- (let lp ((lis lis1) (ans knil))
- (if (null? lis) ans
- (lp (cdr lis) (kons (car lis) ans)))))
-
-
-
-;========================================================================
-; Lower-level parsers and scanners
-;
-; They deal with primitive lexical units (Names, whitespaces, tags)
-; and with pieces of more generic productions. Most of these parsers
-; must be called in appropriate context. For example, ssax:complete-start-tag
-; must be called only when the start-tag has been detected and its GI
-; has been read.
-
-;------------------------------------------------------------------------
-; Low-level parsing code
-
-; Skip the S (whitespace) production as defined by
-; [3] S ::= (#x20 | #x9 | #xD | #xA)
-; The procedure returns the first not-whitespace character it
-; encounters while scanning the PORT. This character is left
-; on the input stream.
-
-(define ssax:S-chars (map ascii->char '(32 10 9 13)))
-
-(define (ssax:skip-S port)
- (skip-while ssax:S-chars port))
-
-
-; Read a Name lexem and return it as string
-; [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':'
-; | CombiningChar | Extender
-; [5] Name ::= (Letter | '_' | ':') (NameChar)*
-;
-; This code supports the XML Namespace Recommendation REC-xml-names,
-; which modifies the above productions as follows:
-;
-; [4] NCNameChar ::= Letter | Digit | '.' | '-' | '_'
-; | CombiningChar | Extender
-; [5] NCName ::= (Letter | '_') (NCNameChar)*
-; As the Rec-xml-names says,
-; "An XML document conforms to this specification if all other tokens
-; [other than element types and attribute names] in the document which
-; are required, for XML conformance, to match the XML production for
-; Name, match this specification's production for NCName."
-; Element types and attribute names must match the production QName,
-; defined below.
-
-; Check to see if a-char may start a NCName
-(define (ssax:ncname-starting-char? a-char)
- (and (char? a-char)
- (or
- (char-alphabetic? a-char)
- (char=? #\_ a-char))))
-
-
-; Read a NCName starting from the current position in the PORT and
-; return it as a symbol.
-(define (ssax:read-NCName port)
- (let ((first-char (peek-char port)))
- (or (ssax:ncname-starting-char? first-char)
- (parser-error port "XMLNS [4] for '" first-char "'")))
- (string->symbol
- (next-token-of
- (lambda (c)
- (cond
- ((eof-object? c) #f)
- ((char-alphabetic? c) c)
- ((string-index "0123456789.-_" c) c)
- (else #f)))
- port)))
-
-; Read a (namespace-) Qualified Name, QName, from the current
-; position in the PORT.
-; From REC-xml-names:
-; [6] QName ::= (Prefix ':')? LocalPart
-; [7] Prefix ::= NCName
-; [8] LocalPart ::= NCName
-; Return: an UNRES-NAME
-(define (ssax:read-QName port)
- (let ((prefix-or-localpart (ssax:read-NCName port)))
- (case (peek-char port)
- ((#\:) ; prefix was given after all
- (read-char port) ; consume the colon
- (cons prefix-or-localpart (ssax:read-NCName port)))
- (else prefix-or-localpart) ; Prefix was omitted
- )))
-
-; The prefix of the pre-defined XML namespace
-(define ssax:Prefix-XML (string->symbol "xml"))
-
-(run-test
- (assert (eq? '_
- (call-with-input-string "_" ssax:read-NCName)))
- (assert (eq? '_
- (call-with-input-string "_" ssax:read-QName)))
- (assert (eq? (string->symbol "_abc_")
- (call-with-input-string "_abc_;" ssax:read-NCName)))
- (assert (eq? (string->symbol "_abc_")
- (call-with-input-string "_abc_;" ssax:read-QName)))
- (assert (eq? (string->symbol "_a.b")
- (call-with-input-string "_a.b " ssax:read-QName)))
- (assert (equal? (cons (string->symbol "_a.b") (string->symbol "d.1-ef-"))
- (call-with-input-string "_a.b:d.1-ef-;" ssax:read-QName)))
- (assert (equal? (cons (string->symbol "a") (string->symbol "b"))
- (call-with-input-string "a:b:c" ssax:read-QName)))
-
- (assert (failed? (call-with-input-string ":abc" ssax:read-NCName)))
- (assert (failed? (call-with-input-string "1:bc" ssax:read-NCName)))
-)
-
-; Compare one RES-NAME or an UNRES-NAME with the other.
-; Return a symbol '<, '>, or '= depending on the result of
-; the comparison.
-; Names without PREFIX are always smaller than those with the PREFIX.
-(define name-compare
- (letrec ((symbol-compare
- (lambda (symb1 symb2)
- (cond
- ((eq? symb1 symb2) '=)
- ((string<? (symbol->string symb1) (symbol->string symb2))
- '<)
- (else '>)))))
- (lambda (name1 name2)
- (cond
- ((symbol? name1) (if (symbol? name2) (symbol-compare name1 name2)
- '<))
- ((symbol? name2) '>)
- ((eq? name2 ssax:largest-unres-name) '<)
- ((eq? name1 ssax:largest-unres-name) '>)
- ((eq? (car name1) (car name2)) ; prefixes the same
- (symbol-compare (cdr name1) (cdr name2)))
- (else (symbol-compare (car name1) (car name2)))))))
-
-; An UNRES-NAME that is postulated to be larger than anything that can occur in
-; a well-formed XML document.
-; name-compare enforces this postulate.
-(define ssax:largest-unres-name (cons
- (string->symbol "#LARGEST-SYMBOL")
- (string->symbol "#LARGEST-SYMBOL")))
-
-(run-test
- (assert (eq? '= (name-compare 'ABC 'ABC)))
- (assert (eq? '< (name-compare 'ABC 'ABCD)))
- (assert (eq? '> (name-compare 'XB 'ABCD)))
- (assert (eq? '> (name-compare '(HTML . PRE) 'PRE)))
- (assert (eq? '< (name-compare 'HTML '(HTML . PRE))))
- (assert (eq? '= (name-compare '(HTML . PRE) '(HTML . PRE))))
- (assert (eq? '< (name-compare '(HTML . PRE) '(XML . PRE))))
- (assert (eq? '> (name-compare '(HTML . PRE) '(HTML . P))))
- (assert (eq? '< (name-compare '(HTML . PRE) ssax:largest-unres-name)))
- (assert (eq? '< (name-compare '(ZZZZ . ZZZ) ssax:largest-unres-name)))
- (assert (eq? '> (name-compare ssax:largest-unres-name '(ZZZZ . ZZZ) )))
-)
-
-
-
-; procedure: ssax:read-markup-token PORT
-; This procedure starts parsing of a markup token. The current position
-; in the stream must be #\<. This procedure scans enough of the input stream
-; to figure out what kind of a markup token it is seeing. The procedure returns
-; an xml-token structure describing the token. Note, generally reading
-; of the current markup is not finished! In particular, no attributes of
-; the start-tag token are scanned.
-;
-; Here's a detailed break out of the return values and the position in the PORT
-; when that particular value is returned:
-; PI-token: only PI-target is read.
-; To finish the Processing Instruction and disregard it,
-; call ssax:skip-pi. ssax:read-attributes may be useful
-; as well (for PIs whose content is attribute-value
-; pairs)
-; END-token: The end tag is read completely; the current position
-; is right after the terminating #\> character.
-; COMMENT is read and skipped completely. The current position
-; is right after "-->" that terminates the comment.
-; CDSECT The current position is right after "<!CDATA["
-; Use ssax:read-cdata-body to read the rest.
-; DECL We have read the keyword (the one that follows "<!")
-; identifying this declaration markup. The current
-; position is after the keyword (usually a
-; whitespace character)
-;
-; START-token We have read the keyword (GI) of this start tag.
-; No attributes are scanned yet. We don't know if this
-; tag has an empty content either.
-; Use ssax:complete-start-tag to finish parsing of
-; the token.
-
-(define ssax:read-markup-token ; procedure ssax:read-markup-token port
- (let ()
- ; we have read "<!-". Skip through the rest of the comment
- ; Return the 'COMMENT token as an indication we saw a comment
- ; and skipped it.
- (define (skip-comment port)
- (assert-curr-char '(#\-) "XML [15], second dash" port)
- (if (not (find-string-from-port? "-->" port))
- (parser-error port "XML [15], no -->"))
- (make-xml-token 'COMMENT #f))
-
- ; we have read "<![" that must begin a CDATA section
- (define (read-cdata port)
- (assert (string=? "CDATA[" (read-string 6 port)))
- (make-xml-token 'CDSECT #f))
-
- (lambda (port)
- (assert-curr-char '(#\<) "start of the token" port)
- (case (peek-char port)
- ((#\/) (read-char port)
- (begin0 (make-xml-token 'END (ssax:read-QName port))
- (ssax:skip-S port)
- (assert-curr-char '(#\>) "XML [42]" port)))
- ((#\?) (read-char port) (make-xml-token 'PI (ssax:read-NCName port)))
- ((#\!)
- (case (peek-next-char port)
- ((#\-) (read-char port) (skip-comment port))
- ((#\[) (read-char port) (read-cdata port))
- (else (make-xml-token 'DECL (ssax:read-NCName port)))))
- (else (make-xml-token 'START (ssax:read-QName port)))))
-))
-
-
-; The current position is inside a PI. Skip till the rest of the PI
-(define (ssax:skip-pi port)
- (if (not (find-string-from-port? "?>" port))
- (parser-error port "Failed to find ?> terminating the PI")))
-
-
-; The current position is right after reading the PITarget. We read the
-; body of PI and return is as a string. The port will point to the
-; character right after '?>' combination that terminates PI.
-; [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
-
-(define (ssax:read-pi-body-as-string port)
- (ssax:skip-S port) ; skip WS after the PI target name
- (string-concatenate/shared
- (let loop ()
- (let ((pi-fragment
- (next-token '() '(#\?) "reading PI content" port)))
- (if (eqv? #\> (peek-next-char port))
- (begin
- (read-char port)
- (cons pi-fragment '()))
- (cons* pi-fragment "?" (loop)))))))
-
-(run-test
- (assert (equal? "p1 content "
- (call-with-input-string "<?pi1 p1 content ?>"
- (lambda (port)
- (ssax:read-markup-token port)
- (ssax:read-pi-body-as-string port)))))
- (assert (equal? "pi2? content? ?"
- (call-with-input-string "<?pi2 pi2? content? ??>"
- (lambda (port)
- (ssax:read-markup-token port)
- (ssax:read-pi-body-as-string port)))))
-)
-
-;(define (ssax:read-pi-body-as-name-values port)
-
-; The current pos in the port is inside an internal DTD subset
-; (e.g., after reading #\[ that begins an internal DTD subset)
-; Skip until the "]>" combination that terminates this DTD
-(define (ssax:skip-internal-dtd port)
- (if (not (find-string-from-port? "]>" port))
- (parser-error port
- "Failed to find ]> terminating the internal DTD subset")))
-
-
-; procedure+: ssax:read-cdata-body PORT STR-HANDLER SEED
-;
-; This procedure must be called after we have read a string "<![CDATA["
-; that begins a CDATA section. The current position must be the first
-; position of the CDATA body. This function reads _lines_ of the CDATA
-; body and passes them to a STR-HANDLER, a character data consumer.
-;
-; The str-handler is a STR-HANDLER, a procedure STRING1 STRING2 SEED.
-; The first STRING1 argument to STR-HANDLER never contains a newline.
-; The second STRING2 argument often will. On the first invocation of
-; the STR-HANDLER, the seed is the one passed to ssax:read-cdata-body
-; as the third argument. The result of this first invocation will be
-; passed as the seed argument to the second invocation of the line
-; consumer, and so on. The result of the last invocation of the
-; STR-HANDLER is returned by the ssax:read-cdata-body. Note a
-; similarity to the fundamental 'fold' iterator.
-;
-; Within a CDATA section all characters are taken at their face value,
-; with only three exceptions:
-; CR, LF, and CRLF are treated as line delimiters, and passed
-; as a single #\newline to the STR-HANDLER
-; "]]>" combination is the end of the CDATA section.
-; &gt; is treated as an embedded #\> character
-; Note, &lt; and &amp; are not specially recognized (and are not expanded)!
-
-(define ssax:read-cdata-body
- (let ((cdata-delimiters (list char-return #\newline #\] #\&)))
-
- (lambda (port str-handler seed)
- (let loop ((seed seed))
- (let ((fragment (next-token '() cdata-delimiters
- "reading CDATA" port)))
- ; that is, we're reading the char after the 'fragment'
- (case (read-char port)
- ((#\newline) (loop (str-handler fragment nl seed)))
- ((#\])
- (if (not (eqv? (peek-char port) #\]))
- (loop (str-handler fragment "]" seed))
- (let check-after-second-braket
- ((seed (if (string-null? fragment) seed
- (str-handler fragment "" seed))))
- (case (peek-next-char port) ; after the second bracket
- ((#\>) (read-char port) seed) ; we have read "]]>"
- ((#\]) (check-after-second-braket
- (str-handler "]" "" seed)))
- (else (loop (str-handler "]]" "" seed)))))))
- ((#\&) ; Note that #\& within CDATA may stand for itself
- (let ((ent-ref ; it does not have to start an entity ref
- (next-token-of (lambda (c)
- (and (not (eof-object? c)) (char-alphabetic? c) c)) port)))
- (cond ; "&gt;" is to be replaced with #\>
- ((and (string=? "gt" ent-ref) (eqv? (peek-char port) #\;))
- (read-char port)
- (loop (str-handler fragment ">" seed)))
- (else
- (loop
- (str-handler ent-ref ""
- (str-handler fragment "&" seed)))))))
- (else ; Must be CR: if the next char is #\newline, skip it
- (if (eqv? (peek-char port) #\newline) (read-char port))
- (loop (str-handler fragment nl seed)))
- ))))))
-
-; a few lines of validation code
-(run-test (letrec
- ((consumer (lambda (fragment foll-fragment seed)
- (cons* (if (equal? foll-fragment (string #\newline))
- " NL" foll-fragment) fragment seed)))
- (test (lambda (str expected-result)
- (newline) (display "body: ") (write str)
- (newline) (display "Result: ")
- (let ((result
- (reverse
- (call-with-input-string (unesc-string str)
- (lambda (port) (ssax:read-cdata-body port consumer '()))
- ))))
- (write result)
- (assert (equal? result expected-result)))))
- )
- (test "]]>" '())
- (test "abcd]]>" '("abcd" ""))
- (test "abcd]]]>" '("abcd" "" "]" ""))
- (test "abcd]]]]>" '("abcd" "" "]" "" "]" ""))
- (test "abcd]]]]]>" '("abcd" "" "]" "" "]" "" "]" ""))
- (test "abcd]]]a]]>" '("abcd" "" "]" "" "]]" "" "a" ""))
- (test "abc%r%ndef%n]]>" '("abc" " NL" "def" " NL"))
- (test "%r%n%r%n]]>" '("" " NL" "" " NL"))
- (test "%r%n%r%na]]>" '("" " NL" "" " NL" "a" ""))
- (test "%r%r%r%na]]>" '("" " NL" "" " NL" "" " NL" "a" ""))
- (test "abc&!!!]]>" '("abc" "&" "" "" "!!!" ""))
- (test "abc]]&gt;&gt&amp;]]]&gt;and]]>"
- '("abc" "" "]]" "" "" ">" "" "&" "gt" "" "" "&" "amp" "" ";" "" "]" ""
- "]]" "" "" ">" "and" ""))
-))
-
-
-; procedure+: ssax:read-char-ref PORT
-;
-; [66] CharRef ::= '&#' [0-9]+ ';'
-; | '&#x' [0-9a-fA-F]+ ';'
-;
-; This procedure must be called after we we have read "&#"
-; that introduces a char reference.
-; The procedure reads this reference and returns the corresponding char
-; The current position in PORT will be after ";" that terminates
-; the char reference
-; Faults detected:
-; WFC: XML-Spec.html#wf-Legalchar
-;
-; According to Section "4.1 Character and Entity References"
-; of the XML Recommendation:
-; "[Definition: A character reference refers to a specific character
-; in the ISO/IEC 10646 character set, for example one not directly
-; accessible from available input devices.]"
-; Therefore, we use a ucscode->string function to convert a character
-; code into the character -- *regardless* of the current character
-; encoding of the input stream.
-
-(define (ssax:read-char-ref port)
- (let* ((base
- (cond ((eqv? (peek-char port) #\x) (read-char port) 16)
- (else 10)))
- (name (next-token '() '(#\;) "XML [66]" port))
- (char-code (string->number name base)))
- (read-char port) ; read the terminating #\; char
- (if (integer? char-code) (ucscode->string char-code)
- (parser-error port "[wf-Legalchar] broken for '" name "'"))))
-
-
-; procedure+: ssax:handle-parsed-entity PORT NAME ENTITIES
-; CONTENT-HANDLER STR-HANDLER SEED
-;
-; Expand and handle a parsed-entity reference
-; port - a PORT
-; name - the name of the parsed entity to expand, a symbol
-; entities - see ENTITIES
-; content-handler -- procedure PORT ENTITIES SEED
-; that is supposed to return a SEED
-; str-handler - a STR-HANDLER. It is called if the entity in question
-; turns out to be a pre-declared entity
-;
-; The result is the one returned by CONTENT-HANDLER or STR-HANDLER
-; Faults detected:
-; WFC: XML-Spec.html#wf-entdeclared
-; WFC: XML-Spec.html#norecursion
-
-(define ssax:predefined-parsed-entities
- `(
- (,(string->symbol "amp") . "&")
- (,(string->symbol "lt") . "<")
- (,(string->symbol "gt") . ">")
- (,(string->symbol "apos") . "'")
- (,(string->symbol "quot") . "\"")))
-
-(define (ssax:handle-parsed-entity port name entities
- content-handler str-handler seed)
- (cond ; First we check the list of the declared entities
- ((assq name entities) =>
- (lambda (decl-entity)
- (let ((ent-body (cdr decl-entity)) ; mark the list to prevent recursion
- (new-entities (cons (cons name #f) entities)))
- (cond
- ((string? ent-body)
- (call-with-input-string ent-body
- (lambda (port) (content-handler port new-entities seed))))
- ((procedure? ent-body)
- (let ((port (ent-body)))
- (begin0
- (content-handler port new-entities seed)
- (close-input-port port))))
- (else
- (parser-error port "[norecursion] broken for " name))))))
- ((assq name ssax:predefined-parsed-entities)
- => (lambda (decl-entity)
- (str-handler (cdr decl-entity) "" seed)))
- ((assq '*DEFAULT* entities) =>
- (lambda (decl-entity)
- (let ((fallback (cdr decl-entity))
- (new-entities (cons (cons name #f) entities)))
- (cond
- ((procedure? fallback)
- (call-with-input-string (fallback port name)
- (lambda (port) (content-handler port new-entities seed))))
- (else
- (parser-error port "[norecursion] broken for " name))))))
- (else (parser-error port "[wf-entdeclared] broken for " name))))
-
-
-
-; The ATTLIST Abstract Data Type
-; Currently is implemented as an assoc list sorted in the ascending
-; order of NAMES.
-
-(define (make-empty-attlist) '())
-
-; Add a name-value pair to the existing attlist preserving the order
-; Return the new list, in the sorted ascending order.
-; Return #f if a pair with the same name already exists in the attlist
-
-(define (attlist-add attlist name-value)
- (if (null? attlist) (cons name-value attlist)
- (case (name-compare (car name-value) (caar attlist))
- ((=) #f)
- ((<) (cons name-value attlist))
- (else (cons (car attlist) (attlist-add (cdr attlist) name-value)))
- )))
-
-(define attlist-null? null?)
-
-; Given an non-null attlist, return a pair of values: the top and the rest
-(define (attlist-remove-top attlist)
- (values (car attlist) (cdr attlist)))
-
-(define (attlist->alist attlist) attlist)
-(define attlist-fold fold)
-
-; procedure+: ssax:read-attributes PORT ENTITIES
-;
-; This procedure reads and parses a production Attribute*
-; [41] Attribute ::= Name Eq AttValue
-; [10] AttValue ::= '"' ([^<&"] | Reference)* '"'
-; | "'" ([^<&'] | Reference)* "'"
-; [25] Eq ::= S? '=' S?
-;
-;
-; The procedure returns an ATTLIST, of Name (as UNRES-NAME), Value (as string)
-; pairs. The current character on the PORT is a non-whitespace character
-; that is not an ncname-starting character.
-;
-; Note the following rules to keep in mind when reading an 'AttValue'
-; "Before the value of an attribute is passed to the application
-; or checked for validity, the XML processor must normalize it as follows:
-; - a character reference is processed by appending the referenced
-; character to the attribute value
-; - an entity reference is processed by recursively processing the
-; replacement text of the entity [see ENTITIES]
-; [named entities amp lt gt quot apos are assumed pre-declared]
-; - a whitespace character (#x20, #xD, #xA, #x9) is processed by appending #x20
-; to the normalized value, except that only a single #x20 is appended for a
-; "#xD#xA" sequence that is part of an external parsed entity or the
-; literal entity value of an internal parsed entity
-; - other characters are processed by appending them to the normalized value "
-;
-;
-; Faults detected:
-; WFC: XML-Spec.html#CleanAttrVals
-; WFC: XML-Spec.html#uniqattspec
-
-(define ssax:read-attributes ; ssax:read-attributes port entities
- (let ((value-delimeters (append ssax:S-chars '(#\< #\&))))
- ; Read the AttValue from the PORT up to the delimiter
- ; (which can be a single or double-quote character,
- ; or even a symbol *eof*)
- ; 'prev-fragments' is the list of string fragments, accumulated
- ; so far, in reverse order.
- ; Return the list of fragments with newly read fragments
- ; prepended.
- (define (read-attrib-value delimiter port entities prev-fragments)
- (let* ((new-fragments
- (cons (next-token '() (cons delimiter value-delimeters)
- "XML [10]" port)
- prev-fragments))
- (cterm (read-char port)))
- (cond
- ((or (eof-object? cterm) (eqv? cterm delimiter))
- new-fragments)
- ((eqv? cterm char-return) ; treat a CR and CRLF as a LF
- (if (eqv? (peek-char port) #\newline) (read-char port))
- (read-attrib-value delimiter port entities
- (cons " " new-fragments)))
- ((memv cterm ssax:S-chars)
- (read-attrib-value delimiter port entities
- (cons " " new-fragments)))
- ((eqv? cterm #\&)
- (cond
- ((eqv? (peek-char port) #\#)
- (read-char port)
- (read-attrib-value delimiter port entities
- (cons (ssax:read-char-ref port) new-fragments)))
- (else
- (read-attrib-value delimiter port entities
- (read-named-entity port entities new-fragments)))))
- (else (parser-error port "[CleanAttrVals] broken")))))
-
- ; we have read "&" that introduces a named entity reference.
- ; read this reference and return the result of
- ; normalizing of the corresponding string
- ; (that is, read-attrib-value is applied to the replacement
- ; text of the entity)
- ; The current position will be after ";" that terminates
- ; the entity reference
- (define (read-named-entity port entities fragments)
- (let ((name (ssax:read-NCName port)))
- (assert-curr-char '(#\;) "XML [68]" port)
- (ssax:handle-parsed-entity port name entities
- (lambda (port entities fragments)
- (read-attrib-value '*eof* port entities fragments))
- (lambda (str1 str2 fragments)
- (if (equal? "" str2) (cons str1 fragments)
- (cons* str2 str1 fragments)))
- fragments)))
-
- (lambda (port entities)
- (let loop ((attr-list (make-empty-attlist)))
- (if (not (ssax:ncname-starting-char? (ssax:skip-S port))) attr-list
- (let ((name (ssax:read-QName port)))
- (ssax:skip-S port)
- (assert-curr-char '(#\=) "XML [25]" port)
- (ssax:skip-S port)
- (let ((delimiter
- (assert-curr-char '(#\' #\" ) "XML [10]" port)))
- (loop
- (or (attlist-add attr-list
- (cons name
- (string-concatenate-reverse/shared
- (read-attrib-value delimiter port entities
- '()))))
- (parser-error port "[uniqattspec] broken for " name))))))))
-))
-
-; a few lines of validation code
-(run-test (letrec
- ((test (lambda (str decl-entities expected-res)
- (newline) (display "input: ") (write str)
- (newline) (display "Result: ")
- (let ((result
- (call-with-input-string (unesc-string str)
- (lambda (port)
- (ssax:read-attributes port decl-entities)))))
- (write result) (newline)
- (assert (equal? result expected-res))))))
- (test "" '() '())
- (test "href='http://a%tb%r%n%r%n%nc'" '()
- `((,(string->symbol "href") . "http://a b c")))
- (test "href='http://a%tb%r%r%n%rc'" '()
- `((,(string->symbol "href") . "http://a b c")))
- (test "_1 ='12&amp;' _2= \"%r%n%t12&#10;3\">" '()
- `((_1 . "12&") (_2 . ,(unesc-string " 12%n3"))))
- (test "%tAbc='&lt;&amp;&gt;&#x0A;'%nNext='12&ent;34' />"
- '((ent . "&lt;xx&gt;"))
- `((,(string->symbol "Abc") . ,(unesc-string "<&>%n"))
- (,(string->symbol "Next") . "12<xx>34")))
- (test "%tAbc='&lt;&amp;&gt;&#x0d;'%nNext='12&ent;34' />"
- '((ent . "&lt;xx&gt;"))
- `((,(string->symbol "Abc") . ,(unesc-string "<&>%r"))
- (,(string->symbol "Next") . "12<xx>34")))
- (test "%tAbc='&lt;&amp;&gt;&#x0A;'%nNext='12&en;34' />"
- `((en . ,(lambda () (open-input-string "&quot;xx&apos;"))))
- `((,(string->symbol "Abc") . ,(unesc-string "<&>%n"))
- (,(string->symbol "Next") . "12\"xx'34")))
- (test "%tAbc='&lt;&amp;&gt;&#x0A;'%nNext='12&ent;34' />"
- '((ent . "&lt;&ent1;T;&gt;") (ent1 . "&amp;"))
- `((,(string->symbol "Abc") . ,(unesc-string "<&>%n"))
- (,(string->symbol "Next") . "12<&T;>34")))
- (test "%tAbc='&lt;&amp;&gt;&#x0A;'%nNext='12&ent;34' />"
- `((*DEFAULT* . ,(lambda (port name)
- (case name
- ((ent) "&lt;&ent1;T;&gt;")
- ((ent1) "&amp;")
- (else (error "unrecognized" name))))))
- `((,(string->symbol "Abc") . ,(unesc-string "<&>%n"))
- (,(string->symbol "Next") . "12<&T;>34")))
- (assert (failed?
- (test "%tAbc='&lt;&amp;&gt;&#x0A;'%nNext='12&ent;34' />"
- '((ent . "<&ent1;T;&gt;") (ent1 . "&amp;")) '())))
- (assert (failed?
- (test "%tAbc='&lt;&amp;&gt;&#x0A;'%nNext='12&ent;34' />"
- '((ent . "&lt;&ent;T;&gt;") (ent1 . "&amp;")) '())))
- (assert (failed?
- (test "%tAbc='&lt;&amp;&gt;&#x0A;'%nNext='12&ent;34' />"
- '((ent . "&lt;&ent1;T;&gt;") (ent1 . "&ent;")) '())))
- (test "html:href='http://a%tb%r%n%r%n%nc'" '()
- `(((,(string->symbol "html") . ,(string->symbol "href"))
- . "http://a b c")))
- (test "html:href='ref1' html:src='ref2'" '()
- `(((,(string->symbol "html") . ,(string->symbol "href"))
- . "ref1")
- ((,(string->symbol "html") . ,(string->symbol "src"))
- . "ref2")))
- (test "html:href='ref1' xml:html='ref2'" '()
- `(((,(string->symbol "html") . ,(string->symbol "href"))
- . "ref1")
- ((,ssax:Prefix-XML . ,(string->symbol "html"))
- . "ref2")))
- (assert (failed? (test "html:href='ref1' html:href='ref2'" '() '())))
- (assert (failed? (test "html:href='<' html:href='ref2'" '() '())))
- (assert (failed? (test "html:href='ref1' html:href='&ref2;'" '() '())))
-))
-
-; ssax:resolve-name PORT UNRES-NAME NAMESPACES apply-default-ns?
-;
-; Convert an UNRES-NAME to a RES-NAME given the appropriate NAMESPACES
-; declarations.
-; the last parameter apply-default-ns? determines if the default
-; namespace applies (for instance, it does not for attribute names)
-;
-; Per REC-xml-names/#nsc-NSDeclared, "xml" prefix is considered pre-declared
-; and bound to the namespace name "http://www.w3.org/XML/1998/namespace".
-;
-; This procedure tests for the namespace constraints:
-; http://www.w3.org/TR/REC-xml-names/#nsc-NSDeclared
-
-(define (ssax:resolve-name port unres-name namespaces apply-default-ns?)
- (cond
- ((pair? unres-name) ; it's a QNAME
- (cons
- (cond
- ((assq (car unres-name) namespaces) => cadr)
- ((eq? (car unres-name) ssax:Prefix-XML) ssax:Prefix-XML)
- (else
- (parser-error port "[nsc-NSDeclared] broken; prefix " (car unres-name))))
- (cdr unres-name)))
- (apply-default-ns? ; Do apply the default namespace, if any
- (let ((default-ns (assq '*DEFAULT* namespaces)))
- (if (and default-ns (cadr default-ns))
- (cons (cadr default-ns) unres-name)
- unres-name))) ; no default namespace declared
- (else unres-name))) ; no prefix, don't apply the default-ns
-
-
-(run-test
- (let* ((namespaces
- '((HTML UHTML . URN-HTML)
- (HTML UHTML-1 . URN-HTML)
- (A UHTML . URN-HTML)))
- (namespaces-def
- (cons
- '(*DEFAULT* DEF . URN-DEF) namespaces))
- (namespaces-undef
- (cons
- '(*DEFAULT* #f . #f) namespaces-def))
- (port (current-input-port)))
-
- (assert (equal? 'ABC
- (ssax:resolve-name port 'ABC namespaces #t)))
- (assert (equal? '(DEF . ABC)
- (ssax:resolve-name port 'ABC namespaces-def #t)))
- (assert (equal? 'ABC
- (ssax:resolve-name port 'ABC namespaces-def #f)))
- (assert (equal? 'ABC
- (ssax:resolve-name port 'ABC namespaces-undef #t)))
- (assert (equal? '(UHTML . ABC)
- (ssax:resolve-name port '(HTML . ABC) namespaces-def #t)))
- (assert (equal? '(UHTML . ABC)
- (ssax:resolve-name port '(HTML . ABC) namespaces-def #f)))
- (assert (equal? `(,ssax:Prefix-XML . space)
- (ssax:resolve-name port
- `(,(string->symbol "xml") . space) namespaces-def #f)))
- (assert (failed?
- (ssax:resolve-name port '(XXX . ABC) namespaces-def #f)))
-))
-
-
-; procedure+: ssax:uri-string->symbol URI-STR
-; Convert a URI-STR to an appropriate symbol
-(define (ssax:uri-string->symbol uri-str)
- (string->symbol uri-str))
-
-; procedure+: ssax:complete-start-tag TAG PORT ELEMS ENTITIES NAMESPACES
-;
-; This procedure is to complete parsing of a start-tag markup. The
-; procedure must be called after the start tag token has been
-; read. TAG is an UNRES-NAME. ELEMS is an instance of xml-decl::elems;
-; it can be #f to tell the function to do _no_ validation of elements
-; and their attributes.
-;
-; This procedure returns several values:
-; ELEM-GI: a RES-NAME.
-; ATTRIBUTES: element's attributes, an ATTLIST of (RES-NAME . STRING)
-; pairs. The list does NOT include xmlns attributes.
-; NAMESPACES: the input list of namespaces amended with namespace
-; (re-)declarations contained within the start-tag under parsing
-; ELEM-CONTENT-MODEL
-
-; On exit, the current position in PORT will be the first character after
-; #\> that terminates the start-tag markup.
-;
-; Faults detected:
-; VC: XML-Spec.html#enum
-; VC: XML-Spec.html#RequiredAttr
-; VC: XML-Spec.html#FixedAttr
-; VC: XML-Spec.html#ValueType
-; WFC: XML-Spec.html#uniqattspec (after namespaces prefixes are resolved)
-; VC: XML-Spec.html#elementvalid
-; WFC: REC-xml-names/#dt-NSName
-
-; Note, although XML Recommendation does not explicitly say it,
-; xmlns and xmlns: attributes don't have to be declared (although they
-; can be declared, to specify their default value)
-
-; Procedure: ssax:complete-start-tag tag-head port elems entities namespaces
-(define ssax:complete-start-tag
-
- (let ((xmlns (string->symbol "xmlns"))
- (largest-dummy-decl-attr (list ssax:largest-unres-name #f #f #f)))
-
- ; Scan through the attlist and validate it, against decl-attrs
- ; Return an assoc list with added fixed or implied attrs.
- ; Note that both attlist and decl-attrs are ATTLISTs, and therefore,
- ; sorted
- (define (validate-attrs port attlist decl-attrs)
-
- ; Check to see decl-attr is not of use type REQUIRED. Add
- ; the association with the default value, if any declared
- (define (add-default-decl decl-attr result)
- (let*-values
- (((attr-name content-type use-type default-value)
- (apply values decl-attr)))
- (and (eq? use-type 'REQUIRED)
- (parser-error port "[RequiredAttr] broken for" attr-name))
- (if default-value
- (cons (cons attr-name default-value) result)
- result)))
-
- (let loop ((attlist attlist) (decl-attrs decl-attrs) (result '()))
- (if (attlist-null? attlist)
- (attlist-fold add-default-decl result decl-attrs)
- (let*-values
- (((attr attr-others)
- (attlist-remove-top attlist))
- ((decl-attr other-decls)
- (if (attlist-null? decl-attrs)
- (values largest-dummy-decl-attr decl-attrs)
- (attlist-remove-top decl-attrs)))
- )
- (case (name-compare (car attr) (car decl-attr))
- ((<)
- (if (or (eq? xmlns (car attr))
- (and (pair? (car attr)) (eq? xmlns (caar attr))))
- (loop attr-others decl-attrs (cons attr result))
- (parser-error port "[ValueType] broken for " attr)))
- ((>)
- (loop attlist other-decls
- (add-default-decl decl-attr result)))
- (else ; matched occurrence of an attr with its declaration
- (let*-values
- (((attr-name content-type use-type default-value)
- (apply values decl-attr)))
- ; Run some tests on the content of the attribute
- (cond
- ((eq? use-type 'FIXED)
- (or (equal? (cdr attr) default-value)
- (parser-error port "[FixedAttr] broken for " attr-name)))
- ((eq? content-type 'CDATA) #t) ; everything goes
- ((pair? content-type)
- (or (member (cdr attr) content-type)
- (parser-error port "[enum] broken for " attr-name "="
- (cdr attr))))
- (else
- (ssax:warn port "declared content type " content-type
- " not verified yet")))
- (loop attr-others other-decls (cons attr result)))))
- ))))
-
-
- ; Add a new namespace declaration to namespaces.
- ; First we convert the uri-str to a uri-symbol and search namespaces for
- ; an association (_ user-prefix . uri-symbol).
- ; If found, we return the argument namespaces with an association
- ; (prefix user-prefix . uri-symbol) prepended.
- ; Otherwise, we prepend (prefix uri-symbol . uri-symbol)
- (define (add-ns port prefix uri-str namespaces)
- (and (equal? "" uri-str)
- (parser-error port "[dt-NSName] broken for " prefix))
- (let ((uri-symbol (ssax:uri-string->symbol uri-str)))
- (let loop ((nss namespaces))
- (cond
- ((null? nss)
- (cons (cons* prefix uri-symbol uri-symbol) namespaces))
- ((eq? uri-symbol (cddar nss))
- (cons (cons* prefix (cadar nss) uri-symbol) namespaces))
- (else (loop (cdr nss)))))))
-
- ; partition attrs into proper attrs and new namespace declarations
- ; return two values: proper attrs and the updated namespace declarations
- (define (adjust-namespace-decl port attrs namespaces)
- (let loop ((attrs attrs) (proper-attrs '()) (namespaces namespaces))
- (cond
- ((null? attrs) (values proper-attrs namespaces))
- ((eq? xmlns (caar attrs)) ; re-decl of the default namespace
- (loop (cdr attrs) proper-attrs
- (if (equal? "" (cdar attrs)) ; un-decl of the default ns
- (cons (cons* '*DEFAULT* #f #f) namespaces)
- (add-ns port '*DEFAULT* (cdar attrs) namespaces))))
- ((and (pair? (caar attrs)) (eq? xmlns (caaar attrs)))
- (loop (cdr attrs) proper-attrs
- (add-ns port (cdaar attrs) (cdar attrs) namespaces)))
- (else
- (loop (cdr attrs) (cons (car attrs) proper-attrs) namespaces)))))
-
- ; The body of the function
- (lambda (tag-head port elems entities namespaces)
- (let*-values
- (((attlist) (ssax:read-attributes port entities))
- ((empty-el-tag?)
- (begin
- (ssax:skip-S port)
- (and
- (eqv? #\/
- (assert-curr-char '(#\> #\/) "XML [40], XML [44], no '>'" port))
- (assert-curr-char '(#\>) "XML [44], no '>'" port))))
- ((elem-content decl-attrs) ; see xml-decl for their type
- (if elems ; elements declared: validate!
- (cond
- ((assoc tag-head elems) =>
- (lambda (decl-elem) ; of type xml-decl::decl-elem
- (values
- (if empty-el-tag? 'EMPTY-TAG (cadr decl-elem))
- (caddr decl-elem))))
- (else
- (parser-error port "[elementvalid] broken, no decl for " tag-head)))
- (values ; non-validating parsing
- (if empty-el-tag? 'EMPTY-TAG 'ANY)
- #f) ; no attributes declared
- ))
- ((merged-attrs) (if decl-attrs (validate-attrs port attlist decl-attrs)
- (attlist->alist attlist)))
- ((proper-attrs namespaces)
- (adjust-namespace-decl port merged-attrs namespaces))
- )
- ;(cerr "proper attrs: " proper-attrs nl)
- ; build the return value
- (values
- (ssax:resolve-name port tag-head namespaces #t)
- (fold-right
- (lambda (name-value attlist)
- (or
- (attlist-add attlist
- (cons (ssax:resolve-name port (car name-value) namespaces #f)
- (cdr name-value)))
- (parser-error port "[uniqattspec] after NS expansion broken for "
- name-value)))
- (make-empty-attlist)
- proper-attrs)
- namespaces
- elem-content)))))
-
-(run-test
- (let* ((urn-a (string->symbol "urn:a"))
- (urn-b (string->symbol "urn:b"))
- (urn-html (string->symbol "http://w3c.org/html"))
- (namespaces
- `((#f '"UHTML" . ,urn-html)
- ('"A" '"UA" . ,urn-a)))
- (test
- (lambda (tag-head-name elems str)
- (call-with-input-string str
- (lambda (port)
- (call-with-values
- (lambda ()
- (ssax:complete-start-tag
- (call-with-input-string tag-head-name
- (lambda (port) (ssax:read-QName port)))
- port
- elems '() namespaces))
- list))))))
-
- ; First test with no validation of elements
- ;(test "TAG1" #f "")
- (assert (equal? `('"TAG1" () ,namespaces ANY)
- (test "TAG1" #f ">")))
- (assert (equal? `('"TAG1" () ,namespaces EMPTY-TAG)
- (test "TAG1" #f "/>")))
- (assert (equal? `('"TAG1" (('"HREF" . "a")) ,namespaces EMPTY-TAG)
- (test "TAG1" #f "HREF='a'/>")))
- (assert (equal? `(('"UA" . '"TAG1") (('"HREF" . "a"))
- ,(cons `(*DEFAULT* '"UA" . ,urn-a) namespaces) ANY)
- (test "TAG1" #f "HREF='a' xmlns='urn:a'>")))
- (assert (equal? `('"TAG1" (('"HREF" . "a"))
- ,(cons '(*DEFAULT* #f . #f) namespaces) ANY)
- (test "TAG1" #f "HREF='a' xmlns=''>")))
- (assert (failed? (test "UA:TAG1" #f "HREF='a' xmlns=''/>")))
- (assert (equal? `(('"UA" . '"TAG1") ((('"UA" . '"HREF") . "a"))
- ,(cons '(*DEFAULT* #f . #f) namespaces) ANY)
- (test "A:TAG1" #f "A:HREF='a' xmlns=''>")))
- (assert (equal? `(('"UA" . '"TAG1") ((('"UA" . '"HREF") . "a"))
- ,(cons `(*DEFAULT* ,urn-b . ,urn-b) namespaces) ANY)
- (test "A:TAG1" #f "A:HREF='a' xmlns='urn:b'>")))
- (assert (failed? (test "B:TAG1" #f "A:HREF='a' xmlns:b=''/>")))
- (assert (equal? `((,urn-b . '"TAG1") ((('"UA" . '"HREF") . "a"))
- ,(cons `('"B" ,urn-b . ,urn-b) namespaces) ANY)
- (test "B:TAG1" #f "A:HREF='a' xmlns:B='urn:b'>")))
- (assert (equal? `((,urn-b . '"TAG1") ((('"UA" . '"HREF") . "a")
- ((,urn-b . '"SRC") . "b"))
- ,(cons `('"B" ,urn-b . ,urn-b) namespaces) ANY)
- (test "B:TAG1" #f
- "B:SRC='b' A:HREF='a' xmlns:B='urn:b'>")))
- (assert (equal? `((,urn-b . '"TAG1") ((('"UA" . '"HREF") . "a")
- ((,urn-b . '"HREF") . "b"))
- ,(cons `('"B" ,urn-b . ,urn-b) namespaces) ANY)
- (test "B:TAG1" #f
- "B:HREF=\"b\" A:HREF='a' xmlns:B='urn:b'>")))
- ; must be an error! Duplicate attr
- (assert (failed? (test "B:TAG1" #f
- "HREF=\"b\" HREF='a' xmlns:B='urn:a'/>")))
- ; must be an error! Duplicate attr after ns expansion
- (assert (failed? (test "B:TAG1" #f
- "B:HREF=\"b\" A:HREF='a' xmlns:B='urn:a'/>")))
- (assert (equal? `(('"UA" . '"TAG1") (('"HREF" . "a")
- (('"UA" . '"HREF") . "b"))
- ,(cons `(*DEFAULT* '"UA" . ,urn-a) namespaces) ANY)
- (test "TAG1" #f
- "A:HREF=\"b\" HREF='a' xmlns='urn:a'>")))
- (assert (equal? `('"TAG1" ((('"UHTML" . '"HREF") . "a")
- ((,urn-b . '"HREF") . "b"))
- ,(append `(
- ('"HTML" '"UHTML" . ,urn-html)
- ('"B" ,urn-b . ,urn-b))
- namespaces) ANY)
- (test "TAG1" #f
- "B:HREF=\"b\" xmlns:B='urn:b' xmlns:HTML='http://w3c.org/html' HTML:HREF='a' >")))
-
- ; Now test the validating parsing
- ; No decl for tag1
- (assert (failed? (test "TAG1" '((TAG2 ANY ()))
- "B:HREF='b' xmlns:B='urn:b'>")))
- ; No decl for HREF elem
-;; (cond-expand
-;; ((not (or scm mit-scheme)) ; Regretfully, SCM treats '() as #f
-;; (assert (failed?
-;; (test "TAG1" '(('"TAG1" ANY ()))
-;; "B:HREF='b' xmlns:B='urn:b'>"))))
-;; (else #t))
- ; No decl for HREF elem
- (assert (failed?
- (test "TAG1" '(('"TAG1" ANY (('"HREF1" CDATA IMPLIED #f))))
- "B:HREF='b' xmlns:B='urn:b'>")))
- (assert (equal? `('"TAG1" (('"HREF" . "b")) ,namespaces EMPTY-TAG)
- (test "TAG1" '(('"TAG1" PCDATA (('"HREF" CDATA REQUIRED #f))))
- "HREF='b'/>")))
- (assert (equal? `('"TAG1" (('"HREF" . "b")) ,namespaces PCDATA)
- (test "TAG1" '(('"TAG1" PCDATA (('"HREF" CDATA REQUIRED #f))))
- "HREF='b'>")))
- ; Req'd attribute not given error
- (assert (failed?
- (test "TAG1" '(('"TAG1" PCDATA (('"HREF" CDATA REQUIRED #f))))
- ">")))
- ; Wrong content-type of the attribute
- (assert (failed?
- (test "TAG1" '(('"TAG1" PCDATA (('"HREF" ("c") REQUIRED #f))))
- "HREF='b'>")))
- (assert (equal? `('"TAG1" (('"HREF" . "b")) ,namespaces PCDATA)
- (test "TAG1" '(('"TAG1" PCDATA (('"HREF" ("c" "b") IMPLIED #f))))
- "HREF='b'>")))
- (assert (equal? `('"TAG1" (('"HREF" . "b")) ,namespaces PCDATA)
- (test "TAG1" '(('"TAG1" PCDATA (('"HREF" CDATA IMPLIED "c"))))
- "HREF='b'>")))
- ; Bad fixed attribute
- (assert (failed?
- (test "TAG1" '(('"TAG1" PCDATA (('"HREF" CDATA FIXED "c"))))
- "HREF='b'>")))
- (assert (equal? `('"TAG1" (('"HREF" . "b")) ,namespaces PCDATA)
- (test "TAG1" '(('"TAG1" PCDATA (('"HREF" CDATA FIXED "b"))))
- "HREF='b'>")))
- (assert (equal? `('"TAG1" (('"HREF" . "b")) ,namespaces PCDATA)
- (test "TAG1" '(('"TAG1" PCDATA (('"HREF" CDATA FIXED "b")))) ">")))
- (assert (equal? `('"TAG1" (('"HREF" . "b")) ,namespaces PCDATA)
- (test "TAG1" '(('"TAG1" PCDATA (('"HREF" CDATA IMPLIED "b")))) ">")))
- (assert (equal? `('"TAG1" () ,namespaces PCDATA)
- (test "TAG1" '(('"TAG1" PCDATA (('"HREF" CDATA IMPLIED #f)))) ">")))
- ; Undeclared attr
- (assert (failed?
- (test "TAG1"
- '(('"TAG1" PCDATA ((('"A" . '"HREF") CDATA IMPLIED "c"))))
- "HREF='b'>")))
- (assert (equal? `('"TAG1" (('"HREF" . "b") (('"UA" . '"HREF") . "c"))
- ,namespaces PCDATA)
- (test "TAG1" '(('"TAG1" PCDATA (('"HREF" CDATA REQUIRED #f)
- (('"A" . '"HREF") CDATA IMPLIED "c"))))
- "HREF='b'>")))
- (assert (equal? `(('"UA" . '"TAG1")
- (('"HREF" . "b") (('"UA" . '"HREF") . "c"))
- ,namespaces PCDATA)
- (test "A:TAG1" '((('"A" . '"TAG1") PCDATA
- (('"HREF" NMTOKEN REQUIRED #f)
- (('"A" . '"HREF") CDATA IMPLIED "c"))))
- "HREF='b'>")))
- (assert (equal? `((,urn-b . '"TAG1") (('"HREF" . "b"))
- ,(cons `('"B" ,urn-b . ,urn-b) namespaces) PCDATA)
- (test "B:TAG1" '((('"B" . '"TAG1") PCDATA (('"HREF" CDATA REQUIRED #f)
- (('"xmlns" . '"B") CDATA IMPLIED "urn:b"))))
- "HREF='b'>")))
- (assert (equal? `((,urn-b . '"TAG1") (((,urn-b . '"HREF") . "b"))
- ,(cons `('"B" ,urn-b . ,urn-b) namespaces) PCDATA)
- (test "B:TAG1" '((('"B" . '"TAG1") PCDATA
- ((('"B" . '"HREF") CDATA REQUIRED #f)
- (('"xmlns" . '"B") CDATA IMPLIED "urn:b"))))
- "B:HREF='b'>")))
- (assert (equal? `((,urn-b . '"TAG1") (('"HREF" . "b"))
- ,(cons `(*DEFAULT* ,urn-b . ,urn-b) namespaces) PCDATA)
- (test "TAG1" '(('"TAG1" PCDATA (('"HREF" CDATA REQUIRED #f)
- ('"xmlns" CDATA IMPLIED "urn:b"))))
- "HREF='b'>")))
- ; xmlns not declared
- (assert (equal? `((,urn-b . '"TAG1") (('"HREF" . "b"))
- ,(cons `(*DEFAULT* ,urn-b . ,urn-b) namespaces) PCDATA)
- (test "TAG1" '(('"TAG1" PCDATA (('"HREF" CDATA REQUIRED #f)
- )))
- "HREF='b' xmlns='urn:b'>")))
- ; xmlns:B not declared
- (assert (equal? `((,urn-b . '"TAG1") (((,urn-b . '"HREF") . "b"))
- ,(cons `('"B" ,urn-b . ,urn-b) namespaces) PCDATA)
- (test "B:TAG1" '((('"B" . '"TAG1") PCDATA
- ((('"B" . '"HREF") CDATA REQUIRED #f)
- )))
- "B:HREF='b' xmlns:B='urn:b'>")))
-))
-
-; procedure+: ssax:read-external-id PORT
-;
-; This procedure parses an ExternalID production:
-; [75] ExternalID ::= 'SYSTEM' S SystemLiteral
-; | 'PUBLIC' S PubidLiteral S SystemLiteral
-; [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'")
-; [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
-; [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9]
-; | [-'()+,./:=?;!*#@$_%]
-;
-; This procedure is supposed to be called when an ExternalID is expected;
-; that is, the current character must be either #\S or #\P that start
-; correspondingly a SYSTEM or PUBLIC token. This procedure returns the
-; SystemLiteral as a string. A PubidLiteral is disregarded if present.
-
-(define (ssax:read-external-id port)
- (let ((discriminator (ssax:read-NCName port)))
- (assert-curr-char ssax:S-chars "space after SYSTEM or PUBLIC" port)
- (ssax:skip-S port)
- (let ((delimiter
- (assert-curr-char '(#\' #\" ) "XML [11], XML [12]" port)))
- (cond
- ((eq? discriminator (string->symbol "SYSTEM"))
- (begin0
- (next-token '() (list delimiter) "XML [11]" port)
- (read-char port) ; reading the closing delim
- ))
- ((eq? discriminator (string->symbol "PUBLIC"))
- (skip-until (list delimiter) port)
- (assert-curr-char ssax:S-chars "space after PubidLiteral" port)
- (ssax:skip-S port)
- (let* ((delimiter
- (assert-curr-char '(#\' #\" ) "XML [11]" port))
- (systemid
- (next-token '() (list delimiter) "XML [11]" port)))
- (read-char port) ; reading the closing delim
- systemid))
- (else
- (parser-error port "XML [75], " discriminator
- " rather than SYSTEM or PUBLIC"))))))
-
-
-;-----------------------------------------------------------------------------
-; Higher-level parsers and scanners
-;
-; They parse productions corresponding to the whole (document) entity
-; or its higher-level pieces (prolog, root element, etc).
-
-
-; Scan the Misc production in the context
-; [1] document ::= prolog element Misc*
-; [22] prolog ::= XMLDecl? Misc* (doctypedec l Misc*)?
-; [27] Misc ::= Comment | PI | S
-;
-; The following function should be called in the prolog or epilog contexts.
-; In these contexts, whitespaces are completely ignored.
-; The return value from ssax:scan-Misc is either a PI-token,
-; a DECL-token, a START token, or EOF.
-; Comments are ignored and not reported.
-
-(define (ssax:scan-Misc port)
- (let loop ((c (ssax:skip-S port)))
- (cond
- ((eof-object? c) c)
- ((not (char=? c #\<))
- (parser-error port "XML [22], char '" c "' unexpected"))
- (else
- (let ((token (ssax:read-markup-token port)))
- (case (xml-token-kind token)
- ((COMMENT) (loop (ssax:skip-S port)))
- ((PI DECL START) token)
- (else
- (parser-error port "XML [22], unexpected token of kind "
- (xml-token-kind token)
- ))))))))
-
-; procedure+: ssax:read-char-data PORT EXPECT-EOF? STR-HANDLER SEED
-;
-; This procedure is to read the character content of an XML document
-; or an XML element.
-; [43] content ::=
-; (element | CharData | Reference | CDSect | PI
-; | Comment)*
-; To be more precise, the procedure reads CharData, expands CDSect
-; and character entities, and skips comments. The procedure stops
-; at a named reference, EOF, at the beginning of a PI or a start/end tag.
-;
-; port
-; a PORT to read
-; expect-eof?
-; a boolean indicating if EOF is normal, i.e., the character
-; data may be terminated by the EOF. EOF is normal
-; while processing a parsed entity.
-; str-handler
-; a STR-HANDLER
-; seed
-; an argument passed to the first invocation of STR-HANDLER.
-;
-; The procedure returns two results: SEED and TOKEN.
-; The SEED is the result of the last invocation of STR-HANDLER, or the
-; original seed if STR-HANDLER was never called.
-;
-; TOKEN can be either an eof-object (this can happen only if
-; expect-eof? was #t), or:
-; - an xml-token describing a START tag or an END-tag;
-; For a start token, the caller has to finish reading it.
-; - an xml-token describing the beginning of a PI. It's up to an
-; application to read or skip through the rest of this PI;
-; - an xml-token describing a named entity reference.
-;
-; CDATA sections and character references are expanded inline and
-; never returned. Comments are silently disregarded.
-;
-; As the XML Recommendation requires, all whitespace in character data
-; must be preserved. However, a CR character (#xD) must be disregarded
-; if it appears before a LF character (#xA), or replaced by a #xA character
-; otherwise. See Secs. 2.10 and 2.11 of the XML Recommendation. See also
-; the canonical XML Recommendation.
-
- ; ssax:read-char-data port expect-eof? str-handler seed
-(define ssax:read-char-data
- (let
- ((terminators-usual (list #\< #\& char-return))
- (terminators-usual-eof (list #\< '*eof* #\& char-return))
-
- (handle-fragment
- (lambda (fragment str-handler seed)
- (if (string-null? fragment) seed
- (str-handler fragment "" seed))))
- )
-
- (lambda (port expect-eof? str-handler seed)
-
- ; Very often, the first character we encounter is #\<
- ; Therefore, we handle this case in a special, fast path
- (if (eqv? #\< (peek-char port))
-
- ; The fast path
- (let ((token (ssax:read-markup-token port)))
- (case (xml-token-kind token)
- ((START END) ; The most common case
- (values seed token))
- ((CDSECT)
- (let ((seed (ssax:read-cdata-body port str-handler seed)))
- (ssax:read-char-data port expect-eof? str-handler seed)))
- ((COMMENT) (ssax:read-char-data port expect-eof?
- str-handler seed))
- (else
- (values seed token))))
-
-
- ; The slow path
- (let ((char-data-terminators
- (if expect-eof? terminators-usual-eof terminators-usual)))
-
- (let loop ((seed seed))
- (let* ((fragment
- (next-token '() char-data-terminators
- "reading char data" port))
- (term-char (peek-char port)) ; one of char-data-terminators
- )
- (if (eof-object? term-char)
- (values
- (handle-fragment fragment str-handler seed)
- term-char)
- (case term-char
- ((#\<)
- (let ((token (ssax:read-markup-token port)))
- (case (xml-token-kind token)
- ((CDSECT)
- (loop
- (ssax:read-cdata-body port str-handler
- (handle-fragment fragment str-handler seed))))
- ((COMMENT)
- (loop (handle-fragment fragment str-handler seed)))
- (else
- (values
- (handle-fragment fragment str-handler seed)
- token)))))
- ((#\&)
- (case (peek-next-char port)
- ((#\#) (read-char port)
- (loop (str-handler fragment
- (ssax:read-char-ref port)
- seed)))
- (else
- (let ((name (ssax:read-NCName port)))
- (assert-curr-char '(#\;) "XML [68]" port)
- (values
- (handle-fragment fragment str-handler seed)
- (make-xml-token 'ENTITY-REF name))))))
- (else ; This must be a CR character
- (if (eqv? (peek-next-char port) #\newline)
- (read-char port))
- (loop (str-handler fragment (string #\newline) seed))))
- ))))))))
-
-
-; a few lines of validation code
-(run-test (letrec
- ((a-tag (make-xml-token 'START (string->symbol "BR")))
- (a-ref (make-xml-token 'ENTITY-REF (string->symbol "lt")))
- (eof-object (lambda () eof-object)) ; a unique value
- (str-handler (lambda (fragment foll-fragment seed)
- (if (string-null? foll-fragment) (cons fragment seed)
- (cons* foll-fragment fragment seed))))
- (test (lambda (str expect-eof? expected-data expected-token)
- (newline) (display "body: ") (write str)
- (newline) (display "Result: ")
- (let*-values
- (((seed token)
- (call-with-input-string (unesc-string str)
- (lambda (port)
- (ssax:read-char-data port expect-eof? str-handler '()))))
- ((result) (reverse seed)))
- (write result)
- (display " ")
- (display token)
- (assert (equal? result (map unesc-string expected-data))
- (if (eq? expected-token eof-object)
- (eof-object? token)
- (equal? token expected-token))))))
- )
- (test "" #t '() eof-object)
- (assert (failed? (test "" #f '() eof-object)))
- (test " " #t '(" ") eof-object)
- (test "<BR/>" #f '() a-tag)
- (test " <BR />" #f '(" ") a-tag)
-
- (test " &lt;" #f '(" ") a-ref)
- (test " a&lt;" #f '(" a") a-ref)
- (test " a &lt;" #f '(" a ") a-ref)
-
- (test " <!-- comment--> a a<BR/>" #f '(" " " a a") a-tag)
- (test " <!-- comment-->%ra a<BR/>" #f '(" " "" "%n" "a a") a-tag)
- (test " <!-- comment-->%r%na a<BR/>" #f '(" " "" "%n" "a a") a-tag)
- (test " <!-- comment-->%r%na%t%r%r%na<BR/>" #f
- '(" " "" "%n" "a%t" "%n" "" "%n" "a") a-tag)
- (test "a<!-- comment--> a a<BR/>" #f '("a" " a a") a-tag)
- (test "&#x21;<BR/>" #f '("" "!") a-tag)
- (test "&#x21;%n<BR/>" #f '("" "!" "%n") a-tag)
- (test "%t&#x21;%n<BR/>" #f '("%t" "!" "%n") a-tag)
- (test "%t&#x21;%na a<BR/>" #f '("%t" "!" "%na a") a-tag)
- (test "%t&#x21;%ra a<BR/>" #f '("%t" "!" "" "%n" "a a") a-tag)
- (test "%t&#x21;%r%na a<BR/>" #f '("%t" "!" "" "%n" "a a") a-tag)
-
- (test " %ta &#x21; b <BR/>" #f '(" %ta " "!" " b ") a-tag)
- (test " %ta &#x20; b <BR/>" #f '(" %ta " " " " b ") a-tag)
-
- (test "<![CDATA[<]]><BR/>" #f '("<") a-tag)
- (test "<![CDATA[]]]><BR/>" #f '("]") a-tag)
- (test "%t<![CDATA[<]]><BR/>" #f '("%t" "<") a-tag)
- (test "%t<![CDATA[<]]>a b<BR/>" #f '("%t" "<" "a b") a-tag)
- (test "%t<![CDATA[<]]> a b<BR/>" #f '("%t" "<" " a b") a-tag)
-
- (test "%td <![CDATA[ <%r%r%n]]> a b<BR/>" #f
- '("%td " " <" "%n" "" "%n" " a b") a-tag)
-))
-
-
-
-; procedure+: ssax:assert-token TOKEN KIND GI
-; Make sure that TOKEN is of anticipated KIND and has anticipated GI
-; Note GI argument may actually be a pair of two symbols, Namespace
-; URI or the prefix, and of the localname.
-; If the assertion fails, error-cont is evaluated by passing it
-; three arguments: token kind gi. The result of error-cont is returned.
-(define (ssax:assert-token token kind gi error-cont)
- (or
- (and (xml-token? token)
- (eq? kind (xml-token-kind token))
- (equal? gi (xml-token-head token)))
- (error-cont token kind gi)))
-
-;========================================================================
-; Highest-level parsers: XML to SXML
-
-; These parsers are a set of syntactic forms to instantiate a SSAX parser.
-; A user can instantiate the parser to do the full validation, or
-; no validation, or any particular validation. The user specifies
-; which PI he wants to be notified about. The user tells what to do
-; with the parsed character and element data. The latter handlers
-; determine if the parsing follows a SAX or a DOM model.
-
-; syntax: ssax:make-pi-parser my-pi-handlers
-; Create a parser to parse and process one Processing Element (PI).
-
-; my-pi-handlers
-; An assoc list of pairs (PI-TAG . PI-HANDLER)
-; where PI-TAG is an NCName symbol, the PI target, and
-; PI-HANDLER is a procedure PORT PI-TAG SEED
-; where PORT points to the first symbol after the PI target.
-; The handler should read the rest of the PI up to and including
-; the combination '?>' that terminates the PI. The handler should
-; return a new seed.
-; One of the PI-TAGs may be the symbol *DEFAULT*. The corresponding
-; handler will handle PIs that no other handler will. If the
-; *DEFAULT* PI-TAG is not specified, ssax:make-pi-parser will assume
-; the default handler that skips the body of the PI
-;
-; The output of the ssax:make-pi-parser is a procedure
-; PORT PI-TAG SEED
-; that will parse the current PI according to the user-specified handlers.
-;
-; The previous version of ssax:make-pi-parser was a low-level macro:
-; (define-macro ssax:make-pi-parser
-; (lambda (my-pi-handlers)
-; `(lambda (port target seed)
-; (case target
-; ; Generate the body of the case statement
-; ,@(let loop ((pi-handlers my-pi-handlers) (default #f))
-; (cond
-; ((null? pi-handlers)
-; (if default `((else (,default port target seed)))
-; '((else
-; (ssax:warn port "Skipping PI: " target nl)
-; (ssax:skip-pi port)
-; seed))))
-; ((eq? '*DEFAULT* (caar pi-handlers))
-; (loop (cdr pi-handlers) (cdar pi-handlers)))
-; (else
-; (cons
-; `((,(caar pi-handlers)) (,(cdar pi-handlers) port target seed))
-; (loop (cdr pi-handlers) default)))))))))
-
-(define-syntax ssax:make-pi-parser
- (syntax-rules ()
- ((ssax:make-pi-parser orig-handlers)
- (letrec-syntax
- ; Generate the clauses of the case statement
- ((loop
- (syntax-rules (*DEFAULT*)
- ((loop () #f accum port target seed) ; no default
- (make-case
- ((else
- (ssax:warn port "Skipping PI: " target nl)
- (ssax:skip-pi port)
- seed)
- . accum)
- () target))
- ((loop () default accum port target seed)
- (make-case
- ((else (default port target seed)) . accum)
- () target))
- ((loop ((*DEFAULT* . default) . handlers) old-def accum
- port target seed)
- (loop handlers default accum port target seed))
- ((loop ((tag . handler) . handlers) default accum port target seed)
- (loop handlers default
- (((tag) (handler port target seed)) . accum)
- port target seed))
- ))
- (make-case ; Reverse the clauses, make the 'case'
- (syntax-rules ()
- ((make-case () clauses target)
- (case target . clauses))
- ((make-case (clause . clauses) accum target)
- (make-case clauses (clause . accum) target)))
- ))
- (lambda (port target seed)
- (loop orig-handlers #f () port target seed))
- ))))
-
-(run-test
- (pp (ssax:make-pi-parser ()))
- (pp (ssax:make-pi-parser ((xml . (lambda (port target seed) seed)))))
- (pp (ssax:make-pi-parser ((xml . (lambda (port target seed) seed))
- (html . list)
- (*DEFAULT* . ssax:warn))))
-)
-
-; syntax: ssax:make-elem-parser my-new-level-seed my-finish-element
-; my-char-data-handler my-pi-handlers
-
-; Create a parser to parse and process one element, including its
-; character content or children elements. The parser is typically
-; applied to the root element of a document.
-
-; my-new-level-seed
-; procedure ELEM-GI ATTRIBUTES NAMESPACES EXPECTED-CONTENT SEED
-; where ELEM-GI is a RES-NAME of the element
-; about to be processed.
-; This procedure is to generate the seed to be passed
-; to handlers that process the content of the element.
-; This is the function identified as 'fdown' in the denotational
-; semantics of the XML parser given in the title comments to this
-; file.
-;
-; my-finish-element
-; procedure ELEM-GI ATTRIBUTES NAMESPACES PARENT-SEED SEED
-; This procedure is called when parsing of ELEM-GI is finished.
-; The SEED is the result from the last content parser (or
-; from my-new-level-seed if the element has the empty content).
-; PARENT-SEED is the same seed as was passed to my-new-level-seed.
-; The procedure is to generate a seed that will be the result
-; of the element parser.
-; This is the function identified as 'fup' in the denotational
-; semantics of the XML parser given in the title comments to this
-; file.
-;
-; my-char-data-handler
-; A STR-HANDLER
-;
-; my-pi-handlers
-; See ssax:make-pi-handler above
-;
-
-; The generated parser is a
-; procedure START-TAG-HEAD PORT ELEMS ENTITIES
-; NAMESPACES PRESERVE-WS? SEED
-; The procedure must be called after the start tag token has been
-; read. START-TAG-HEAD is an UNRES-NAME from the start-element tag.
-; ELEMS is an instance of xml-decl::elems.
-; See ssax:complete-start-tag::preserve-ws?
-
-; Faults detected:
-; VC: XML-Spec.html#elementvalid
-; WFC: XML-Spec.html#GIMatch
-
-
-(define-syntax ssax:make-elem-parser
- (syntax-rules ()
- ((ssax:make-elem-parser my-new-level-seed my-finish-element
- my-char-data-handler my-pi-handlers)
-
- (lambda (start-tag-head port elems entities namespaces
- preserve-ws? seed)
-
- (define xml-space-gi (cons ssax:Prefix-XML
- (string->symbol "space")))
-
- (let handle-start-tag ((start-tag-head start-tag-head)
- (port port) (entities entities)
- (namespaces namespaces)
- (preserve-ws? preserve-ws?) (parent-seed seed))
- (let*-values
- (((elem-gi attributes namespaces expected-content)
- (ssax:complete-start-tag start-tag-head port elems
- entities namespaces))
- ((seed)
- (my-new-level-seed elem-gi attributes
- namespaces expected-content parent-seed)))
- (case expected-content
- ((EMPTY-TAG)
- (my-finish-element
- elem-gi attributes namespaces parent-seed seed))
- ((EMPTY) ; The end tag must immediately follow
- (ssax:assert-token
- (and (eqv? #\< (ssax:skip-S port)) (ssax:read-markup-token port))
- 'END start-tag-head
- (lambda (token exp-kind exp-head)
- (parser-error port "[elementvalid] broken for " token
- " while expecting "
- exp-kind exp-head)))
- (my-finish-element
- elem-gi attributes namespaces parent-seed seed))
- (else ; reading the content...
- (let ((preserve-ws? ; inherit or set the preserve-ws? flag
- (cond
- ((assoc xml-space-gi attributes) =>
- (lambda (name-value)
- (equal? "preserve" (cdr name-value))))
- (else preserve-ws?))))
- (let loop ((port port) (entities entities)
- (expect-eof? #f) (seed seed))
- (let*-values
- (((seed term-token)
- (ssax:read-char-data port expect-eof?
- my-char-data-handler seed)))
- (if (eof-object? term-token)
- seed
- (case (xml-token-kind term-token)
- ((END)
- (ssax:assert-token term-token 'END start-tag-head
- (lambda (token exp-kind exp-head)
- (parser-error port "[GIMatch] broken for "
- term-token " while expecting "
- exp-kind exp-head)))
- (my-finish-element
- elem-gi attributes namespaces parent-seed seed))
- ((PI)
- (let ((seed
- ((ssax:make-pi-parser my-pi-handlers)
- port (xml-token-head term-token) seed)))
- (loop port entities expect-eof? seed)))
- ((ENTITY-REF)
- (let ((seed
- (ssax:handle-parsed-entity
- port (xml-token-head term-token)
- entities
- (lambda (port entities seed)
- (loop port entities #t seed))
- my-char-data-handler
- seed))) ; keep on reading the content after ent
- (loop port entities expect-eof? seed)))
- ((START) ; Start of a child element
- (if (eq? expected-content 'PCDATA)
- (parser-error port "[elementvalid] broken for "
- elem-gi
- " with char content only; unexpected token "
- term-token))
- ; Do other validation of the element content
- (let ((seed
- (handle-start-tag
- (xml-token-head term-token)
- port entities namespaces
- preserve-ws? seed)))
- (loop port entities expect-eof? seed)))
- (else
- (parser-error port "XML [43] broken for "
- term-token))))))))
- )))
-))))
-
-
-; syntax: ssax:make-parser user-handler-tag user-handler-proc ...
-;
-; Create an XML parser, an instance of the XML parsing framework.
-; This will be a SAX, a DOM, or a specialized parser depending
-; on the supplied user-handlers.
-
-; user-handler-tag is a symbol that identifies a procedural expression
-; that follows the tag. Given below are tags and signatures of the
-; corresponding procedures. Not all tags have to be specified. If some
-; are omitted, reasonable defaults will apply.
-;
-
-; tag: DOCTYPE
-; handler-procedure: PORT DOCNAME SYSTEMID INTERNAL-SUBSET? SEED
-; If internal-subset? is #t, the current position in the port
-; is right after we have read #\[ that begins the internal DTD subset.
-; We must finish reading of this subset before we return
-; (or must call skip-internal-subset if we aren't interested in reading it).
-; The port at exit must be at the first symbol after the whole
-; DOCTYPE declaration.
-; The handler-procedure must generate four values:
-; ELEMS ENTITIES NAMESPACES SEED
-; See xml-decl::elems for ELEMS. It may be #f to switch off the validation.
-; NAMESPACES will typically contain USER-PREFIXes for selected URI-SYMBs.
-; The default handler-procedure skips the internal subset,
-; if any, and returns (values #f '() '() seed)
-
-; tag: UNDECL-ROOT
-; handler-procedure: ELEM-GI SEED
-; where ELEM-GI is an UNRES-NAME of the root element. This procedure
-; is called when an XML document under parsing contains _no_ DOCTYPE
-; declaration.
-; The handler-procedure, as a DOCTYPE handler procedure above,
-; must generate four values:
-; ELEMS ENTITIES NAMESPACES SEED
-; The default handler-procedure returns (values #f '() '() seed)
-
-; tag: DECL-ROOT
-; handler-procedure: ELEM-GI SEED
-; where ELEM-GI is an UNRES-NAME of the root element. This procedure
-; is called when an XML document under parsing does contains the DOCTYPE
-; declaration.
-; The handler-procedure must generate a new SEED (and verify
-; that the name of the root element matches the doctype, if the handler
-; so wishes).
-; The default handler-procedure is the identity function.
-
-; tag: NEW-LEVEL-SEED
-; handler-procedure: see ssax:make-elem-parser, my-new-level-seed
-
-; tag: FINISH-ELEMENT
-; handler-procedure: see ssax:make-elem-parser, my-finish-element
-
-; tag: CHAR-DATA-HANDLER
-; handler-procedure: see ssax:make-elem-parser, my-char-data-handler
-
-; tag: PI
-; handler-procedure: see ssax:make-pi-parser
-; The default value is '()
-
-; The generated parser is a
-; procedure PORT SEED
-
-; This procedure parses the document prolog and then exits to
-; an element parser (created by ssax:make-elem-parser) to handle
-; the rest.
-;
-; [1] document ::= prolog element Misc*
-; [22] prolog ::= XMLDecl? Misc* (doctypedec | Misc*)?
-; [27] Misc ::= Comment | PI | S
-;
-; [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S?
-; ('[' (markupdecl | PEReference | S)* ']' S?)? '>'
-; [29] markupdecl ::= elementdecl | AttlistDecl
-; | EntityDecl
-; | NotationDecl | PI
-; | Comment
-;
-
-
-; This is ssax:make-parser with all the (specialization) handlers given
-; as positional arguments. It is called by ssax:make-parser, see below
-(define-syntax ssax:make-parser/positional-args
- (syntax-rules ()
- ((ssax:make-parser/positional-args
- *handler-DOCTYPE
- *handler-UNDECL-ROOT
- *handler-DECL-ROOT
- *handler-NEW-LEVEL-SEED
- *handler-FINISH-ELEMENT
- *handler-CHAR-DATA-HANDLER
- *handler-PI)
- (lambda (port seed)
-
- ; We must've just scanned the DOCTYPE token
- ; Handle the doctype declaration and exit to
- ; scan-for-significant-prolog-token-2, and eventually, to the
- ; element parser.
- (define (handle-decl port token-head seed)
- (or (eq? (string->symbol "DOCTYPE") token-head)
- (parser-error port "XML [22], expected DOCTYPE declaration, found "
- token-head))
- (assert-curr-char ssax:S-chars "XML [28], space after DOCTYPE" port)
- (ssax:skip-S port)
- (let*-values
- (((docname) (ssax:read-QName port))
- ((systemid)
- (and (ssax:ncname-starting-char? (ssax:skip-S port))
- (ssax:read-external-id port)))
- ((internal-subset?)
- (begin (ssax:skip-S port)
- (eqv? #\[ (assert-curr-char '(#\> #\[)
- "XML [28], end-of-DOCTYPE" port))))
- ((elems entities namespaces seed)
- (*handler-DOCTYPE port docname systemid
- internal-subset? seed))
- )
- (scan-for-significant-prolog-token-2 port elems entities namespaces
- seed)))
-
-
- ; Scan the leading PIs until we encounter either a doctype declaration
- ; or a start token (of the root element)
- ; In the latter two cases, we exit to the appropriate continuation
- (define (scan-for-significant-prolog-token-1 port seed)
- (let ((token (ssax:scan-Misc port)))
- (if (eof-object? token)
- (parser-error port "XML [22], unexpected EOF")
- (case (xml-token-kind token)
- ((PI)
- (let ((seed
- ((ssax:make-pi-parser *handler-PI)
- port (xml-token-head token) seed)))
- (scan-for-significant-prolog-token-1 port seed)))
- ((DECL) (handle-decl port (xml-token-head token) seed))
- ((START)
- (let*-values
- (((elems entities namespaces seed)
- (*handler-UNDECL-ROOT (xml-token-head token) seed)))
- (element-parser (xml-token-head token) port elems
- entities namespaces #f seed)))
- (else (parser-error port "XML [22], unexpected markup "
- token))))))
-
-
- ; Scan PIs after the doctype declaration, till we encounter
- ; the start tag of the root element. After that we exit
- ; to the element parser
- (define (scan-for-significant-prolog-token-2 port elems entities
- namespaces seed)
- (let ((token (ssax:scan-Misc port)))
- (if (eof-object? token)
- (parser-error port "XML [22], unexpected EOF")
- (case (xml-token-kind token)
- ((PI)
- (let ((seed
- ((ssax:make-pi-parser *handler-PI)
- port (xml-token-head token) seed)))
- (scan-for-significant-prolog-token-2 port elems entities
- namespaces seed)))
- ((START)
- (element-parser (xml-token-head token) port elems
- entities namespaces #f
- (*handler-DECL-ROOT (xml-token-head token) seed)))
- (else (parser-error port "XML [22], unexpected markup "
- token))))))
-
-
- ; A procedure start-tag-head port elems entities namespaces
- ; preserve-ws? seed
- (define element-parser
- (ssax:make-elem-parser *handler-NEW-LEVEL-SEED
- *handler-FINISH-ELEMENT
- *handler-CHAR-DATA-HANDLER
- *handler-PI))
-
- ; Get the ball rolling ...
- (scan-for-significant-prolog-token-1 port seed)
-))))
-
-
-
-; The following meta-macro turns a regular macro (with positional
-; arguments) into a form with keyword (labeled) arguments. We later
-; use the meta-macro to convert ssax:make-parser/positional-args into
-; ssax:make-parser. The latter provides a prettier (with labeled
-; arguments and defaults) interface to
-; ssax:make-parser/positional-args
-;
-; ssax:define-labeled-arg-macro LABELED-ARG-MACRO-NAME
-; (POS-MACRO-NAME ARG-DESCRIPTOR ...)
-; expands into the definition of a macro
-; LABELED-ARG-MACRO-NAME KW-NAME KW-VALUE KW-NAME1 KW-VALUE1 ...
-; which, in turn, expands into
-; POS-MACRO-NAME ARG1 ARG2 ...
-; where each ARG1 etc. comes either from KW-VALUE or from
-; the deafult part of ARG-DESCRIPTOR. ARG1 corresponds to the first
-; ARG-DESCRIPTOR, ARG2 corresponds to the second descriptor, etc.
-; Here ARG-DESCRIPTOR describes one argument of the positional macro.
-; It has the form
-; (ARG-NAME DEFAULT-VALUE)
-; or
-; (ARG-NAME)
-; In the latter form, the default value is not given, so that the invocation of
-; LABELED-ARG-MACRO-NAME must mention the corresponding parameter.
-; ARG-NAME can be anything: an identifier, a string, or even a number.
-
-
-(define-syntax ssax:define-labeled-arg-macro
- (syntax-rules ()
- ((ssax:define-labeled-arg-macro
- labeled-arg-macro-name
- (positional-macro-name
- (arg-name . arg-def) ...))
- (define-syntax labeled-arg-macro-name
- (syntax-rules ()
- ((labeled-arg-macro-name . kw-val-pairs)
- (letrec-syntax
- ((find
- (syntax-rules (arg-name ...)
- ((find k-args (arg-name . default) arg-name
- val . others) ; found arg-name among kw-val-pairs
- (next val . k-args)) ...
- ((find k-args key arg-no-match-name val . others)
- (find k-args key . others))
- ((find k-args (arg-name default)) ; default must be here
- (next default . k-args)) ...
- ))
- (next ; pack the continuation to find
- (syntax-rules ()
- ((next val vals key . keys)
- (find ((val . vals) . keys) key . kw-val-pairs))
- ((next val vals) ; processed all arg-descriptors
- (rev-apply (val) vals))))
- (rev-apply
- (syntax-rules ()
- ((rev-apply form (x . xs))
- (rev-apply (x . form) xs))
- ((rev-apply form ()) form))))
- (next positional-macro-name ()
- (arg-name . arg-def) ...))))))))
-
-
-; The definition of ssax:make-parser
-(ssax:define-labeled-arg-macro ssax:make-parser
- (ssax:make-parser/positional-args
- (DOCTYPE
- (lambda (port docname systemid internal-subset? seed)
- (when internal-subset?
- (ssax:warn port "Internal DTD subset is not currently handled ")
- (ssax:skip-internal-dtd port))
- (ssax:warn port "DOCTYPE DECL " docname " "
- systemid " found and skipped")
- (values #f '() '() seed)
- ))
- (UNDECL-ROOT
- (lambda (elem-gi seed) (values #f '() '() seed)))
- (DECL-ROOT
- (lambda (elem-gi seed) seed))
- (NEW-LEVEL-SEED) ; required
- (FINISH-ELEMENT) ; required
- (CHAR-DATA-HANDLER) ; required
- (PI ())
- ))
-
-(run-test
- (letrec ((simple-parser
- (lambda (str doctype-fn)
- (call-with-input-string str
- (lambda (port)
- ((ssax:make-parser
- NEW-LEVEL-SEED
- (lambda (elem-gi attributes namespaces
- expected-content seed)
- '())
-
- FINISH-ELEMENT
- (lambda (elem-gi attributes namespaces parent-seed seed)
- (let
- ((seed (if (null? namespaces) (reverse seed)
- (cons (list '*NAMESPACES* namespaces)
- (reverse seed)))))
- (let ((seed (if (attlist-null? attributes) seed
- (cons
- (cons '@
- (map (lambda (attr)
- (list (car attr) (cdr attr)))
- (attlist->alist attributes)))
- seed))))
- (cons (cons elem-gi seed) parent-seed))))
-
- CHAR-DATA-HANDLER
- (lambda (string1 string2 seed)
- (if (string-null? string2) (cons string1 seed)
- (cons* string2 string1 seed)))
-
- DOCTYPE
- (lambda (port docname systemid internal-subset? seed)
- (when internal-subset?
- (ssax:warn port
- "Internal DTD subset is not currently handled ")
- (ssax:skip-internal-dtd port))
- (ssax:warn port "DOCTYPE DECL " docname " "
- systemid " found and skipped")
- (doctype-fn docname seed))
-
- UNDECL-ROOT
- (lambda (elem-gi seed)
- (doctype-fn elem-gi seed))
- )
- port '())))))
-
- (dummy-doctype-fn (lambda (elem-gi seed) (values #f '() '() seed)))
- (test
- (lambda (str doctype-fn expected)
- (cout nl "Parsing: " str nl)
- (let ((result (simple-parser (unesc-string str) doctype-fn)))
- (write result)
- (assert (equal? result expected)))))
- )
-
- (test "<BR/>" dummy-doctype-fn '(('"BR")))
- (assert (failed? (test "<BR>" dummy-doctype-fn '())))
- (test "<BR></BR>" dummy-doctype-fn '(('"BR")))
- (assert (failed? (test "<BR></BB>" dummy-doctype-fn '())))
-
- (test " <A HREF='URL'> link <I>itlink </I> &amp;amp;</A>"
- dummy-doctype-fn
- '(('"A" (@ ('"HREF" "URL")) " link " ('"I" "itlink ")
- " " "&" "amp;")))
-
- (test
- " <A HREF='URL' xml:space='preserve'> link <I>itlink </I> &amp;amp;</A>" dummy-doctype-fn
- '(('"A" (@ ('"HREF" "URL") (('"xml" . '"space") "preserve"))
- " link " ('"I" "itlink ") " " "&" "amp;")))
-
- (test " <A HREF='URL' xml:space='preserve'> link <I xml:space='default'>itlink </I> &amp;amp;</A>" dummy-doctype-fn
- '(('"A" (@ ('"HREF" "URL") (('"xml" . '"space") "preserve"))
- " link "
- ('"I" (@ (('"xml" . '"space") "default")) "itlink ")
- " " "&" "amp;")))
- (test "<itemize><item>This is item 1 </item>%n<!-- Just:a comment --><item>Item 2</item>%n </itemize>" dummy-doctype-fn
- `(('"itemize" ('"item" "This is item 1 ")
- ,(unesc-string "%n") ('"item" "Item 2") ,(unesc-string "%n "))))
- (test " <P><![CDATA[<BR>%n<![CDATA[<BR>]]&gt;]]></P>"
- dummy-doctype-fn `(('"P" "<BR>" ,nl "<![CDATA[<BR>" "]]" "" ">")))
-
- (test " <P><![CDATA[<BR>%r<![CDATA[<BR>]]&gt;]]></P>"
- dummy-doctype-fn `(('"P" "<BR>" ,nl "<![CDATA[<BR>" "]]" "" ">")))
-
- (test "<?xml version='1.0'?>%n%n<Reports TStamp='1'></Reports>"
- dummy-doctype-fn '(('"Reports" (@ ('"TStamp" "1")))))
- (test "%n<?PI xxx?><!-- Comment %n -%r-->%n<?PI1 zzz?><T/>"
- dummy-doctype-fn '(('"T")))
- (test "<!DOCTYPE T SYSTEM 'system1' ><!-- comment -->%n<T/>"
- (lambda (elem-gi seed) (assert (equal? elem-gi ''"T"))
- (values #f '() '() seed))
- '(('"T")))
- (test "<!DOCTYPE T PUBLIC '//EN/T' \"system1\" [ <!ELEMENT a 'aa'> ]>%n<?pi?><T/>"
- (lambda (elem-gi seed) (assert (equal? elem-gi ''"T"))
- (values #f '() '() seed))
- '(('"T")))
- (test "<BR/>"
- (lambda (elem-gi seed)
- (values '(('"BR" EMPTY ())) '() '() seed)) '(('"BR")))
- (test "<BR></BR>"
- (lambda (elem-gi seed)
- (values '(('"BR" EMPTY ())) '() '() seed)) '(('"BR")))
- (assert (failed? (test "<BR>aa</BR>"
- (lambda (elem-gi seed)
- (values '(('"BR" EMPTY ())) '() '() seed)) '())))
- (test "<BR>aa</BR>"
- (lambda (elem-gi seed)
- (values '(('"BR" PCDATA ())) '() '() seed)) '(('"BR" "aa")))
- (assert (failed? (test "<BR>a<I>a</I></BR>"
- (lambda (elem-gi seed)
- (values '(('"BR" PCDATA ())) '() '() seed)) '())))
- (test "<BR>a<I>a</I></BR>"
- (lambda (elem-gi seed)
- (values '(('"BR" ANY ()) ('"I" PCDATA ())) '() '() seed))
- '(('"BR" "a" ('"I" "a"))))
-
-
- (test "<DIV>Example: \"&example;\"</DIV>"
- (lambda (elem-gi seed)
- (values #f '((example . "<P>An ampersand (&#38;) may be escaped numerically (&#38;#38;) or with a general entity (&amp;amp;).</P>")) '() seed))
- '(('"DIV" "Example: \""
- ('"P" "An ampersand (" "&" ") may be escaped numerically (" "&" "#38;) or with a general entity (" "&" "amp;).") "\"")))
- (test "<DIV>Example: \"&example;\" <P/></DIV>"
- (lambda (elem-gi seed)
- (values #f '(('"quote" . "<I>example:</I> ex")
- ('"example" . "<Q>&quote;!</Q>?")) '() seed))
- '(('"DIV" "Example: \"" ('"Q" ('"I" "example:") " ex" "!") "?"
- "\" " ('"P"))))
- (assert (failed?
- (test "<DIV>Example: \"&example;\" <P/></DIV>"
- (lambda (elem-gi seed)
- (values #f '(('"quote" . "<I>example:")
- ('"example" . "<Q>&quote;</I>!</Q>?")) '() seed))
- '())))
-
- (test "<DIV A:B='A' B='B' xmlns:A='URI1' xmlns='URI1'><A:P xmlns=''><BR/></A:P></DIV>"
- (lambda (elem-gi seed)
- (values #f '() '() seed))
- '((('"URI1" . '"DIV") (@ ('"B" "B") (('"URI1" . '"B") "A"))
- (*NAMESPACES* (('"A" '"URI1" . '"URI1")
- (*DEFAULT* '"URI1" . '"URI1")))
- (('"URI1" . '"P")
- (*NAMESPACES* ((*DEFAULT* #f . #f) ('"A" '"URI1" . '"URI1")
- (*DEFAULT* '"URI1" . '"URI1")))
- ('"BR"
- (*NAMESPACES* ((*DEFAULT* #f . #f)
- ('"A" '"URI1" . '"URI1")
- (*DEFAULT* '"URI1" . '"URI1"))))))))
- (test "<DIV A:B='A' B='B' xmlns:A='URI1' xmlns='URI1'><A:P xmlns=''><BR/></A:P></DIV>"
- (lambda (elem-gi seed)
- (values #f '() '((#f '"UA" . '"URI1")) seed))
- '((('"UA" . '"DIV") (@ ('"B" "B") (('"UA" . '"B") "A"))
- (*NAMESPACES* (('"A" '"UA" . '"URI1")
- (*DEFAULT* '"UA" . '"URI1") (#f '"UA" . '"URI1")))
- (('"UA" . '"P")
- (*NAMESPACES* ((*DEFAULT* #f . #f) ('"A" '"UA" . '"URI1")
- (*DEFAULT* '"UA" . '"URI1") (#f '"UA" . '"URI1")))
- ('"BR"
- (*NAMESPACES* ((*DEFAULT* #f . #f) ('"A" '"UA" . '"URI1")
- (*DEFAULT* '"UA" . '"URI1")
- (#f '"UA" . '"URI1"))))))))
- ; uniqattr should fail
- (assert (failed?
- (test "<DIV A:B='A' B='B' xmlns:A='URI1' xmlns='URI1'><A:P xmlns=''><BR/></A:P></DIV>"
- (lambda (elem-gi seed)
- (values
- `(('"DIV" ANY (('"B" CDATA IMPLIED #f)
- (('"A" . '"B") CDATA IMPLIED #f)
- (('"C" . '"B") CDATA IMPLIED "xx")
- (('"xmlns" . '"C") CDATA IMPLIED "URI1")
- ))
- (('"A" . '"P") ANY ()) ('"BR" '"EMPTY" ()))
- '() '((#f '"UA" . '"URI1")) seed))
- '())))
- ; prefix C undeclared
- (assert (failed?
- (test "<DIV A:B='A' B='B' xmlns:A='URI1' xmlns='URI1'><A:P xmlns=''><BR/></A:P></DIV>"
- (lambda (elem-gi seed)
- (values
- '(('"DIV" ANY (('"B" CDATA IMPLIED #f)
- ('"xmlns" CDATA IMPLIED "URI1")
- (('"A" . '"B") CDATA IMPLIED #f)
- (('"C" . '"B") CDATA IMPLIED "xx")
- ))
- (('"A" . '"P") ANY ()) ('"BR" EMPTY ()))
- '() '((#f '"UA" . '"URI1")) seed))
- '())))
-
- ; contradiction to xmlns declaration
- (assert (failed?
- (test "<DIV A:B='A' B='B' xmlns:A='URI1' xmlns='URI1'><A:P xmlns=''><BR/></A:P></DIV>"
- (lambda (elem-gi seed)
- (values
- '(('"DIV" ANY (('"B" CDATA IMPLIED #f)
- ('"xmlns" CDATA FIXED "URI2")
- (('"A" . '"B") CDATA IMPLIED #f)
- ))
- (('"A" . '"P") ANY ()) ('"BR" EMPTY ()))
- '() '((#f '"UA" . '"URI1")) seed))
- '())))
-
- (test "<DIV A:B='A' B='B' xmlns:A='URI1' xmlns='URI1'><A:P xmlns=''><BR/></A:P></DIV>"
- (lambda (elem-gi seed)
- (values
- '(('"DIV" ANY (('"B" CDATA IMPLIED #f)
- ('"xmlns" CDATA FIXED "URI1")
- (('"A" . '"B") CDATA IMPLIED #f)
- ))
- (('"A" . '"P") ANY ()) ('"BR" EMPTY ()))
- '() '((#f '"UA" . '"URI1")) seed))
- '((('"UA" . '"DIV") (@ ('"B" "B") (('"UA" . '"B") "A"))
- (*NAMESPACES* ((*DEFAULT* '"UA" . '"URI1")
- ('"A" '"UA" . '"URI1") (#f '"UA" . '"URI1")))
- (('"UA" . '"P")
- (*NAMESPACES* ((*DEFAULT* #f . #f)
- (*DEFAULT* '"UA" . '"URI1")
- ('"A" '"UA" . '"URI1") (#f '"UA" . '"URI1")))
- ('"BR"
- (*NAMESPACES* ((*DEFAULT* #f . #f) (*DEFAULT* '"UA" . '"URI1")
- ('"A" '"UA" . '"URI1") (#f '"UA" . '"URI1"))))))))
-
- (test "<DIV A:B='A' B='B' xmlns:A='URI1' xmlns='URI1'><A:P xmlns=''><BR/></A:P></DIV>"
- (lambda (elem-gi seed)
- (values
- '(('"DIV" ANY (('"B" CDATA IMPLIED #f)
- (('"A" . '"B") CDATA IMPLIED #f)
- (('"C" . '"B") CDATA IMPLIED "xx")
- (('"xmlns" . '"C") CDATA IMPLIED "URI2")
- ))
- (('"A" . '"P") ANY ()) ('"BR" EMPTY ()))
- '() '((#f '"UA" . '"URI1")) seed))
- '((('"UA" . '"DIV") (@ ('"B" "B") (('"UA" . '"B") "A")
- (('"URI2" . '"B") "xx"))
- (*NAMESPACES* ((*DEFAULT* '"UA" . '"URI1")
- ('"A" '"UA" . '"URI1")
- ('"C" '"URI2" . '"URI2")
- (#f '"UA" . '"URI1")))
- (('"UA" . '"P")
- (*NAMESPACES* ((*DEFAULT* #f . #f) (*DEFAULT* '"UA" . '"URI1")
- ('"A" '"UA" . '"URI1")
- ('"C" '"URI2" . '"URI2") (#f '"UA" . '"URI1")))
- ('"BR"
- (*NAMESPACES* ((*DEFAULT* #f . #f)
- (*DEFAULT* '"UA" . '"URI1")
- ('"A" '"UA" . '"URI1")
- ('"C" '"URI2" . '"URI2")
- (#f '"UA" . '"URI1"))))))))
-))
-
-
-
-;========================================================================
-; Highest-level parsers: XML to SXML
-;
-
-; First, a few utility procedures that turned out useful
-
-; ssax:reverse-collect-str LIST-OF-FRAGS -> LIST-OF-FRAGS
-; given the list of fragments (some of which are text strings)
-; reverse the list and concatenate adjacent text strings.
-; We can prove from the general case below that if LIST-OF-FRAGS
-; has zero or one element, the result of the procedure is equal?
-; to its argument. This fact justifies the shortcut evaluation below.
-(define (ssax:reverse-collect-str fragments)
- (cond
- ((null? fragments) '()) ; a shortcut
- ((null? (cdr fragments)) fragments) ; see the comment above
- (else
- (let loop ((fragments fragments) (result '()) (strs '()))
- (cond
- ((null? fragments)
- (if (null? strs) result
- (cons (string-concatenate/shared strs) result)))
- ((string? (car fragments))
- (loop (cdr fragments) result (cons (car fragments) strs)))
- (else
- (loop (cdr fragments)
- (cons
- (car fragments)
- (if (null? strs) result
- (cons (string-concatenate/shared strs) result)))
- '())))))))
-
-
-; ssax:reverse-collect-str-drop-ws LIST-OF-FRAGS -> LIST-OF-FRAGS
-; given the list of fragments (some of which are text strings)
-; reverse the list and concatenate adjacent text strings.
-; We also drop "unsignificant" whitespace, that is, whitespace
-; in front, behind and between elements. The whitespace that
-; is included in character data is not affected.
-; We use this procedure to "intelligently" drop "insignificant"
-; whitespace in the parsed SXML. If the strict compliance with
-; the XML Recommendation regarding the whitespace is desired, please
-; use the ssax:reverse-collect-str procedure instead.
-
-(define (ssax:reverse-collect-str-drop-ws fragments)
- (cond
- ((null? fragments) '()) ; a shortcut
- ((null? (cdr fragments)) ; another shortcut
- (if (and (string? (car fragments)) (string-whitespace? (car fragments)))
- '() fragments)) ; remove trailing ws
- (else
- (let loop ((fragments fragments) (result '()) (strs '())
- (all-whitespace? #t))
- (cond
- ((null? fragments)
- (if all-whitespace? result ; remove leading ws
- (cons (string-concatenate/shared strs) result)))
- ((string? (car fragments))
- (loop (cdr fragments) result (cons (car fragments) strs)
- (and all-whitespace?
- (string-whitespace? (car fragments)))))
- (else
- (loop (cdr fragments)
- (cons
- (car fragments)
- (if all-whitespace? result
- (cons (string-concatenate/shared strs) result)))
- '() #t)))))))
-
-
-; procedure: ssax:xml->sxml PORT NAMESPACE-PREFIX-ASSIG
-;
-; This is an instance of a SSAX parser above that returns an SXML
-; representation of the XML document to be read from PORT.
-; NAMESPACE-PREFIX-ASSIG is a list of (USER-PREFIX . URI-STRING)
-; that assigns USER-PREFIXes to certain namespaces identified by
-; particular URI-STRINGs. It may be an empty list.
-; The procedure returns an SXML tree. The port points out to the
-; first character after the root element.
-
-(define (ssax:xml->sxml port namespace-prefix-assig)
- (letrec
- ((namespaces
- (map (lambda (el)
- (cons* #f (car el) (ssax:uri-string->symbol (cdr el))))
- namespace-prefix-assig))
-
- (RES-NAME->SXML
- (lambda (res-name)
- (string->symbol
- (string-append
- (symbol->string (car res-name))
- ":"
- (symbol->string (cdr res-name))))))
-
- )
- (let ((result
- (reverse
- ((ssax:make-parser
- NEW-LEVEL-SEED
- (lambda (elem-gi attributes namespaces
- expected-content seed)
- '())
-
- FINISH-ELEMENT
- (lambda (elem-gi attributes namespaces parent-seed seed)
- (let ((seed (ssax:reverse-collect-str seed))
- (attrs
- (attlist-fold
- (lambda (attr accum)
- (cons (list
- (if (symbol? (car attr)) (car attr)
- (RES-NAME->SXML (car attr)))
- (cdr attr)) accum))
- '() attributes)))
- (cons
- (cons
- (if (symbol? elem-gi) elem-gi
- (RES-NAME->SXML elem-gi))
- (if (null? attrs) seed
- (cons (cons '@ attrs) seed)))
- parent-seed)))
-
- CHAR-DATA-HANDLER
- (lambda (string1 string2 seed)
- (if (string-null? string2) (cons string1 seed)
- (cons* string2 string1 seed)))
-
- DOCTYPE
- (lambda (port docname systemid internal-subset? seed)
- (when internal-subset?
- (ssax:warn port
- "Internal DTD subset is not currently handled ")
- (ssax:skip-internal-dtd port))
- (ssax:warn port "DOCTYPE DECL " docname " "
- systemid " found and skipped")
- (values #f '() namespaces seed))
-
- UNDECL-ROOT
- (lambda (elem-gi seed)
- (values #f '() namespaces seed))
-
- PI
- ((*DEFAULT* .
- (lambda (port pi-tag seed)
- (cons
- (list '*PI* pi-tag (ssax:read-pi-body-as-string port))
- seed))))
- )
- port '()))))
- (cons '*TOP*
- (if (null? namespace-prefix-assig) result
- (cons
- (list '@ (cons '*NAMESPACES*
- (map (lambda (ns) (list (car ns) (cdr ns)))
- namespace-prefix-assig)))
- result)))
-)))
-
-; For backwards compatibility
-(define SSAX:XML->SXML ssax:xml->sxml)
-
-
-; a few lines of validation code
-(run-test (letrec
- ((test (lambda (str namespace-assig expected-res)
- (newline) (display "input: ")
- (write (unesc-string str)) (newline) (display "Result: ")
- (let ((result
- (call-with-input-string (unesc-string str)
- (lambda (port)
- (ssax:xml->sxml port namespace-assig)))))
- (pp result)
- (assert (equal_? result expected-res))))))
-
- (test " <BR/>" '() '(*TOP* (BR)))
- (test "<BR></BR>" '() '(*TOP* (BR)))
- (test " <BR CLEAR='ALL'%nCLASS='Class1'/>" '()
- '(*TOP* (BR (@ (CLEAR "ALL") (CLASS "Class1")))))
- (test " <A HREF='URL'> link <I>itlink </I> &amp;amp;</A>" '()
- '(*TOP* (A (@ (HREF "URL")) " link " (I "itlink ") " &amp;")))
- (test " <A HREF='URL' xml:space='preserve'> link <I>itlink </I> &amp;amp;</A>" '()
- '(*TOP* (A (@ (xml:space "preserve") (HREF "URL"))
- " link " (I "itlink ") " &amp;")))
- (test " <A HREF='URL' xml:space='preserve'> link <I xml:space='default'>itlink </I> &amp;amp;</A>" '()
- '(*TOP* (A (@ (xml:space "preserve") (HREF "URL"))
- " link " (I (@ (xml:space "default"))
- "itlink ") " &amp;")))
- (test " <P><?pi1 p1 content ?>?<?pi2 pi2? content? ??></P>" '()
- '(*TOP* (P (*PI* pi1 "p1 content ") "?"
- (*PI* pi2 "pi2? content? ?"))))
- (test " <P>some text <![CDATA[<]]>1%n&quot;<B>strong</B>&quot;%r</P>"
- '()
- `(*TOP* (P ,(unesc-string "some text <1%n\"")
- (B "strong") ,(unesc-string "\"%n"))))
- (test " <P><![CDATA[<BR>%n<![CDATA[<BR>]]&gt;]]></P>" '()
- `(*TOP* (P ,(unesc-string "<BR>%n<![CDATA[<BR>]]>"))))
-; (test "<T1><T2>it&apos;s%r%nand that%n</T2>%r%n%r%n%n</T1>" '()
-; '(*TOP* (T1 (T2 "it's%nand that%n") "%n%n%n")))
- (test "<T1><T2>it&apos;s%r%nand that%n</T2>%r%n%r%n%n</T1>" '()
- `(*TOP* (T1 (T2 ,(unesc-string "it's%nand that%n")) ,(unesc-string "%n%n%n"))))
- (test "<T1><T2>it&apos;s%rand that%n</T2>%r%n%r%n%n</T1>" '()
- `(*TOP* (T1 (T2 ,(unesc-string "it's%nand that%n")) ,(unesc-string "%n%n%n"))))
- (test "<!DOCTYPE T SYSTEM 'system1' ><!-- comment -->%n<T/>" '()
- '(*TOP* (T)))
- (test "<?xml version='1.0'?>%n<WEIGHT unit=\"pound\">%n<NET certified='certified'> 67 </NET>%n<GROSS> 95 </GROSS>%n</WEIGHT>" '()
- `(*TOP* (*PI* xml "version='1.0'") (WEIGHT (@ (unit "pound"))
- ,nl (NET (@ (certified "certified")) " 67 ") ,nl
- (GROSS " 95 ") ,nl)
- ))
-; (test "<?xml version='1.0'?>%n<WEIGHT unit=\"pound\">%n<NET certified='certified'> 67 </NET>%n<GROSS> 95 </GROSS>%n</WEIGHT>" '()
-; '(*TOP* (*PI* xml "version='1.0'") (WEIGHT (@ (unit "pound"))
-; "%n" (NET (@ (certified "certified")) " 67 ")
-; "%n" (GROSS " 95 ") "%n")
-; ))
- (test "<DIV A:B='A' B='B' xmlns:A='URI1' xmlns='URI1'><A:P xmlns=''><BR/></A:P></DIV>" '()
- '(*TOP* (URI1:DIV (@ (URI1:B "A") (B "B")) (URI1:P (BR)))))
- (test "<DIV A:B='A' B='B' xmlns:A='URI1' xmlns='URI1'><A:P xmlns=''><BR/></A:P></DIV>" '((UA . "URI1"))
- '(*TOP* (@ (*NAMESPACES* (UA "URI1")))
- (UA:DIV (@ (UA:B "A") (B "B")) (UA:P (BR)))))
-
- ; A few tests from XML Namespaces Recommendation
- (test (string-append
- "<x xmlns:edi='http://ecommerce.org/schema'>"
- "<!-- the 'taxClass' attribute's ns http://ecommerce.org/schema -->"
- "<lineItem edi:taxClass='exempt'>Baby food</lineItem>" nl
- "</x>") '()
- `(*TOP*
- (x (lineItem
- (@ (http://ecommerce.org/schema:taxClass "exempt"))
- "Baby food") ,nl)))
- (test (string-append
- "<x xmlns:edi='http://ecommerce.org/schema'>"
- "<!-- the 'taxClass' attribute's ns http://ecommerce.org/schema -->"
- "<lineItem edi:taxClass='exempt'>Baby food</lineItem>"
- "</x>") '((EDI . "http://ecommerce.org/schema"))
- '(*TOP*
- (@ (*NAMESPACES* (EDI "http://ecommerce.org/schema")))
- (x (lineItem
- (@ (EDI:taxClass "exempt"))
- "Baby food"))))
-
- (test (string-append
- "<bk:book xmlns:bk='urn:loc.gov:books' "
- "xmlns:isbn='urn:ISBN:0-395-36341-6'>"
- "<bk:title>Cheaper by the Dozen</bk:title>"
- "<isbn:number>1568491379</isbn:number></bk:book>")
- '()
- '(*TOP* (urn:loc.gov:books:book
- (urn:loc.gov:books:title "Cheaper by the Dozen")
- (urn:ISBN:0-395-36341-6:number "1568491379"))))
-
- (test (string-append
- "<!-- initially, the default namespace is 'books' -->"
- "<book xmlns='urn:loc.gov:books' "
- "xmlns:isbn='urn:ISBN:0-395-36341-6'>"
- "<title>Cheaper by the Dozen</title>"
- "<isbn:number>1568491379</isbn:number>"
- "<notes>"
- "<!-- make HTML the default namespace for some commentary -->"
- "<p xmlns='urn:w3-org-ns:HTML'>"
- "This is a <i>funny</i> book!"
- "</p>"
- "</notes>"
- "</book>") '()
- '(*TOP* (urn:loc.gov:books:book
- (urn:loc.gov:books:title "Cheaper by the Dozen")
- (urn:ISBN:0-395-36341-6:number "1568491379")
- (urn:loc.gov:books:notes
- (urn:w3-org-ns:HTML:p
- "This is a " (urn:w3-org-ns:HTML:i "funny")
- " book!")))))
-
- (test (string-append
- "<Beers>"
- "<!-- the default namespace is now that of HTML -->"
- "<table xmlns='http://www.w3.org/TR/REC-html40'>"
- "<th><td>Name</td><td>Origin</td><td>Description</td></th>"
- "<tr>"
- "<!-- no default namespace inside table cells -->"
- "<td><brandName xmlns=\"\">Huntsman</brandName></td>"
- "<td><origin xmlns=''>Bath, UK</origin></td>"
- "<td>"
- "<details xmlns=''><class>Bitter</class><hop>Fuggles</hop>"
- "<pro>Wonderful hop, light alcohol, good summer beer</pro>"
- "<con>Fragile; excessive variance pub to pub</con>"
- "</details>"
- "</td>"
- "</tr>"
- "</table>"
- "</Beers>")
- '((html . "http://www.w3.org/TR/REC-html40"))
- '(*TOP*
- (@ (*NAMESPACES* (html "http://www.w3.org/TR/REC-html40")))
- (Beers (html:table
- (html:th (html:td "Name")
- (html:td "Origin")
- (html:td "Description"))
- (html:tr (html:td (brandName "Huntsman"))
- (html:td (origin "Bath, UK"))
- (html:td
- (details
- (class "Bitter")
- (hop "Fuggles")
- (pro "Wonderful hop, light alcohol, good summer beer")
- (con "Fragile; excessive variance pub to pub"))))))))
-
- (test (string-append
- "<!-- 1 --><RESERVATION xmlns:HTML='http://www.w3.org/TR/REC-html40'>"
- "<!-- 2 --><NAME HTML:CLASS=\"largeSansSerif\">Layman, A</NAME>"
- "<!-- 3 --><SEAT CLASS='Y' HTML:CLASS=\"largeMonotype\">33B</SEAT>"
- "<!-- 4 --><HTML:A HREF='/cgi-bin/ResStatus'>Check Status</HTML:A>"
- "<!-- 5 --><DEPARTURE>1997-05-24T07:55:00+1</DEPARTURE></RESERVATION>")
- '((HTML . "http://www.w3.org/TR/REC-html40"))
- '(*TOP*
- (@ (*NAMESPACES* (HTML "http://www.w3.org/TR/REC-html40")))
- (RESERVATION
- (NAME (@ (HTML:CLASS "largeSansSerif")) "Layman, A")
- (SEAT (@ (HTML:CLASS "largeMonotype") (CLASS "Y")) "33B")
- (HTML:A (@ (HREF "/cgi-bin/ResStatus")) "Check Status")
- (DEPARTURE "1997-05-24T07:55:00+1"))))
- ; Part of RDF from the XML Infoset
- (test (string-concatenate/shared '(
- "<?xml version='1.0' encoding='utf-8' standalone='yes'?>"
- "<!-- this can be decoded as US-ASCII or iso-8859-1 as well,"
- " since it contains no characters outside the US-ASCII repertoire -->"
- "<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'"
- " xmlns:rdfs='http://www.w3.org/2000/01/rdf-schema#'"
- " xmlns='http://www.w3.org/2001/02/infoset#'>"
- "<rdfs:Class ID='Boolean'/>"
- "<Boolean ID='Boolean.true'/>"
- "<Boolean ID='Boolean.false'/>"
- "<!--Info item classes-->"
- "<rdfs:Class ID='InfoItem'/>"
- "<rdfs:Class ID='Document' rdfs:subClassOf='#InfoItem'/>"
- "<rdfs:Class ID='Element' rdfs:subClassOf='#InfoItem'/>"
- "<rdfs:Class ID='Attribute' rdfs:subClassOf='#InfoItem'/>"
- "<rdfs:Class ID='InfoItemSet'
- rdfs:subClassOf='http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag'/>"
- "<rdfs:Class ID='AttributeSet' rdfs:subClassOf='#InfoItemSet'/>"
- "<!--Info item properties-->"
- "<rdfs:Property ID='allDeclarationsProcessed'>"
- "<rdfs:domain resource='#Document'/>"
- "<rdfs:range resource='#Boolean'/></rdfs:Property>"
- "<rdfs:Property ID='attributes'>"
- "<rdfs:domain resource='#Element'/>"
- "<rdfs:range resource='#AttributeSet'/>"
- "</rdfs:Property>"
- "</rdf:RDF>"))
- '((RDF . "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
- (RDFS . "http://www.w3.org/2000/01/rdf-schema#")
- (ISET . "http://www.w3.org/2001/02/infoset#"))
- '(*TOP* (@ (*NAMESPACES*
- (RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
- (RDFS "http://www.w3.org/2000/01/rdf-schema#")
- (ISET "http://www.w3.org/2001/02/infoset#")))
- (*PI* xml "version='1.0' encoding='utf-8' standalone='yes'")
- (RDF:RDF
- (RDFS:Class (@ (ID "Boolean")))
- (ISET:Boolean (@ (ID "Boolean.true")))
- (ISET:Boolean (@ (ID "Boolean.false")))
- (RDFS:Class (@ (ID "InfoItem")))
- (RDFS:Class (@ (RDFS:subClassOf "#InfoItem") (ID "Document")))
- (RDFS:Class (@ (RDFS:subClassOf "#InfoItem") (ID "Element")))
- (RDFS:Class (@ (RDFS:subClassOf "#InfoItem") (ID "Attribute")))
- (RDFS:Class
- (@ (RDFS:subClassOf
- "http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag")
- (ID "InfoItemSet")))
- (RDFS:Class
- (@ (RDFS:subClassOf "#InfoItemSet") (ID "AttributeSet")))
- (RDFS:Property
- (@ (ID "allDeclarationsProcessed"))
- (RDFS:domain (@ (resource "#Document")))
- (RDFS:range (@ (resource "#Boolean"))))
- (RDFS:Property
- (@ (ID "attributes"))
- (RDFS:domain (@ (resource "#Element")))
- (RDFS:range (@ (resource "#AttributeSet")))))))
-
- ; Part of RDF from RSS of the Daemon News Mall
- (test (string-concatenate/shared (list-intersperse '(
- "<?xml version='1.0'?><rdf:RDF "
- "xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' "
- "xmlns='http://my.netscape.com/rdf/simple/0.9/'>"
- "<channel>"
- "<title>Daemon News Mall</title>"
- "<link>http://mall.daemonnews.org/</link>"
- "<description>Central source for all your BSD needs</description>"
- "</channel>"
- "<item>"
- "<title>Daemon News Jan/Feb Issue NOW Available! Subscribe $24.95</title>"
- "<link>http://mall.daemonnews.org/?page=shop/flypage&amp;product_id=880</link>"
- "</item>"
- "<item>"
- "<title>The Design and Implementation of the 4.4BSD Operating System $54.95</title>"
- "<link>http://mall.daemonnews.org/?page=shop/flypage&amp;product_id=912&amp;category_id=1761</link>"
- "</item>"
- "</rdf:RDF>")
- (string #\newline)
- ))
- '((RDF . "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
- (RSS . "http://my.netscape.com/rdf/simple/0.9/")
- (ISET . "http://www.w3.org/2001/02/infoset#"))
- `(*TOP* (@ (*NAMESPACES*
- (RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
- (RSS "http://my.netscape.com/rdf/simple/0.9/")
- (ISET "http://www.w3.org/2001/02/infoset#")))
- (*PI* xml "version='1.0'")
- (RDF:RDF ,nl
- (RSS:channel ,nl
- (RSS:title "Daemon News Mall") ,nl
- (RSS:link "http://mall.daemonnews.org/") ,nl
- (RSS:description "Central source for all your BSD needs") ,nl) ,nl
- (RSS:item ,nl
- (RSS:title
- "Daemon News Jan/Feb Issue NOW Available! Subscribe $24.95") ,nl
- (RSS:link
- "http://mall.daemonnews.org/?page=shop/flypage&product_id=880") ,nl) ,nl
- (RSS:item ,nl
- (RSS:title
- "The Design and Implementation of the 4.4BSD Operating System $54.95") ,nl
- (RSS:link
- "http://mall.daemonnews.org/?page=shop/flypage&product_id=912&category_id=1761") ,nl) ,nl)))
-
- (test (string-concatenate/shared
- '("<Forecasts TStamp='958082142'>"
- "<TAF TStamp='958066200' LatLon='36.583, -121.850' BId='724915'"
- " SName='KMRY, MONTEREY PENINSULA'>"
- "<VALID TRange='958068000, 958154400'>111730Z 111818</VALID>"
- "<PERIOD TRange='958068000, 958078800'>"
- "<PREVAILING>31010KT P6SM FEW030</PREVAILING>"
- "</PERIOD>"
- "<PERIOD TRange='958078800, 958104000' Title='FM2100'>"
- "<PREVAILING>29016KT P6SM FEW040</PREVAILING>"
- "</PERIOD>"
- "<PERIOD TRange='958104000, 958154400' Title='FM0400'>"
- "<PREVAILING>29010KT P6SM SCT200</PREVAILING>"
- "<VAR Title='BECMG 0708' TRange='958114800, 958118400'>VRB05KT</VAR>"
- "</PERIOD></TAF>"
- "</Forecasts>"))
- '()
- '(*TOP* (Forecasts
- (@ (TStamp "958082142"))
- (TAF (@ (TStamp "958066200")
- (SName "KMRY, MONTEREY PENINSULA")
- (LatLon "36.583, -121.850")
- (BId "724915"))
- (VALID (@ (TRange "958068000, 958154400")) "111730Z 111818")
- (PERIOD (@ (TRange "958068000, 958078800"))
- (PREVAILING "31010KT P6SM FEW030"))
- (PERIOD (@ (Title "FM2100") (TRange "958078800, 958104000"))
- (PREVAILING "29016KT P6SM FEW040"))
- (PERIOD (@ (Title "FM0400") (TRange "958104000, 958154400"))
- (PREVAILING "29010KT P6SM SCT200")
- (VAR (@ (Title "BECMG 0708")
- (TRange "958114800, 958118400"))
- "VRB05KT"))))))
-))
-
-(run-test
- (newline)
- (display "All tests passed")
- (newline)
-)
-; XML/HTML processing in Scheme
-; SXML expression tree transformers
-;
-; IMPORT
-; A prelude appropriate for your Scheme system
-; (myenv-bigloo.scm, myenv-mit.scm, etc.)
-;
-; EXPORT
-; (provide SRV:send-reply
-; post-order pre-post-order replace-range)
-;
-; See vSXML-tree-trans.scm for the validation code, which also
-; serves as usage examples.
-;
-; $Id: SXML-tree-trans.scm,v 1.6 2003/04/25 19:16:15 oleg Exp $
-
-
-; Output the 'fragments'
-; The fragments are a list of strings, characters,
-; numbers, thunks, #f, #t -- and other fragments.
-; The function traverses the tree depth-first, writes out
-; strings and characters, executes thunks, and ignores
-; #f and '().
-; The function returns #t if anything was written at all;
-; otherwise the result is #f
-; If #t occurs among the fragments, it is not written out
-; but causes the result of SRV:send-reply to be #t
-
-(define (SRV:send-reply . fragments)
- (let loop ((fragments fragments) (result #f))
- (cond
- ((null? fragments) result)
- ((not (car fragments)) (loop (cdr fragments) result))
- ((null? (car fragments)) (loop (cdr fragments) result))
- ((eq? #t (car fragments)) (loop (cdr fragments) #t))
- ((pair? (car fragments))
- (loop (cdr fragments) (loop (car fragments) result)))
- ((procedure? (car fragments))
- ((car fragments))
- (loop (cdr fragments) #t))
- (else
- (display (car fragments))
- (loop (cdr fragments) #t)))))
-
-
-
-;------------------------------------------------------------------------
-; Traversal of an SXML tree or a grove:
-; a <Node> or a <Nodelist>
-;
-; A <Node> and a <Nodelist> are mutually-recursive datatypes that
-; underlie the SXML tree:
-; <Node> ::= (name . <Nodelist>) | "text string"
-; An (ordered) set of nodes is just a list of the constituent nodes:
-; <Nodelist> ::= (<Node> ...)
-; Nodelists, and Nodes other than text strings are both lists. A
-; <Nodelist> however is either an empty list, or a list whose head is
-; not a symbol (an atom in general). A symbol at the head of a node is
-; either an XML name (in which case it's a tag of an XML element), or
-; an administrative name such as '@'.
-; See SXPath.scm and SSAX.scm for more information on SXML.
-
-
-; Pre-Post-order traversal of a tree and creation of a new tree:
-; pre-post-order:: <tree> x <bindings> -> <new-tree>
-; where
-; <bindings> ::= (<binding> ...)
-; <binding> ::= (<trigger-symbol> *preorder* . <handler>) |
-; (<trigger-symbol> *macro* . <handler>) |
-; (<trigger-symbol> <new-bindings> . <handler>) |
-; (<trigger-symbol> . <handler>)
-; <trigger-symbol> ::= XMLname | *text* | *default*
-; <handler> :: <trigger-symbol> x [<tree>] -> <new-tree>
-;
-; The pre-post-order function visits the nodes and nodelists
-; pre-post-order (depth-first). For each <Node> of the form (name
-; <Node> ...) it looks up an association with the given 'name' among
-; its <bindings>. If failed, pre-post-order tries to locate a
-; *default* binding. It's an error if the latter attempt fails as
-; well. Having found a binding, the pre-post-order function first
-; checks to see if the binding is of the form
-; (<trigger-symbol> *preorder* . <handler>)
-; If it is, the handler is 'applied' to the current node. Otherwise,
-; the pre-post-order function first calls itself recursively for each
-; child of the current node, with <new-bindings> prepended to the
-; <bindings> in effect. The result of these calls is passed to the
-; <handler> (along with the head of the current <Node>). To be more
-; precise, the handler is _applied_ to the head of the current node
-; and its processed children. The result of the handler, which should
-; also be a <tree>, replaces the current <Node>. If the current <Node>
-; is a text string or other atom, a special binding with a symbol
-; *text* is looked up.
-;
-; A binding can also be of a form
-; (<trigger-symbol> *macro* . <handler>)
-; This is equivalent to *preorder* described above. However, the result
-; is re-processed again, with the current stylesheet.
-
-(define (pre-post-order tree bindings)
- (let* ((default-binding (assq '*default* bindings))
- (text-binding (or (assq '*text* bindings) default-binding))
- (text-handler ; Cache default and text bindings
- (and text-binding
- (if (procedure? (cdr text-binding))
- (cdr text-binding) (cddr text-binding)))))
- (let loop ((tree tree))
- (cond
- ((null? tree) '())
- ((not (pair? tree))
- (let ((trigger '*text*))
- (if text-handler (text-handler trigger tree)
- (error "Unknown binding for " trigger " and no default"))))
- ((not (symbol? (car tree))) (map loop tree)) ; tree is a nodelist
- (else ; tree is an SXML node
- (let* ((trigger (car tree))
- (binding (or (assq trigger bindings) default-binding)))
- (cond
- ((not binding)
- (error "Unknown binding for " trigger " and no default"))
- ((not (pair? (cdr binding))) ; must be a procedure: handler
- (apply (cdr binding) trigger (map loop (cdr tree))))
- ((eq? '*preorder* (cadr binding))
- (apply (cddr binding) tree))
- ((eq? '*macro* (cadr binding))
- (loop (apply (cddr binding) tree)))
- (else ; (cadr binding) is a local binding
- (apply (cddr binding) trigger
- (pre-post-order (cdr tree) (append (cadr binding) bindings)))
- ))))))))
-
-; post-order is a strict subset of pre-post-order without *preorder*
-; (let alone *macro*) traversals.
-; Now pre-post-order is actually faster than the old post-order.
-; The function post-order is deprecated and is aliased below for
-; backward compatibility.
-(define post-order pre-post-order)
-
-;------------------------------------------------------------------------
-; Extended tree fold
-; tree = atom | (node-name tree ...)
-;
-; foldts fdown fup fhere seed (Leaf str) = fhere seed str
-; foldts fdown fup fhere seed (Nd kids) =
-; fup seed $ foldl (foldts fdown fup fhere) (fdown seed) kids
-
-; procedure fhere: seed -> atom -> seed
-; procedure fdown: seed -> node -> seed
-; procedure fup: parent-seed -> last-kid-seed -> node -> seed
-; foldts returns the final seed
-
-(define (foldts fdown fup fhere seed tree)
- (cond
- ((null? tree) seed)
- ((not (pair? tree)) ; An atom
- (fhere seed tree))
- (else
- (let loop ((kid-seed (fdown seed tree)) (kids (cdr tree)))
- (if (null? kids)
- (fup seed kid-seed tree)
- (loop (foldts fdown fup fhere kid-seed (car kids))
- (cdr kids)))))))
-
-;------------------------------------------------------------------------
-; Traverse a forest depth-first and cut/replace ranges of nodes.
-;
-; The nodes that define a range don't have to have the same immediate
-; parent, don't have to be on the same level, and the end node of a
-; range doesn't even have to exist. A replace-range procedure removes
-; nodes from the beginning node of the range up to (but not including)
-; the end node of the range. In addition, the beginning node of the
-; range can be replaced by a node or a list of nodes. The range of
-; nodes is cut while depth-first traversing the forest. If all
-; branches of the node are cut a node is cut as well. The procedure
-; can cut several non-overlapping ranges from a forest.
-
-; replace-range:: BEG-PRED x END-PRED x FOREST -> FOREST
-; where
-; type FOREST = (NODE ...)
-; type NODE = Atom | (Name . FOREST) | FOREST
-;
-; The range of nodes is specified by two predicates, beg-pred and end-pred.
-; beg-pred:: NODE -> #f | FOREST
-; end-pred:: NODE -> #f | FOREST
-; The beg-pred predicate decides on the beginning of the range. The node
-; for which the predicate yields non-#f marks the beginning of the range
-; The non-#f value of the predicate replaces the node. The value can be a
-; list of nodes. The replace-range procedure then traverses the tree and skips
-; all the nodes, until the end-pred yields non-#f. The value of the end-pred
-; replaces the end-range node. The new end node and its brothers will be
-; re-scanned.
-; The predicates are evaluated pre-order. We do not descend into a node that
-; is marked as the beginning of the range.
-
-(define (replace-range beg-pred end-pred forest)
-
- ; loop forest keep? new-forest
- ; forest is the forest to traverse
- ; new-forest accumulates the nodes we will keep, in the reverse
- ; order
- ; If keep? is #t, keep the curr node if atomic. If the node is not atomic,
- ; traverse its children and keep those that are not in the skip range.
- ; If keep? is #f, skip the current node if atomic. Otherwise,
- ; traverse its children. If all children are skipped, skip the node
- ; as well.
-
- (define (loop forest keep? new-forest)
- (if (null? forest) (values (reverse new-forest) keep?)
- (let ((node (car forest)))
- (if keep?
- (cond ; accumulate mode
- ((beg-pred node) => ; see if the node starts the skip range
- (lambda (repl-branches) ; if so, skip/replace the node
- (loop (cdr forest) #f
- (append (reverse repl-branches) new-forest))))
- ((not (pair? node)) ; it's an atom, keep it
- (loop (cdr forest) keep? (cons node new-forest)))
- (else
- (let*-values
- (((node?) (symbol? (car node))) ; or is it a nodelist?
- ((new-kids keep?) ; traverse its children
- (loop (if node? (cdr node) node) #t '())))
- (loop (cdr forest) keep?
- (cons
- (if node? (cons (car node) new-kids) new-kids)
- new-forest)))))
- ; skip mode
- (cond
- ((end-pred node) => ; end the skip range
- (lambda (repl-branches) ; repl-branches will be re-scanned
- (loop (append repl-branches (cdr forest)) #t
- new-forest)))
- ((not (pair? node)) ; it's an atom, skip it
- (loop (cdr forest) keep? new-forest))
- (else
- (let*-values
- (((node?) (symbol? (car node))) ; or is it a nodelist?
- ((new-kids keep?) ; traverse its children
- (loop (if node? (cdr node) node) #f '())))
- (loop (cdr forest) keep?
- (if (or keep? (pair? new-kids))
- (cons
- (if node? (cons (car node) new-kids) new-kids)
- new-forest)
- new-forest) ; if all kids are skipped
- )))))))) ; skip the node too
-
- (let*-values (((new-forest keep?) (loop forest #t '())))
- new-forest))
-
-; XML processing in Scheme
-; SXPath -- SXML Query Language
-;
-; SXPath is a query language for SXML, an instance of XML Information
-; set (Infoset) in the form of s-expressions. See SSAX.scm for the
-; definition of SXML and more details. SXPath is also a translation into
-; Scheme of an XML Path Language, XPath:
-; http://www.w3.org/TR/xpath
-; XPath and SXPath describe means of selecting a set of Infoset's items
-; or their properties.
-;
-; To facilitate queries, XPath maps the XML Infoset into an explicit
-; tree, and introduces important notions of a location path and a
-; current, context node. A location path denotes a selection of a set of
-; nodes relative to a context node. Any XPath tree has a distinguished,
-; root node -- which serves as the context node for absolute location
-; paths. Location path is recursively defined as a location step joined
-; with a location path. A location step is a simple query of the
-; database relative to a context node. A step may include expressions
-; that further filter the selected set. Each node in the resulting set
-; is used as a context node for the adjoining location path. The result
-; of the step is a union of the sets returned by the latter location
-; paths.
-;
-; The SXML representation of the XML Infoset (see SSAX.scm) is rather
-; suitable for querying as it is. Bowing to the XPath specification,
-; we will refer to SXML information items as 'Nodes':
-; <Node> ::= <Element> | <attributes-coll> | <attrib>
-; | "text string" | <PI>
-; This production can also be described as
-; <Node> ::= (name . <Nodeset>) | "text string"
-; An (ordered) set of nodes is just a list of the constituent nodes:
-; <Nodeset> ::= (<Node> ...)
-; Nodesets, and Nodes other than text strings are both lists. A
-; <Nodeset> however is either an empty list, or a list whose head is not
-; a symbol. A symbol at the head of a node is either an XML name (in
-; which case it's a tag of an XML element), or an administrative name
-; such as '@'. This uniform list representation makes processing rather
-; simple and elegant, while avoiding confusion. The multi-branch tree
-; structure formed by the mutually-recursive datatypes <Node> and
-; <Nodeset> lends itself well to processing by functional languages.
-;
-; A location path is in fact a composite query over an XPath tree or
-; its branch. A singe step is a combination of a projection, selection
-; or a transitive closure. Multiple steps are combined via join and
-; union operations. This insight allows us to _elegantly_ implement
-; XPath as a sequence of projection and filtering primitives --
-; converters -- joined by _combinators_. Each converter takes a node
-; and returns a nodeset which is the result of the corresponding query
-; relative to that node. A converter can also be called on a set of
-; nodes. In that case it returns a union of the corresponding queries over
-; each node in the set. The union is easily implemented as a list
-; append operation as all nodes in a SXML tree are considered
-; distinct, by XPath conventions. We also preserve the order of the
-; members in the union. Query combinators are high-order functions:
-; they take converter(s) (which is a Node|Nodeset -> Nodeset function)
-; and compose or otherwise combine them. We will be concerned with
-; only relative location paths [XPath]: an absolute location path is a
-; relative path applied to the root node.
-;
-; Similarly to XPath, SXPath defines full and abbreviated notations
-; for location paths. In both cases, the abbreviated notation can be
-; mechanically expanded into the full form by simple rewriting
-; rules. In case of SXPath the corresponding rules are given as
-; comments to a sxpath function, below. The regression test suite at
-; the end of this file shows a representative sample of SXPaths in
-; both notations, juxtaposed with the corresponding XPath
-; expressions. Most of the samples are borrowed literally from the
-; XPath specification, while the others are adjusted for our running
-; example, tree1.
-;
-; To do:
-; Rename filter to node-filter or ns-filter
-; Use ;=== for chapters, ;--- for sections, and ;^^^ for end sections
-;
-; $Id: SXPath-old.scm,v 1.4 2004/07/07 16:02:31 sperber Exp $
-
-
- ; See http://pobox.com/~oleg/ftp/Scheme/myenv.scm
- ; See http://pobox.com/~oleg/ftp/Scheme/myenv-scm.scm
- ; See http://pobox.com/~oleg/ftp/Scheme/myenv-bigloo.scm
-;(module SXPath
-; (include "myenv-bigloo.scm")) ; For use with Bigloo 2.2b
-;(load "myenv-scm.scm") ; For use with SCM v5d2
-;(include "myenv.scm") ; For use with Gambit-C 3.0
-
-
-
-(define (nodeset? x)
- (or (and (pair? x) (not (symbol? (car x)))) (null? x)))
-
-;-------------------------
-; Basic converters and applicators
-; A converter is a function
-; type Converter = Node|Nodeset -> Nodeset
-; A converter can also play a role of a predicate: in that case, if a
-; converter, applied to a node or a nodeset, yields a non-empty
-; nodeset, the converter-predicate is deemed satisfied. Throughout
-; this file a nil nodeset is equivalent to #f in denoting a failure.
-
-; The following function implements a 'Node test' as defined in
-; Sec. 2.3 of XPath document. A node test is one of the components of a
-; location step. It is also a converter-predicate in SXPath.
-;
-; The function node-typeof? takes a type criterion and returns a function,
-; which, when applied to a node, will tell if the node satisfies
-; the test.
-; node-typeof? :: Crit -> Node -> Boolean
-;
-; The criterion 'crit' is a symbol, one of the following:
-; id - tests if the Node has the right name (id)
-; @ - tests if the Node is an <attributes-coll>
-; * - tests if the Node is an <Element>
-; *text* - tests if the Node is a text node
-; *PI* - tests if the Node is a PI node
-; *any* - #t for any type of Node
-
-(define (node-typeof? crit)
- (lambda (node)
- (case crit
- ((*) (and (pair? node) (not (memq (car node) '(@ *PI*)))))
- ((*any*) #t)
- ((*text*) (string? node))
- (else
- (and (pair? node) (eq? crit (car node))))
-)))
-
-
-; Curried equivalence converter-predicates
-(define (node-eq? other)
- (lambda (node)
- (eq? other node)))
-
-(define (node-equal? other)
- (lambda (node)
- (equal? other node)))
-
-; node-pos:: N -> Nodeset -> Nodeset, or
-; node-pos:: N -> Converter
-; Select the N'th element of a Nodeset and return as a singular Nodeset;
-; Return an empty nodeset if the Nth element does not exist.
-; ((node-pos 1) Nodeset) selects the node at the head of the Nodeset,
-; if exists; ((node-pos 2) Nodeset) selects the Node after that, if
-; exists.
-; N can also be a negative number: in that case the node is picked from
-; the tail of the list.
-; ((node-pos -1) Nodeset) selects the last node of a non-empty nodeset;
-; ((node-pos -2) Nodeset) selects the last but one node, if exists.
-
-(define (node-pos n)
- (lambda (nodeset)
- (cond
- ((not (nodeset? nodeset)) '())
- ((null? nodeset) nodeset)
- ((eqv? n 1) (list (car nodeset)))
- ((negative? n) ((node-pos (+ n 1 (length nodeset))) nodeset))
- (else
- (assert (positive? n))
- ((node-pos (dec n)) (cdr nodeset))))))
-
-; filter:: Converter -> Converter
-; A filter applicator, which introduces a filtering context. The argument
-; converter is considered a predicate, with either #f or nil result meaning
-; failure.
-(define (filter pred?)
- (lambda (lst) ; a nodeset or a node (will be converted to a singleton nset)
- (let loop ((lst (if (nodeset? lst) lst (list lst))) (res '()))
- (if (null? lst)
- (reverse res)
- (let ((pred-result (pred? (car lst))))
- (loop (cdr lst)
- (if (and pred-result (not (null? pred-result)))
- (cons (car lst) res)
- res)))))))
-
-; take-until:: Converter -> Converter, or
-; take-until:: Pred -> Node|Nodeset -> Nodeset
-; Given a converter-predicate and a nodeset, apply the predicate to
-; each element of the nodeset, until the predicate yields anything but #f or
-; nil. Return the elements of the input nodeset that have been processed
-; till that moment (that is, which fail the predicate).
-; take-until is a variation of the filter above: take-until passes
-; elements of an ordered input set till (but not including) the first
-; element that satisfies the predicate.
-; The nodeset returned by ((take-until (not pred)) nset) is a subset --
-; to be more precise, a prefix -- of the nodeset returned by
-; ((filter pred) nset)
-
-(define (take-until pred?)
- (lambda (lst) ; a nodeset or a node (will be converted to a singleton nset)
- (let loop ((lst (if (nodeset? lst) lst (list lst))))
- (if (null? lst) lst
- (let ((pred-result (pred? (car lst))))
- (if (and pred-result (not (null? pred-result)))
- '()
- (cons (car lst) (loop (cdr lst)))))
- ))))
-
-
-; take-after:: Converter -> Converter, or
-; take-after:: Pred -> Node|Nodeset -> Nodeset
-; Given a converter-predicate and a nodeset, apply the predicate to
-; each element of the nodeset, until the predicate yields anything but #f or
-; nil. Return the elements of the input nodeset that have not been processed:
-; that is, return the elements of the input nodeset that follow the first
-; element that satisfied the predicate.
-; take-after along with take-until partition an input nodeset into three
-; parts: the first element that satisfies a predicate, all preceding
-; elements and all following elements.
-
-(define (take-after pred?)
- (lambda (lst) ; a nodeset or a node (will be converted to a singleton nset)
- (let loop ((lst (if (nodeset? lst) lst (list lst))))
- (if (null? lst) lst
- (let ((pred-result (pred? (car lst))))
- (if (and pred-result (not (null? pred-result)))
- (cdr lst)
- (loop (cdr lst))))
- ))))
-
-; Apply proc to each element of lst and return the list of results.
-; if proc returns a nodeset, splice it into the result
-;
-; From another point of view, map-union is a function Converter->Converter,
-; which places an argument-converter in a joining context.
-
-(define (map-union proc lst)
- (if (null? lst) lst
- (let ((proc-res (proc (car lst))))
- ((if (nodeset? proc-res) append cons)
- proc-res (map-union proc (cdr lst))))))
-
-; node-reverse :: Converter, or
-; node-reverse:: Node|Nodeset -> Nodeset
-; Reverses the order of nodes in the nodeset
-; This basic converter is needed to implement a reverse document order
-; (see the XPath Recommendation).
-(define node-reverse
- (lambda (node-or-nodeset)
- (if (not (nodeset? node-or-nodeset)) (list node-or-nodeset)
- (reverse node-or-nodeset))))
-
-; node-trace:: String -> Converter
-; (node-trace title) is an identity converter. In addition it prints out
-; a node or nodeset it is applied to, prefixed with the 'title'.
-; This converter is very useful for debugging.
-
-(define (node-trace title)
- (lambda (node-or-nodeset)
- (cout nl "-->")
- (display title)
- (display " :")
- (pretty-print node-or-nodeset)
- node-or-nodeset))
-
-
-;-------------------------
-; Converter combinators
-;
-; Combinators are higher-order functions that transmogrify a converter
-; or glue a sequence of converters into a single, non-trivial
-; converter. The goal is to arrive at converters that correspond to
-; XPath location paths.
-;
-; From a different point of view, a combinator is a fixed, named
-; _pattern_ of applying converters. Given below is a complete set of
-; such patterns that together implement XPath location path
-; specification. As it turns out, all these combinators can be built
-; from a small number of basic blocks: regular functional composition,
-; map-union and filter applicators, and the nodeset union.
-
-
-
-; select-kids:: Pred -> Node -> Nodeset
-; Given a Node, return an (ordered) subset its children that satisfy
-; the Pred (a converter, actually)
-; select-kids:: Pred -> Nodeset -> Nodeset
-; The same as above, but select among children of all the nodes in
-; the Nodeset
-;
-; More succinctly, the signature of this function is
-; select-kids:: Converter -> Converter
-
-(define (select-kids test-pred?)
- (lambda (node) ; node or node-set
- (cond
- ((null? node) node)
- ((not (pair? node)) '()) ; No children
- ((symbol? (car node))
- ((filter test-pred?) (cdr node))) ; it's a single node
- (else (map-union (select-kids test-pred?) node)))))
-
-
-; node-self:: Pred -> Node -> Nodeset, or
-; node-self:: Converter -> Converter
-; Similar to select-kids but apply to the Node itself rather
-; than to its children. The resulting Nodeset will contain either one
-; component, or will be empty (if the Node failed the Pred).
-(define node-self filter)
-
-
-; node-join:: [LocPath] -> Node|Nodeset -> Nodeset, or
-; node-join:: [Converter] -> Converter
-; join the sequence of location steps or paths as described
-; in the title comments above.
-(define (node-join . selectors)
- (lambda (nodeset) ; Nodeset or node
- (let loop ((nodeset nodeset) (selectors selectors))
- (if (null? selectors) nodeset
- (loop
- (if (nodeset? nodeset)
- (map-union (car selectors) nodeset)
- ((car selectors) nodeset))
- (cdr selectors))))))
-
-
-; node-reduce:: [LocPath] -> Node|Nodeset -> Nodeset, or
-; node-reduce:: [Converter] -> Converter
-; A regular functional composition of converters.
-; From a different point of view,
-; ((apply node-reduce converters) nodeset)
-; is equivalent to
-; (foldl apply nodeset converters)
-; i.e., folding, or reducing, a list of converters with the nodeset
-; as a seed.
-(define (node-reduce . converters)
- (lambda (nodeset) ; Nodeset or node
- (let loop ((nodeset nodeset) (converters converters))
- (if (null? converters) nodeset
- (loop ((car converters) nodeset) (cdr converters))))))
-
-
-; node-or:: [Converter] -> Converter
-; This combinator applies all converters to a given node and
-; produces the union of their results.
-; This combinator corresponds to a union, '|' operation for XPath
-; location paths.
-; (define (node-or . converters)
-; (lambda (node-or-nodeset)
-; (if (null? converters) node-or-nodeset
-; (append
-; ((car converters) node-or-nodeset)
-; ((apply node-or (cdr converters)) node-or-nodeset)))))
-; More optimal implementation follows
-(define (node-or . converters)
- (lambda (node-or-nodeset)
- (let loop ((result '()) (converters converters))
- (if (null? converters) result
- (loop (append result (or ((car converters) node-or-nodeset) '()))
- (cdr converters))))))
-
-
-; node-closure:: Converter -> Converter
-; Select all _descendants_ of a node that satisfy a converter-predicate.
-; This combinator is similar to select-kids but applies to
-; grand... children as well.
-; This combinator implements the "descendant::" XPath axis
-; Conceptually, this combinator can be expressed as
-; (define (node-closure f)
-; (node-or
-; (select-kids f)
-; (node-reduce (select-kids (node-typeof? '*)) (node-closure f))))
-; This definition, as written, looks somewhat like a fixpoint, and it
-; will run forever. It is obvious however that sooner or later
-; (select-kids (node-typeof? '*)) will return an empty nodeset. At
-; this point further iterations will no longer affect the result and
-; can be stopped.
-
-(define (node-closure test-pred?)
- (lambda (node) ; Nodeset or node
- (let loop ((parent node) (result '()))
- (if (null? parent) result
- (loop ((select-kids (node-typeof? '*)) parent)
- (append result
- ((select-kids test-pred?) parent)))
- ))))
-
-; node-parent:: RootNode -> Converter
-; (node-parent rootnode) yields a converter that returns a parent of a
-; node it is applied to. If applied to a nodeset, it returns the list
-; of parents of nodes in the nodeset. The rootnode does not have
-; to be the root node of the whole SXML tree -- it may be a root node
-; of a branch of interest.
-; Given the notation of Philip Wadler's paper on semantics of XSLT,
-; parent(x) = { y | y=subnode*(root), x=subnode(y) }
-; Therefore, node-parent is not the fundamental converter: it can be
-; expressed through the existing ones. Yet node-parent is a rather
-; convenient converter. It corresponds to a parent:: axis of SXPath.
-; Note that the parent:: axis can be used with an attribute node as well!
-
-(define (node-parent rootnode)
- (lambda (node) ; Nodeset or node
- (if (nodeset? node) (map-union (node-parent rootnode) node)
- (let ((pred
- (node-or
- (node-reduce
- (node-self (node-typeof? '*))
- (select-kids (node-eq? node)))
- (node-join
- (select-kids (node-typeof? '@))
- (select-kids (node-eq? node))))))
- ((node-or
- (node-self pred)
- (node-closure pred))
- rootnode)))))
-
-;-------------------------
-; Evaluate an abbreviated SXPath
-; sxpath:: AbbrPath -> Converter, or
-; sxpath:: AbbrPath -> Node|Nodeset -> Nodeset
-; AbbrPath is a list. It is translated to the full SXPath according
-; to the following rewriting rules
-; (sxpath '()) -> (node-join)
-; (sxpath '(path-component ...)) ->
-; (node-join (sxpath1 path-component) (sxpath '(...)))
-; (sxpath1 '//) -> (node-or
-; (node-self (node-typeof? '*any*))
-; (node-closure (node-typeof? '*any*)))
-; (sxpath1 '(equal? x)) -> (select-kids (node-equal? x))
-; (sxpath1 '(eq? x)) -> (select-kids (node-eq? x))
-; (sxpath1 ?symbol) -> (select-kids (node-typeof? ?symbol)
-; (sxpath1 procedure) -> procedure
-; (sxpath1 '(?symbol ...)) -> (sxpath1 '((?symbol) ...))
-; (sxpath1 '(path reducer ...)) ->
-; (node-reduce (sxpath path) (sxpathr reducer) ...)
-; (sxpathr number) -> (node-pos number)
-; (sxpathr path-filter) -> (filter (sxpath path-filter))
-
-(define (sxpath path)
- (lambda (nodeset)
- (let loop ((nodeset nodeset) (path path))
- (cond
- ((null? path) nodeset)
- ((nodeset? nodeset)
- (map-union (sxpath path) nodeset))
- ((procedure? (car path))
- (loop ((car path) nodeset) (cdr path)))
- ((eq? '// (car path))
- (loop
- ((if (nodeset? nodeset) append cons) nodeset
- ((node-closure (node-typeof? '*any*)) nodeset))
- (cdr path)))
- ((symbol? (car path))
- (loop ((select-kids (node-typeof? (car path))) nodeset)
- (cdr path)))
- ((and (pair? (car path)) (eq? 'equal? (caar path)))
- (loop ((select-kids (apply node-equal? (cdar path))) nodeset)
- (cdr path)))
- ((and (pair? (car path)) (eq? 'eq? (caar path)))
- (loop ((select-kids (apply node-eq? (cdar path))) nodeset)
- (cdr path)))
- ((pair? (car path))
- (let reducer ((nodeset
- (if (symbol? (caar path))
- ((select-kids (node-typeof? (caar path))) nodeset)
- (loop nodeset (caar path))))
- (reducing-path (cdar path)))
- (cond
- ((null? reducing-path) (loop nodeset (cdr path)))
- ((number? (car reducing-path))
- (reducer ((node-pos (car reducing-path)) nodeset)
- (cdr reducing-path)))
- (else
- (reducer ((filter (sxpath (car reducing-path))) nodeset)
- (cdr reducing-path))))))
- (else
- (error "Invalid path step: " (car path)))
-))))
-
-;------------------------------------------------------------------------
-; Sample XPath/SXPath expressions: regression test suite for the
-; implementation above.
-
-; A running example
-
-(define tree1
- '(html
- (head (title "Slides"))
- (body
- (p (@ (align "center"))
- (table (@ (style "font-size: x-large"))
- (tr
- (td (@ (align "right")) "Talks ")
- (td (@ (align "center")) " = ")
- (td " slides + transition"))
- (tr (td)
- (td (@ (align "center")) " = ")
- (td " data + control"))
- (tr (td)
- (td (@ (align "center")) " = ")
- (td " programs"))))
- (ul
- (li (a (@ (href "slides/slide0001.gif")) "Introduction"))
- (li (a (@ (href "slides/slide0010.gif")) "Summary")))
- )))
-
-
-; Example from a posting "Re: DrScheme and XML",
-; Shriram Krishnamurthi, comp.lang.scheme, Nov. 26. 1999.
-; http://www.deja.com/getdoc.xp?AN=553507805
-(define tree3
- '(poem (@ (title "The Lovesong of J. Alfred Prufrock")
- (poet "T. S. Eliot"))
- (stanza
- (line "Let us go then, you and I,")
- (line "When the evening is spread out against the sky")
- (line "Like a patient etherized upon a table:"))
- (stanza
- (line "In the room the women come and go")
- (line "Talking of Michaelangelo."))))
-
-; Validation Test harness
-
-(define-syntax run-test
- (syntax-rules (define)
- ((run-test "scan-exp" (define vars body))
- (define vars (run-test "scan-exp" body)))
- ((run-test "scan-exp" ?body)
- (letrec-syntax
- ((scan-exp ; (scan-exp body k)
- (syntax-rules (quote quasiquote !)
- ((scan-exp '() (k-head ! . args))
- (k-head '() . args))
- ((scan-exp (quote (hd . tl)) k)
- (scan-lit-lst (hd . tl) (do-wrap ! quasiquote k)))
- ((scan-exp (quasiquote (hd . tl)) k)
- (scan-lit-lst (hd . tl) (do-wrap ! quasiquote k)))
- ((scan-exp (quote x) (k-head ! . args))
- (k-head
- (if (string? (quote x)) (string->symbol (quote x)) (quote x))
- . args))
- ((scan-exp (hd . tl) k)
- (scan-exp hd (do-tl ! scan-exp tl k)))
- ((scan-exp x (k-head ! . args))
- (k-head x . args))))
- (do-tl
- (syntax-rules (!)
- ((do-tl processed-hd fn () (k-head ! . args))
- (k-head (processed-hd) . args))
- ((do-tl processed-hd fn old-tl k)
- (fn old-tl (do-cons ! processed-hd k)))))
- (do-cons
- (syntax-rules (!)
- ((do-cons processed-tl processed-hd (k-head ! . args))
- (k-head (processed-hd . processed-tl) . args))))
- (do-wrap
- (syntax-rules (!)
- ((do-wrap val fn (k-head ! . args))
- (k-head (fn val) . args))))
- (do-finish
- (syntax-rules ()
- ((do-finish new-body) new-body)))
-
- (scan-lit-lst ; scan literal list
- (syntax-rules (quote unquote unquote-splicing !)
- ((scan-lit-lst '() (k-head ! . args))
- (k-head '() . args))
- ((scan-lit-lst (quote (hd . tl)) k)
- (do-tl quote scan-lit-lst ((hd . tl)) k))
- ((scan-lit-lst (unquote x) k)
- (scan-exp x (do-wrap ! unquote k)))
- ((scan-lit-lst (unquote-splicing x) k)
- (scan-exp x (do-wrap ! unquote-splicing k)))
- ((scan-lit-lst (quote x) (k-head ! . args))
- (k-head
- ,(if (string? (quote x)) (string->symbol (quote x)) (quote x))
- . args))
- ((scan-lit-lst (hd . tl) k)
- (scan-lit-lst hd (do-tl ! scan-lit-lst tl k)))
- ((scan-lit-lst x (k-head ! . args))
- (k-head x . args))))
- )
- (scan-exp ?body (do-finish !))))
- ((run-test body ...)
- (begin
- (run-test "scan-exp" body) ...))
-))
-
-; Overwrite the above macro to switch the tests off
-; (define-macro (run-test selector node expected-result) #f)
-
-; Location path, full form: child::para
-; Location path, abbreviated form: para
-; selects the para element children of the context node
-
-(let ((tree
- '(elem (@) (para (@) "para") (br (@)) "cdata" (para (@) "second par"))
- )
- (expected '((para (@) "para") (para (@) "second par")))
- )
- (run-test (select-kids (node-typeof? 'para)) tree expected)
- (run-test (sxpath '(para)) tree expected)
-)
-
-; Location path, full form: child::*
-; Location path, abbreviated form: *
-; selects all element children of the context node
-
-(let ((tree
- '(elem (@) (para (@) "para") (br (@)) "cdata" (para "second par"))
- )
- (expected
- '((para (@) "para") (br (@)) (para "second par")))
- )
- (run-test (select-kids (node-typeof? '*)) tree expected)
- (run-test (sxpath '(*)) tree expected)
-)
-
-
-
-; Location path, full form: child::text()
-; Location path, abbreviated form: text()
-; selects all text node children of the context node
-(let ((tree
- '(elem (@) (para (@) "para") (br (@)) "cdata" (para "second par"))
- )
- (expected
- '("cdata"))
- )
- (run-test (select-kids (node-typeof? '*text*)) tree expected)
- (run-test (sxpath '(*text*)) tree expected)
-)
-
-
-; Location path, full form: child::node()
-; Location path, abbreviated form: node()
-; selects all the children of the context node, whatever their node type
-(let* ((tree
- '(elem (@) (para (@) "para") (br (@)) "cdata" (para "second par"))
- )
- (expected (cdr tree))
- )
- (run-test (select-kids (node-typeof? '*any*)) tree expected)
- (run-test (sxpath '(*any*)) tree expected)
-)
-
-; Location path, full form: child::*/child::para
-; Location path, abbreviated form: */para
-; selects all para grandchildren of the context node
-
-(let ((tree
- '(elem (@) (para (@) "para") (br (@)) "cdata" (para "second par")
- (div (@ (name "aa")) (para "third para")))
- )
- (expected
- '((para "third para")))
- )
- (run-test
- (node-join (select-kids (node-typeof? '*))
- (select-kids (node-typeof? 'para)))
- tree expected)
- (run-test (sxpath '(* para)) tree expected)
-)
-
-
-; Location path, full form: attribute::name
-; Location path, abbreviated form: @name
-; selects the 'name' attribute of the context node
-
-(let ((tree
- '(elem (@ (name "elem") (id "idz"))
- (para (@) "para") (br (@)) "cdata" (para (@) "second par")
- (div (@ (name "aa")) (para (@) "third para")))
- )
- (expected
- '((name "elem")))
- )
- (run-test
- (node-join (select-kids (node-typeof? '@))
- (select-kids (node-typeof? 'name)))
- tree expected)
- (run-test (sxpath '(@ name)) tree expected)
-)
-
-; Location path, full form: attribute::*
-; Location path, abbreviated form: @*
-; selects all the attributes of the context node
-(let ((tree
- '(elem (@ (name "elem") (id "idz"))
- (para (@) "para") (br (@)) "cdata" (para "second par")
- (div (@ (name "aa")) (para (@) "third para")))
- )
- (expected
- '((name "elem") (id "idz")))
- )
- (run-test
- (node-join (select-kids (node-typeof? '@))
- (select-kids (node-typeof? '*)))
- tree expected)
- (run-test (sxpath '(@ *)) tree expected)
-)
-
-
-; Location path, full form: descendant::para
-; Location path, abbreviated form: .//para
-; selects the para element descendants of the context node
-
-(let ((tree
- '(elem (@ (name "elem") (id "idz"))
- (para (@) "para") (br (@)) "cdata" (para "second par")
- (div (@ (name "aa")) (para (@) "third para")))
- )
- (expected
- '((para (@) "para") (para "second par") (para (@) "third para")))
- )
- (run-test
- (node-closure (node-typeof? 'para))
- tree expected)
- (run-test (sxpath '(// para)) tree expected)
-)
-
-; Location path, full form: self::para
-; Location path, abbreviated form: _none_
-; selects the context node if it is a para element; otherwise selects nothing
-
-(let ((tree
- '(elem (@ (name "elem") (id "idz"))
- (para (@) "para") (br (@)) "cdata" (para "second par")
- (div (@ (name "aa")) (para (@) "third para")))
- )
- )
- (run-test (node-self (node-typeof? 'para)) tree '())
- (run-test (node-self (node-typeof? 'elem)) tree (list tree))
-)
-
-; Location path, full form: descendant-or-self::node()
-; Location path, abbreviated form: //
-; selects the context node, all the children (including attribute nodes)
-; of the context node, and all the children of all the (element)
-; descendants of the context node.
-; This is _almost_ a powerset of the context node.
-(let* ((tree
- '(para (@ (name "elem") (id "idz"))
- (para (@) "para") (br (@)) "cdata" (para "second par")
- (div (@ (name "aa")) (para (@) "third para")))
- )
- (expected
- (cons tree
- (append (cdr tree)
- '((@) "para" (@) "second par"
- (@ (name "aa")) (para (@) "third para")
- (@) "third para"))))
- )
- (run-test
- (node-or
- (node-self (node-typeof? '*any*))
- (node-closure (node-typeof? '*any*)))
- tree expected)
- (run-test (sxpath '(//)) tree expected)
-)
-
-; Location path, full form: ancestor::div
-; Location path, abbreviated form: _none_
-; selects all div ancestors of the context node
-; This Location expression is equivalent to the following:
-; /descendant-or-self::div[descendant::node() = curr_node]
-; This shows that the ancestor:: axis is actually redundant. Still,
-; it can be emulated as the following SXPath expression demonstrates.
-
-; The insight behind "ancestor::div" -- selecting all "div" ancestors
-; of the current node -- is
-; S[ancestor::div] context_node =
-; { y | y=subnode*(root), context_node=subnode(subnode*(y)),
-; isElement(y), name(y) = "div" }
-; We observe that
-; { y | y=subnode*(root), pred(y) }
-; can be expressed in SXPath as
-; ((node-or (node-self pred) (node-closure pred)) root-node)
-; The composite predicate 'isElement(y) & name(y) = "div"' corresponds to
-; (node-self (node-typeof? 'div)) in SXPath. Finally, filter
-; context_node=subnode(subnode*(y)) is tantamount to
-; (node-closure (node-eq? context-node)), whereas node-reduce denotes the
-; the composition of converters-predicates in the filtering context.
-
-(let*
- ((root
- '(div (@ (name "elem") (id "idz"))
- (para (@) "para") (br (@)) "cdata" (para (@) "second par")
- (div (@ (name "aa")) (para (@) "third para"))))
- (context-node ; /descendant::any()[child::text() == "third para"]
- (car
- ((node-closure
- (select-kids
- (node-equal? "third para")))
- root)))
- (pred
- (node-reduce (node-self (node-typeof? 'div))
- (node-closure (node-eq? context-node))
- ))
- )
- (run-test
- (node-or
- (node-self pred)
- (node-closure pred))
- root
- (cons root
- '((div (@ (name "aa")) (para (@) "third para")))))
-)
-
-
-
-; Location path, full form: child::div/descendant::para
-; Location path, abbreviated form: div//para
-; selects the para element descendants of the div element
-; children of the context node
-
-(let ((tree
- '(elem (@ (name "elem") (id "idz"))
- (para (@) "para") (br (@)) "cdata" (para "second par")
- (div (@ (name "aa")) (para (@) "third para")
- (div (para "fourth para"))))
- )
- (expected
- '((para (@) "third para") (para "fourth para")))
- )
- (run-test
- (node-join
- (select-kids (node-typeof? 'div))
- (node-closure (node-typeof? 'para)))
- tree expected)
- (run-test (sxpath '(div // para)) tree expected)
-)
-
-
-; Location path, full form: /descendant::olist/child::item
-; Location path, abbreviated form: //olist/item
-; selects all the item elements that have an olist parent (which is not root)
-; and that are in the same document as the context node
-; See the following test.
-
-; Location path, full form: /descendant::td/attribute::align
-; Location path, abbreviated form: //td/@align
-; Selects 'align' attributes of all 'td' elements in tree1
-(let ((tree tree1)
- (expected
- '((align "right") (align "center") (align "center") (align "center"))
- ))
- (run-test
- (node-join
- (node-closure (node-typeof? 'td))
- (select-kids (node-typeof? '@))
- (select-kids (node-typeof? 'align)))
- tree expected)
- (run-test (sxpath '(// td @ align)) tree expected)
-)
-
-
-; Location path, full form: /descendant::td[attribute::align]
-; Location path, abbreviated form: //td[@align]
-; Selects all td elements that have an attribute 'align' in tree1
-(let ((tree tree1)
- (expected
- '((td (@ (align "right")) "Talks ") (td (@ (align "center")) " = ")
- (td (@ (align "center")) " = ") (td (@ (align "center")) " = "))
- ))
- (run-test
- (node-reduce
- (node-closure (node-typeof? 'td))
- (filter
- (node-join
- (select-kids (node-typeof? '@))
- (select-kids (node-typeof? 'align)))))
- tree expected)
- (run-test (sxpath `(// td ,(node-self (sxpath '(@ align))))) tree expected)
- (run-test (sxpath '(// (td (@ align)))) tree expected)
- (run-test (sxpath '(// ((td) (@ align)))) tree expected)
- ; note! (sxpath ...) is a converter. Therefore, it can be used
- ; as any other converter, for example, in the full-form SXPath.
- ; Thus we can mix the full and abbreviated form SXPath's freely.
- (run-test
- (node-reduce
- (node-closure (node-typeof? 'td))
- (filter
- (sxpath '(@ align))))
- tree expected)
-)
-
-
-; Location path, full form: /descendant::td[attribute::align = "right"]
-; Location path, abbreviated form: //td[@align = "right"]
-; Selects all td elements that have an attribute align = "right" in tree1
-(let ((tree tree1)
- (expected
- '((td (@ (align "right")) "Talks "))
- ))
- (run-test
- (node-reduce
- (node-closure (node-typeof? 'td))
- (filter
- (node-join
- (select-kids (node-typeof? '@))
- (select-kids (node-equal? '(align "right"))))))
- tree expected)
- (run-test (sxpath '(// (td (@ (equal? (align "right")))))) tree expected)
-)
-
-; Location path, full form: child::para[position()=1]
-; Location path, abbreviated form: para[1]
-; selects the first para child of the context node
-(let ((tree
- '(elem (@ (name "elem") (id "idz"))
- (para (@) "para") (br (@)) "cdata" (para "second par")
- (div (@ (name "aa")) (para (@) "third para")))
- )
- (expected
- '((para (@) "para"))
- ))
- (run-test
- (node-reduce
- (select-kids (node-typeof? 'para))
- (node-pos 1))
- tree expected)
- (run-test (sxpath '((para 1))) tree expected)
-)
-
-; Location path, full form: child::para[position()=last()]
-; Location path, abbreviated form: para[last()]
-; selects the last para child of the context node
-(let ((tree
- '(elem (@ (name "elem") (id "idz"))
- (para (@) "para") (br (@)) "cdata" (para "second par")
- (div (@ (name "aa")) (para (@) "third para")))
- )
- (expected
- '((para "second par"))
- ))
- (run-test
- (node-reduce
- (select-kids (node-typeof? 'para))
- (node-pos -1))
- tree expected)
- (run-test (sxpath '((para -1))) tree expected)
-)
-
-; Illustrating the following Note of Sec 2.5 of XPath:
-; "NOTE: The location path //para[1] does not mean the same as the
-; location path /descendant::para[1]. The latter selects the first
-; descendant para element; the former selects all descendant para
-; elements that are the first para children of their parents."
-
-(let ((tree
- '(elem (@ (name "elem") (id "idz"))
- (para (@) "para") (br (@)) "cdata" (para "second par")
- (div (@ (name "aa")) (para (@) "third para")))
- )
- )
- (run-test
- (node-reduce ; /descendant::para[1] in SXPath
- (node-closure (node-typeof? 'para))
- (node-pos 1))
- tree '((para (@) "para")))
- (run-test (sxpath '(// (para 1))) tree
- '((para (@) "para") (para (@) "third para")))
-)
-
-; Location path, full form: parent::node()
-; Location path, abbreviated form: ..
-; selects the parent of the context node. The context node may be
-; an attribute node!
-; For the last test:
-; Location path, full form: parent::*/attribute::name
-; Location path, abbreviated form: ../@name
-; Selects the name attribute of the parent of the context node
-
-(let* ((tree
- '(elem (@ (name "elem") (id "idz"))
- (para (@) "para") (br (@)) "cdata" (para "second par")
- (div (@ (name "aa")) (para (@) "third para")))
- )
- (para1 ; the first para node
- (car ((sxpath '(para)) tree)))
- (para3 ; the third para node
- (car ((sxpath '(div para)) tree)))
- (div ; div node
- (car ((sxpath '(// div)) tree)))
- )
- (run-test
- (node-parent tree)
- para1 (list tree))
- (run-test
- (node-parent tree)
- para3 (list div))
- (run-test ; checking the parent of an attribute node
- (node-parent tree)
- ((sxpath '(@ name)) div) (list div))
- (run-test
- (node-join
- (node-parent tree)
- (select-kids (node-typeof? '@))
- (select-kids (node-typeof? 'name)))
- para3 '((name "aa")))
- (run-test
- (sxpath `(,(node-parent tree) @ name))
- para3 '((name "aa")))
-)
-
-; Location path, full form: following-sibling::chapter[position()=1]
-; Location path, abbreviated form: none
-; selects the next chapter sibling of the context node
-; The path is equivalent to
-; let cnode = context-node
-; in
-; parent::* / child::chapter [take-after node_eq(self::*,cnode)]
-; [position()=1]
-(let* ((tree
- '(document
- (preface "preface")
- (chapter (@ (id "one")) "Chap 1 text")
- (chapter (@ (id "two")) "Chap 2 text")
- (chapter (@ (id "three")) "Chap 3 text")
- (chapter (@ (id "four")) "Chap 4 text")
- (epilogue "Epilogue text")
- (appendix (@ (id "A")) "App A text")
- (References "References"))
- )
- (a-node ; to be used as a context node
- (car ((sxpath '(// (chapter (@ (equal? (id "two")))))) tree)))
- (expected
- '((chapter (@ (id "three")) "Chap 3 text")))
- )
- (run-test
- (node-reduce
- (node-join
- (node-parent tree)
- (select-kids (node-typeof? 'chapter)))
- (take-after (node-eq? a-node))
- (node-pos 1)
- )
- a-node expected)
-)
-
-; preceding-sibling::chapter[position()=1]
-; selects the previous chapter sibling of the context node
-; The path is equivalent to
-; let cnode = context-node
-; in
-; parent::* / child::chapter [take-until node_eq(self::*,cnode)]
-; [position()=-1]
-(let* ((tree
- '(document
- (preface "preface")
- (chapter (@ (id "one")) "Chap 1 text")
- (chapter (@ (id "two")) "Chap 2 text")
- (chapter (@ (id "three")) "Chap 3 text")
- (chapter (@ (id "four")) "Chap 4 text")
- (epilogue "Epilogue text")
- (appendix (@ (id "A")) "App A text")
- (References "References"))
- )
- (a-node ; to be used as a context node
- (car ((sxpath '(// (chapter (@ (equal? (id "three")))))) tree)))
- (expected
- '((chapter (@ (id "two")) "Chap 2 text")))
- )
- (run-test
- (node-reduce
- (node-join
- (node-parent tree)
- (select-kids (node-typeof? 'chapter)))
- (take-until (node-eq? a-node))
- (node-pos -1)
- )
- a-node expected)
-)
-
-
-; /descendant::figure[position()=42]
-; selects the forty-second figure element in the document
-; See the next example, which is more general.
-
-; Location path, full form:
-; child::table/child::tr[position()=2]/child::td[position()=3]
-; Location path, abbreviated form: table/tr[2]/td[3]
-; selects the third td of the second tr of the table
-(let ((tree ((node-closure (node-typeof? 'p)) tree1))
- (expected
- '((td " data + control"))
- ))
- (run-test
- (node-join
- (select-kids (node-typeof? 'table))
- (node-reduce (select-kids (node-typeof? 'tr))
- (node-pos 2))
- (node-reduce (select-kids (node-typeof? 'td))
- (node-pos 3)))
- tree expected)
- (run-test (sxpath '(table (tr 2) (td 3))) tree expected)
-)
-
-
-; Location path, full form:
-; child::para[attribute::type='warning'][position()=5]
-; Location path, abbreviated form: para[@type='warning'][5]
-; selects the fifth para child of the context node that has a type
-; attribute with value warning
-(let ((tree
- '(chapter
- (para "para1")
- (para (@ (type "warning")) "para 2")
- (para (@ (type "warning")) "para 3")
- (para (@ (type "warning")) "para 4")
- (para (@ (type "warning")) "para 5")
- (para (@ (type "warning")) "para 6"))
- )
- (expected
- '((para (@ (type "warning")) "para 6"))
- ))
- (run-test
- (node-reduce
- (select-kids (node-typeof? 'para))
- (filter
- (node-join
- (select-kids (node-typeof? '@))
- (select-kids (node-equal? '(type "warning")))))
- (node-pos 5))
- tree expected)
- (run-test (sxpath '( (((para (@ (equal? (type "warning"))))) 5 ) ))
- tree expected)
- (run-test (sxpath '( (para (@ (equal? (type "warning"))) 5 ) ))
- tree expected)
-)
-
-
-; Location path, full form:
-; child::para[position()=5][attribute::type='warning']
-; Location path, abbreviated form: para[5][@type='warning']
-; selects the fifth para child of the context node if that child has a 'type'
-; attribute with value warning
-(let ((tree
- '(chapter
- (para "para1")
- (para (@ (type "warning")) "para 2")
- (para (@ (type "warning")) "para 3")
- (para (@ (type "warning")) "para 4")
- (para (@ (type "warning")) "para 5")
- (para (@ (type "warning")) "para 6"))
- )
- (expected
- '((para (@ (type "warning")) "para 5"))
- ))
- (run-test
- (node-reduce
- (select-kids (node-typeof? 'para))
- (node-pos 5)
- (filter
- (node-join
- (select-kids (node-typeof? '@))
- (select-kids (node-equal? '(type "warning"))))))
- tree expected)
- (run-test (sxpath '( (( (para 5)) (@ (equal? (type "warning"))))))
- tree expected)
- (run-test (sxpath '( (para 5 (@ (equal? (type "warning")))) ))
- tree expected)
-)
-
-; Location path, full form:
-; child::*[self::chapter or self::appendix]
-; Location path, semi-abbreviated form: *[self::chapter or self::appendix]
-; selects the chapter and appendix children of the context node
-(let ((tree
- '(document
- (preface "preface")
- (chapter (@ (id "one")) "Chap 1 text")
- (chapter (@ (id "two")) "Chap 2 text")
- (chapter (@ (id "three")) "Chap 3 text")
- (epilogue "Epilogue text")
- (appendix (@ (id "A")) "App A text")
- (References "References"))
- )
- (expected
- '((chapter (@ (id "one")) "Chap 1 text")
- (chapter (@ (id "two")) "Chap 2 text")
- (chapter (@ (id "three")) "Chap 3 text")
- (appendix (@ (id "A")) "App A text"))
- ))
- (run-test
- (node-join
- (select-kids (node-typeof? '*))
- (filter
- (node-or
- (node-self (node-typeof? 'chapter))
- (node-self (node-typeof? 'appendix)))))
- tree expected)
- (run-test (sxpath `(* ,(node-or (node-self (node-typeof? 'chapter))
- (node-self (node-typeof? 'appendix)))))
- tree expected)
-)
-
-
-; Location path, full form: child::chapter[child::title='Introduction']
-; Location path, abbreviated form: chapter[title = 'Introduction']
-; selects the chapter children of the context node that have one or more
-; title children with string-value equal to Introduction
-; See a similar example: //td[@align = "right"] above.
-
-; Location path, full form: child::chapter[child::title]
-; Location path, abbreviated form: chapter[title]
-; selects the chapter children of the context node that have one or
-; more title children
-; See a similar example //td[@align] above.
-
-(cerr nl "Example with tree3: extracting the first lines of every stanza" nl)
-(let ((tree tree3)
- (expected
- '("Let us go then, you and I," "In the room the women come and go")
- ))
- (run-test
- (node-join
- (node-closure (node-typeof? 'stanza))
- (node-reduce
- (select-kids (node-typeof? 'line)) (node-pos 1))
- (select-kids (node-typeof? '*text*)))
- tree expected)
- (run-test (sxpath '(// stanza (line 1) *text*)) tree expected)
-)
-
-;
-; syntax: assert ?expr ?expr ... [report: ?r-exp ?r-exp ...]
-;
-; If (and ?expr ?expr ...) evaluates to anything but #f, the result
-; is the value of that expression.
-; If (and ?expr ?expr ...) evaluates to #f, an error is reported.
-; The error message will show the failed expressions, as well
-; as the values of selected variables (or expressions, in general).
-; The user may explicitly specify the expressions whose
-; values are to be printed upon assertion failure -- as ?r-exp that
-; follow the identifier 'report:'
-; Typically, ?r-exp is either a variable or a string constant.
-; If the user specified no ?r-exp, the values of variables that are
-; referenced in ?expr will be printed upon the assertion failure.
-
-(define-syntax assert
- (syntax-rules (report\:)
- ((assert "doit" (expr ...) (r-exp ...))
- (cond
- ((and expr ...) => (lambda (x) x))
- (else
- (error "assertion failure: ~a" (list '(and expr ...) r-exp ...)))))
- ((assert "collect" (expr ...))
- (assert "doit" (expr ...) ()))
- ((assert "collect" (expr ...) report\: r-exp ...)
- (assert "doit" (expr ...) (r-exp ...)))
- ((assert "collect" (expr ...) expr1 stuff ...)
- (assert "collect" (expr ... expr1) stuff ...))
- ((assert stuff ...)
- (assert "collect" () stuff ...))))
-
-(define-syntax assure
- (syntax-rules ()
- ((assure exp error-msg)
- (assert exp report\: error-msg))));****************************************************************************
-; Simple Parsing of input
-;
-; The following simple functions surprisingly often suffice to parse
-; an input stream. They either skip, or build and return tokens,
-; according to inclusion or delimiting semantics. The list of
-; characters to expect, include, or to break at may vary from one
-; invocation of a function to another. This allows the functions to
-; easily parse even context-sensitive languages.
-;
-; EOF is generally frowned on, and thrown up upon if encountered.
-; Exceptions are mentioned specifically. The list of expected characters
-; (characters to skip until, or break-characters) may include an EOF
-; "character", which is to be coded as symbol *eof*
-;
-; The input stream to parse is specified as a PORT, which is usually
-; the last (and optional) argument. It defaults to the current input
-; port if omitted.
-;
-; IMPORT
-; This package relies on a function parser-error, which must be defined
-; by a user of the package. The function has the following signature:
-; parser-error PORT MESSAGE SPECIALISING-MSG*
-; Many procedures of this package call parser-error to report a parsing
-; error. The first argument is a port, which typically points to the
-; offending character or its neighborhood. Most of the Scheme systems
-; let the user query a PORT for the current position. MESSAGE is the
-; description of the error. Other arguments supply more details about
-; the problem.
-; myenv.scm, myenv-bigloo.scm or a similar prelude is assumed.
-; From SRFI-13, string-concatenate-reverse
-; If a particular implementation lacks SRFI-13 support, please
-; include the file srfi-13-local.scm
-;
-; $Id: input-parse.scm,v 1.7 2004/07/07 16:02:31 sperber Exp $
-
-;------------------------------------------------------------------------
-
-; -- procedure+: peek-next-char [PORT]
-; advances to the next character in the PORT and peeks at it.
-; This function is useful when parsing LR(1)-type languages
-; (one-char-read-ahead).
-; The optional argument PORT defaults to the current input port.
-
-(define-opt (peek-next-char (optional (port (current-input-port))))
- (read-char port)
- (peek-char port))
-
-
-;------------------------------------------------------------------------
-
-; -- procedure+: assert-curr-char CHAR-LIST STRING [PORT]
-; Reads a character from the PORT and looks it up
-; in the CHAR-LIST of expected characters
-; If the read character was found among expected, it is returned
-; Otherwise, the procedure writes a nasty message using STRING
-; as a comment, and quits.
-; The optional argument PORT defaults to the current input port.
-;
-(define-opt (assert-curr-char expected-chars comment
- (optional (port (current-input-port))))
- (let ((c (read-char port)))
- (if (memv c expected-chars) c
- (parser-error port "Wrong character " c
- " (0x" (if (eof-object? c) "*eof*"
- (number->string (char->integer c) 16)) ") "
- comment ". " expected-chars " expected"))))
-
-
-; -- procedure+: skip-until CHAR-LIST [PORT]
-; Reads and skips characters from the PORT until one of the break
-; characters is encountered. This break character is returned.
-; The break characters are specified as the CHAR-LIST. This list
-; may include EOF, which is to be coded as a symbol *eof*
-;
-; -- procedure+: skip-until NUMBER [PORT]
-; Skips the specified NUMBER of characters from the PORT and returns #f
-;
-; The optional argument PORT defaults to the current input port.
-
-
-(define-opt (skip-until arg (optional (port (current-input-port))) )
- (cond
- ((number? arg) ; skip 'arg' characters
- (do ((i arg (dec i)))
- ((not (positive? i)) #f)
- (if (eof-object? (read-char port))
- (parser-error port "Unexpected EOF while skipping "
- arg " characters"))))
- (else ; skip until break-chars (=arg)
- (let loop ((c (read-char port)))
- (cond
- ((memv c arg) c)
- ((eof-object? c)
- (if (memq '*eof* arg) c
- (parser-error port "Unexpected EOF while skipping until " arg)))
- (else (loop (read-char port))))))))
-
-
-; -- procedure+: skip-while CHAR-LIST [PORT]
-; Reads characters from the PORT and disregards them,
-; as long as they are mentioned in the CHAR-LIST.
-; The first character (which may be EOF) peeked from the stream
-; that is NOT a member of the CHAR-LIST is returned. This character
-; is left on the stream.
-; The optional argument PORT defaults to the current input port.
-
-(define-opt (skip-while skip-chars (optional (port (current-input-port))) )
- (do ((c (peek-char port) (peek-char port)))
- ((not (memv c skip-chars)) c)
- (read-char port)))
-
-; whitespace const
-
-;------------------------------------------------------------------------
-; Stream tokenizers
-
-
-; -- procedure+:
-; next-token PREFIX-CHAR-LIST BREAK-CHAR-LIST [COMMENT-STRING] [PORT]
-; skips any number of the prefix characters (members of the
-; PREFIX-CHAR-LIST), if any, and reads the sequence of characters
-; up to (but not including) a break character, one of the
-; BREAK-CHAR-LIST.
-; The string of characters thus read is returned.
-; The break character is left on the input stream
-; The list of break characters may include EOF, which is to be coded as
-; a symbol *eof*. Otherwise, EOF is fatal, generating an error message
-; including a specified COMMENT-STRING (if any)
-;
-; The optional argument PORT defaults to the current input port.
-;
-; Note: since we can't tell offhand how large the token being read is
-; going to be, we make a guess, pre-allocate a string, and grow it by
-; quanta if necessary. The quantum is always the length of the string
-; before it was extended the last time. Thus the algorithm does
-; a Fibonacci-type extension, which has been proven optimal.
-; Note, explicit port specification in read-char, peek-char helps.
-
-; Procedure: input-parse:init-buffer
-; returns an initial buffer for next-token* procedures.
-; The input-parse:init-buffer may allocate a new buffer per each invocation:
-; (define (input-parse:init-buffer) (make-string 32))
-; Size 32 turns out to be fairly good, on average.
-; That policy is good only when a Scheme system is multi-threaded with
-; preemptive scheduling, or when a Scheme system supports shared substrings.
-; In all the other cases, it's better for input-parse:init-buffer to
-; return the same static buffer. next-token* functions return a copy
-; (a substring) of accumulated data, so the same buffer can be reused.
-; We shouldn't worry about an incoming token being too large:
-; next-token will use another chunk automatically. Still,
-; the best size for the static buffer is to allow most of the tokens to fit in.
-; Using a static buffer _dramatically_ reduces the amount of produced garbage
-; (e.g., during XML parsing).
-
-(define input-parse:init-buffer
- (let ((buffer (make-string 512)))
- (lambda () buffer)))
-
-
- ; See a better version below
-(define-opt (next-token-old prefix-skipped-chars break-chars
- (optional (comment "") (port (current-input-port))) )
- (let* ((buffer (input-parse:init-buffer))
- (curr-buf-len (string-length buffer))
- (quantum curr-buf-len))
- (let loop ((i 0) (c (skip-while prefix-skipped-chars port)))
- (cond
- ((memv c break-chars) (substring buffer 0 i))
- ((eof-object? c)
- (if (memq '*eof* break-chars)
- (substring buffer 0 i) ; was EOF expected?
- (parser-error port "EOF while reading a token " comment)))
- (else
- (if (>= i curr-buf-len) ; make space for i-th char in buffer
- (begin ; -> grow the buffer by the quantum
- (set! buffer (string-append buffer (make-string quantum)))
- (set! quantum curr-buf-len)
- (set! curr-buf-len (string-length buffer))))
- (string-set! buffer i c)
- (read-char port) ; move to the next char
- (loop (inc i) (peek-char port))
- )))))
-
-
-; A better version of next-token, which accumulates the characters
-; in chunks, and later on reverse-concatenates them, using
-; SRFI-13 if available.
-; The overhead of copying characters is only 100% (or even smaller: bulk
-; string copying might be well-optimised), compared to the (hypothetical)
-; circumstance if we had known the size of the token beforehand.
-; For small tokens, the code performs just as above. For large
-; tokens, we expect an improvement. Note, the code also has no
-; assignments.
-; See next-token-comp.scm
-
-(define-opt (next-token prefix-skipped-chars break-chars
- (optional (comment "") (port (current-input-port))) )
- (let outer ((buffer (input-parse:init-buffer)) (filled-buffer-l '())
- (c (skip-while prefix-skipped-chars port)))
- (let ((curr-buf-len (string-length buffer)))
- (let loop ((i 0) (c c))
- (cond
- ((memv c break-chars)
- (if (null? filled-buffer-l) (substring buffer 0 i)
- (string-concatenate-reverse filled-buffer-l buffer i)))
- ((eof-object? c)
- (if (memq '*eof* break-chars) ; was EOF expected?
- (if (null? filled-buffer-l) (substring buffer 0 i)
- (string-concatenate-reverse filled-buffer-l buffer i))
- (parser-error port "EOF while reading a token " comment)))
- ((>= i curr-buf-len)
- (outer (make-string curr-buf-len)
- (cons buffer filled-buffer-l) c))
- (else
- (string-set! buffer i c)
- (read-char port) ; move to the next char
- (loop (inc i) (peek-char port))))))))
-
-; -- procedure+: next-token-of INC-CHARSET [PORT]
-; Reads characters from the PORT that belong to the list of characters
-; INC-CHARSET. The reading stops at the first character which is not
-; a member of the set. This character is left on the stream.
-; All the read characters are returned in a string.
-;
-; -- procedure+: next-token-of PRED [PORT]
-; Reads characters from the PORT for which PRED (a procedure of one
-; argument) returns non-#f. The reading stops at the first character
-; for which PRED returns #f. That character is left on the stream.
-; All the results of evaluating of PRED up to #f are returned in a
-; string.
-;
-; PRED is a procedure that takes one argument (a character
-; or the EOF object) and returns a character or #f. The returned
-; character does not have to be the same as the input argument
-; to the PRED. For example,
-; (next-token-of (lambda (c)
-; (cond ((eof-object? c) #f)
-; ((char-alphabetic? c) (char-downcase c))
-; (else #f))))
-; will try to read an alphabetic token from the current
-; input port, and return it in lower case.
-;
-; The optional argument PORT defaults to the current input port.
-;
-; This procedure is similar to next-token but only it implements
-; an inclusion rather than delimiting semantics.
-
-(define-opt (next-token-of incl-list/pred
- (optional (port (current-input-port))) )
- (let* ((buffer (input-parse:init-buffer))
- (curr-buf-len (string-length buffer)))
- (if (procedure? incl-list/pred)
- (let outer ((buffer buffer) (filled-buffer-l '()))
- (let loop ((i 0))
- (if (>= i curr-buf-len) ; make sure we have space
- (outer (make-string curr-buf-len) (cons buffer filled-buffer-l))
- (let ((c (incl-list/pred (peek-char port))))
- (if c
- (begin
- (string-set! buffer i c)
- (read-char port) ; move to the next char
- (loop (inc i)))
- ; incl-list/pred decided it had had enough
- (if (null? filled-buffer-l) (substring buffer 0 i)
- (string-concatenate-reverse filled-buffer-l buffer i)))))))
-
- ; incl-list/pred is a list of allowed characters
- (let outer ((buffer buffer) (filled-buffer-l '()))
- (let loop ((i 0))
- (if (>= i curr-buf-len) ; make sure we have space
- (outer (make-string curr-buf-len) (cons buffer filled-buffer-l))
- (let ((c (peek-char port)))
- (cond
- ((not (memv c incl-list/pred))
- (if (null? filled-buffer-l) (substring buffer 0 i)
- (string-concatenate-reverse filled-buffer-l buffer i)))
- (else
- (string-set! buffer i c)
- (read-char port) ; move to the next char
- (loop (inc i))))))))
- )))
-
-
-; -- procedure+: read-text-line [PORT]
-; Reads one line of text from the PORT, and returns it as a string.
-; A line is a (possibly empty) sequence of characters terminated
-; by CR, CRLF or LF (or even the end of file).
-; The terminating character (or CRLF combination) is removed from
-; the input stream. The terminating character(s) is not a part
-; of the return string either.
-; If EOF is encountered before any character is read, the return
-; value is EOF.
-;
-; The optional argument PORT defaults to the current input port.
-
-(define *read-line-breaks* (list char-newline char-return '*eof*))
-
-(define-opt (read-text-line (optional (port (current-input-port))) )
- (if (eof-object? (peek-char port)) (peek-char port)
- (let* ((line
- (next-token '() *read-line-breaks*
- "reading a line" port))
- (c (read-char port))) ; must be either \n or \r or EOF
- (and (eqv? c char-return) (eqv? (peek-char port) #\newline)
- (read-char port)) ; skip \n that follows \r
- line)))
-
-
-; -- procedure+: read-string N [PORT]
-; Reads N characters from the PORT, and returns them in a string.
-; If EOF is encountered before N characters are read, a shorter string
-; will be returned.
-; If N is not positive, an empty string will be returned.
-; The optional argument PORT defaults to the current input port.
-
-(define-opt (read-string n (optional (port (current-input-port))) )
- (if (not (positive? n)) ""
- (let ((buffer (make-string n)))
- (let loop ((i 0) (c (read-char port)))
- (if (eof-object? c) (substring buffer 0 i)
- (let ((i1 (inc i)))
- (string-set! buffer i c)
- (if (= i1 n) buffer
- (loop i1 (read-char port)))))))))
-
-;;;; (sxml xpath) -- SXPath
-;;;;
-;;;; Copyright (C) 2009 Free Software Foundation, Inc.
-;;;; Modified 2004 by Andy Wingo <wingo at pobox dot com>.
-;;;; Written 2001 by Oleg Kiselyov <oleg at pobox dot com> SXPath.scm.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary:
-;;
-;;@heading SXPath: SXML Query Language
-;;
-;; SXPath is a query language for SXML, an instance of XML Information
-;; set (Infoset) in the form of s-expressions. See @code{(sxml ssax)}
-;; for the definition of SXML and more details. SXPath is also a
-;; translation into Scheme of an XML Path Language,
-;; @uref{http://www.w3.org/TR/xpath,XPath}. XPath and SXPath describe
-;; means of selecting a set of Infoset's items or their properties.
-;;
-;; To facilitate queries, XPath maps the XML Infoset into an explicit
-;; tree, and introduces important notions of a location path and a
-;; current, context node. A location path denotes a selection of a set of
-;; nodes relative to a context node. Any XPath tree has a distinguished,
-;; root node -- which serves as the context node for absolute location
-;; paths. Location path is recursively defined as a location step joined
-;; with a location path. A location step is a simple query of the
-;; database relative to a context node. A step may include expressions
-;; that further filter the selected set. Each node in the resulting set
-;; is used as a context node for the adjoining location path. The result
-;; of the step is a union of the sets returned by the latter location
-;; paths.
-;;
-;; The SXML representation of the XML Infoset (see SSAX.scm) is rather
-;; suitable for querying as it is. Bowing to the XPath specification,
-;; we will refer to SXML information items as 'Nodes':
-;;@example
-;; <Node> ::= <Element> | <attributes-coll> | <attrib>
-;; | "text string" | <PI>
-;;@end example
-;; This production can also be described as
-;;@example
-;; <Node> ::= (name . <Nodeset>) | "text string"
-;;@end example
-;; An (ordered) set of nodes is just a list of the constituent nodes:
-;;@example
-;; <Nodeset> ::= (<Node> ...)
-;;@end example
-;; Nodesets, and Nodes other than text strings are both lists. A
-;; <Nodeset> however is either an empty list, or a list whose head is not
-;; a symbol. A symbol at the head of a node is either an XML name (in
-;; which case it's a tag of an XML element), or an administrative name
-;; such as '@@'. This uniform list representation makes processing rather
-;; simple and elegant, while avoiding confusion. The multi-branch tree
-;; structure formed by the mutually-recursive datatypes <Node> and
-;; <Nodeset> lends itself well to processing by functional languages.
-;;
-;; A location path is in fact a composite query over an XPath tree or
-;; its branch. A singe step is a combination of a projection, selection
-;; or a transitive closure. Multiple steps are combined via join and
-;; union operations. This insight allows us to @emph{elegantly}
-;; implement XPath as a sequence of projection and filtering primitives
-;; -- converters -- joined by @dfn{combinators}. Each converter takes a
-;; node and returns a nodeset which is the result of the corresponding
-;; query relative to that node. A converter can also be called on a set
-;; of nodes. In that case it returns a union of the corresponding
-;; queries over each node in the set. The union is easily implemented as
-;; a list append operation as all nodes in a SXML tree are considered
-;; distinct, by XPath conventions. We also preserve the order of the
-;; members in the union. Query combinators are high-order functions:
-;; they take converter(s) (which is a Node|Nodeset -> Nodeset function)
-;; and compose or otherwise combine them. We will be concerned with only
-;; relative location paths [XPath]: an absolute location path is a
-;; relative path applied to the root node.
-;;
-;; Similarly to XPath, SXPath defines full and abbreviated notations
-;; for location paths. In both cases, the abbreviated notation can be
-;; mechanically expanded into the full form by simple rewriting
-;; rules. In case of SXPath the corresponding rules are given as
-;; comments to a sxpath function, below. The regression test suite at
-;; the end of this file shows a representative sample of SXPaths in
-;; both notations, juxtaposed with the corresponding XPath
-;; expressions. Most of the samples are borrowed literally from the
-;; XPath specification, while the others are adjusted for our running
-;; example, tree1.
-;;
-;;; Code:
-
-(define-module (sxml xpath)
- #\use-module (ice-9 pretty-print)
- #\export (nodeset? node-typeof? node-eq? node-equal? node-pos
- filter take-until take-after map-union node-reverse
- node-trace select-kids node-self node-join node-reduce
- node-or node-closure node-parent
- sxpath))
-
-;; Upstream version:
-; $Id: SXPath.scm,v 3.5 2001/01/12 23:20:35 oleg Exp oleg $
-
-(define (nodeset? x)
- (or (and (pair? x) (not (symbol? (car x)))) (null? x)))
-
-;-------------------------
-; Basic converters and applicators
-; A converter is a function
-; type Converter = Node|Nodeset -> Nodeset
-; A converter can also play a role of a predicate: in that case, if a
-; converter, applied to a node or a nodeset, yields a non-empty
-; nodeset, the converter-predicate is deemed satisfied. Throughout
-; this file a nil nodeset is equivalent to #f in denoting a failure.
-
-; The following function implements a 'Node test' as defined in
-; Sec. 2.3 of XPath document. A node test is one of the components of a
-; location step. It is also a converter-predicate in SXPath.
-;
-; The function node-typeof? takes a type criterion and returns a function,
-; which, when applied to a node, will tell if the node satisfies
-; the test.
-; node-typeof? :: Crit -> Node -> Boolean
-;
-; The criterion 'crit' is a symbol, one of the following:
-; id - tests if the Node has the right name (id)
-; @ - tests if the Node is an <attributes-coll>
-; * - tests if the Node is an <Element>
-; *text* - tests if the Node is a text node
-; *PI* - tests if the Node is a PI node
-; *any* - #t for any type of Node
-
-(define (node-typeof? crit)
- (lambda (node)
- (case crit
- ((*) (and (pair? node) (not (memq (car node) '(@ *PI*)))))
- ((*any*) #t)
- ((*text*) (string? node))
- (else
- (and (pair? node) (eq? crit (car node))))
-)))
-
-
-; Curried equivalence converter-predicates
-(define (node-eq? other)
- (lambda (node)
- (eq? other node)))
-
-(define (node-equal? other)
- (lambda (node)
- (equal? other node)))
-
-; node-pos:: N -> Nodeset -> Nodeset, or
-; node-pos:: N -> Converter
-; Select the N'th element of a Nodeset and return as a singular Nodeset;
-; Return an empty nodeset if the Nth element does not exist.
-; ((node-pos 1) Nodeset) selects the node at the head of the Nodeset,
-; if exists; ((node-pos 2) Nodeset) selects the Node after that, if
-; exists.
-; N can also be a negative number: in that case the node is picked from
-; the tail of the list.
-; ((node-pos -1) Nodeset) selects the last node of a non-empty nodeset;
-; ((node-pos -2) Nodeset) selects the last but one node, if exists.
-
-(define (node-pos n)
- (lambda (nodeset)
- (cond
- ((not (nodeset? nodeset)) '())
- ((null? nodeset) nodeset)
- ((eqv? n 1) (list (car nodeset)))
- ((negative? n) ((node-pos (+ n 1 (length nodeset))) nodeset))
- (else
- (or (positive? n) (error "yikes!"))
- ((node-pos (1- n)) (cdr nodeset))))))
-
-; filter:: Converter -> Converter
-; A filter applicator, which introduces a filtering context. The argument
-; converter is considered a predicate, with either #f or nil result meaning
-; failure.
-(define (filter pred?)
- (lambda (lst) ; a nodeset or a node (will be converted to a singleton nset)
- (let loop ((lst (if (nodeset? lst) lst (list lst))) (res '()))
- (if (null? lst)
- (reverse res)
- (let ((pred-result (pred? (car lst))))
- (loop (cdr lst)
- (if (and pred-result (not (null? pred-result)))
- (cons (car lst) res)
- res)))))))
-
-; take-until:: Converter -> Converter, or
-; take-until:: Pred -> Node|Nodeset -> Nodeset
-; Given a converter-predicate and a nodeset, apply the predicate to
-; each element of the nodeset, until the predicate yields anything but #f or
-; nil. Return the elements of the input nodeset that have been processed
-; till that moment (that is, which fail the predicate).
-; take-until is a variation of the filter above: take-until passes
-; elements of an ordered input set till (but not including) the first
-; element that satisfies the predicate.
-; The nodeset returned by ((take-until (not pred)) nset) is a subset --
-; to be more precise, a prefix -- of the nodeset returned by
-; ((filter pred) nset)
-
-(define (take-until pred?)
- (lambda (lst) ; a nodeset or a node (will be converted to a singleton nset)
- (let loop ((lst (if (nodeset? lst) lst (list lst))))
- (if (null? lst) lst
- (let ((pred-result (pred? (car lst))))
- (if (and pred-result (not (null? pred-result)))
- '()
- (cons (car lst) (loop (cdr lst)))))
- ))))
-
-
-; take-after:: Converter -> Converter, or
-; take-after:: Pred -> Node|Nodeset -> Nodeset
-; Given a converter-predicate and a nodeset, apply the predicate to
-; each element of the nodeset, until the predicate yields anything but #f or
-; nil. Return the elements of the input nodeset that have not been processed:
-; that is, return the elements of the input nodeset that follow the first
-; element that satisfied the predicate.
-; take-after along with take-until partition an input nodeset into three
-; parts: the first element that satisfies a predicate, all preceding
-; elements and all following elements.
-
-(define (take-after pred?)
- (lambda (lst) ; a nodeset or a node (will be converted to a singleton nset)
- (let loop ((lst (if (nodeset? lst) lst (list lst))))
- (if (null? lst) lst
- (let ((pred-result (pred? (car lst))))
- (if (and pred-result (not (null? pred-result)))
- (cdr lst)
- (loop (cdr lst))))
- ))))
-
-; Apply proc to each element of lst and return the list of results.
-; if proc returns a nodeset, splice it into the result
-;
-; From another point of view, map-union is a function Converter->Converter,
-; which places an argument-converter in a joining context.
-
-(define (map-union proc lst)
- (if (null? lst) lst
- (let ((proc-res (proc (car lst))))
- ((if (nodeset? proc-res) append cons)
- proc-res (map-union proc (cdr lst))))))
-
-; node-reverse :: Converter, or
-; node-reverse:: Node|Nodeset -> Nodeset
-; Reverses the order of nodes in the nodeset
-; This basic converter is needed to implement a reverse document order
-; (see the XPath Recommendation).
-(define node-reverse
- (lambda (node-or-nodeset)
- (if (not (nodeset? node-or-nodeset)) (list node-or-nodeset)
- (reverse node-or-nodeset))))
-
-; node-trace:: String -> Converter
-; (node-trace title) is an identity converter. In addition it prints out
-; a node or nodeset it is applied to, prefixed with the 'title'.
-; This converter is very useful for debugging.
-
-(define (node-trace title)
- (lambda (node-or-nodeset)
- (display "\n-->")
- (display title)
- (display " :")
- (pretty-print node-or-nodeset)
- node-or-nodeset))
-
-
-;-------------------------
-; Converter combinators
-;
-; Combinators are higher-order functions that transmogrify a converter
-; or glue a sequence of converters into a single, non-trivial
-; converter. The goal is to arrive at converters that correspond to
-; XPath location paths.
-;
-; From a different point of view, a combinator is a fixed, named
-; _pattern_ of applying converters. Given below is a complete set of
-; such patterns that together implement XPath location path
-; specification. As it turns out, all these combinators can be built
-; from a small number of basic blocks: regular functional composition,
-; map-union and filter applicators, and the nodeset union.
-
-
-
-; select-kids:: Pred -> Node -> Nodeset
-; Given a Node, return an (ordered) subset its children that satisfy
-; the Pred (a converter, actually)
-; select-kids:: Pred -> Nodeset -> Nodeset
-; The same as above, but select among children of all the nodes in
-; the Nodeset
-;
-; More succinctly, the signature of this function is
-; select-kids:: Converter -> Converter
-
-(define (select-kids test-pred?)
- (lambda (node) ; node or node-set
- (cond
- ((null? node) node)
- ((not (pair? node)) '()) ; No children
- ((symbol? (car node))
- ((filter test-pred?) (cdr node))) ; it's a single node
- (else (map-union (select-kids test-pred?) node)))))
-
-
-; node-self:: Pred -> Node -> Nodeset, or
-; node-self:: Converter -> Converter
-; Similar to select-kids but apply to the Node itself rather
-; than to its children. The resulting Nodeset will contain either one
-; component, or will be empty (if the Node failed the Pred).
-(define node-self filter)
-
-
-; node-join:: [LocPath] -> Node|Nodeset -> Nodeset, or
-; node-join:: [Converter] -> Converter
-; join the sequence of location steps or paths as described
-; in the title comments above.
-(define (node-join . selectors)
- (lambda (nodeset) ; Nodeset or node
- (let loop ((nodeset nodeset) (selectors selectors))
- (if (null? selectors) nodeset
- (loop
- (if (nodeset? nodeset)
- (map-union (car selectors) nodeset)
- ((car selectors) nodeset))
- (cdr selectors))))))
-
-
-; node-reduce:: [LocPath] -> Node|Nodeset -> Nodeset, or
-; node-reduce:: [Converter] -> Converter
-; A regular functional composition of converters.
-; From a different point of view,
-; ((apply node-reduce converters) nodeset)
-; is equivalent to
-; (foldl apply nodeset converters)
-; i.e., folding, or reducing, a list of converters with the nodeset
-; as a seed.
-(define (node-reduce . converters)
- (lambda (nodeset) ; Nodeset or node
- (let loop ((nodeset nodeset) (converters converters))
- (if (null? converters) nodeset
- (loop ((car converters) nodeset) (cdr converters))))))
-
-
-; node-or:: [Converter] -> Converter
-; This combinator applies all converters to a given node and
-; produces the union of their results.
-; This combinator corresponds to a union, '|' operation for XPath
-; location paths.
-; (define (node-or . converters)
-; (lambda (node-or-nodeset)
-; (if (null? converters) node-or-nodeset
-; (append
-; ((car converters) node-or-nodeset)
-; ((apply node-or (cdr converters)) node-or-nodeset)))))
-; More optimal implementation follows
-(define (node-or . converters)
- (lambda (node-or-nodeset)
- (let loop ((result '()) (converters converters))
- (if (null? converters) result
- (loop (append result (or ((car converters) node-or-nodeset) '()))
- (cdr converters))))))
-
-
-; node-closure:: Converter -> Converter
-; Select all _descendants_ of a node that satisfy a converter-predicate.
-; This combinator is similar to select-kids but applies to
-; grand... children as well.
-; This combinator implements the "descendant::" XPath axis
-; Conceptually, this combinator can be expressed as
-; (define (node-closure f)
-; (node-or
-; (select-kids f)
-; (node-reduce (select-kids (node-typeof? '*)) (node-closure f))))
-; This definition, as written, looks somewhat like a fixpoint, and it
-; will run forever. It is obvious however that sooner or later
-; (select-kids (node-typeof? '*)) will return an empty nodeset. At
-; this point further iterations will no longer affect the result and
-; can be stopped.
-
-(define (node-closure test-pred?)
- (lambda (node) ; Nodeset or node
- (let loop ((parent node) (result '()))
- (if (null? parent) result
- (loop ((select-kids (node-typeof? '*)) parent)
- (append result
- ((select-kids test-pred?) parent)))
- ))))
-
-; node-parent:: RootNode -> Converter
-; (node-parent rootnode) yields a converter that returns a parent of a
-; node it is applied to. If applied to a nodeset, it returns the list
-; of parents of nodes in the nodeset. The rootnode does not have
-; to be the root node of the whole SXML tree -- it may be a root node
-; of a branch of interest.
-; Given the notation of Philip Wadler's paper on semantics of XSLT,
-; parent(x) = { y | y=subnode*(root), x=subnode(y) }
-; Therefore, node-parent is not the fundamental converter: it can be
-; expressed through the existing ones. Yet node-parent is a rather
-; convenient converter. It corresponds to a parent:: axis of SXPath.
-; Note that the parent:: axis can be used with an attribute node as well!
-
-(define (node-parent rootnode)
- (lambda (node) ; Nodeset or node
- (if (nodeset? node) (map-union (node-parent rootnode) node)
- (let ((pred
- (node-or
- (node-reduce
- (node-self (node-typeof? '*))
- (select-kids (node-eq? node)))
- (node-join
- (select-kids (node-typeof? '@))
- (select-kids (node-eq? node))))))
- ((node-or
- (node-self pred)
- (node-closure pred))
- rootnode)))))
-
-;-------------------------
-; Evaluate an abbreviated SXPath
-; sxpath:: AbbrPath -> Converter, or
-; sxpath:: AbbrPath -> Node|Nodeset -> Nodeset
-; AbbrPath is a list. It is translated to the full SXPath according
-; to the following rewriting rules
-; (sxpath '()) -> (node-join)
-; (sxpath '(path-component ...)) ->
-; (node-join (sxpath1 path-component) (sxpath '(...)))
-; (sxpath1 '//) -> (node-or
-; (node-self (node-typeof? '*any*))
-; (node-closure (node-typeof? '*any*)))
-; (sxpath1 '(equal? x)) -> (select-kids (node-equal? x))
-; (sxpath1 '(eq? x)) -> (select-kids (node-eq? x))
-; (sxpath1 ?symbol) -> (select-kids (node-typeof? ?symbol)
-; (sxpath1 procedure) -> procedure
-; (sxpath1 '(?symbol ...)) -> (sxpath1 '((?symbol) ...))
-; (sxpath1 '(path reducer ...)) ->
-; (node-reduce (sxpath path) (sxpathr reducer) ...)
-; (sxpathr number) -> (node-pos number)
-; (sxpathr path-filter) -> (filter (sxpath path-filter))
-
-(define (sxpath path)
- (lambda (nodeset)
- (let loop ((nodeset nodeset) (path path))
- (cond
- ((null? path) nodeset)
- ((nodeset? nodeset)
- (map-union (sxpath path) nodeset))
- ((procedure? (car path))
- (loop ((car path) nodeset) (cdr path)))
- ((eq? '// (car path))
- (loop
- ((if (nodeset? nodeset) append cons) nodeset
- ((node-closure (node-typeof? '*any*)) nodeset))
- (cdr path)))
- ((symbol? (car path))
- (loop ((select-kids (node-typeof? (car path))) nodeset)
- (cdr path)))
- ((and (pair? (car path)) (eq? 'equal? (caar path)))
- (loop ((select-kids (apply node-equal? (cdar path))) nodeset)
- (cdr path)))
- ((and (pair? (car path)) (eq? 'eq? (caar path)))
- (loop ((select-kids (apply node-eq? (cdar path))) nodeset)
- (cdr path)))
- ((pair? (car path))
- (let reducer ((nodeset
- (if (symbol? (caar path))
- ((select-kids (node-typeof? (caar path))) nodeset)
- (loop nodeset (caar path))))
- (reducing-path (cdar path)))
- (cond
- ((null? reducing-path) (loop nodeset (cdr path)))
- ((number? (car reducing-path))
- (reducer ((node-pos (car reducing-path)) nodeset)
- (cdr reducing-path)))
- (else
- (reducer ((filter (sxpath (car reducing-path))) nodeset)
- (cdr reducing-path))))))
- (else
- (error "Invalid path step: " (car path)))))))
-
-;;; arch-tag: c4e57abf-6b61-4612-a6aa-d1536d440774
-;;; xpath.scm ends here
-;;; ck, to facilitate applicative-order macro programming
-
-;;; Copyright (C) 2012 Free Software Foundation, Inc
-;;; Copyright (C) 2009, 2011 Oleg Kiselyov
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;
-;;;
-;;; Originally written by Oleg Kiselyov and later contributed to Guile.
-;;;
-;;; Based on the CK machine introduced in:
-;;;
-;;; Matthias Felleisen and Daniel P. Friedman: Control operators, the
-;;; SECD machine, and the lambda-calculus. In Martin Wirsing, editor,
-;;; Formal Description of Programming Concepts III, pages
-;;; 193-217. Elsevier, Amsterdam, 1986.
-;;;
-;;; See http://okmij.org/ftp/Scheme/macros.html#ck-macros for details.
-;;;
-
-(define-module (system base ck)
- #\export (ck))
-
-(define-syntax ck
- (syntax-rules (quote)
- ((ck () 'v) v) ; yield the value on empty stack
-
- ((ck (((op ...) ea ...) . s) 'v) ; re-focus on the other argument, ea
- (ck-arg s (op ... 'v) ea ...))
-
- ((ck s (op ea ...)) ; Focus: handling an application;
- (ck-arg s (op) ea ...)))) ; check if args are values
-
-(define-syntax ck-arg
- (syntax-rules (quote)
- ((ck-arg s (op va ...)) ; all arguments are evaluated,
- (op s va ...)) ; do the redex
-
- ((ck-arg s (op ...) 'v ea1 ...) ; optimization when the first ea
- (ck-arg s (op ... 'v) ea1 ...)) ; was already a value
-
- ((ck-arg s (op ...) ea ea1 ...) ; focus on ea, to evaluate it
- (ck (((op ...) ea1 ...) . s) ea))))
-;;; High-level compiler interface
-
-;; Copyright (C) 2001, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (system base compile)
- #\use-module (system base syntax)
- #\use-module (system base language)
- #\use-module (system base message)
- #\use-module (system vm vm) ;; FIXME: there's a reason for this, can't remember why tho
- #\use-module (ice-9 regex)
- #\use-module (ice-9 optargs)
- #\use-module (ice-9 receive)
- #\export (compiled-file-name
- compile-file
- compile-and-load
- read-and-compile
- compile
- decompile))
-
-
-;;;
-;;; Compiler
-;;;
-
-(define (call-once thunk)
- (let ((entered #f))
- (dynamic-wind
- (lambda ()
- (if entered
- (error "thunk may only be entered once: ~a" thunk))
- (set! entered #t))
- thunk
- (lambda () #t))))
-
-;; emacs: (put 'call-with-output-file/atomic 'scheme-indent-function 1)
-(define* (call-with-output-file/atomic filename proc #\optional reference)
- (let* ((template (string-append filename ".XXXXXX"))
- (tmp (mkstemp! template "wb")))
- (call-once
- (lambda ()
- (with-throw-handler #t
- (lambda ()
- (proc tmp)
- ;; Chmodding by name instead of by port allows this chmod to
- ;; work on systems without fchmod, like MinGW.
- (let ((perms (or (false-if-exception (stat:perms (stat reference)))
- (lognot (umask)))))
- (chmod template (logand #o0666 perms)))
- (close-port tmp)
- (rename-file template filename))
- (lambda args
- (close-port tmp)
- (delete-file template)))))))
-
-(define (ensure-language x)
- (if (language? x)
- x
- (lookup-language x)))
-
-;; Throws an exception if `dir' is not writable. The mkdir occurs
-;; before the check, so that we avoid races (possibly due to parallel
-;; compilation).
-;;
-(define (ensure-directory dir)
- (catch 'system-error
- (lambda ()
- (mkdir dir))
- (lambda (k subr fmt args rest)
- (let ((errno (and (pair? rest) (car rest))))
- (cond
- ((eqv? errno EEXIST)
- ;; Assume it's a writable directory, to avoid TOCTOU errors,
- ;; as well as UID/EUID mismatches that occur with access(2).
- #t)
- ((eqv? errno ENOENT)
- (ensure-directory (dirname dir))
- (ensure-directory dir))
- (else
- (throw k subr fmt args rest)))))))
-
-;;; This function is among the trickiest I've ever written. I tried many
-;;; variants. In the end, simple is best, of course.
-;;;
-;;; After turning this around a number of times, it seems that the
-;;; desired behavior is that .go files should exist in a path, for
-;;; searching. That is orthogonal to this function. For writing .go
-;;; files, either you know where they should go, in which case you tell
-;;; compile-file explicitly, as in the srcdir != builddir case; or you
-;;; don't know, in which case this function is called, and we just put
-;;; them in your own ccache dir in ~/.cache/guile/ccache.
-;;;
-;;; See also boot-9.scm:load.
-(define (compiled-file-name file)
- ;; FIXME: would probably be better just to append SHA1(canon-path)
- ;; to the %compile-fallback-path, to avoid deep directory stats.
- (define (canonical->suffix canon)
- (cond
- ((string-prefix? "/" canon) canon)
- ((and (> (string-length canon) 2)
- (eqv? (string-ref canon 1) #\:))
- ;; Paths like C:... transform to /C...
- (string-append "/" (substring canon 0 1) (substring canon 2)))
- (else canon)))
- (define (compiled-extension)
- (cond ((or (null? %load-compiled-extensions)
- (string-null? (car %load-compiled-extensions)))
- (warn "invalid %load-compiled-extensions"
- %load-compiled-extensions)
- ".go")
- (else (car %load-compiled-extensions))))
- (and %compile-fallback-path
- (let ((f (string-append
- %compile-fallback-path
- (canonical->suffix (canonicalize-path file))
- (compiled-extension))))
- (and (false-if-exception (ensure-directory (dirname f)))
- f))))
-
-(define* (compile-file file #\key
- (output-file #f)
- (from (current-language))
- (to 'objcode)
- (env (default-environment from))
- (opts '())
- (canonicalization 'relative))
- (with-fluids ((%file-port-name-canonicalization canonicalization))
- (let* ((comp (or output-file (compiled-file-name file)
- (error "failed to create path for auto-compiled file"
- file)))
- (in (open-input-file file))
- (enc (file-encoding in)))
- ;; Choose the input encoding deterministically.
- (set-port-encoding! in (or enc "UTF-8"))
-
- (ensure-directory (dirname comp))
- (call-with-output-file/atomic comp
- (lambda (port)
- ((language-printer (ensure-language to))
- (read-and-compile in #\env env #\from from #\to to #\opts opts)
- port))
- file)
- comp)))
-
-(define* (compile-and-load file #\key (from (current-language)) (to 'value)
- (env (current-module)) (opts '())
- (canonicalization 'relative))
- (with-fluids ((%file-port-name-canonicalization canonicalization))
- (read-and-compile (open-input-file file)
- #\from from #\to to #\opts opts
- #\env env)))
-
-
-;;;
-;;; Compiler interface
-;;;
-
-(define (compile-passes from to opts)
- (map cdr
- (or (lookup-compilation-order from to)
- (error "no way to compile" from "to" to))))
-
-(define (compile-fold passes exp env opts)
- (let lp ((passes passes) (x exp) (e env) (cenv env) (first? #t))
- (if (null? passes)
- (values x e cenv)
- (receive (x e new-cenv) ((car passes) x e opts)
- (lp (cdr passes) x e (if first? new-cenv cenv) #f)))))
-
-(define (find-language-joint from to)
- (let lp ((in (reverse (or (lookup-compilation-order from to)
- (error "no way to compile" from "to" to))))
- (lang to))
- (cond ((null? in) to)
- ((language-joiner lang) lang)
- (else
- (lp (cdr in) (caar in))))))
-
-(define (default-language-joiner lang)
- (lambda (exps env)
- (if (and (pair? exps) (null? (cdr exps)))
- (car exps)
- (error
- "Multiple expressions read and compiled, but language has no joiner"
- lang))))
-
-(define (read-and-parse lang port cenv)
- (let ((exp ((language-reader lang) port cenv)))
- (cond
- ((eof-object? exp) exp)
- ((language-parser lang) => (lambda (parse) (parse exp)))
- (else exp))))
-
-(define* (read-and-compile port #\key
- (from (current-language))
- (to 'objcode)
- (env (default-environment from))
- (opts '()))
- (let ((from (ensure-language from))
- (to (ensure-language to)))
- (let ((joint (find-language-joint from to)))
- (parameterize ((current-language from))
- (let lp ((exps '()) (env #f) (cenv env))
- (let ((x (read-and-parse (current-language) port cenv)))
- (cond
- ((eof-object? x)
- (close-port port)
- (compile ((or (language-joiner joint)
- (default-language-joiner joint))
- (reverse exps)
- env)
- #\from joint #\to to
- ;; env can be false if no expressions were read.
- #\env (or env (default-environment joint))
- #\opts opts))
- (else
- ;; compile-fold instead of compile so we get the env too
- (receive (jexp jenv jcenv)
- (compile-fold (compile-passes (current-language) joint opts)
- x cenv opts)
- (lp (cons jexp exps) jenv jcenv))))))))))
-
-(define* (compile x #\key
- (from (current-language))
- (to 'value)
- (env (default-environment from))
- (opts '()))
-
- (let ((warnings (memq #\warnings opts)))
- (if (pair? warnings)
- (let ((warnings (cadr warnings)))
- ;; Sanity-check the requested warnings.
- (for-each (lambda (w)
- (or (lookup-warning-type w)
- (warning 'unsupported-warning #f w)))
- warnings))))
-
- (receive (exp env cenv)
- (compile-fold (compile-passes from to opts) x env opts)
- exp))
-
-
-;;;
-;;; Decompiler interface
-;;;
-
-(define (decompile-passes from to opts)
- (map cdr
- (or (lookup-decompilation-order from to)
- (error "no way to decompile" from "to" to))))
-
-(define (decompile-fold passes exp env opts)
- (if (null? passes)
- (values exp env)
- (receive (exp env) ((car passes) exp env opts)
- (decompile-fold (cdr passes) exp env opts))))
-
-(define* (decompile x #\key
- (env #f)
- (from 'value)
- (to 'assembly)
- (opts '()))
- (decompile-fold (decompile-passes from to opts)
- x
- env
- opts))
-;;; -*- mode: scheme; coding: utf-8; -*-
-;;;
-;;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or modify it
-;;; under the terms of the GNU Lesser General Public License as published by
-;;; the Free Software Foundation; either version 3 of the License, or (at
-;;; your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful, but
-;;; WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
-;;; General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public License
-;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-(define-module (system base lalr)
-
- ;; XXX: In theory this import is not needed but the evaluator (not the
- ;; compiler) complains about `lexical-token' being unbound when expanding
- ;; `(define-record-type lexical-token ...)' if we omit it.
- #\use-module (srfi srfi-9)
-
- #\export (lalr-parser print-states
-
- make-lexical-token lexical-token?
- lexical-token-category
- lexical-token-source
- lexical-token-value
-
- make-source-location source-location?
- source-location-input
- source-location-line
- source-location-column
- source-location-offset
- source-location-length
- source-location->source-properties
-
- ;; `lalr-parser' is a defmacro, which produces code that refers to
- ;; these drivers.
- lr-driver glr-driver))
-
-;; The LALR parser generator was written by Dominique Boucher. It's available
-;; from http://code.google.com/p/lalr-scm/ and released under the LGPLv3+.
-(include-from-path "system/base/lalr.upstream.scm")
-
-(define (source-location->source-properties loc)
- `((filename . ,(source-location-input loc))
- (line . ,(source-location-line loc))
- (column . ,(source-location-column loc))))
-;;;
-;;;; An Efficient and Portable LALR(1) Parser Generator for Scheme
-;;;
-;; Copyright 2014 Jan Nieuwenhuizen <janneke@gnu.org>
-;; Copyright 1993, 2010 Dominique Boucher
-;;
-;; This program is free software: you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public License
-;; as published by the Free Software Foundation, either version 3 of
-;; the License, or (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
-(define *lalr-scm-version* "2.5.0")
-
-
-(cond-expand
-
- ;; -- Gambit-C
- (gambit
-
- (define-macro (def-macro form . body)
- `(define-macro ,form (let () ,@body)))
-
- (def-macro (BITS-PER-WORD) 28)
- (def-macro (logical-or x . y) `(bitwise-ior ,x ,@y))
- (def-macro (lalr-error msg obj) `(error ,msg ,obj))
-
- (define pprint pretty-print)
- (define lalr-keyword? keyword?)
- (define (note-source-location lvalue tok) lvalue))
-
- ;; --
- (bigloo
- (define-macro (def-macro form . body)
- `(define-macro ,form (let () ,@body)))
-
- (define pprint (lambda (obj) (write obj) (newline)))
- (define lalr-keyword? keyword?)
- (def-macro (BITS-PER-WORD) 29)
- (def-macro (logical-or x . y) `(bit-or ,x ,@y))
- (def-macro (lalr-error msg obj) `(error "lalr-parser" ,msg ,obj))
- (define (note-source-location lvalue tok) lvalue))
-
- ;; -- Chicken
- (chicken
-
- (define-macro (def-macro form . body)
- `(define-macro ,form (let () ,@body)))
-
- (define pprint pretty-print)
- (define lalr-keyword? symbol?)
- (def-macro (BITS-PER-WORD) 30)
- (def-macro (logical-or x . y) `(bitwise-ior ,x ,@y))
- (def-macro (lalr-error msg obj) `(error ,msg ,obj))
- (define (note-source-location lvalue tok) lvalue))
-
- ;; -- STKlos
- (stklos
- (require "pp")
-
- (define (pprint form) (pp form \:port (current-output-port)))
-
- (define lalr-keyword? keyword?)
- (define-macro (BITS-PER-WORD) 30)
- (define-macro (logical-or x . y) `(bit-or ,x ,@y))
- (define-macro (lalr-error msg obj) `(error 'lalr-parser ,msg ,obj))
- (define (note-source-location lvalue tok) lvalue))
-
- ;; -- Guile
- (guile
- (use-modules (ice-9 pretty-print))
- (use-modules (srfi srfi-9))
-
- (define pprint pretty-print)
- (define lalr-keyword? symbol?)
- (define-macro (BITS-PER-WORD) 30)
- (define-macro (logical-or x . y) `(logior ,x ,@y))
- (define-macro (lalr-error msg obj) `(error ,msg ,obj))
- (define (note-source-location lvalue tok)
- (if (and (supports-source-properties? lvalue)
- (not (source-property lvalue 'loc))
- (lexical-token? tok))
- (set-source-property! lvalue 'loc (lexical-token-source tok)))
- lvalue))
-
-
- ;; -- Kawa
- (kawa
- (require 'pretty-print)
- (define (BITS-PER-WORD) 30)
- (define logical-or logior)
- (define (lalr-keyword? obj) (keyword? obj))
- (define (pprint obj) (pretty-print obj))
- (define (lalr-error msg obj) (error msg obj))
- (define (note-source-location lvalue tok) lvalue))
-
- ;; -- SISC
- (sisc
- (import logicops)
- (import record)
-
- (define pprint pretty-print)
- (define lalr-keyword? symbol?)
- (define-macro BITS-PER-WORD (lambda () 32))
- (define-macro logical-or (lambda (x . y) `(logor ,x ,@y)))
- (define-macro (lalr-error msg obj) `(error "~a ~S:" ,msg ,obj))
- (define (note-source-location lvalue tok) lvalue))
-
- (else
- (error "Unsupported Scheme system")))
-
-
-(define-record-type lexical-token
- (make-lexical-token category source value)
- lexical-token?
- (category lexical-token-category)
- (source lexical-token-source)
- (value lexical-token-value))
-
-
-(define-record-type source-location
- (make-source-location input line column offset length)
- source-location?
- (input source-location-input)
- (line source-location-line)
- (column source-location-column)
- (offset source-location-offset)
- (length source-location-length))
-
-
-
- ;; - Macros pour la gestion des vecteurs de bits
-
-(define-macro (lalr-parser . arguments)
- (define (set-bit v b)
- (let ((x (quotient b (BITS-PER-WORD)))
- (y (expt 2 (remainder b (BITS-PER-WORD)))))
- (vector-set! v x (logical-or (vector-ref v x) y))))
-
- (define (bit-union v1 v2 n)
- (do ((i 0 (+ i 1)))
- ((= i n))
- (vector-set! v1 i (logical-or (vector-ref v1 i)
- (vector-ref v2 i)))))
-
- ;; - Macro pour les structures de donnees
-
- (define (new-core) (make-vector 4 0))
- (define (set-core-number! c n) (vector-set! c 0 n))
- (define (set-core-acc-sym! c s) (vector-set! c 1 s))
- (define (set-core-nitems! c n) (vector-set! c 2 n))
- (define (set-core-items! c i) (vector-set! c 3 i))
- (define (core-number c) (vector-ref c 0))
- (define (core-acc-sym c) (vector-ref c 1))
- (define (core-nitems c) (vector-ref c 2))
- (define (core-items c) (vector-ref c 3))
-
- (define (new-shift) (make-vector 3 0))
- (define (set-shift-number! c x) (vector-set! c 0 x))
- (define (set-shift-nshifts! c x) (vector-set! c 1 x))
- (define (set-shift-shifts! c x) (vector-set! c 2 x))
- (define (shift-number s) (vector-ref s 0))
- (define (shift-nshifts s) (vector-ref s 1))
- (define (shift-shifts s) (vector-ref s 2))
-
- (define (new-red) (make-vector 3 0))
- (define (set-red-number! c x) (vector-set! c 0 x))
- (define (set-red-nreds! c x) (vector-set! c 1 x))
- (define (set-red-rules! c x) (vector-set! c 2 x))
- (define (red-number c) (vector-ref c 0))
- (define (red-nreds c) (vector-ref c 1))
- (define (red-rules c) (vector-ref c 2))
-
-
- (define (new-set nelem)
- (make-vector nelem 0))
-
-
- (define (vector-map f v)
- (let ((vm-n (- (vector-length v) 1)))
- (let loop ((vm-low 0) (vm-high vm-n))
- (if (= vm-low vm-high)
- (vector-set! v vm-low (f (vector-ref v vm-low) vm-low))
- (let ((vm-middle (quotient (+ vm-low vm-high) 2)))
- (loop vm-low vm-middle)
- (loop (+ vm-middle 1) vm-high))))))
-
-
- ;; - Constantes
- (define STATE-TABLE-SIZE 1009)
-
-
- ;; - Tableaux
- (define rrhs #f)
- (define rlhs #f)
- (define ritem #f)
- (define nullable #f)
- (define derives #f)
- (define fderives #f)
- (define firsts #f)
- (define kernel-base #f)
- (define kernel-end #f)
- (define shift-symbol #f)
- (define shift-set #f)
- (define red-set #f)
- (define state-table #f)
- (define acces-symbol #f)
- (define reduction-table #f)
- (define shift-table #f)
- (define consistent #f)
- (define lookaheads #f)
- (define LA #f)
- (define LAruleno #f)
- (define lookback #f)
- (define goto-map #f)
- (define from-state #f)
- (define to-state #f)
- (define includes #f)
- (define F #f)
- (define action-table #f)
-
- ;; - Variables
- (define nitems #f)
- (define nrules #f)
- (define nvars #f)
- (define nterms #f)
- (define nsyms #f)
- (define nstates #f)
- (define first-state #f)
- (define last-state #f)
- (define final-state #f)
- (define first-shift #f)
- (define last-shift #f)
- (define first-reduction #f)
- (define last-reduction #f)
- (define nshifts #f)
- (define maxrhs #f)
- (define ngotos #f)
- (define token-set-size #f)
-
- (define driver-name 'lr-driver)
-
- (define (glr-driver?)
- (eq? driver-name 'glr-driver))
- (define (lr-driver?)
- (eq? driver-name 'lr-driver))
-
- (define (gen-tables! tokens gram )
- (initialize-all)
- (rewrite-grammar
- tokens
- gram
- (lambda (terms terms/prec vars gram gram/actions)
- (set! the-terminals/prec (list->vector terms/prec))
- (set! the-terminals (list->vector terms))
- (set! the-nonterminals (list->vector vars))
- (set! nterms (length terms))
- (set! nvars (length vars))
- (set! nsyms (+ nterms nvars))
- (let ((no-of-rules (length gram/actions))
- (no-of-items (let loop ((l gram/actions) (count 0))
- (if (null? l)
- count
- (loop (cdr l) (+ count (length (caar l))))))))
- (pack-grammar no-of-rules no-of-items gram)
- (set-derives)
- (set-nullable)
- (generate-states)
- (lalr)
- (build-tables)
- (compact-action-table terms)
- gram/actions))))
-
-
- (define (initialize-all)
- (set! rrhs #f)
- (set! rlhs #f)
- (set! ritem #f)
- (set! nullable #f)
- (set! derives #f)
- (set! fderives #f)
- (set! firsts #f)
- (set! kernel-base #f)
- (set! kernel-end #f)
- (set! shift-symbol #f)
- (set! shift-set #f)
- (set! red-set #f)
- (set! state-table (make-vector STATE-TABLE-SIZE '()))
- (set! acces-symbol #f)
- (set! reduction-table #f)
- (set! shift-table #f)
- (set! consistent #f)
- (set! lookaheads #f)
- (set! LA #f)
- (set! LAruleno #f)
- (set! lookback #f)
- (set! goto-map #f)
- (set! from-state #f)
- (set! to-state #f)
- (set! includes #f)
- (set! F #f)
- (set! action-table #f)
- (set! nstates #f)
- (set! first-state #f)
- (set! last-state #f)
- (set! final-state #f)
- (set! first-shift #f)
- (set! last-shift #f)
- (set! first-reduction #f)
- (set! last-reduction #f)
- (set! nshifts #f)
- (set! maxrhs #f)
- (set! ngotos #f)
- (set! token-set-size #f)
- (set! rule-precedences '()))
-
-
- (define (pack-grammar no-of-rules no-of-items gram)
- (set! nrules (+ no-of-rules 1))
- (set! nitems no-of-items)
- (set! rlhs (make-vector nrules #f))
- (set! rrhs (make-vector nrules #f))
- (set! ritem (make-vector (+ 1 nitems) #f))
-
- (let loop ((p gram) (item-no 0) (rule-no 1))
- (if (not (null? p))
- (let ((nt (caar p)))
- (let loop2 ((prods (cdar p)) (it-no2 item-no) (rl-no2 rule-no))
- (if (null? prods)
- (loop (cdr p) it-no2 rl-no2)
- (begin
- (vector-set! rlhs rl-no2 nt)
- (vector-set! rrhs rl-no2 it-no2)
- (let loop3 ((rhs (car prods)) (it-no3 it-no2))
- (if (null? rhs)
- (begin
- (vector-set! ritem it-no3 (- rl-no2))
- (loop2 (cdr prods) (+ it-no3 1) (+ rl-no2 1)))
- (begin
- (vector-set! ritem it-no3 (car rhs))
- (loop3 (cdr rhs) (+ it-no3 1))))))))))))
-
-
- (define (set-derives)
- (define delts (make-vector (+ nrules 1) 0))
- (define dset (make-vector nvars -1))
-
- (let loop ((i 1) (j 0)) ; i = 0
- (if (< i nrules)
- (let ((lhs (vector-ref rlhs i)))
- (if (>= lhs 0)
- (begin
- (vector-set! delts j (cons i (vector-ref dset lhs)))
- (vector-set! dset lhs j)
- (loop (+ i 1) (+ j 1)))
- (loop (+ i 1) j)))))
-
- (set! derives (make-vector nvars 0))
-
- (let loop ((i 0))
- (if (< i nvars)
- (let ((q (let loop2 ((j (vector-ref dset i)) (s '()))
- (if (< j 0)
- s
- (let ((x (vector-ref delts j)))
- (loop2 (cdr x) (cons (car x) s)))))))
- (vector-set! derives i q)
- (loop (+ i 1))))))
-
-
-
- (define (set-nullable)
- (set! nullable (make-vector nvars #f))
- (let ((squeue (make-vector nvars #f))
- (rcount (make-vector (+ nrules 1) 0))
- (rsets (make-vector nvars #f))
- (relts (make-vector (+ nitems nvars 1) #f)))
- (let loop ((r 0) (s2 0) (p 0))
- (let ((*r (vector-ref ritem r)))
- (if *r
- (if (< *r 0)
- (let ((symbol (vector-ref rlhs (- *r))))
- (if (and (>= symbol 0)
- (not (vector-ref nullable symbol)))
- (begin
- (vector-set! nullable symbol #t)
- (vector-set! squeue s2 symbol)
- (loop (+ r 1) (+ s2 1) p))))
- (let loop2 ((r1 r) (any-tokens #f))
- (let* ((symbol (vector-ref ritem r1)))
- (if (> symbol 0)
- (loop2 (+ r1 1) (or any-tokens (>= symbol nvars)))
- (if (not any-tokens)
- (let ((ruleno (- symbol)))
- (let loop3 ((r2 r) (p2 p))
- (let ((symbol (vector-ref ritem r2)))
- (if (> symbol 0)
- (begin
- (vector-set! rcount ruleno
- (+ (vector-ref rcount ruleno) 1))
- (vector-set! relts p2
- (cons (vector-ref rsets symbol)
- ruleno))
- (vector-set! rsets symbol p2)
- (loop3 (+ r2 1) (+ p2 1)))
- (loop (+ r2 1) s2 p2)))))
- (loop (+ r1 1) s2 p))))))
- (let loop ((s1 0) (s3 s2))
- (if (< s1 s3)
- (let loop2 ((p (vector-ref rsets (vector-ref squeue s1))) (s4 s3))
- (if p
- (let* ((x (vector-ref relts p))
- (ruleno (cdr x))
- (y (- (vector-ref rcount ruleno) 1)))
- (vector-set! rcount ruleno y)
- (if (= y 0)
- (let ((symbol (vector-ref rlhs ruleno)))
- (if (and (>= symbol 0)
- (not (vector-ref nullable symbol)))
- (begin
- (vector-set! nullable symbol #t)
- (vector-set! squeue s4 symbol)
- (loop2 (car x) (+ s4 1)))
- (loop2 (car x) s4)))
- (loop2 (car x) s4))))
- (loop (+ s1 1) s4)))))))))
-
-
-
- (define (set-firsts)
- (set! firsts (make-vector nvars '()))
-
- ;; -- initialization
- (let loop ((i 0))
- (if (< i nvars)
- (let loop2 ((sp (vector-ref derives i)))
- (if (null? sp)
- (loop (+ i 1))
- (let ((sym (vector-ref ritem (vector-ref rrhs (car sp)))))
- (if (< -1 sym nvars)
- (vector-set! firsts i (sinsert sym (vector-ref firsts i))))
- (loop2 (cdr sp)))))))
-
- ;; -- reflexive and transitive closure
- (let loop ((continue #t))
- (if continue
- (let loop2 ((i 0) (cont #f))
- (if (>= i nvars)
- (loop cont)
- (let* ((x (vector-ref firsts i))
- (y (let loop3 ((l x) (z x))
- (if (null? l)
- z
- (loop3 (cdr l)
- (sunion (vector-ref firsts (car l)) z))))))
- (if (equal? x y)
- (loop2 (+ i 1) cont)
- (begin
- (vector-set! firsts i y)
- (loop2 (+ i 1) #t))))))))
-
- (let loop ((i 0))
- (if (< i nvars)
- (begin
- (vector-set! firsts i (sinsert i (vector-ref firsts i)))
- (loop (+ i 1))))))
-
-
-
-
- (define (set-fderives)
- (set! fderives (make-vector nvars #f))
-
- (set-firsts)
-
- (let loop ((i 0))
- (if (< i nvars)
- (let ((x (let loop2 ((l (vector-ref firsts i)) (fd '()))
- (if (null? l)
- fd
- (loop2 (cdr l)
- (sunion (vector-ref derives (car l)) fd))))))
- (vector-set! fderives i x)
- (loop (+ i 1))))))
-
-
- (define (closure core)
- ;; Initialization
- (define ruleset (make-vector nrules #f))
-
- (let loop ((csp core))
- (if (not (null? csp))
- (let ((sym (vector-ref ritem (car csp))))
- (if (< -1 sym nvars)
- (let loop2 ((dsp (vector-ref fderives sym)))
- (if (not (null? dsp))
- (begin
- (vector-set! ruleset (car dsp) #t)
- (loop2 (cdr dsp))))))
- (loop (cdr csp)))))
-
- (let loop ((ruleno 1) (csp core) (itemsetv '())) ; ruleno = 0
- (if (< ruleno nrules)
- (if (vector-ref ruleset ruleno)
- (let ((itemno (vector-ref rrhs ruleno)))
- (let loop2 ((c csp) (itemsetv2 itemsetv))
- (if (and (pair? c)
- (< (car c) itemno))
- (loop2 (cdr c) (cons (car c) itemsetv2))
- (loop (+ ruleno 1) c (cons itemno itemsetv2)))))
- (loop (+ ruleno 1) csp itemsetv))
- (let loop2 ((c csp) (itemsetv2 itemsetv))
- (if (pair? c)
- (loop2 (cdr c) (cons (car c) itemsetv2))
- (reverse itemsetv2))))))
-
-
-
- (define (allocate-item-sets)
- (set! kernel-base (make-vector nsyms 0))
- (set! kernel-end (make-vector nsyms #f)))
-
-
- (define (allocate-storage)
- (allocate-item-sets)
- (set! red-set (make-vector (+ nrules 1) 0)))
-
- ; --
-
-
- (define (initialize-states)
- (let ((p (new-core)))
- (set-core-number! p 0)
- (set-core-acc-sym! p #f)
- (set-core-nitems! p 1)
- (set-core-items! p '(0))
-
- (set! first-state (list p))
- (set! last-state first-state)
- (set! nstates 1)))
-
-
-
- (define (generate-states)
- (allocate-storage)
- (set-fderives)
- (initialize-states)
- (let loop ((this-state first-state))
- (if (pair? this-state)
- (let* ((x (car this-state))
- (is (closure (core-items x))))
- (save-reductions x is)
- (new-itemsets is)
- (append-states)
- (if (> nshifts 0)
- (save-shifts x))
- (loop (cdr this-state))))))
-
-
- (define (new-itemsets itemset)
- ;; - Initialization
- (set! shift-symbol '())
- (let loop ((i 0))
- (if (< i nsyms)
- (begin
- (vector-set! kernel-end i '())
- (loop (+ i 1)))))
-
- (let loop ((isp itemset))
- (if (pair? isp)
- (let* ((i (car isp))
- (sym (vector-ref ritem i)))
- (if (>= sym 0)
- (begin
- (set! shift-symbol (sinsert sym shift-symbol))
- (let ((x (vector-ref kernel-end sym)))
- (if (null? x)
- (begin
- (vector-set! kernel-base sym (cons (+ i 1) x))
- (vector-set! kernel-end sym (vector-ref kernel-base sym)))
- (begin
- (set-cdr! x (list (+ i 1)))
- (vector-set! kernel-end sym (cdr x)))))))
- (loop (cdr isp)))))
-
- (set! nshifts (length shift-symbol)))
-
-
-
- (define (get-state sym)
- (let* ((isp (vector-ref kernel-base sym))
- (n (length isp))
- (key (let loop ((isp1 isp) (k 0))
- (if (null? isp1)
- (modulo k STATE-TABLE-SIZE)
- (loop (cdr isp1) (+ k (car isp1))))))
- (sp (vector-ref state-table key)))
- (if (null? sp)
- (let ((x (new-state sym)))
- (vector-set! state-table key (list x))
- (core-number x))
- (let loop ((sp1 sp))
- (if (and (= n (core-nitems (car sp1)))
- (let loop2 ((i1 isp) (t (core-items (car sp1))))
- (if (and (pair? i1)
- (= (car i1)
- (car t)))
- (loop2 (cdr i1) (cdr t))
- (null? i1))))
- (core-number (car sp1))
- (if (null? (cdr sp1))
- (let ((x (new-state sym)))
- (set-cdr! sp1 (list x))
- (core-number x))
- (loop (cdr sp1))))))))
-
-
- (define (new-state sym)
- (let* ((isp (vector-ref kernel-base sym))
- (n (length isp))
- (p (new-core)))
- (set-core-number! p nstates)
- (set-core-acc-sym! p sym)
- (if (= sym nvars) (set! final-state nstates))
- (set-core-nitems! p n)
- (set-core-items! p isp)
- (set-cdr! last-state (list p))
- (set! last-state (cdr last-state))
- (set! nstates (+ nstates 1))
- p))
-
-
- ; --
-
- (define (append-states)
- (set! shift-set
- (let loop ((l (reverse shift-symbol)))
- (if (null? l)
- '()
- (cons (get-state (car l)) (loop (cdr l)))))))
-
- ; --
-
- (define (save-shifts core)
- (let ((p (new-shift)))
- (set-shift-number! p (core-number core))
- (set-shift-nshifts! p nshifts)
- (set-shift-shifts! p shift-set)
- (if last-shift
- (begin
- (set-cdr! last-shift (list p))
- (set! last-shift (cdr last-shift)))
- (begin
- (set! first-shift (list p))
- (set! last-shift first-shift)))))
-
- (define (save-reductions core itemset)
- (let ((rs (let loop ((l itemset))
- (if (null? l)
- '()
- (let ((item (vector-ref ritem (car l))))
- (if (< item 0)
- (cons (- item) (loop (cdr l)))
- (loop (cdr l))))))))
- (if (pair? rs)
- (let ((p (new-red)))
- (set-red-number! p (core-number core))
- (set-red-nreds! p (length rs))
- (set-red-rules! p rs)
- (if last-reduction
- (begin
- (set-cdr! last-reduction (list p))
- (set! last-reduction (cdr last-reduction)))
- (begin
- (set! first-reduction (list p))
- (set! last-reduction first-reduction)))))))
-
-
- ; --
-
- (define (lalr)
- (set! token-set-size (+ 1 (quotient nterms (BITS-PER-WORD))))
- (set-accessing-symbol)
- (set-shift-table)
- (set-reduction-table)
- (set-max-rhs)
- (initialize-LA)
- (set-goto-map)
- (initialize-F)
- (build-relations)
- (digraph includes)
- (compute-lookaheads))
-
- (define (set-accessing-symbol)
- (set! acces-symbol (make-vector nstates #f))
- (let loop ((l first-state))
- (if (pair? l)
- (let ((x (car l)))
- (vector-set! acces-symbol (core-number x) (core-acc-sym x))
- (loop (cdr l))))))
-
- (define (set-shift-table)
- (set! shift-table (make-vector nstates #f))
- (let loop ((l first-shift))
- (if (pair? l)
- (let ((x (car l)))
- (vector-set! shift-table (shift-number x) x)
- (loop (cdr l))))))
-
- (define (set-reduction-table)
- (set! reduction-table (make-vector nstates #f))
- (let loop ((l first-reduction))
- (if (pair? l)
- (let ((x (car l)))
- (vector-set! reduction-table (red-number x) x)
- (loop (cdr l))))))
-
- (define (set-max-rhs)
- (let loop ((p 0) (curmax 0) (length 0))
- (let ((x (vector-ref ritem p)))
- (if x
- (if (>= x 0)
- (loop (+ p 1) curmax (+ length 1))
- (loop (+ p 1) (max curmax length) 0))
- (set! maxrhs curmax)))))
-
- (define (initialize-LA)
- (define (last l)
- (if (null? (cdr l))
- (car l)
- (last (cdr l))))
-
- (set! consistent (make-vector nstates #f))
- (set! lookaheads (make-vector (+ nstates 1) #f))
-
- (let loop ((count 0) (i 0))
- (if (< i nstates)
- (begin
- (vector-set! lookaheads i count)
- (let ((rp (vector-ref reduction-table i))
- (sp (vector-ref shift-table i)))
- (if (and rp
- (or (> (red-nreds rp) 1)
- (and sp
- (not
- (< (vector-ref acces-symbol
- (last (shift-shifts sp)))
- nvars)))))
- (loop (+ count (red-nreds rp)) (+ i 1))
- (begin
- (vector-set! consistent i #t)
- (loop count (+ i 1))))))
-
- (begin
- (vector-set! lookaheads nstates count)
- (let ((c (max count 1)))
- (set! LA (make-vector c #f))
- (do ((j 0 (+ j 1))) ((= j c)) (vector-set! LA j (new-set token-set-size)))
- (set! LAruleno (make-vector c -1))
- (set! lookback (make-vector c #f)))
- (let loop ((i 0) (np 0))
- (if (< i nstates)
- (if (vector-ref consistent i)
- (loop (+ i 1) np)
- (let ((rp (vector-ref reduction-table i)))
- (if rp
- (let loop2 ((j (red-rules rp)) (np2 np))
- (if (null? j)
- (loop (+ i 1) np2)
- (begin
- (vector-set! LAruleno np2 (car j))
- (loop2 (cdr j) (+ np2 1)))))
- (loop (+ i 1) np))))))))))
-
-
- (define (set-goto-map)
- (set! goto-map (make-vector (+ nvars 1) 0))
- (let ((temp-map (make-vector (+ nvars 1) 0)))
- (let loop ((ng 0) (sp first-shift))
- (if (pair? sp)
- (let loop2 ((i (reverse (shift-shifts (car sp)))) (ng2 ng))
- (if (pair? i)
- (let ((symbol (vector-ref acces-symbol (car i))))
- (if (< symbol nvars)
- (begin
- (vector-set! goto-map symbol
- (+ 1 (vector-ref goto-map symbol)))
- (loop2 (cdr i) (+ ng2 1)))
- (loop2 (cdr i) ng2)))
- (loop ng2 (cdr sp))))
-
- (let loop ((k 0) (i 0))
- (if (< i nvars)
- (begin
- (vector-set! temp-map i k)
- (loop (+ k (vector-ref goto-map i)) (+ i 1)))
-
- (begin
- (do ((i 0 (+ i 1)))
- ((>= i nvars))
- (vector-set! goto-map i (vector-ref temp-map i)))
-
- (set! ngotos ng)
- (vector-set! goto-map nvars ngotos)
- (vector-set! temp-map nvars ngotos)
- (set! from-state (make-vector ngotos #f))
- (set! to-state (make-vector ngotos #f))
-
- (do ((sp first-shift (cdr sp)))
- ((null? sp))
- (let* ((x (car sp))
- (state1 (shift-number x)))
- (do ((i (shift-shifts x) (cdr i)))
- ((null? i))
- (let* ((state2 (car i))
- (symbol (vector-ref acces-symbol state2)))
- (if (< symbol nvars)
- (let ((k (vector-ref temp-map symbol)))
- (vector-set! temp-map symbol (+ k 1))
- (vector-set! from-state k state1)
- (vector-set! to-state k state2))))))))))))))
-
-
- (define (map-goto state symbol)
- (let loop ((low (vector-ref goto-map symbol))
- (high (- (vector-ref goto-map (+ symbol 1)) 1)))
- (if (> low high)
- (begin
- (display (list "Error in map-goto" state symbol)) (newline)
- 0)
- (let* ((middle (quotient (+ low high) 2))
- (s (vector-ref from-state middle)))
- (cond
- ((= s state)
- middle)
- ((< s state)
- (loop (+ middle 1) high))
- (else
- (loop low (- middle 1))))))))
-
-
- (define (initialize-F)
- (set! F (make-vector ngotos #f))
- (do ((i 0 (+ i 1))) ((= i ngotos)) (vector-set! F i (new-set token-set-size)))
-
- (let ((reads (make-vector ngotos #f)))
-
- (let loop ((i 0) (rowp 0))
- (if (< i ngotos)
- (let* ((rowf (vector-ref F rowp))
- (stateno (vector-ref to-state i))
- (sp (vector-ref shift-table stateno)))
- (if sp
- (let loop2 ((j (shift-shifts sp)) (edges '()))
- (if (pair? j)
- (let ((symbol (vector-ref acces-symbol (car j))))
- (if (< symbol nvars)
- (if (vector-ref nullable symbol)
- (loop2 (cdr j) (cons (map-goto stateno symbol)
- edges))
- (loop2 (cdr j) edges))
- (begin
- (set-bit rowf (- symbol nvars))
- (loop2 (cdr j) edges))))
- (if (pair? edges)
- (vector-set! reads i (reverse edges))))))
- (loop (+ i 1) (+ rowp 1)))))
- (digraph reads)))
-
- (define (add-lookback-edge stateno ruleno gotono)
- (let ((k (vector-ref lookaheads (+ stateno 1))))
- (let loop ((found #f) (i (vector-ref lookaheads stateno)))
- (if (and (not found) (< i k))
- (if (= (vector-ref LAruleno i) ruleno)
- (loop #t i)
- (loop found (+ i 1)))
-
- (if (not found)
- (begin (display "Error in add-lookback-edge : ")
- (display (list stateno ruleno gotono)) (newline))
- (vector-set! lookback i
- (cons gotono (vector-ref lookback i))))))))
-
-
- (define (transpose r-arg n)
- (let ((new-end (make-vector n #f))
- (new-R (make-vector n #f)))
- (do ((i 0 (+ i 1)))
- ((= i n))
- (let ((x (list 'bidon)))
- (vector-set! new-R i x)
- (vector-set! new-end i x)))
- (do ((i 0 (+ i 1)))
- ((= i n))
- (let ((sp (vector-ref r-arg i)))
- (if (pair? sp)
- (let loop ((sp2 sp))
- (if (pair? sp2)
- (let* ((x (car sp2))
- (y (vector-ref new-end x)))
- (set-cdr! y (cons i (cdr y)))
- (vector-set! new-end x (cdr y))
- (loop (cdr sp2))))))))
- (do ((i 0 (+ i 1)))
- ((= i n))
- (vector-set! new-R i (cdr (vector-ref new-R i))))
-
- new-R))
-
-
-
- (define (build-relations)
-
- (define (get-state stateno symbol)
- (let loop ((j (shift-shifts (vector-ref shift-table stateno)))
- (stno stateno))
- (if (null? j)
- stno
- (let ((st2 (car j)))
- (if (= (vector-ref acces-symbol st2) symbol)
- st2
- (loop (cdr j) st2))))))
-
- (set! includes (make-vector ngotos #f))
- (do ((i 0 (+ i 1)))
- ((= i ngotos))
- (let ((state1 (vector-ref from-state i))
- (symbol1 (vector-ref acces-symbol (vector-ref to-state i))))
- (let loop ((rulep (vector-ref derives symbol1))
- (edges '()))
- (if (pair? rulep)
- (let ((*rulep (car rulep)))
- (let loop2 ((rp (vector-ref rrhs *rulep))
- (stateno state1)
- (states (list state1)))
- (let ((*rp (vector-ref ritem rp)))
- (if (> *rp 0)
- (let ((st (get-state stateno *rp)))
- (loop2 (+ rp 1) st (cons st states)))
- (begin
-
- (if (not (vector-ref consistent stateno))
- (add-lookback-edge stateno *rulep i))
-
- (let loop2 ((done #f)
- (stp (cdr states))
- (rp2 (- rp 1))
- (edgp edges))
- (if (not done)
- (let ((*rp (vector-ref ritem rp2)))
- (if (< -1 *rp nvars)
- (loop2 (not (vector-ref nullable *rp))
- (cdr stp)
- (- rp2 1)
- (cons (map-goto (car stp) *rp) edgp))
- (loop2 #t stp rp2 edgp)))
-
- (loop (cdr rulep) edgp))))))))
- (vector-set! includes i edges)))))
- (set! includes (transpose includes ngotos)))
-
-
-
- (define (compute-lookaheads)
- (let ((n (vector-ref lookaheads nstates)))
- (let loop ((i 0))
- (if (< i n)
- (let loop2 ((sp (vector-ref lookback i)))
- (if (pair? sp)
- (let ((LA-i (vector-ref LA i))
- (F-j (vector-ref F (car sp))))
- (bit-union LA-i F-j token-set-size)
- (loop2 (cdr sp)))
- (loop (+ i 1))))))))
-
-
-
- (define (digraph relation)
- (define infinity (+ ngotos 2))
- (define INDEX (make-vector (+ ngotos 1) 0))
- (define VERTICES (make-vector (+ ngotos 1) 0))
- (define top 0)
- (define R relation)
-
- (define (traverse i)
- (set! top (+ 1 top))
- (vector-set! VERTICES top i)
- (let ((height top))
- (vector-set! INDEX i height)
- (let ((rp (vector-ref R i)))
- (if (pair? rp)
- (let loop ((rp2 rp))
- (if (pair? rp2)
- (let ((j (car rp2)))
- (if (= 0 (vector-ref INDEX j))
- (traverse j))
- (if (> (vector-ref INDEX i)
- (vector-ref INDEX j))
- (vector-set! INDEX i (vector-ref INDEX j)))
- (let ((F-i (vector-ref F i))
- (F-j (vector-ref F j)))
- (bit-union F-i F-j token-set-size))
- (loop (cdr rp2))))))
- (if (= (vector-ref INDEX i) height)
- (let loop ()
- (let ((j (vector-ref VERTICES top)))
- (set! top (- top 1))
- (vector-set! INDEX j infinity)
- (if (not (= i j))
- (begin
- (bit-union (vector-ref F i)
- (vector-ref F j)
- token-set-size)
- (loop)))))))))
-
- (let loop ((i 0))
- (if (< i ngotos)
- (begin
- (if (and (= 0 (vector-ref INDEX i))
- (pair? (vector-ref R i)))
- (traverse i))
- (loop (+ i 1))))))
-
-
- ;; ----------------------------------------------------------------------
- ;; operator precedence management
- ;; ----------------------------------------------------------------------
-
- ;; a vector of precedence descriptors where each element
- ;; is of the form (terminal type precedence)
- (define the-terminals/prec #f) ; terminal symbols with precedence
- ; the precedence is an integer >= 0
- (define (get-symbol-precedence sym)
- (caddr (vector-ref the-terminals/prec sym)))
- ; the operator type is either 'none, 'left, 'right, or 'nonassoc
- (define (get-symbol-assoc sym)
- (cadr (vector-ref the-terminals/prec sym)))
-
- (define rule-precedences '())
- (define (add-rule-precedence! rule sym)
- (set! rule-precedences
- (cons (cons rule sym) rule-precedences)))
-
- (define (get-rule-precedence ruleno)
- (cond
- ((assq ruleno rule-precedences)
- => (lambda (p)
- (get-symbol-precedence (cdr p))))
- (else
- ;; process the rule symbols from left to right
- (let loop ((i (vector-ref rrhs ruleno))
- (prec 0))
- (let ((item (vector-ref ritem i)))
- ;; end of rule
- (if (< item 0)
- prec
- (let ((i1 (+ i 1)))
- (if (>= item nvars)
- ;; it's a terminal symbol
- (loop i1 (get-symbol-precedence (- item nvars)))
- (loop i1 prec)))))))))
-
- ;; ----------------------------------------------------------------------
- ;; Build the various tables
- ;; ----------------------------------------------------------------------
-
- (define expected-conflicts 0)
-
- (define (build-tables)
-
- (define (resolve-conflict sym rule)
- (let ((sym-prec (get-symbol-precedence sym))
- (sym-assoc (get-symbol-assoc sym))
- (rule-prec (get-rule-precedence rule)))
- (cond
- ((> sym-prec rule-prec) 'shift)
- ((< sym-prec rule-prec) 'reduce)
- ((eq? sym-assoc 'left) 'reduce)
- ((eq? sym-assoc 'right) 'shift)
- (else 'none))))
-
- (define conflict-messages '())
-
- (define (add-conflict-message . l)
- (set! conflict-messages (cons l conflict-messages)))
-
- (define (log-conflicts)
- (if (> (length conflict-messages) expected-conflicts)
- (for-each
- (lambda (message)
- (for-each display message)
- (newline))
- conflict-messages)))
-
- ;; --- Add an action to the action table
- (define (add-action state symbol new-action)
- (let* ((state-actions (vector-ref action-table state))
- (actions (assv symbol state-actions)))
- (if (pair? actions)
- (let ((current-action (cadr actions)))
- (if (not (= new-action current-action))
- ;; -- there is a conflict
- (begin
- (if (and (<= current-action 0) (<= new-action 0))
- ;; --- reduce/reduce conflict
- (begin
- (add-conflict-message
- "%% Reduce/Reduce conflict (reduce " (- new-action) ", reduce " (- current-action)
- ") on '" (get-symbol (+ symbol nvars)) "' in state " state)
- (if (glr-driver?)
- (set-cdr! (cdr actions) (cons new-action (cddr actions)))
- (set-car! (cdr actions) (max current-action new-action))))
- ;; --- shift/reduce conflict
- ;; can we resolve the conflict using precedences?
- (case (resolve-conflict symbol (- current-action))
- ;; -- shift
- ((shift) (if (glr-driver?)
- (set-cdr! (cdr actions) (cons new-action (cddr actions)))
- (set-car! (cdr actions) new-action)))
- ;; -- reduce
- ((reduce) #f) ; well, nothing to do...
- ;; -- signal a conflict!
- (else (add-conflict-message
- "%% Shift/Reduce conflict (shift " new-action ", reduce " (- current-action)
- ") on '" (get-symbol (+ symbol nvars)) "' in state " state)
- (if (glr-driver?)
- (set-cdr! (cdr actions) (cons new-action (cddr actions)))
- (set-car! (cdr actions) new-action))))))))
-
- (vector-set! action-table state (cons (list symbol new-action) state-actions)))
- ))
-
- (define (add-action-for-all-terminals state action)
- (do ((i 1 (+ i 1)))
- ((= i nterms))
- (add-action state i action)))
-
- (set! action-table (make-vector nstates '()))
-
- (do ((i 0 (+ i 1))) ; i = state
- ((= i nstates))
- (let ((red (vector-ref reduction-table i)))
- (if (and red (>= (red-nreds red) 1))
- (if (and (= (red-nreds red) 1) (vector-ref consistent i))
- (if (glr-driver?)
- (add-action-for-all-terminals i (- (car (red-rules red))))
- (add-action i 'default (- (car (red-rules red)))))
- (let ((k (vector-ref lookaheads (+ i 1))))
- (let loop ((j (vector-ref lookaheads i)))
- (if (< j k)
- (let ((rule (- (vector-ref LAruleno j)))
- (lav (vector-ref LA j)))
- (let loop2 ((token 0) (x (vector-ref lav 0)) (y 1) (z 0))
- (if (< token nterms)
- (begin
- (let ((in-la-set? (modulo x 2)))
- (if (= in-la-set? 1)
- (add-action i token rule)))
- (if (= y (BITS-PER-WORD))
- (loop2 (+ token 1)
- (vector-ref lav (+ z 1))
- 1
- (+ z 1))
- (loop2 (+ token 1) (quotient x 2) (+ y 1) z)))))
- (loop (+ j 1)))))))))
-
- (let ((shiftp (vector-ref shift-table i)))
- (if shiftp
- (let loop ((k (shift-shifts shiftp)))
- (if (pair? k)
- (let* ((state (car k))
- (symbol (vector-ref acces-symbol state)))
- (if (>= symbol nvars)
- (add-action i (- symbol nvars) state))
- (loop (cdr k))))))))
-
- (add-action final-state 0 'accept)
- (log-conflicts))
-
- (define (compact-action-table terms)
- (define (most-common-action acts)
- (let ((accums '()))
- (let loop ((l acts))
- (if (pair? l)
- (let* ((x (cadar l))
- (y (assv x accums)))
- (if (and (number? x) (< x 0))
- (if y
- (set-cdr! y (+ 1 (cdr y)))
- (set! accums (cons `(,x . 1) accums))))
- (loop (cdr l)))))
-
- (let loop ((l accums) (max 0) (sym #f))
- (if (null? l)
- sym
- (let ((x (car l)))
- (if (> (cdr x) max)
- (loop (cdr l) (cdr x) (car x))
- (loop (cdr l) max sym)))))))
-
- (define (translate-terms acts)
- (map (lambda (act)
- (cons (list-ref terms (car act))
- (cdr act)))
- acts))
-
- (do ((i 0 (+ i 1)))
- ((= i nstates))
- (let ((acts (vector-ref action-table i)))
- (if (vector? (vector-ref reduction-table i))
- (let ((act (most-common-action acts)))
- (vector-set! action-table i
- (cons `(*default* ,(if act act '*error*))
- (translate-terms
- (lalr-filter (lambda (x)
- (not (and (= (length x) 2)
- (eq? (cadr x) act))))
- acts)))))
- (vector-set! action-table i
- (cons `(*default* *error*)
- (translate-terms acts)))))))
-
-
-
- ;; --
-
- (define (rewrite-grammar tokens grammar k)
-
- (define eoi '*eoi*)
-
- (define (check-terminal term terms)
- (cond
- ((not (valid-terminal? term))
- (lalr-error "invalid terminal: " term))
- ((member term terms)
- (lalr-error "duplicate definition of terminal: " term))))
-
- (define (prec->type prec)
- (cdr (assq prec '((left_ . left)
- (right_ . right)
- (nonassoc_ . nonassoc)))))
-
- (cond
- ;; --- a few error conditions
- ((not (list? tokens))
- (lalr-error "Invalid token list_ " tokens))
- ((not (pair? grammar))
- (lalr-error "Grammar definition must have a non-empty list of productions" '()))
-
- (else
- ;; --- check the terminals
- (let loop1 ((lst tokens)
- (rev-terms '())
- (rev-terms/prec '())
- (prec-level 0))
- (if (pair? lst)
- (let ((term (car lst)))
- (cond
- ((pair? term)
- (if (and (memq (car term) '(left_ right_ nonassoc_))
- (not (null? (cdr term))))
- (let ((prec (+ prec-level 1))
- (optype (prec->type (car term))))
- (let loop-toks ((l (cdr term))
- (rev-terms rev-terms)
- (rev-terms/prec rev-terms/prec))
- (if (null? l)
- (loop1 (cdr lst) rev-terms rev-terms/prec prec)
- (let ((term (car l)))
- (check-terminal term rev-terms)
- (loop-toks
- (cdr l)
- (cons term rev-terms)
- (cons (list term optype prec) rev-terms/prec))))))
-
- (lalr-error "invalid operator precedence specification_ " term)))
-
- (else
- (check-terminal term rev-terms)
- (loop1 (cdr lst)
- (cons term rev-terms)
- (cons (list term 'none 0) rev-terms/prec)
- prec-level))))
-
- ;; --- check the grammar rules
- (let loop2 ((lst grammar) (rev-nonterm-defs '()))
- (if (pair? lst)
- (let ((def (car lst)))
- (if (not (pair? def))
- (lalr-error "Nonterminal definition must be a non-empty list" '())
- (let ((nonterm (car def)))
- (cond ((not (valid-nonterminal? nonterm))
- (lalr-error "Invalid nonterminal_" nonterm))
- ((or (member nonterm rev-terms)
- (assoc nonterm rev-nonterm-defs))
- (lalr-error "Nonterminal previously defined_" nonterm))
- (else
- (loop2 (cdr lst)
- (cons def rev-nonterm-defs)))))))
- (let* ((terms (cons eoi (cons 'error (reverse rev-terms))))
- (terms/prec (cons '(eoi none 0) (cons '(error none 0) (reverse rev-terms/prec))))
- (nonterm-defs (reverse rev-nonterm-defs))
- (nonterms (cons '*start* (map car nonterm-defs))))
- (if (= (length nonterms) 1)
- (lalr-error "Grammar must contain at least one nonterminal" '())
- (let loop-defs ((defs (cons `(*start* (,(cadr nonterms) ,eoi) \_ $1)
- nonterm-defs))
- (ruleno 0)
- (comp-defs '()))
- (if (pair? defs)
- (let* ((nonterm-def (car defs))
- (compiled-def (rewrite-nonterm-def
- nonterm-def
- ruleno
- terms nonterms)))
- (loop-defs (cdr defs)
- (+ ruleno (length compiled-def))
- (cons compiled-def comp-defs)))
-
- (let ((compiled-nonterm-defs (reverse comp-defs)))
- (k terms
- terms/prec
- nonterms
- (map (lambda (x) (cons (caaar x) (map cdar x)))
- compiled-nonterm-defs)
- (apply append compiled-nonterm-defs))))))))))))))
-
-
- (define (rewrite-nonterm-def nonterm-def ruleno terms nonterms)
-
- (define No-NT (length nonterms))
-
- (define (encode x)
- (let ((PosInNT (pos-in-list x nonterms)))
- (if PosInNT
- PosInNT
- (let ((PosInT (pos-in-list x terms)))
- (if PosInT
- (+ No-NT PosInT)
- (lalr-error "undefined symbol _ " x))))))
-
- (define (process-prec-directive rhs ruleno)
- (let loop ((l rhs))
- (if (null? l)
- '()
- (let ((first (car l))
- (rest (cdr l)))
- (cond
- ((or (member first terms) (member first nonterms))
- (cons first (loop rest)))
- ((and (pair? first)
- (eq? (car first) 'prec_))
- (if (and (pair? (cdr first))
- (null? (cddr first))
- (member (cadr first) terms))
- (if (null? rest)
- (begin
- (add-rule-precedence! ruleno (pos-in-list (cadr first) terms))
- (loop rest))
- (lalr-error "prec_ directive should be at end of rule_ " rhs))
- (lalr-error "Invalid prec_ directive_ " first)))
- (else
- (lalr-error "Invalid terminal or nonterminal_ " first)))))))
-
- (define (check-error-production rhs)
- (let loop ((rhs rhs))
- (if (pair? rhs)
- (begin
- (if (and (eq? (car rhs) 'error)
- (or (null? (cdr rhs))
- (not (member (cadr rhs) terms))
- (not (null? (cddr rhs)))))
- (lalr-error "Invalid 'error' production. A single terminal symbol must follow the 'error' token._" rhs))
- (loop (cdr rhs))))))
-
-
- (if (not (pair? (cdr nonterm-def)))
- (lalr-error "At least one production needed for nonterminal_" (car nonterm-def))
- (let ((name (symbol->string (car nonterm-def))))
- (let loop1 ((lst (cdr nonterm-def))
- (i 1)
- (rev-productions-and-actions '()))
- (if (not (pair? lst))
- (reverse rev-productions-and-actions)
- (let* ((rhs (process-prec-directive (car lst) (+ ruleno i -1)))
- (rest (cdr lst))
- (prod (map encode (cons (car nonterm-def) rhs))))
- ;; -- check for undefined tokens
- (for-each (lambda (x)
- (if (not (or (member x terms) (member x nonterms)))
- (lalr-error "Invalid terminal or nonterminal_" x)))
- rhs)
- ;; -- check 'error' productions
- (check-error-production rhs)
-
- (if (and (pair? rest)
- (eq? (car rest) '_)
- (pair? (cdr rest)))
- (loop1 (cddr rest)
- (+ i 1)
- (cons (cons prod (cadr rest))
- rev-productions-and-actions))
- (let* ((rhs-length (length rhs))
- (action
- (cons 'vector
- (cons (list 'quote (string->symbol
- (string-append
- name
- "-"
- (number->string i))))
- (let loop-j ((j 1))
- (if (> j rhs-length)
- '()
- (cons (string->symbol
- (string-append
- "$"
- (number->string j)))
- (loop-j (+ j 1)))))))))
- (loop1 rest
- (+ i 1)
- (cons (cons prod action)
- rev-productions-and-actions))))))))))
-
- (define (valid-nonterminal? x)
- (symbol? x))
-
- (define (valid-terminal? x)
- (symbol? x)) ; DB
-
- ;; ----------------------------------------------------------------------
- ;; Miscellaneous
- ;; ----------------------------------------------------------------------
- (define (pos-in-list x lst)
- (let loop ((lst lst) (i 0))
- (cond ((not (pair? lst)) #f)
- ((equal? (car lst) x) i)
- (else (loop (cdr lst) (+ i 1))))))
-
- (define (sunion lst1 lst2) ; union of sorted lists
- (let loop ((L1 lst1)
- (L2 lst2))
- (cond ((null? L1) L2)
- ((null? L2) L1)
- (else
- (let ((x (car L1)) (y (car L2)))
- (cond
- ((> x y)
- (cons y (loop L1 (cdr L2))))
- ((< x y)
- (cons x (loop (cdr L1) L2)))
- (else
- (loop (cdr L1) L2))
- ))))))
-
- (define (sinsert elem lst)
- (let loop ((l1 lst))
- (if (null? l1)
- (cons elem l1)
- (let ((x (car l1)))
- (cond ((< elem x)
- (cons elem l1))
- ((> elem x)
- (cons x (loop (cdr l1))))
- (else
- l1))))))
-
- (define (lalr-filter p lst)
- (let loop ((l lst))
- (if (null? l)
- '()
- (let ((x (car l)) (y (cdr l)))
- (if (p x)
- (cons x (loop y))
- (loop y))))))
-
- ;; ----------------------------------------------------------------------
- ;; Debugging tools ...
- ;; ----------------------------------------------------------------------
- (define the-terminals #f) ; names of terminal symbols
- (define the-nonterminals #f) ; non-terminals
-
- (define (print-item item-no)
- (let loop ((i item-no))
- (let ((v (vector-ref ritem i)))
- (if (>= v 0)
- (loop (+ i 1))
- (let* ((rlno (- v))
- (nt (vector-ref rlhs rlno)))
- (display (vector-ref the-nonterminals nt)) (display " --> ")
- (let loop ((i (vector-ref rrhs rlno)))
- (let ((v (vector-ref ritem i)))
- (if (= i item-no)
- (display ". "))
- (if (>= v 0)
- (begin
- (display (get-symbol v))
- (display " ")
- (loop (+ i 1)))
- (begin
- (display " (rule ")
- (display (- v))
- (display ")")
- (newline))))))))))
-
- (define (get-symbol n)
- (if (>= n nvars)
- (vector-ref the-terminals (- n nvars))
- (vector-ref the-nonterminals n)))
-
-
- (define (print-states)
- (define (print-action act)
- (cond
- ((eq? act '*error*)
- (display " _ Error"))
- ((eq? act 'accept)
- (display " _ Accept input"))
- ((< act 0)
- (display " _ reduce using rule ")
- (display (- act)))
- (else
- (display " _ shift and goto state ")
- (display act)))
- (newline)
- #t)
-
- (define (print-actions acts)
- (let loop ((l acts))
- (if (null? l)
- #t
- (let ((sym (caar l))
- (act (cadar l)))
- (display " ")
- (cond
- ((eq? sym 'default)
- (display "default action"))
- (else
- (if (number? sym)
- (display (get-symbol (+ sym nvars)))
- (display sym))))
- (print-action act)
- (loop (cdr l))))))
-
- (if (not action-table)
- (begin
- (display "No generated parser available!")
- (newline)
- #f)
- (begin
- (display "State table") (newline)
- (display "-----------") (newline) (newline)
-
- (let loop ((l first-state))
- (if (null? l)
- #t
- (let* ((core (car l))
- (i (core-number core))
- (items (core-items core))
- (actions (vector-ref action-table i)))
- (display "state ") (display i) (newline)
- (newline)
- (for-each (lambda (x) (display " ") (print-item x))
- items)
- (newline)
- (print-actions actions)
- (newline)
- (loop (cdr l))))))))
-
-
-
- ;; ----------------------------------------------------------------------
-
- (define build-goto-table
- (lambda ()
- `(vector
- ,@(map
- (lambda (shifts)
- (list 'quote
- (if shifts
- (let loop ((l (shift-shifts shifts)))
- (if (null? l)
- '()
- (let* ((state (car l))
- (symbol (vector-ref acces-symbol state)))
- (if (< symbol nvars)
- (cons `(,symbol . ,state)
- (loop (cdr l)))
- (loop (cdr l))))))
- '())))
- (vector->list shift-table)))))
-
-
- (define build-reduction-table
- (lambda (gram/actions)
- `(vector
- '()
- ,@(map
- (lambda (p)
- (let ((act (cdr p)))
- `(lambda ,(if (eq? driver-name 'lr-driver)
- '(___stack ___sp ___goto-table ___push yypushback)
- '(___sp ___goto-table ___push))
- ,(let* ((nt (caar p)) (rhs (cdar p)) (n (length rhs)))
- `(let* (,@(if act
- (let loop ((i 1) (l rhs))
- (if (pair? l)
- (let ((rest (cdr l))
- (ns (number->string (+ (- n i) 1))))
- (cons
- `(tok ,(if (eq? driver-name 'lr-driver)
- `(vector-ref ___stack (- ___sp ,(- (* i 2) 1)))
- `(list-ref ___sp ,(+ (* (- i 1) 2) 1))))
- (cons
- `(,(string->symbol (string-append "$" ns))
- (if (lexical-token? tok) (lexical-token-value tok) tok))
- (cons
- `(,(string->symbol (string-append "@" ns))
- (if (lexical-token? tok) (lexical-token-source tok) tok))
- (loop (+ i 1) rest)))))
- '()))
- '()))
- ,(if (= nt 0)
- '$1
- `(___push ,n ,nt ,(cdr p) ,@(if (eq? driver-name 'lr-driver) '() '(___sp))
- ,(if (eq? driver-name 'lr-driver)
- `(vector-ref ___stack (- ___sp ,(length rhs)))
- `(list-ref ___sp ,(length rhs))))))))))
-
- gram/actions))))
-
-
-
- ;; Options
-
- (define *valid-options*
- (list
- (cons 'out-table_
- (lambda (option)
- (and (list? option)
- (= (length option) 2)
- (string? (cadr option)))))
- (cons 'output_
- (lambda (option)
- (and (list? option)
- (= (length option) 3)
- (symbol? (cadr option))
- (string? (caddr option)))))
- (cons 'expect_
- (lambda (option)
- (and (list? option)
- (= (length option) 2)
- (integer? (cadr option))
- (>= (cadr option) 0))))
-
- (cons 'driver_
- (lambda (option)
- (and (list? option)
- (= (length option) 2)
- (symbol? (cadr option))
- (memq (cadr option) '(lr glr)))))))
-
-
- (define (validate-options options)
- (for-each
- (lambda (option)
- (let ((p (assoc (car option) *valid-options*)))
- (if (or (not p)
- (not ((cdr p) option)))
- (lalr-error "Invalid option_" option))))
- options))
-
-
- (define (output-parser! options code)
- (let ((option (assq 'output_ options)))
- (if option
- (let ((parser-name (cadr option))
- (file-name (caddr option)))
- (with-output-to-file file-name
- (lambda ()
- (pprint `(define ,parser-name ,code))
- (newline)))))))
-
-
- (define (output-table! options)
- (let ((option (assq 'out-table_ options)))
- (if option
- (let ((file-name (cadr option)))
- (with-output-to-file file-name print-states)))))
-
-
- (define (set-expected-conflicts! options)
- (let ((option (assq 'expect_ options)))
- (set! expected-conflicts (if option (cadr option) 0))))
-
- (define (set-driver-name! options)
- (let ((option (assq 'driver_ options)))
- (if option
- (let ((driver-type (cadr option)))
- (set! driver-name (if (eq? driver-type 'glr) 'glr-driver 'lr-driver))))))
-
-
- ;; -- arguments
-
- (define (extract-arguments lst proc)
- (let loop ((options '())
- (tokens '())
- (rules '())
- (lst lst))
- (if (pair? lst)
- (let ((p (car lst)))
- (cond
- ((and (pair? p)
- (lalr-keyword? (car p))
- (assq (car p) *valid-options*))
- (loop (cons p options) tokens rules (cdr lst)))
- (else
- (proc options p (cdr lst)))))
- (lalr-error "Malformed lalr-parser form" lst))))
-
-
- (define (build-driver options tokens rules)
- (validate-options options)
- (set-expected-conflicts! options)
- (set-driver-name! options)
- (let* ((gram/actions (gen-tables! tokens rules))
- (code `(,driver-name ',action-table ,(build-goto-table) ,(build-reduction-table gram/actions))))
-
- (output-table! options)
- (output-parser! options code)
- code))
-
- (extract-arguments arguments build-driver))
-
-
-
-;;;
-;;;; --
-;;;; Implementation of the lr-driver
-;;;
-
-
-(cond-expand
- (gambit
- (declare
- (standard-bindings)
- (fixnum)
- (block)
- (not safe)))
- (chicken
- (declare
- (uses extras)
- (usual-integrations)
- (fixnum)
- (not safe)))
- (else))
-
-
-;;;
-;;;; Source location utilities
-;;;
-
-
-;; This function assumes that src-location-1 and src-location-2 are source-locations
-;; Returns #f if they are not locations for the same input
-(define (combine-locations src-location-1 src-location-2)
- (let ((offset-1 (source-location-offset src-location-1))
- (offset-2 (source-location-offset src-location-2))
- (length-1 (source-location-length src-location-1))
- (length-2 (source-location-length src-location-2)))
-
- (cond ((not (equal? (source-location-input src-location-1)
- (source-location-input src-location-2)))
- #f)
- ((or (not (number? offset-1)) (not (number? offset-2))
- (not (number? length-1)) (not (number? length-2))
- (< offset-1 0) (< offset-2 0)
- (< length-1 0) (< length-2 0))
- (make-source-location (source-location-input src-location-1)
- (source-location-line src-location-1)
- (source-location-column src-location-1)
- -1 -1))
- ((<= offset-1 offset-2)
- (make-source-location (source-location-input src-location-1)
- (source-location-line src-location-1)
- (source-location-column src-location-1)
- offset-1
- (- (+ offset-2 length-2) offset-1)))
- (else
- (make-source-location (source-location-input src-location-1)
- (source-location-line src-location-1)
- (source-location-column src-location-1)
- offset-2
- (- (+ offset-1 length-1) offset-2))))))
-
-
-;;;
-;;;; LR-driver
-;;;
-
-
-(define *max-stack-size* 500)
-
-(define (lr-driver action-table goto-table reduction-table)
- (define ___atable action-table)
- (define ___gtable goto-table)
- (define ___rtable reduction-table)
-
- (define ___lexerp #f)
- (define ___errorp #f)
-
- (define ___stack #f)
- (define ___sp 0)
-
- (define ___curr-input #f)
- (define ___reuse-input #f)
-
- (define ___input #f)
- (define (___consume)
- (set! ___input (if ___reuse-input ___curr-input (___lexerp)))
- (set! ___reuse-input #f)
- (set! ___curr-input ___input))
-
- (define (___pushback)
- (set! ___reuse-input #t))
-
- (define (___initstack)
- (set! ___stack (make-vector *max-stack-size* 0))
- (set! ___sp 0))
-
- (define (___growstack)
- (let ((new-stack (make-vector (* 2 (vector-length ___stack)) 0)))
- (let loop ((i (- (vector-length ___stack) 1)))
- (if (>= i 0)
- (begin
- (vector-set! new-stack i (vector-ref ___stack i))
- (loop (- i 1)))))
- (set! ___stack new-stack)))
-
- (define (___checkstack)
- (if (>= ___sp (vector-length ___stack))
- (___growstack)))
-
- (define (___push delta new-category lvalue tok)
- (set! ___sp (- ___sp (* delta 2)))
- (let* ((state (vector-ref ___stack ___sp))
- (new-state (cdr (assoc new-category (vector-ref ___gtable state)))))
- (set! ___sp (+ ___sp 2))
- (___checkstack)
- (vector-set! ___stack ___sp new-state)
- (vector-set! ___stack (- ___sp 1) (note-source-location lvalue tok))))
-
- (define (___reduce st)
- ((vector-ref ___rtable st) ___stack ___sp ___gtable ___push ___pushback))
-
- (define (___shift token attribute)
- (set! ___sp (+ ___sp 2))
- (___checkstack)
- (vector-set! ___stack (- ___sp 1) attribute)
- (vector-set! ___stack ___sp token))
-
- (define (___action x l)
- (let ((y (assoc x l)))
- (if y (cadr y) (cadar l))))
-
- (define (___recover tok)
- (let find-state ((sp ___sp))
- (if (< sp 0)
- (set! ___sp sp)
- (let* ((state (vector-ref ___stack sp))
- (act (assoc 'error (vector-ref ___atable state))))
- (if act
- (begin
- (set! ___sp sp)
- (___sync (cadr act) tok))
- (find-state (- sp 2)))))))
-
- (define (___sync state tok)
- (let ((sync-set (map car (cdr (vector-ref ___atable state)))))
- (set! ___sp (+ ___sp 4))
- (___checkstack)
- (vector-set! ___stack (- ___sp 3) #f)
- (vector-set! ___stack (- ___sp 2) state)
- (let skip ()
- (let ((i (___category ___input)))
- (if (eq? i '*eoi*)
- (set! ___sp -1)
- (if (memq i sync-set)
- (let ((act (assoc i (vector-ref ___atable state))))
- (vector-set! ___stack (- ___sp 1) #f)
- (vector-set! ___stack ___sp (cadr act)))
- (begin
- (___consume)
- (skip))))))))
-
- (define (___category tok)
- (if (lexical-token? tok)
- (lexical-token-category tok)
- tok))
-
- (define (___run)
- (let loop ()
- (if ___input
- (let* ((state (vector-ref ___stack ___sp))
- (i (___category ___input))
- (act (___action i (vector-ref ___atable state))))
-
- (cond ((not (symbol? i))
- (___errorp "Syntax error_ invalid token_ " ___input)
- #f)
-
- ;; Input succesfully parsed
- ((eq? act 'accept)
- (vector-ref ___stack 1))
-
- ;; Syntax error in input
- ((eq? act '*error*)
- (if (eq? i '*eoi*)
- (begin
- (___errorp "Syntax error_ unexpected end of input")
- #f)
- (begin
- (___errorp "Syntax error_ unexpected token _ " ___input)
- (___recover i)
- (if (>= ___sp 0)
- (set! ___input #f)
- (begin
- (set! ___sp 0)
- (set! ___input '*eoi*)))
- (loop))))
-
- ;; Shift current token on top of the stack
- ((>= act 0)
- (___shift act ___input)
- (set! ___input (if (eq? i '*eoi*) '*eoi* #f))
- (loop))
-
- ;; Reduce by rule (- act)
- (else
- (___reduce (- act))
- (loop))))
-
- ;; no lookahead, so check if there is a default action
- ;; that does not require the lookahead
- (let* ((state (vector-ref ___stack ___sp))
- (acts (vector-ref ___atable state))
- (defact (if (pair? acts) (cadar acts) #f)))
- (if (and (= 1 (length acts)) (< defact 0))
- (___reduce (- defact))
- (___consume))
- (loop)))))
-
-
- (lambda (lexerp errorp)
- (set! ___errorp errorp)
- (set! ___lexerp lexerp)
- (___initstack)
- (___run)))
-
-
-;;;
-;;;; Simple-minded GLR-driver
-;;;
-
-
-(define (glr-driver action-table goto-table reduction-table)
- (define ___atable action-table)
- (define ___gtable goto-table)
- (define ___rtable reduction-table)
-
- (define ___lexerp #f)
- (define ___errorp #f)
-
- ;; -- Input handling
-
- (define *input* #f)
- (define (initialize-lexer lexer)
- (set! ___lexerp lexer)
- (set! *input* #f))
- (define (consume)
- (set! *input* (___lexerp)))
-
- (define (token-category tok)
- (if (lexical-token? tok)
- (lexical-token-category tok)
- tok))
-
- (define (token-attribute tok)
- (if (lexical-token? tok)
- (lexical-token-value tok)
- tok))
-
- ;; -- Processes (stacks) handling
-
- (define *processes* '())
-
- (define (initialize-processes)
- (set! *processes* '()))
- (define (add-process process)
- (set! *processes* (cons process *processes*)))
- (define (get-processes)
- (reverse *processes*))
-
- (define (for-all-processes proc)
- (let ((processes (get-processes)))
- (initialize-processes)
- (for-each proc processes)))
-
- ;; -- parses
- (define *parses* '())
- (define (get-parses)
- *parses*)
- (define (initialize-parses)
- (set! *parses* '()))
- (define (add-parse parse)
- (set! *parses* (cons parse *parses*)))
-
-
- (define (push delta new-category lvalue stack tok)
- (let* ((stack (drop stack (* delta 2)))
- (state (car stack))
- (new-state (cdr (assv new-category (vector-ref ___gtable state)))))
- (cons new-state (cons (note-source-location lvalue tok) stack))))
-
- (define (reduce state stack)
- ((vector-ref ___rtable state) stack ___gtable push))
-
- (define (shift state symbol stack)
- (cons state (cons symbol stack)))
-
- (define (get-actions token action-list)
- (let ((pair (assoc token action-list)))
- (if pair
- (cdr pair)
- (cdar action-list)))) ;; get the default action
-
-
- (define (run)
- (let loop-tokens ()
- (consume)
- (let ((symbol (token-category *input*)))
- (for-all-processes
- (lambda (process)
- (let loop ((stacks (list process)) (active-stacks '()))
- (cond ((pair? stacks)
- (let* ((stack (car stacks))
- (state (car stack)))
- (let actions-loop ((actions (get-actions symbol (vector-ref ___atable state)))
- (active-stacks active-stacks))
- (if (pair? actions)
- (let ((action (car actions))
- (other-actions (cdr actions)))
- (cond ((eq? action '*error*)
- (actions-loop other-actions active-stacks))
- ((eq? action 'accept)
- (add-parse (car (take-right stack 2)))
- (actions-loop other-actions active-stacks))
- ((>= action 0)
- (let ((new-stack (shift action *input* stack)))
- (add-process new-stack))
- (actions-loop other-actions active-stacks))
- (else
- (let ((new-stack (reduce (- action) stack)))
- (actions-loop other-actions (cons new-stack active-stacks))))))
- (loop (cdr stacks) active-stacks)))))
- ((pair? active-stacks)
- (loop (reverse active-stacks) '())))))))
- (if (pair? (get-processes))
- (loop-tokens))))
-
-
- (lambda (lexerp errorp)
- (set! ___errorp errorp)
- (initialize-lexer lexerp)
- (initialize-processes)
- (initialize-parses)
- (add-process '(0))
- (run)
- (get-parses)))
-
-
-(define (drop l n)
- (cond ((and (> n 0) (pair? l))
- (drop (cdr l) (- n 1)))
- (else
- l)))
-
-(define (take-right l n)
- (drop l (- (length l) n)));;; Multi-language support
-
-;; Copyright (C) 2001, 2009, 2010, 2011, 2013 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-;; 02110-1301 USA
-
-;;; Code_
-
-(define-module (system base language)
- #\use-module (system base syntax)
- #\export (define-language language? lookup-language make-language
- language-name language-title language-reader
- language-printer language-parser
- language-compilers language-decompilers language-evaluator
- language-joiner language-for-humans?
- language-make-default-environment
-
- lookup-compilation-order lookup-decompilation-order
- invalidate-compilation-cache! default-environment
-
- *current-language*)
-
- #\re-export (current-language))
-
-
-;;;
-;;; Language class
-;;;
-
-(define-record/keywords <language>
- name
- title
- reader
- printer
- (parser #f)
- (compilers '())
- (decompilers '())
- (evaluator #f)
- (joiner #f)
- (for-humans? #t)
- (make-default-environment make-fresh-user-module))
-
-(define-macro (define-language name . spec)
- `(begin
- (invalidate-compilation-cache!)
- (define ,name (make-language #\name ',name ,@spec))))
-
-(define (lookup-language name)
- (let ((m (resolve-module `(language ,name spec))))
- (if (module-bound? m name)
- (module-ref m name)
- (error "no such language" name))))
-
-(define *compilation-cache* '())
-(define *decompilation-cache* '())
-
-(define (invalidate-compilation-cache!)
- (set! *decompilation-cache* '())
- (set! *compilation-cache* '()))
-
-(define (compute-translation-order from to language-translators)
- (cond
- ((not (language? to))
- (compute-translation-order from (lookup-language to) language-translators))
- (else
- (let lp ((from from) (seen '()))
- (cond
- ((not (language? from))
- (lp (lookup-language from) seen))
- ((eq? from to) (reverse! seen))
- ((memq from seen) #f)
- (else (or-map (lambda (pair)
- (lp (car pair) (acons from (cdr pair) seen)))
- (language-translators from))))))))
-
-(define (lookup-compilation-order from to)
- (let ((key (cons from to)))
- (or (assoc-ref *compilation-cache* key)
- (let ((order (compute-translation-order from to language-compilers)))
- (set! *compilation-cache*
- (acons key order *compilation-cache*))
- order))))
-
-(define (lookup-decompilation-order from to)
- (let ((key (cons from to)))
- (or (assoc-ref *decompilation-cache* key)
- ;; trickery!
- (let ((order (and=>
- (compute-translation-order to from language-decompilers)
- reverse!)))
- (set! *decompilation-cache* (acons key order *decompilation-cache*))
- order))))
-
-(define (default-environment lang)
- "Return the default compilation environment for source language LANG."
- ((language-make-default-environment
- (if (language? lang) lang (lookup-language lang)))))
-
-
-
-;;;
-;;; Current language
-;;;
-
-;; Deprecated; use current-language instead.
-(define *current-language* (parameter-fluid current-language))
-;;; User interface messages
-
-;; Copyright (C) 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Commentary_
-;;;
-;;; This module provide a simple interface to send messages to the user.
-;;; TODO_ Internationalize messages.
-;;;
-;;; Code_
-
-(define-module (system base message)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-9)
- #\use-module (ice-9 match)
- #\export (*current-warning-port*
- *current-warning-prefix*
- warning
-
- warning-type? warning-type-name warning-type-description
- warning-type-printer lookup-warning-type
-
- %warning-types))
-
-
-;;;
-;;; Source location
-;;;
-
-(define (location-string loc)
- (if (pair? loc)
- (format #f "~a_~a_~a"
- (or (assoc-ref loc 'filename) "<stdin>")
- (1+ (assoc-ref loc 'line))
- (assoc-ref loc 'column))
- "<unknown-location>"))
-
-
-;;;
-;;; Warnings
-;;;
-
-;; This name existed before %current-warning-port was introduced, but
-;; otherwise it is a deprecated binding.
-(define *current-warning-port*
- ;; Can't play the identifier-syntax deprecation game in Guile 2.0, as
- ;; other modules might depend on this being a normal binding and not a
- ;; syntax binding.
- (parameter-fluid current-warning-port))
-
-(define *current-warning-prefix*
- ;; Prefix string when emitting a warning.
- (make-fluid ";;; "))
-
-
-(define-record-type <warning-type>
- (make-warning-type name description printer)
- warning-type?
- (name warning-type-name)
- (description warning-type-description)
- (printer warning-type-printer))
-
-(define %warning-types
- ;; List of known warning types.
- (map (lambda (args)
- (apply make-warning-type args))
-
- (let-syntax ((emit
- (lambda (s)
- (syntax-case s ()
- ((_ port fmt args ...)
- (string? (syntax->datum #'fmt))
- (with-syntax ((fmt
- (string-append "~a"
- (syntax->datum
- #'fmt))))
- #'(format port fmt
- (fluid-ref *current-warning-prefix*)
- args ...)))))))
- `((unsupported-warning ;; a "meta warning"
- "warn about unknown warning types"
- ,(lambda (port unused name)
- (emit port "warning_ unknown warning type `~A'~%"
- name)))
-
- (unused-variable
- "report unused variables"
- ,(lambda (port loc name)
- (emit port "~A_ warning_ unused variable `~A'~%"
- loc name)))
-
- (unused-toplevel
- "report unused local top-level variables"
- ,(lambda (port loc name)
- (emit port "~A_ warning_ possibly unused local top-level variable `~A'~%"
- loc name)))
-
- (unbound-variable
- "report possibly unbound variables"
- ,(lambda (port loc name)
- (emit port "~A_ warning_ possibly unbound variable `~A'~%"
- loc name)))
-
- (arity-mismatch
- "report procedure arity mismatches (wrong number of arguments)"
- ,(lambda (port loc name certain?)
- (if certain?
- (emit port
- "~A_ warning_ wrong number of arguments to `~A'~%"
- loc name)
- (emit port
- "~A_ warning_ possibly wrong number of arguments to `~A'~%"
- loc name))))
-
- (duplicate-case-datum
- "report a duplicate datum in a case expression"
- ,(lambda (port loc datum clause case-expr)
- (emit port
- "~A_ warning_ duplicate datum ~S in clause ~S of case expression ~S~%"
- loc datum clause case-expr)))
-
- (bad-case-datum
- "report a case datum that cannot be meaningfully compared using `eqv?'"
- ,(lambda (port loc datum clause case-expr)
- (emit port
- "~A_ warning_ datum ~S cannot be meaningfully compared using `eqv?' in clause ~S of case expression ~S~%"
- loc datum clause case-expr)))
-
- (format
- "report wrong number of arguments to `format'"
- ,(lambda (port loc . rest)
- (define (escape-newlines str)
- (list->string
- (string-fold-right (lambda (c r)
- (if (eq? c #\newline)
- (append '(#\\ #\n) r)
- (cons c r)))
- '()
- str)))
-
- (define (range min max)
- (cond ((eq? min 'any)
- (if (eq? max 'any)
- "any number" ;; can't happen
- (emit #f "up to ~a" max)))
- ((eq? max 'any)
- (emit #f "at least ~a" min))
- ((= min max) (number->string min))
- (else
- (emit #f "~a to ~a" min max))))
-
- (match rest
- (('simple-format fmt opt)
- (emit port
- "~A_ warning_ ~S_ unsupported format option ~~~A, use (ice-9 format) instead~%"
- loc (escape-newlines fmt) opt))
- (('wrong-format-arg-count fmt min max actual)
- (emit port
- "~A_ warning_ ~S_ wrong number of `format' arguments_ expected ~A, got ~A~%"
- loc (escape-newlines fmt)
- (range min max) actual))
- (('syntax-error 'unterminated-iteration fmt)
- (emit port "~A_ warning_ ~S_ unterminated iteration~%"
- loc (escape-newlines fmt)))
- (('syntax-error 'unterminated-conditional fmt)
- (emit port "~A_ warning_ ~S_ unterminated conditional~%"
- loc (escape-newlines fmt)))
- (('syntax-error 'unexpected-semicolon fmt)
- (emit port "~A_ warning_ ~S_ unexpected `~~;'~%"
- loc (escape-newlines fmt)))
- (('syntax-error 'unexpected-conditional-termination fmt)
- (emit port "~A_ warning_ ~S_ unexpected `~~]'~%"
- loc (escape-newlines fmt)))
- (('wrong-port wrong-port)
- (emit port
- "~A_ warning_ ~S_ wrong port argument~%"
- loc wrong-port))
- (('wrong-format-string fmt)
- (emit port
- "~A_ warning_ ~S_ wrong format string~%"
- loc fmt))
- (('non-literal-format-string)
- (emit port
- "~A_ warning_ non-literal format string~%"
- loc))
- (('wrong-num-args count)
- (emit port
- "~A_ warning_ wrong number of arguments to `format'~%"
- loc))
- (else
- (emit port "~A_ `format' warning~%" loc)))))))))
-
-(define (lookup-warning-type name)
- "Return the warning type NAME or `#f' if not found."
- (find (lambda (wt)
- (eq? name (warning-type-name wt)))
- %warning-types))
-
-(define (warning type location . args)
- "Emit a warning of type TYPE for source location LOCATION (a source
-property alist) using the data in ARGS."
- (let ((wt (lookup-warning-type type))
- (port (current-warning-port)))
- (if (warning-type? wt)
- (apply (warning-type-printer wt)
- port (location-string location)
- args)
- (format port "~A_ unknown warning type `~A'_ ~A~%"
- (location-string location) type args))))
-
-;;; message.scm ends here
-;;; pmatch, a simple matcher
-
-;;; Copyright (C) 2009, 2010, 2012 Free Software Foundation, Inc
-;;; Copyright (C) 2005,2006,2007 Oleg Kiselyov
-;;; Copyright (C) 2007 Daniel P. Friedman
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Originally written by Oleg Kiselyov for LeanTAP in Kanren, which is
-;;; available under the MIT license.
-;;;
-;;; http_//kanren.cvs.sourceforge.net/viewvc/kanren/kanren/mini/leanTAP.scm?view=log
-;;;
-;;; This version taken from_
-;;; αKanren_ A Fresh Name in Nominal Logic Programming
-;;; by William E. Byrd and Daniel P. Friedman
-;;; Proceedings of the 2007 Workshop on Scheme and Functional Programming
-;;; Université Laval Technical Report DIUL-RT-0701
-
-;;; To be clear_ the original code is MIT-licensed, and the modifications
-;;; made to it by Guile are under Guile's license (currently LGPL v3+).
-
-;;; Code_
-
-(define-module (system base pmatch)
- #\export-syntax (pmatch))
-
-(define-syntax-rule (pmatch e cs ...)
- (let ((v e)) (pmatch1 v cs ...)))
-
-(define-syntax pmatch1
- (syntax-rules (else guard)
- ((_ v) (if #f #f))
- ((_ v (else e0 e ...)) (let () e0 e ...))
- ((_ v (pat (guard g ...) e0 e ...) cs ...)
- (let ((fk (lambda () (pmatch1 v cs ...))))
- (ppat v pat
- (if (and g ...) (let () e0 e ...) (fk))
- (fk))))
- ((_ v (pat e0 e ...) cs ...)
- (let ((fk (lambda () (pmatch1 v cs ...))))
- (ppat v pat (let () e0 e ...) (fk))))))
-
-(define-syntax ppat
- (syntax-rules (_ quote unquote)
- ((_ v _ kt kf) kt)
- ((_ v () kt kf) (if (null? v) kt kf))
- ((_ v (quote lit) kt kf)
- (if (equal? v (quote lit)) kt kf))
- ((_ v (unquote var) kt kf) (let ((var v)) kt))
- ((_ v (x . y) kt kf)
- (if (pair? v)
- (let ((vx (car v)) (vy (cdr v)))
- (ppat vx x (ppat vy y kt kf) kf))
- kf))
- ((_ v lit kt kf) (if (eq? v (quote lit)) kt kf))))
-;;; Guile VM specific syntaxes and utilities
-
-;; Copyright (C) 2001, 2009, 2016 Free Software Foundation, Inc
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code_
-
-(define-module (system base syntax)
- #\export (%compute-initargs)
- #\export-syntax (define-type define-record define-record/keywords
- record-case transform-record))
-
-(define (symbol-trim-both sym pred)
- (string->symbol (string-trim-both (symbol->string sym) pred)))
-(define (trim-brackets sym)
- (symbol-trim-both sym (list->char-set '(#\< #\>))))
-
-
-;;;
-;;; Type
-;;;
-
-(define-macro (define-type name . rest)
- (let ((name (if (pair? name) (car name) name))
- (opts (if (pair? name) (cdr name) '())))
- (let ((printer (kw-arg-ref opts #\printer))
- (common-slots (or (kw-arg-ref opts #\common-slots) '())))
- `(begin ,@(map (lambda (def)
- `(define-record ,(if printer
- `(,(car def) ,printer)
- (car def))
- ,@common-slots
- ,@(cdr def)))
- rest)
- ,@(map (lambda (common-slot i)
- `(define ,(symbol-append (trim-brackets name)
- '- common-slot)
- (make-procedure-with-setter
- (lambda (x) (struct-ref x ,i))
- (lambda (x v) (struct-set! x ,i v)))))
- common-slots (iota (length common-slots)))))))
-
-
-;;;
-;;; Record
-;;;
-
-(define-macro (define-record name-form . slots)
- (let* ((name (if (pair? name-form) (car name-form) name-form))
- (printer (and (pair? name-form) (cadr name-form)))
- (slot-names (map (lambda (slot) (if (pair? slot) (car slot) slot))
- slots))
- (stem (trim-brackets name)))
- `(begin
- (define ,name (make-record-type ,(symbol->string name) ',slot-names
- ,@(if printer (list printer) '())))
- ,(let* ((reqs (let lp ((slots slots))
- (if (or (null? slots) (not (symbol? (car slots))))
- '()
- (cons (car slots) (lp (cdr slots))))))
- (opts (list-tail slots (length reqs)))
- (tail (module-gensym "defrec")))
- `(define (,(symbol-append 'make- stem) ,@reqs . ,tail)
- (let ,(map (lambda (o)
- `(,(car o) (cond ((null? ,tail) ,(cadr o))
- (else (let ((_x (car ,tail)))
- (set! ,tail (cdr ,tail))
- _x)))))
- opts)
- (make-struct ,name 0 ,@slot-names))))
- (define ,(symbol-append stem '?) (record-predicate ,name))
- ,@(map (lambda (sname)
- `(define ,(symbol-append stem '- sname)
- (make-procedure-with-setter
- (record-accessor ,name ',sname)
- (record-modifier ,name ',sname))))
- slot-names))))
-
-;; like the former, but accepting keyword arguments in addition to
-;; optional arguments
-(define-macro (define-record/keywords name-form . slots)
- (let* ((name (if (pair? name-form) (car name-form) name-form))
- (printer (and (pair? name-form) (cadr name-form)))
- (slot-names (map (lambda (slot) (if (pair? slot) (car slot) slot))
- slots))
- (stem (trim-brackets name)))
- `(begin
- (define ,name (make-record-type ,(symbol->string name) ',slot-names
- ,@(if printer (list printer) '())))
- (define ,(symbol-append 'make- stem)
- (let ((slots (list ,@(map (lambda (slot)
- (if (pair? slot)
- `(cons ',(car slot) ,(cadr slot))
- `',slot))
- slots)))
- (constructor (record-constructor ,name)))
- (lambda args
- (apply constructor (%compute-initargs args slots)))))
- (define ,(symbol-append stem '?) (record-predicate ,name))
- ,@(map (lambda (sname)
- `(define ,(symbol-append stem '- sname)
- (make-procedure-with-setter
- (record-accessor ,name ',sname)
- (record-modifier ,name ',sname))))
- slot-names))))
-
-(define (%compute-initargs args slots)
- (define (finish out)
- (map (lambda (slot)
- (let ((name (if (pair? slot) (car slot) slot)))
- (cond ((assq name out) => cdr)
- ((pair? slot) (cdr slot))
- (else (error "unbound slot" args slots name)))))
- slots))
- (let lp ((in args) (positional slots) (out '()))
- (cond
- ((null? in)
- (finish out))
- ((keyword? (car in))
- (let ((sym (keyword->symbol (car in))))
- (cond
- ((and (not (memq sym slots))
- (not (assq sym (filter pair? slots))))
- (error "unknown slot" sym))
- ((assq sym out) (error "slot already set" sym out))
- (else (lp (cddr in) '() (acons sym (cadr in) out))))))
- ((null? positional)
- (error "too many initargs" args slots))
- (else
- (lp (cdr in) (cdr positional)
- (let ((slot (car positional)))
- (acons (if (pair? slot) (car slot) slot)
- (car in)
- out)))))))
-
-;; So, dear reader. It is pleasant indeed around this fire or at this
-;; cafe or in this room, is it not? I think so too.
-;;
-;; This macro used to generate code that looked like this_
-;;
-;; `(((record-predicate ,record-type) ,r)
-;; (let ,(map (lambda (slot)
-;; (if (pair? slot)
-;; `(,(car slot) ((record-accessor ,record-type ',(cadr slot)) ,r))
-;; `(,slot ((record-accessor ,record-type ',slot) ,r))))
-;; slots)
-;; ,@body)))))
-;;
-;; But this was a hot spot, so computing all those predicates and
-;; accessors all the time was getting expensive, so we did a terrible
-;; thing_ we decided that since above we're already defining accessors
-;; and predicates with computed names, we might as well just rely on that fact here.
-;;
-;; It's a bit nasty, I agree. But it is fast.
-;;
-;;scheme@(guile-user)> (with-statprof #\hz 1000 #\full-stacks? #t (resolve-module '(oop goops)))% cumulative self
-;; time seconds seconds name
-;; 8.82 0.03 0.01 glil->assembly
-;; 8.82 0.01 0.01 record-type-fields
-;; 5.88 0.01 0.01 %compute-initargs
-;; 5.88 0.01 0.01 list-index
-
-
-;;; So ugly... but I am too ignorant to know how to make it better.
-(define-syntax record-case
- (lambda (x)
- (syntax-case x ()
- ((_ record clause ...)
- (let ((r (syntax r))
- (rtd (syntax rtd)))
- (define (process-clause tag fields exprs)
- (let ((infix (trim-brackets (syntax->datum tag))))
- (with-syntax ((tag tag)
- (((f . accessor) ...)
- (let lp ((fields fields))
- (syntax-case fields ()
- (() (syntax ()))
- (((v0 f0) f1 ...)
- (acons (syntax v0)
- (datum->syntax x
- (symbol-append infix '- (syntax->datum
- (syntax f0))))
- (lp (syntax (f1 ...)))))
- ((f0 f1 ...)
- (acons (syntax f0)
- (datum->syntax x
- (symbol-append infix '- (syntax->datum
- (syntax f0))))
- (lp (syntax (f1 ...))))))))
- ((e0 e1 ...)
- (syntax-case exprs ()
- (() (syntax (#t)))
- ((e0 e1 ...) (syntax (e0 e1 ...))))))
- (syntax
- ((eq? rtd tag)
- (let ((f (accessor r))
- ...)
- e0 e1 ...))))))
- (with-syntax
- ((r r)
- (rtd rtd)
- ((processed ...)
- (let lp ((clauses (syntax (clause ...)))
- (out '()))
- (syntax-case clauses (else)
- (()
- (reverse! (cons (syntax
- (else (error "unhandled record" r)))
- out)))
- (((else e0 e1 ...))
- (reverse! (cons (syntax (else e0 e1 ...)) out)))
- (((else e0 e1 ...) . rest)
- (syntax-violation 'record-case
- "bad else clause placement"
- (syntax x)
- (syntax (else e0 e1 ...))))
- ((((<foo> f0 ...) e0 ...) . rest)
- (lp (syntax rest)
- (cons (process-clause (syntax <foo>)
- (syntax (f0 ...))
- (syntax (e0 ...)))
- out)))))))
- (syntax
- (let* ((r record)
- (rtd (struct-vtable r)))
- (cond processed ...)))))))))
-
-
-;; Here we take the terrorism to another level. Nasty, but the client
-;; code looks good.
-
-(define-macro (transform-record type-and-common record . clauses)
- (let ((r (module-gensym "rec"))
- (rtd (module-gensym "rtd"))
- (type-stem (trim-brackets (car type-and-common))))
- (define (make-stem s)
- (symbol-append type-stem '- s))
- (define (further-predicates x record-stem slots)
- (define (access slot)
- `(,(symbol-append (make-stem record-stem) '- slot) ,x))
- (let lp ((in slots) (out '()))
- (cond ((null? in) out)
- ((pair? (car in))
- (let ((slot (caar in))
- (arg (cadar in)))
- (cond ((symbol? arg)
- (lp (cdr in) out))
- ((pair? arg)
- (lp (cdr in)
- (append (further-predicates (access slot)
- (car arg)
- (cdr arg))
- out)))
- (else (lp (cdr in) (cons `(eq? ,(access slot) ',arg)
- out))))))
- (else (lp (cdr in) out)))))
- (define (let-clauses x record-stem slots)
- (define (access slot)
- `(,(symbol-append (make-stem record-stem) '- slot) ,x))
- (let lp ((in slots) (out '()))
- (cond ((null? in) out)
- ((pair? (car in))
- (let ((slot (caar in))
- (arg (cadar in)))
- (cond ((symbol? arg)
- (lp (cdr in)
- (cons `(,arg ,(access slot)) out)))
- ((pair? arg)
- (lp (cdr in)
- (append (let-clauses (access slot)
- (car arg)
- (cdr arg))
- out)))
- (else
- (lp (cdr in) out)))))
- (else
- (lp (cdr in)
- (cons `(,(car in) ,(access (car in))) out))))))
- (define (transform-expr x)
- (cond ((not (pair? x)) x)
- ((eq? (car x) '->)
- (if (= (length x) 2)
- (let ((form (cadr x)))
- `(,(symbol-append 'make- (make-stem (car form)))
- ,@(cdr type-and-common)
- ,@(map (lambda (y)
- (if (and (pair? y) (eq? (car y) 'unquote))
- (transform-expr (cadr y))
- y))
- (cdr form))))
- (error "bad -> form" x)))
- (else (cons (car x) (map transform-expr (cdr x))))))
- (define (process-clause clause)
- (if (eq? (car clause) 'else)
- clause
- (let ((stem (caar clause))
- (slots (cdar clause))
- (body (cdr clause)))
- (let ((record-type (symbol-append '< (make-stem stem) '>)))
- `((and (eq? ,rtd ,record-type)
- ,@(reverse (further-predicates r stem slots)))
- (let ,(reverse (let-clauses r stem slots))
- ,@(if (pair? body)
- (map transform-expr body)
- '((if #f #f)))))))))
- `(let* ((,r ,record)
- (,rtd (struct-vtable ,r))
- ,@(map (lambda (slot)
- `(,slot (,(make-stem slot) ,r)))
- (cdr type-and-common)))
- (cond ,@(let ((clauses (map process-clause clauses)))
- (if (assq 'else clauses)
- clauses
- (append clauses `((else (error "unhandled record" ,r))))))))))
-;;; Compilation targets
-
-;; Copyright (C) 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-;; 02110-1301 USA
-
-;;; Code_
-
-(define-module (system base target)
- #\use-module (rnrs bytevectors)
- #\use-module (ice-9 regex)
- #\export (target-type with-target
-
- target-cpu target-vendor target-os
-
- target-endianness target-word-size))
-
-
-
-;;;
-;;; Target types
-;;;
-
-(define %native-word-size
- ;; The native word size. Note_ don't use `word-size' from
- ;; (system vm objcode) to avoid a circular dependency.
- ((@ (system foreign) sizeof) '*))
-
-(define %target-type (make-fluid %host-type))
-(define %target-endianness (make-fluid (native-endianness)))
-(define %target-word-size (make-fluid %native-word-size))
-
-(define (validate-target target)
- (if (or (not (string? target))
- (let ((parts (string-split target #\-)))
- (or (< (length parts) 3)
- (or-map string-null? parts))))
- (error "invalid target" target)))
-
-(define (with-target target thunk)
- (validate-target target)
- (let ((cpu (triplet-cpu target)))
- (with-fluids ((%target-type target)
- (%target-endianness (cpu-endianness cpu))
- (%target-word-size (triplet-pointer-size target)))
- (thunk))))
-
-(define (cpu-endianness cpu)
- "Return the endianness for CPU."
- (if (string=? cpu (triplet-cpu %host-type))
- (native-endianness)
- (cond ((string-match "^i[0-9]86$" cpu)
- (endianness little))
- ((member cpu '("x86_64" "ia64"
- "powerpcle" "powerpc64le" "mipsel" "mips64el" "nios2" "sh3" "sh4" "alpha"))
- (endianness little))
- ((member cpu '("sparc" "sparc64" "powerpc" "powerpc64" "spu"
- "mips" "mips64" "m68k" "s390x"))
- (endianness big))
- ((string-match "^arm.*el" cpu)
- (endianness little))
- ((string-match "^arm.*eb" cpu)
- (endianness big))
- ((string-prefix? "arm" cpu) ;ARMs are LE by default
- (endianness little))
- ((string-match "^aarch64.*be" cpu)
- (endianness big))
- ((string=? "aarch64" cpu)
- (endianness little))
- (else
- (error "unknown CPU endianness" cpu)))))
-
-(define (triplet-pointer-size triplet)
- "Return the size of pointers in bytes for TRIPLET."
- (let ((cpu (triplet-cpu triplet)))
- (cond ((and (string=? cpu (triplet-cpu %host-type))
- (string=? (triplet-os triplet) (triplet-os %host-type)))
- %native-word-size)
-
- ((string-match "^i[0-9]86$" cpu) 4)
-
- ;; Although GNU config.guess doesn't yet recognize them,
- ;; Debian (ab)uses the OS part to denote the specific ABI
- ;; being used_ <http_//wiki.debian.org/Multiarch/Tuples>.
- ;; See <http_//www.linux-mips.org/wiki/WhatsWrongWithO32N32N64>
- ;; for details on the MIPS ABIs.
- ((string-match "^mips64.*-gnuabi64" triplet) 8) ; n64 ABI
- ((string-match "^mips64" cpu) 4) ; n32 or o32
-
- ((string-match "^x86_64-.*-gnux32" triplet) 4) ; x32
-
- ((string-match "64$" cpu) 8)
- ((string-match "64_?[lbe][lbe]$" cpu) 8)
- ((member cpu '("sparc" "powerpc" "mips" "mipsel" "nios2" "m68k" "sh3" "sh4")) 4)
- ((member cpu '("s390x" "alpha")) 8)
- ((string-match "^arm.*" cpu) 4)
- (else (error "unknown CPU word size" cpu)))))
-
-(define (triplet-cpu t)
- (substring t 0 (string-index t #\-)))
-
-(define (triplet-vendor t)
- (let ((start (1+ (string-index t #\-))))
- (substring t start (string-index t #\- start))))
-
-(define (triplet-os t)
- (let ((start (1+ (string-index t #\- (1+ (string-index t #\-))))))
- (substring t start)))
-
-
-(define (target-type)
- "Return the GNU configuration triplet of the target platform."
- (fluid-ref %target-type))
-
-(define (target-cpu)
- "Return the CPU name of the target platform."
- (triplet-cpu (target-type)))
-
-(define (target-vendor)
- "Return the vendor name of the target platform."
- (triplet-vendor (target-type)))
-
-(define (target-os)
- "Return the operating system name of the target platform."
- (triplet-os (target-type)))
-
-(define (target-endianness)
- "Return the endianness object of the target platform."
- (fluid-ref %target-endianness))
-
-(define (target-word-size)
- "Return the word size, in bytes, of the target platform."
- (fluid-ref %target-word-size))
-;;; 'SCM' type tag decoding.
-;;; Copyright (C) 2014, 2015 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or modify it
-;;; under the terms of the GNU Lesser General Public License as published by
-;;; the Free Software Foundation; either version 3 of the License, or (at
-;;; your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful, but
-;;; WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
-;;; General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public License
-;;; along with this program. If not, see <http_//www.gnu.org/licenses/>.
-
-(define-module (system base types)
- #\use-module (rnrs bytevectors)
- #\use-module (rnrs io ports)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-9)
- #\use-module (srfi srfi-9 gnu)
- #\use-module (srfi srfi-11)
- #\use-module (srfi srfi-26)
- #\use-module (srfi srfi-60)
- #\use-module (ice-9 match)
- #\use-module (ice-9 iconv)
- #\use-module (ice-9 format)
- #\use-module (ice-9 vlist)
- #\use-module (system foreign)
- #\export (%word-size
-
- memory-backend
- memory-backend?
- %ffi-memory-backend
- dereference-word
- memory-port
- type-number->name
-
- inferior-object?
- inferior-object-kind
- inferior-object-sub-kind
- inferior-object-address
-
- inferior-fluid?
- inferior-fluid-number
-
- inferior-struct?
- inferior-struct-name
- inferior-struct-fields
-
- scm->object))
-
-;;; Commentary_
-;;;
-;;; 'SCM' type tag decoding, primarily to support Guile debugging in GDB.
-;;;
-;;; Code_
-
-
-;;;
-;;; Memory back-ends.
-;;;
-
-(define %word-size
- ;; The pointer size.
- (sizeof '*))
-
-(define-record-type <memory-backend>
- (memory-backend peek open type-name)
- memory-backend?
- (peek memory-backend-peek)
- (open memory-backend-open)
- (type-name memory-backend-type-name)) ; for SMOBs and ports
-
-(define %ffi-memory-backend
- ;; The FFI back-end to access the current process's memory. The main
- ;; purpose of this back-end is to allow testing.
- (let ()
- (define (dereference-word address)
- (let* ((ptr (make-pointer address))
- (bv (pointer->bytevector ptr %word-size)))
- (bytevector-uint-ref bv 0 (native-endianness) %word-size)))
-
- (define (open address size)
- (define current-address address)
-
- (define (read-memory! bv index count)
- (let* ((ptr (make-pointer current-address))
- (mem (pointer->bytevector ptr count)))
- (bytevector-copy! mem 0 bv index count)
- (set! current-address (+ current-address count))
- count))
-
- (if size
- (let* ((ptr (make-pointer address))
- (bv (pointer->bytevector ptr size)))
- (open-bytevector-input-port bv))
- (let ((port (make-custom-binary-input-port "ffi-memory"
- read-memory!
- #f #f #f)))
- (setvbuf port _IONBF)
- port)))
-
- (memory-backend dereference-word open #f)))
-
-(define-inlinable (dereference-word backend address)
- "Return the word at ADDRESS, using BACKEND."
- (let ((peek (memory-backend-peek backend)))
- (peek address)))
-
-(define-syntax memory-port
- (syntax-rules ()
- "Return an input port to the SIZE bytes at ADDRESS, using BACKEND. When
-SIZE is omitted, return an unbounded port to the memory at ADDRESS."
- ((_ backend address)
- (let ((open (memory-backend-open backend)))
- (open address #f)))
- ((_ backend address size)
- (if (zero? size)
- ;; GDB's 'open-memory' raises an error when size
- ;; is zero, so we must handle that case specially.
- (open-bytevector-input-port '#vu8())
- (let ((open (memory-backend-open backend)))
- (open address size))))))
-
-(define (get-word port)
- "Read a word from PORT and return it as an integer."
- (let ((bv (get-bytevector-n port %word-size)))
- (bytevector-uint-ref bv 0 (native-endianness) %word-size)))
-
-(define-inlinable (type-number->name backend kind number)
- "Return the name of the type NUMBER of KIND, where KIND is one of
-'smob or 'port, or #f if the information is unavailable."
- (let ((proc (memory-backend-type-name backend)))
- (and proc (proc kind number))))
-
-
-;;;
-;;; Matching bit patterns and cells.
-;;;
-
-(define-syntax match-cell-words
- (syntax-rules (bytevector)
- ((_ port ((bytevector name len) rest ...) body)
- (let ((name (get-bytevector-n port len))
- (remainder (modulo len %word-size)))
- (unless (zero? remainder)
- (get-bytevector-n port (- %word-size remainder)))
- (match-cell-words port (rest ...) body)))
- ((_ port (name rest ...) body)
- (let ((name (get-word port)))
- (match-cell-words port (rest ...) body)))
- ((_ port () body)
- body)))
-
-(define-syntax match-bit-pattern
- (syntax-rules (& !! = _)
- ((match-bit-pattern bits ((a !! b) & n = c) consequent alternate)
- (let ((tag (logand bits n)))
- (if (= tag c)
- (let ((b tag)
- (a (logand bits (bitwise-not n))))
- consequent)
- alternate)))
- ((match-bit-pattern bits (x & n = c) consequent alternate)
- (let ((tag (logand bits n)))
- (if (= tag c)
- (let ((x bits))
- consequent)
- alternate)))
- ((match-bit-pattern bits (_ & n = c) consequent alternate)
- (let ((tag (logand bits n)))
- (if (= tag c)
- consequent
- alternate)))
- ((match-bit-pattern bits ((a << n) !! c) consequent alternate)
- (let ((tag (bitwise-and bits (- (expt 2 n) 1))))
- (if (= tag c)
- (let ((a (arithmetic-shift bits (- n))))
- consequent)
- alternate)))))
-
-(define-syntax match-cell-clauses
- (syntax-rules ()
- ((_ port tag (((tag-pattern thing ...) body) rest ...))
- (match-bit-pattern tag tag-pattern
- (match-cell-words port (thing ...) body)
- (match-cell-clauses port tag (rest ...))))
- ((_ port tag ())
- (inferior-object 'unmatched-tag tag))))
-
-(define-syntax match-cell
- (syntax-rules ()
- "Match a cell---i.e., a non-immediate value other than a pair. The
-cell's contents are read from PORT."
- ((_ port (pattern body ...) ...)
- (let ((port* port)
- (tag (get-word port)))
- (match-cell-clauses port* tag
- ((pattern (begin body ...))
- ...))))))
-
-(define-syntax match-scm-clauses
- (syntax-rules ()
- ((_ bits
- (bit-pattern body ...)
- rest ...)
- (match-bit-pattern bits bit-pattern
- (begin body ...)
- (match-scm-clauses bits rest ...)))
- ((_ bits)
- 'unmatched-scm)))
-
-(define-syntax match-scm
- (syntax-rules ()
- "Match BITS, an integer representation of an 'SCM' value, against
-CLAUSES. Each clause must have the form_
-
- (PATTERN BODY ...)
-
-PATTERN is a bit pattern that may specify bitwise operations on BITS to
-determine if it matches. TEMPLATE specify the name of the variable to bind
-the matching bits, possibly with bitwise operations to extract it from BITS."
- ((_ bits clauses ...)
- (let ((bits* bits))
- (match-scm-clauses bits* clauses ...)))))
-
-
-;;;
-;;; Tags---keep in sync with libguile/tags.h!
-;;;
-
-;; Immediate values.
-(define %tc2-int 2)
-(define %tc3-imm24 4)
-
-(define %tc3-cons 0)
-(define %tc3-int1 %tc2-int)
-(define %tc3-int2 (+ %tc2-int 4))
-
-(define %tc8-char (+ 8 %tc3-imm24))
-(define %tc8-flag (+ %tc3-imm24 0))
-
-;; Cell types.
-(define %tc3-struct 1)
-(define %tc7-symbol 5)
-(define %tc7-variable 7)
-(define %tc7-vector 13)
-(define %tc7-wvect 15)
-(define %tc7-string 21)
-(define %tc7-number 23)
-(define %tc7-hashtable 29)
-(define %tc7-pointer 31)
-(define %tc7-fluid 37)
-(define %tc7-stringbuf 39)
-(define %tc7-dynamic-state 45)
-(define %tc7-frame 47)
-(define %tc7-objcode 53)
-(define %tc7-vm 55)
-(define %tc7-vm-continuation 71)
-(define %tc7-bytevector 77)
-(define %tc7-program 79)
-(define %tc7-array 85)
-(define %tc7-bitvector 87)
-(define %tc7-port 125)
-(define %tc7-smob 127)
-
-(define %tc16-bignum (+ %tc7-number (* 1 256)))
-(define %tc16-real (+ %tc7-number (* 2 256)))
-(define %tc16-complex (+ %tc7-number (* 3 256)))
-(define %tc16-fraction (+ %tc7-number (* 4 256)))
-
-
-;; "Stringbufs".
-(define-record-type <stringbuf>
- (stringbuf string)
- stringbuf?
- (string stringbuf-contents))
-
-(set-record-type-printer! <stringbuf>
- (lambda (stringbuf port)
- (display "#<stringbuf " port)
- (write (stringbuf-contents stringbuf) port)
- (display "#>" port)))
-
-;; Structs.
-(define-record-type <inferior-struct>
- (inferior-struct name fields)
- inferior-struct?
- (name inferior-struct-name)
- (fields inferior-struct-fields set-inferior-struct-fields!))
-
-(define print-inferior-struct
- (let ((%printed-struct (make-parameter vlist-null)))
- (lambda (struct port)
- (if (vhash-assq struct (%printed-struct))
- (format port "#-1#")
- (begin
- (format port "#<struct ~a"
- (inferior-struct-name struct))
- (parameterize ((%printed-struct
- (vhash-consq struct #t (%printed-struct))))
- (for-each (lambda (field)
- (if (eq? field struct)
- (display " #0#" port)
- (format port " ~s" field)))
- (inferior-struct-fields struct)))
- (format port " ~x>" (object-address struct)))))))
-
-(set-record-type-printer! <inferior-struct> print-inferior-struct)
-
-;; Fluids.
-(define-record-type <inferior-fluid>
- (inferior-fluid number value)
- inferior-fluid?
- (number inferior-fluid-number)
- (value inferior-fluid-value))
-
-(set-record-type-printer! <inferior-fluid>
- (lambda (fluid port)
- (match fluid
- (($ <inferior-fluid> number)
- (format port "#<fluid ~a ~x>"
- number
- (object-address fluid))))))
-
-;; Object type to represent complex objects from the inferior process that
-;; cannot be really converted to usable Scheme objects in the current
-;; process.
-(define-record-type <inferior-object>
- (%inferior-object kind sub-kind address)
- inferior-object?
- (kind inferior-object-kind)
- (sub-kind inferior-object-sub-kind)
- (address inferior-object-address))
-
-(define inferior-object
- (case-lambda
- "Return an object representing an inferior object at ADDRESS, of type
-KIND/SUB-KIND."
- ((kind address)
- (%inferior-object kind #f address))
- ((kind sub-kind address)
- (%inferior-object kind sub-kind address))))
-
-(set-record-type-printer! <inferior-object>
- (lambda (io port)
- (match io
- (($ <inferior-object> kind sub-kind address)
- (format port "#<~a ~_[~*~;~a ~]~x>"
- kind sub-kind sub-kind
- address)))))
-
-(define (inferior-smob backend type-number address)
- "Return an object representing the SMOB at ADDRESS whose type is
-TYPE-NUMBER."
- (inferior-object 'smob
- (or (type-number->name backend 'smob type-number)
- type-number)
- address))
-
-(define (inferior-port backend type-number address)
- "Return an object representing the port at ADDRESS whose type is
-TYPE-NUMBER."
- (inferior-object 'port
- (or (type-number->name backend 'port type-number)
- type-number)
- address))
-
-(define %visited-cells
- ;; Vhash of mapping addresses of already visited cells to the
- ;; corresponding inferior object. This is used to detect and represent
- ;; cycles.
- (make-parameter vlist-null))
-
-(define-syntax visited
- (syntax-rules (->)
- ((_ (address -> object) body ...)
- (parameterize ((%visited-cells (vhash-consv address object
- (%visited-cells))))
- body ...))))
-
-(define (address->inferior-struct address vtable-data-address backend)
- "Read the struct at ADDRESS using BACKEND. Return an 'inferior-struct'
-object representing it."
- (define %vtable-layout-index 0)
- (define %vtable-name-index 5)
-
- (let* ((layout-address (+ vtable-data-address
- (* %vtable-layout-index %word-size)))
- (layout-bits (dereference-word backend layout-address))
- (layout (scm->object layout-bits backend))
- (name-address (+ vtable-data-address
- (* %vtable-name-index %word-size)))
- (name-bits (dereference-word backend name-address))
- (name (scm->object name-bits backend)))
- (if (symbol? layout)
- (let* ((layout (symbol->string layout))
- (len (/ (string-length layout) 2))
- (slots (dereference-word backend (+ address %word-size)))
- (port (memory-port backend slots (* len %word-size)))
- (fields (get-bytevector-n port (* len %word-size)))
- (result (inferior-struct name #f)))
-
- ;; Keep track of RESULT so callees can refer to it if we are
- ;; decoding a circular struct.
- (visited (address -> result)
- (let ((values (map (cut scm->object <> backend)
- (bytevector->uint-list fields
- (native-endianness)
- %word-size))))
- (set-inferior-struct-fields! result values)
- result)))
- (inferior-object 'invalid-struct address))))
-
-(define* (cell->object address #\optional (backend %ffi-memory-backend))
- "Return an object representing the object at ADDRESS, reading from memory
-using BACKEND."
- (or (and=> (vhash-assv address (%visited-cells)) cdr) ; circular object
- (let ((port (memory-port backend address)))
- (match-cell port
- (((vtable-data-address & 7 = %tc3-struct))
- (address->inferior-struct address
- (- vtable-data-address %tc3-struct)
- backend))
- (((_ & #x7f = %tc7-symbol) buf hash props)
- (match (cell->object buf backend)
- (($ <stringbuf> string)
- (string->symbol string))))
- (((_ & #x7f = %tc7-variable) obj)
- (inferior-object 'variable address))
- (((_ & #x7f = %tc7-string) buf start len)
- (match (cell->object buf backend)
- (($ <stringbuf> string)
- (substring string start (+ start len)))))
- (((_ & #x047f = %tc7-stringbuf) len (bytevector buf len))
- (stringbuf (bytevector->string buf "ISO-8859-1")))
- (((_ & #x047f = (bitwise-ior #x400 %tc7-stringbuf))
- len (bytevector buf (* 4 len)))
- (stringbuf (bytevector->string buf (match (native-endianness)
- ('little "UTF-32LE")
- ('big "UTF-32BE")))))
- (((_ & #x7f = %tc7-bytevector) len address)
- (let ((bv-port (memory-port backend address len)))
- (get-bytevector-n bv-port len)))
- ((((len << 7) !! %tc7-vector) weakv-data)
- (let* ((len (arithmetic-shift len -1))
- (words (get-bytevector-n port (* len %word-size)))
- (vector (make-vector len)))
- (visited (address -> vector)
- (fold (lambda (element index)
- (vector-set! vector index element)
- (+ 1 index))
- 0
- (map (cut scm->object <> backend)
- (bytevector->uint-list words (native-endianness)
- %word-size)))
- vector)))
- (((_ & #x7f = %tc7-wvect))
- (inferior-object 'weak-vector address)) ; TODO_ show elements
- ((((n << 8) !! %tc7-fluid) init-value)
- (inferior-fluid n #f)) ; TODO_ show current value
- (((_ & #x7f = %tc7-dynamic-state))
- (inferior-object 'dynamic-state address))
- ((((flags+type << 8) !! %tc7-port))
- (inferior-port backend (logand flags+type #xff) address))
- (((_ & #x7f = %tc7-program))
- (inferior-object 'program address))
- (((_ & #xffff = %tc16-bignum))
- (inferior-object 'bignum address))
- (((_ & #xffff = %tc16-real) pad)
- (let* ((address (+ address (* 2 %word-size)))
- (port (memory-port backend address (sizeof double)))
- (words (get-bytevector-n port (sizeof double))))
- (bytevector-ieee-double-ref words 0 (native-endianness))))
- (((_ & #x7f = %tc7-number) mpi)
- (inferior-object 'number address))
- (((_ & #x7f = %tc7-hashtable) buckets meta-data unused)
- (inferior-object 'hash-table address))
- (((_ & #x7f = %tc7-pointer) address)
- (make-pointer address))
- (((_ & #x7f = %tc7-objcode))
- (inferior-object 'objcode address))
- (((_ & #x7f = %tc7-vm))
- (inferior-object 'vm address))
- (((_ & #x7f = %tc7-vm-continuation))
- (inferior-object 'vm-continuation address))
- (((_ & #x7f = %tc7-array))
- (inferior-object 'array address))
- (((_ & #x7f = %tc7-bitvector))
- (inferior-object 'bitvector address))
- ((((smob-type << 8) !! %tc7-smob) word1)
- (inferior-smob backend smob-type address))))))
-
-
-(define* (scm->object bits #\optional (backend %ffi-memory-backend))
- "Return the Scheme object corresponding to BITS, the bits of an 'SCM'
-object."
- (match-scm bits
- (((integer << 2) !! %tc2-int)
- integer)
- ((address & 6 = %tc3-cons)
- (let* ((type (dereference-word backend address))
- (pair? (not (bit-set? 0 type))))
- (if pair?
- (or (and=> (vhash-assv address (%visited-cells)) cdr)
- (let ((car type)
- (cdrloc (+ address %word-size))
- (pair (cons *unspecified* *unspecified*)))
- (visited (address -> pair)
- (set-car! pair (scm->object car backend))
- (set-cdr! pair
- (scm->object (dereference-word backend cdrloc)
- backend))
- pair)))
- (cell->object address backend))))
- (((char << 8) !! %tc8-char)
- (integer->char char))
- (((flag << 8) !! %tc8-flag)
- (case flag
- ((0) #f)
- ((1) #nil)
- ((3) '())
- ((4) #t)
- ((8) (if #f #f))
- ((9) (inferior-object 'undefined bits))
- ((10) (eof-object))
- ((11) (inferior-object 'unbound bits))))))
-
-;;; Local Variables_
-;;; eval_ (put 'match-scm 'scheme-indent-function 1)
-;;; eval_ (put 'match-cell 'scheme-indent-function 1)
-;;; eval_ (put 'visited 'scheme-indent-function 1)
-;;; End_
-
-;;; types.scm ends here
-;;;; Copyright (C) 2010, 2011, 2013 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 2.1 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-(define-module (system foreign)
- #\use-module (rnrs bytevectors)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-9)
- #\use-module (srfi srfi-9 gnu)
- #\export (void
- float double
- short
- unsigned-short
- int unsigned-int long unsigned-long size_t ssize_t ptrdiff_t
- int8 uint8
- uint16 int16
- uint32 int32
- uint64 int64
-
- sizeof alignof
-
- %null-pointer
- null-pointer?
- pointer?
- make-pointer
- pointer->scm
- scm->pointer
- pointer-address
-
- pointer->bytevector
- bytevector->pointer
- set-pointer-finalizer!
-
- dereference-pointer
- string->pointer
- pointer->string
-
- pointer->procedure
- ;; procedure->pointer (see below)
- make-c-struct parse-c-struct
-
- define-wrapped-pointer-type))
-
-(eval-when (expand load eval)
- (load-extension (string-append "libguile-" (effective-version))
- "scm_init_foreign"))
-
-
-;;;
-;;; Pointers.
-;;;
-
-(define (null-pointer? pointer)
- "Return true if POINTER is the null pointer."
- (= (pointer-address pointer) 0))
-
-(if (defined? 'procedure->pointer)
- (export procedure->pointer))
-
-
-;;;
-;;; Structures.
-;;;
-
-(define bytevector-pointer-ref
- (case (sizeof '*)
- ((8) (lambda (bv offset)
- (make-pointer (bytevector-u64-native-ref bv offset))))
- ((4) (lambda (bv offset)
- (make-pointer (bytevector-u32-native-ref bv offset))))
- (else (error "what machine is this?"))))
-
-(define bytevector-pointer-set!
- (case (sizeof '*)
- ((8) (lambda (bv offset ptr)
- (bytevector-u64-native-set! bv offset (pointer-address ptr))))
- ((4) (lambda (bv offset ptr)
- (bytevector-u32-native-set! bv offset (pointer-address ptr))))
- (else (error "what machine is this?"))))
-
-(define *writers*
- `((,float . ,bytevector-ieee-single-native-set!)
- (,double . ,bytevector-ieee-double-native-set!)
- (,int8 . ,bytevector-s8-set!)
- (,uint8 . ,bytevector-u8-set!)
- (,int16 . ,bytevector-s16-native-set!)
- (,uint16 . ,bytevector-u16-native-set!)
- (,int32 . ,bytevector-s32-native-set!)
- (,uint32 . ,bytevector-u32-native-set!)
- (,int64 . ,bytevector-s64-native-set!)
- (,uint64 . ,bytevector-u64-native-set!)
- (* . ,bytevector-pointer-set!)))
-
-(define *readers*
- `((,float . ,bytevector-ieee-single-native-ref)
- (,double . ,bytevector-ieee-double-native-ref)
- (,int8 . ,bytevector-s8-ref)
- (,uint8 . ,bytevector-u8-ref)
- (,int16 . ,bytevector-s16-native-ref)
- (,uint16 . ,bytevector-u16-native-ref)
- (,int32 . ,bytevector-s32-native-ref)
- (,uint32 . ,bytevector-u32-native-ref)
- (,int64 . ,bytevector-s64-native-ref)
- (,uint64 . ,bytevector-u64-native-ref)
- (* . ,bytevector-pointer-ref)))
-
-
-(define (align off alignment)
- (1+ (logior (1- off) (1- alignment))))
-
-(define (write-c-struct bv offset types vals)
- (let lp ((offset offset) (types types) (vals vals))
- (cond
- ((not (pair? types))
- (or (null? vals)
- (error "too many values" vals)))
- ((not (pair? vals))
- (error "too few values" types))
- (else
- ;; alignof will error-check
- (let* ((type (car types))
- (offset (align offset (alignof type))))
- (if (pair? type)
- (write-c-struct bv offset (car types) (car vals))
- ((assv-ref *writers* type) bv offset (car vals)))
- (lp (+ offset (sizeof type)) (cdr types) (cdr vals)))))))
-
-(define (read-c-struct bv offset types)
- (let lp ((offset offset) (types types) (vals '()))
- (cond
- ((not (pair? types))
- (reverse vals))
- (else
- ;; alignof will error-check
- (let* ((type (car types))
- (offset (align offset (alignof type))))
- (lp (+ offset (sizeof type)) (cdr types)
- (cons (if (pair? type)
- (read-c-struct bv offset (car types))
- ((assv-ref *readers* type) bv offset))
- vals)))))))
-
-(define (make-c-struct types vals)
- (let ((bv (make-bytevector (sizeof types) 0)))
- (write-c-struct bv 0 types vals)
- (bytevector->pointer bv)))
-
-(define (parse-c-struct foreign types)
- (let ((size (fold (lambda (type total)
- (+ (sizeof type)
- (align total (alignof type))))
- 0
- types)))
- (read-c-struct (pointer->bytevector foreign size) 0 types)))
-
-
-;;;
-;;; Wrapped pointer types.
-;;;
-
-(define-syntax define-wrapped-pointer-type
- (lambda (stx)
- "Define helper procedures to wrap pointer objects into Scheme
-objects with a disjoint type. Specifically, this macro defines PRED, a
-predicate for the new Scheme type, WRAP, a procedure that takes a
-pointer object and returns an object that satisfies PRED, and UNWRAP
-which does the reverse. PRINT must name a user-defined object printer."
- (syntax-case stx ()
- ((_ type-name pred wrap unwrap print)
- (with-syntax ((%wrap (datum->syntax #'wrap (gensym "wrap"))))
- #'(begin
- (define-record-type type-name
- (%wrap pointer)
- pred
- (pointer unwrap))
- (define wrap
- ;; Use a weak hash table to preserve pointer identity, i.e.,
- ;; PTR1 == PTR2 <-> (eq? (wrap PTR1) (wrap PTR2)).
- (let ((ptr->obj (make-weak-value-hash-table 3000)))
- (lambda (ptr)
- ;; XXX_ We can't use `hash-create-handle!' +
- ;; `set-cdr!' here because the former would create a
- ;; weak-cdr pair but the latter wouldn't register a
- ;; disappearing link (see `scm_hash_fn_set_x'.)
- (or (hash-ref ptr->obj ptr)
- (let ((o (%wrap ptr)))
- (hash-set! ptr->obj ptr o)
- o)))))
- (set-record-type-printer! type-name print)))))))
-;;; Repl commands
-
-;; Copyright (C) 2001, 2009, 2010, 2011, 2013 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-;; 02110-1301 USA
-
-;;; Code_
-
-(define-module (system repl command)
- #\use-module (system base syntax)
- #\use-module (system base pmatch)
- #\use-module (system base compile)
- #\use-module (system repl common)
- #\use-module (system repl debug)
- #\use-module (system vm objcode)
- #\use-module (system vm program)
- #\use-module (system vm trap-state)
- #\use-module (system vm vm)
- #\use-module ((system vm frame) #\select (frame-return-values))
- #\autoload (system base language) (lookup-language language-reader)
- #\autoload (system vm trace) (call-with-trace)
- #\use-module (ice-9 format)
- #\use-module (ice-9 session)
- #\use-module (ice-9 documentation)
- #\use-module (ice-9 and-let-star)
- #\use-module (ice-9 rdelim)
- #\use-module (ice-9 control)
- #\use-module ((ice-9 pretty-print) #\select ((pretty-print . pp)))
- #\use-module ((system vm inspect) #\select ((inspect . %inspect)))
- #\use-module (statprof)
- #\export (meta-command define-meta-command))
-
-
-;;;
-;;; Meta command interface
-;;;
-
-(define *command-table*
- '((help (help h) (show) (apropos a) (describe d))
- (module (module m) (import use) (load l) (reload re) (binding b) (in))
- (language (language L))
- (compile (compile c) (compile-file cc)
- (expand exp) (optimize opt)
- (disassemble x) (disassemble-file xx))
- (profile (time t) (profile pr) (trace tr))
- (debug (backtrace bt) (up) (down) (frame fr)
- (procedure proc) (locals) (error-message error)
- (break br bp) (break-at-source break-at bs)
- (step s) (step-instruction si)
- (next n) (next-instruction ni)
- (finish)
- (tracepoint tp)
- (traps) (delete del) (disable) (enable)
- (registers regs))
- (inspect (inspect i) (pretty-print pp))
- (system (gc) (statistics stat) (option o)
- (quit q continue cont))))
-
-(define *show-table*
- '((show (warranty w) (copying c) (version v))))
-
-(define (group-name g) (car g))
-(define (group-commands g) (cdr g))
-
-(define *command-infos* (make-hash-table))
-(define (command-name c) (car c))
-(define (command-abbrevs c) (cdr c))
-(define (command-info c) (hashq-ref *command-infos* (command-name c)))
-(define (command-procedure c) (command-info-procedure (command-info c)))
-(define (command-doc c) (procedure-documentation (command-procedure c)))
-
-(define (make-command-info proc arguments-reader)
- (cons proc arguments-reader))
-
-(define (command-info-procedure info)
- (car info))
-
-(define (command-info-arguments-reader info)
- (cdr info))
-
-(define (command-usage c)
- (let ((doc (command-doc c)))
- (substring doc 0 (string-index doc #\newline))))
-
-(define (command-summary c)
- (let* ((doc (command-doc c))
- (start (1+ (string-index doc #\newline))))
- (cond ((string-index doc #\newline start)
- => (lambda (end) (substring doc start end)))
- (else (substring doc start)))))
-
-(define (lookup-group name)
- (assq name *command-table*))
-
-(define* (lookup-command key #\optional (table *command-table*))
- (let loop ((groups table) (commands '()))
- (cond ((and (null? groups) (null? commands)) #f)
- ((null? commands)
- (loop (cdr groups) (cdar groups)))
- ((memq key (car commands)) (car commands))
- (else (loop groups (cdr commands))))))
-
-(define* (display-group group #\optional (abbrev? #t))
- (format #t "~_(~A~) Commands~_[~; [abbrev]~]_~2%" (group-name group) abbrev?)
- (for-each (lambda (c)
- (display-summary (command-usage c)
- (if abbrev? (command-abbrevs c) '())
- (command-summary c)))
- (group-commands group))
- (newline))
-
-(define (display-command command)
- (display "Usage_ ")
- (display (command-doc command))
- (newline))
-
-(define (display-summary usage abbrevs summary)
- (let* ((usage-len (string-length usage))
- (abbrevs (if (pair? abbrevs)
- (format #f "[,~A~{ ,~A~}]" (car abbrevs) (cdr abbrevs))
- ""))
- (abbrevs-len (string-length abbrevs)))
- (format #t " ,~A~A~A - ~A\n"
- usage
- (cond
- ((> abbrevs-len 32)
- (error "abbrevs too long" abbrevs))
- ((> (+ usage-len abbrevs-len) 32)
- (format #f "~%~v_" (+ 2 (- 32 abbrevs-len))))
- (else
- (format #f "~v_" (- 32 abbrevs-len usage-len))))
- abbrevs
- summary)))
-
-(define (read-command repl)
- (catch #t
- (lambda () (read))
- (lambda (key . args)
- (pmatch args
- ((,subr ,msg ,args . ,rest)
- (format #t "Throw to key `~a' while reading command_\n" key)
- (display-error #f (current-output-port) subr msg args rest))
- (else
- (format #t "Throw to key `~a' with args `~s' while reading command.\n"
- key args)))
- (force-output)
- *unspecified*)))
-
-(define (read-command-arguments c repl)
- ((command-info-arguments-reader (command-info c)) repl))
-
-(define (meta-command repl)
- (let ((command (read-command repl)))
- (cond
- ((eq? command *unspecified*)) ; read error, already signalled; pass.
- ((not (symbol? command))
- (format #t "Meta-command not a symbol_ ~s~%" command))
- ((lookup-command command)
- => (lambda (c)
- (and=> (read-command-arguments c repl)
- (lambda (args) (apply (command-procedure c) repl args)))))
- (else
- (format #t "Unknown meta command_ ~A~%" command)))))
-
-(define (add-meta-command! name category proc argument-reader)
- (hashq-set! *command-infos* name (make-command-info proc argument-reader))
- (if category
- (let ((entry (assq category *command-table*)))
- (if entry
- (set-cdr! entry (append (cdr entry) (list (list name))))
- (set! *command-table*
- (append *command-table*
- (list (list category (list name)))))))))
-
-(define-syntax define-meta-command
- (syntax-rules ()
- ((_ ((name category) repl (expression0 ...) . datums) docstring b0 b1 ...)
- (add-meta-command!
- 'name
- 'category
- (lambda* (repl expression0 ... . datums)
- docstring
- b0 b1 ...)
- (lambda (repl)
- (define (handle-read-error form-name key args)
- (pmatch args
- ((,subr ,msg ,args . ,rest)
- (format #t "Throw to key `~a' while reading ~@[argument `~A' of ~]command `~A'_\n"
- key form-name 'name)
- (display-error #f (current-output-port) subr msg args rest))
- (else
- (format #t "Throw to key `~a' with args `~s' while reading ~@[ argument `~A' of ~]command `~A'.\n"
- key args form-name 'name)))
- (abort))
- (% (let* ((expression0
- (catch #t
- (lambda ()
- (repl-reader
- ""
- (lambda* (#\optional (port (current-input-port)))
- ((language-reader (repl-language repl))
- port (current-module)))))
- (lambda (k . args)
- (handle-read-error 'expression0 k args))))
- ...)
- (append
- (list expression0 ...)
- (catch #t
- (lambda ()
- (let ((port (open-input-string (read-line))))
- (let lp ((out '()))
- (let ((x (read port)))
- (if (eof-object? x)
- (reverse out)
- (lp (cons x out)))))))
- (lambda (k . args)
- (handle-read-error #f k args)))))
- (lambda (k) #f))))) ; the abort handler
-
- ((_ ((name category) repl . datums) docstring b0 b1 ...)
- (define-meta-command ((name category) repl () . datums)
- docstring b0 b1 ...))
-
- ((_ (name repl (expression0 ...) . datums) docstring b0 b1 ...)
- (define-meta-command ((name #f) repl (expression0 ...) . datums)
- docstring b0 b1 ...))
-
- ((_ (name repl . datums) docstring b0 b1 ...)
- (define-meta-command ((name #f) repl () . datums)
- docstring b0 b1 ...))))
-
-
-
-;;;
-;;; Help commands
-;;;
-
-(define-meta-command (help repl . args)
- "help [all | GROUP | [-c] COMMAND]
-Show help.
-
-With one argument, tries to look up the argument as a group name, giving
-help on that group if successful. Otherwise tries to look up the
-argument as a command, giving help on the command.
-
-If there is a command whose name is also a group name, use the ,help
--c COMMAND form to give help on the command instead of the group.
-
-Without any argument, a list of help commands and command groups
-are displayed."
- (pmatch args
- (()
- (display-group (lookup-group 'help))
- (display "Command Groups_\n\n")
- (display-summary "help all" #f "List all commands")
- (for-each (lambda (g)
- (let* ((name (symbol->string (group-name g)))
- (usage (string-append "help " name))
- (header (string-append "List " name " commands")))
- (display-summary usage #f header)))
- (cdr *command-table*))
- (newline)
- (display
- "Type `,help -c COMMAND' to show documentation of a particular command.")
- (newline))
- ((all)
- (for-each display-group *command-table*))
- ((,group) (guard (lookup-group group))
- (display-group (lookup-group group)))
- ((,command) (guard (lookup-command command))
- (display-command (lookup-command command)))
- ((-c ,command) (guard (lookup-command command))
- (display-command (lookup-command command)))
- ((,command)
- (format #t "Unknown command or group_ ~A~%" command))
- ((-c ,command)
- (format #t "Unknown command_ ~A~%" command))
- (else
- (format #t "Bad arguments_ ~A~%" args))))
-
-(define-meta-command (show repl . args)
- "show [TOPIC]
-Gives information about Guile.
-
-With one argument, tries to show a particular piece of information;
-
-currently supported topics are `warranty' (or `w'), `copying' (or `c'),
-and `version' (or `v').
-
-Without any argument, a list of topics is displayed."
- (pmatch args
- (()
- (display-group (car *show-table*) #f)
- (newline))
- ((,topic) (guard (lookup-command topic *show-table*))
- ((command-procedure (lookup-command topic *show-table*)) repl))
- ((,command)
- (format #t "Unknown topic_ ~A~%" command))
- (else
- (format #t "Bad arguments_ ~A~%" args))))
-
-;;; `warranty', `copying' and `version' are "hidden" meta-commands, only
-;;; accessible via `show'. They have an entry in *command-infos* but not
-;;; in *command-table*.
-
-(define-meta-command (warranty repl)
- "show warranty
-Details on the lack of warranty."
- (display *warranty*)
- (newline))
-
-(define-meta-command (copying repl)
- "show copying
-Show the LGPLv3."
- (display *copying*)
- (newline))
-
-(define-meta-command (version repl)
- "show version
-Version information."
- (display *version*)
- (newline))
-
-(define-meta-command (apropos repl regexp)
- "apropos REGEXP
-Find bindings/modules/packages."
- (apropos (->string regexp)))
-
-(define-meta-command (describe repl (form))
- "describe OBJ
-Show description/documentation."
- (display
- (object-documentation
- (let ((input (repl-parse repl form)))
- (if (symbol? input)
- (module-ref (current-module) input)
- (repl-eval repl input)))))
- (newline))
-
-(define-meta-command (option repl . args)
- "option [NAME] [EXP]
-List/show/set options."
- (pmatch args
- (()
- (for-each (lambda (spec)
- (format #t " ~A~24t~A\n" (car spec) (cadr spec)))
- (repl-options repl)))
- ((,name)
- (display (repl-option-ref repl name))
- (newline))
- ((,name ,exp)
- ;; Would be nice to evaluate in the current language, but the REPL
- ;; option parser doesn't permit that, currently.
- (repl-option-set! repl name (eval exp (current-module))))))
-
-(define-meta-command (quit repl)
- "quit
-Quit this session."
- (throw 'quit))
-
-
-;;;
-;;; Module commands
-;;;
-
-(define-meta-command (module repl . args)
- "module [MODULE]
-Change modules / Show current module."
- (pmatch args
- (() (puts (module-name (current-module))))
- ((,mod-name) (guard (list? mod-name))
- (set-current-module (resolve-module mod-name)))
- (,mod-name (set-current-module (resolve-module mod-name)))))
-
-(define-meta-command (import repl . args)
- "import [MODULE ...]
-Import modules / List those imported."
- (let ()
- (define (use name)
- (let ((mod (resolve-interface name)))
- (if mod
- (module-use! (current-module) mod)
- (format #t "No such module_ ~A~%" name))))
- (if (null? args)
- (for-each puts (map module-name (module-uses (current-module))))
- (for-each use args))))
-
-(define-meta-command (load repl file)
- "load FILE
-Load a file in the current module."
- (load (->string file)))
-
-(define-meta-command (reload repl . args)
- "reload [MODULE]
-Reload the given module, or the current module if none was given."
- (pmatch args
- (() (reload-module (current-module)))
- ((,mod-name) (guard (list? mod-name))
- (reload-module (resolve-module mod-name)))
- (,mod-name (reload-module (resolve-module mod-name)))))
-
-(define-meta-command (binding repl)
- "binding
-List current bindings."
- (module-for-each (lambda (k v) (format #t "~23A ~A\n" k v))
- (current-module)))
-
-(define-meta-command (in repl module command-or-expression . args)
- "in MODULE COMMAND-OR-EXPRESSION
-Evaluate an expression or command in the context of module."
- (let ((m (resolve-module module #\ensure #f)))
- (if m
- (pmatch command-or-expression
- (('unquote ,command) (guard (lookup-command command))
- (save-module-excursion
- (lambda ()
- (set-current-module m)
- (apply (command-procedure (lookup-command command)) repl args))))
- (,expression
- (guard (null? args))
- (repl-print repl (eval expression m)))
- (else
- (format #t "Invalid arguments to `in'_ expected a single expression or a command.\n")))
- (format #t "No such module_ ~s\n" module))))
-
-
-;;;
-;;; Language commands
-;;;
-
-(define-meta-command (language repl name)
- "language LANGUAGE
-Change languages."
- (let ((lang (lookup-language name))
- (cur (repl-language repl)))
- (format #t "Happy hacking with ~a! To switch back, type `,L ~a'.\n"
- (language-title lang) (language-name cur))
- (current-language lang)
- (set! (repl-language repl) lang)))
-
-
-;;;
-;;; Compile commands
-;;;
-
-(define-meta-command (compile repl (form))
- "compile EXP
-Generate compiled code."
- (let ((x (repl-compile repl (repl-parse repl form))))
- (cond ((objcode? x) (guile_disassemble x))
- (else (repl-print repl x)))))
-
-(define-meta-command (compile-file repl file . opts)
- "compile-file FILE
-Compile a file."
- (compile-file (->string file) #\opts opts))
-
-(define-meta-command (expand repl (form))
- "expand EXP
-Expand any macros in a form."
- (let ((x (repl-expand repl (repl-parse repl form))))
- (run-hook before-print-hook x)
- (pp x)))
-
-(define-meta-command (optimize repl (form))
- "optimize EXP
-Run the optimizer on a piece of code and print the result."
- (let ((x (repl-optimize repl (repl-parse repl form))))
- (run-hook before-print-hook x)
- (pp x)))
-
-(define (guile_disassemble x)
- ((@ (language assembly disassemble) disassemble) x))
-
-(define-meta-command (disassemble repl (form))
- "disassemble EXP
-Disassemble a compiled procedure."
- (let ((obj (repl-eval repl (repl-parse repl form))))
- (if (or (program? obj) (objcode? obj))
- (guile_disassemble obj)
- (format #t "Argument to ,disassemble not a procedure or objcode_ ~a~%"
- obj))))
-
-(define-meta-command (disassemble-file repl file)
- "disassemble-file FILE
-Disassemble a file."
- (guile_disassemble (load-objcode (->string file))))
-
-
-;;;
-;;; Profile commands
-;;;
-
-(define-meta-command (time repl (form))
- "time EXP
-Time execution."
- (let* ((gc-start (gc-run-time))
- (real-start (get-internal-real-time))
- (run-start (get-internal-run-time))
- (result (repl-eval repl (repl-parse repl form)))
- (run-end (get-internal-run-time))
- (real-end (get-internal-real-time))
- (gc-end (gc-run-time)))
- (define (diff start end)
- (/ (- end start) 1.0 internal-time-units-per-second))
- (repl-print repl result)
- (format #t ";; ~,6Fs real time, ~,6Fs run time. ~,6Fs spent in GC.\n"
- (diff real-start real-end)
- (diff run-start run-end)
- (diff gc-start gc-end))
- result))
-
-(define-meta-command (profile repl (form) . opts)
- "profile EXP
-Profile execution."
- ;; FIXME opts
- (apply statprof
- (repl-prepare-eval-thunk repl (repl-parse repl form))
- opts))
-
-(define-meta-command (trace repl (form) . opts)
- "trace EXP
-Trace execution."
- ;; FIXME_ doc options, or somehow deal with them better
- (apply call-with-trace
- (repl-prepare-eval-thunk repl (repl-parse repl form))
- (cons* #\width (terminal-width) opts)))
-
-
-;;;
-;;; Debug commands
-;;;
-
-(define-syntax define-stack-command
- (lambda (x)
- (syntax-case x ()
- ((_ (name repl . args) docstring body body* ...)
- #`(define-meta-command (name repl . args)
- docstring
- (let ((debug (repl-debug repl)))
- (if debug
- (letrec-syntax
- ((#,(datum->syntax #'repl 'frames)
- (identifier-syntax (debug-frames debug)))
- (#,(datum->syntax #'repl 'message)
- (identifier-syntax (debug-error-message debug)))
- (#,(datum->syntax #'repl 'for-trap?)
- (identifier-syntax (debug-for-trap? debug)))
- (#,(datum->syntax #'repl 'index)
- (identifier-syntax
- (id (debug-index debug))
- ((set! id exp) (set! (debug-index debug) exp))))
- (#,(datum->syntax #'repl 'cur)
- (identifier-syntax
- (vector-ref #,(datum->syntax #'repl 'frames)
- #,(datum->syntax #'repl 'index)))))
- body body* ...)
- (format #t "Nothing to debug.~%"))))))))
-
-(define-stack-command (backtrace repl #\optional count
- #\key (width (terminal-width)) full?)
- "backtrace [COUNT] [#:width W] [#full? F]
-Print a backtrace.
-
-Print a backtrace of all stack frames, or innermost COUNT frames.
-If COUNT is negative, the last COUNT frames will be shown."
- (print-frames frames
- #\count count
- #\width width
- #\full? full?
- #\for-trap? for-trap?))
-
-(define-stack-command (up repl #\optional (count 1))
- "up [COUNT]
-Select a calling stack frame.
-
-Select and print stack frames that called this one.
-An argument says how many frames up to go."
- (cond
- ((or (not (integer? count)) (<= count 0))
- (format #t "Invalid argument to `up'_ expected a positive integer for COUNT.~%"))
- ((>= (+ count index) (vector-length frames))
- (cond
- ((= index (1- (vector-length frames)))
- (format #t "Already at outermost frame.\n"))
- (else
- (set! index (1- (vector-length frames)))
- (print-frame cur #\index index
- #\next-source? (and (zero? index) for-trap?)))))
- (else
- (set! index (+ count index))
- (print-frame cur #\index index
- #\next-source? (and (zero? index) for-trap?)))))
-
-(define-stack-command (down repl #\optional (count 1))
- "down [COUNT]
-Select a called stack frame.
-
-Select and print stack frames called by this one.
-An argument says how many frames down to go."
- (cond
- ((or (not (integer? count)) (<= count 0))
- (format #t "Invalid argument to `down'_ expected a positive integer for COUNT.~%"))
- ((< (- index count) 0)
- (cond
- ((zero? index)
- (format #t "Already at innermost frame.\n"))
- (else
- (set! index 0)
- (print-frame cur #\index index #\next-source? for-trap?))))
- (else
- (set! index (- index count))
- (print-frame cur #\index index
- #\next-source? (and (zero? index) for-trap?)))))
-
-(define-stack-command (frame repl #\optional idx)
- "frame [IDX]
-Show a frame.
-
-Show the selected frame.
-With an argument, select a frame by index, then show it."
- (cond
- (idx
- (cond
- ((or (not (integer? idx)) (< idx 0))
- (format #t "Invalid argument to `frame'_ expected a non-negative integer for IDX.~%"))
- ((< idx (vector-length frames))
- (set! index idx)
- (print-frame cur #\index index
- #\next-source? (and (zero? index) for-trap?)))
- (else
- (format #t "No such frame.~%"))))
- (else (print-frame cur #\index index
- #\next-source? (and (zero? index) for-trap?)))))
-
-(define-stack-command (procedure repl)
- "procedure
-Print the procedure for the selected frame."
- (repl-print repl (frame-procedure cur)))
-
-(define-stack-command (locals repl #\key (width (terminal-width)))
- "locals
-Show local variables.
-
-Show locally-bound variables in the selected frame."
- (print-locals cur #\width width))
-
-(define-stack-command (error-message repl)
- "error-message
-Show error message.
-
-Display the message associated with the error that started the current
-debugging REPL."
- (format #t "~a~%" (if (string? message) message "No error message")))
-
-(define-meta-command (break repl (form))
- "break PROCEDURE
-Break on calls to PROCEDURE.
-
-Starts a recursive prompt when PROCEDURE is called."
- (let ((proc (repl-eval repl (repl-parse repl form))))
- (if (not (procedure? proc))
- (error "Not a procedure_ ~a" proc)
- (let ((idx (add-trap-at-procedure-call! proc)))
- (format #t "Trap ~a_ ~a.~%" idx (trap-name idx))))))
-
-(define-meta-command (break-at-source repl file line)
- "break-at-source FILE LINE
-Break when control reaches the given source location.
-
-Starts a recursive prompt when control reaches line LINE of file FILE.
-Note that the given source location must be inside a procedure."
- (let ((file (if (symbol? file) (symbol->string file) file)))
- (let ((idx (add-trap-at-source-location! file line)))
- (format #t "Trap ~a_ ~a.~%" idx (trap-name idx)))))
-
-(define (repl-pop-continuation-resumer repl msg)
- ;; Capture the dynamic environment with this prompt thing. The
- ;; result is a procedure that takes a frame.
- (% (call-with-values
- (lambda ()
- (abort
- (lambda (k)
- ;; Call frame->stack-vector before reinstating the
- ;; continuation, so that we catch the %stacks fluid at
- ;; the time of capture.
- (lambda (frame)
- (k frame
- (frame->stack-vector
- (frame-previous frame)))))))
- (lambda (from stack)
- (format #t "~a~%" msg)
- (let ((vals (frame-return-values from)))
- (if (null? vals)
- (format #t "No return values.~%")
- (begin
- (format #t "Return values_~%")
- (for-each (lambda (x) (repl-print repl x)) vals))))
- ((module-ref (resolve-interface '(system repl repl)) 'start-repl)
- #\debug (make-debug stack 0 msg #t))))))
-
-(define-stack-command (finish repl)
- "finish
-Run until the current frame finishes.
-
-Resume execution, breaking when the current frame finishes."
- (let ((handler (repl-pop-continuation-resumer
- repl (format #f "Return from ~a" cur))))
- (add-ephemeral-trap-at-frame-finish! cur handler)
- (throw 'quit)))
-
-(define (repl-next-resumer msg)
- ;; Capture the dynamic environment with this prompt thing. The
- ;; result is a procedure that takes a frame.
- (% (let ((stack (abort
- (lambda (k)
- ;; Call frame->stack-vector before reinstating the
- ;; continuation, so that we catch the %stacks fluid
- ;; at the time of capture.
- (lambda (frame)
- (k (frame->stack-vector frame)))))))
- (format #t "~a~%" msg)
- ((module-ref (resolve-interface '(system repl repl)) 'start-repl)
- #\debug (make-debug stack 0 msg #t)))))
-
-(define-stack-command (step repl)
- "step
-Step until control reaches a different source location.
-
-Step until control reaches a different source location."
- (let ((msg (format #f "Step into ~a" cur)))
- (add-ephemeral-stepping-trap! cur (repl-next-resumer msg)
- #\into? #t #\instruction? #f)
- (throw 'quit)))
-
-(define-stack-command (step-instruction repl)
- "step-instruction
-Step until control reaches a different instruction.
-
-Step until control reaches a different VM instruction."
- (let ((msg (format #f "Step into ~a" cur)))
- (add-ephemeral-stepping-trap! cur (repl-next-resumer msg)
- #\into? #t #\instruction? #t)
- (throw 'quit)))
-
-(define-stack-command (next repl)
- "next
-Step until control reaches a different source location in the current frame.
-
-Step until control reaches a different source location in the current frame."
- (let ((msg (format #f "Step into ~a" cur)))
- (add-ephemeral-stepping-trap! cur (repl-next-resumer msg)
- #\into? #f #\instruction? #f)
- (throw 'quit)))
-
-(define-stack-command (next-instruction repl)
- "next-instruction
-Step until control reaches a different instruction in the current frame.
-
-Step until control reaches a different VM instruction in the current frame."
- (let ((msg (format #f "Step into ~a" cur)))
- (add-ephemeral-stepping-trap! cur (repl-next-resumer msg)
- #\into? #f #\instruction? #t)
- (throw 'quit)))
-
-(define-meta-command (tracepoint repl (form))
- "tracepoint PROCEDURE
-Add a tracepoint to PROCEDURE.
-
-A tracepoint will print out the procedure and its arguments, when it is
-called, and its return value(s) when it returns."
- (let ((proc (repl-eval repl (repl-parse repl form))))
- (if (not (procedure? proc))
- (error "Not a procedure_ ~a" proc)
- (let ((idx (add-trace-at-procedure-call! proc)))
- (format #t "Trap ~a_ ~a.~%" idx (trap-name idx))))))
-
-(define-meta-command (traps repl)
- "traps
-Show the set of currently attached traps.
-
-Show the set of currently attached traps (breakpoints and tracepoints)."
- (let ((traps (list-traps)))
- (if (null? traps)
- (format #t "No traps set.~%")
- (for-each (lambda (idx)
- (format #t " ~a_ ~a~a~%"
- idx (trap-name idx)
- (if (trap-enabled? idx) "" " (disabled)")))
- traps))))
-
-(define-meta-command (delete repl idx)
- "delete IDX
-Delete a trap.
-
-Delete a trap."
- (if (not (integer? idx))
- (error "expected a trap index (a non-negative integer)" idx)
- (delete-trap! idx)))
-
-(define-meta-command (disable repl idx)
- "disable IDX
-Disable a trap.
-
-Disable a trap."
- (if (not (integer? idx))
- (error "expected a trap index (a non-negative integer)" idx)
- (disable-trap! idx)))
-
-(define-meta-command (enable repl idx)
- "enable IDX
-Enable a trap.
-
-Enable a trap."
- (if (not (integer? idx))
- (error "expected a trap index (a non-negative integer)" idx)
- (enable-trap! idx)))
-
-(define-stack-command (registers repl)
- "registers
-Print registers.
-
-Print the registers of the current frame."
- (print-registers cur))
-
-(define-meta-command (width repl #\optional x)
- "width [X]
-Set debug output width.
-
-Set the number of screen columns in the output from `backtrace' and
-`locals'."
- (terminal-width x)
- (format #t "Set screen width to ~a columns.~%" (terminal-width)))
-
-
-
-;;;
-;;; Inspection commands
-;;;
-
-(define-meta-command (inspect repl (form))
- "inspect EXP
-Inspect the result(s) of evaluating EXP."
- (call-with-values (repl-prepare-eval-thunk repl (repl-parse repl form))
- (lambda args
- (for-each %inspect args))))
-
-(define-meta-command (pretty-print repl (form))
- "pretty-print EXP
-Pretty-print the result(s) of evaluating EXP."
- (call-with-values (repl-prepare-eval-thunk repl (repl-parse repl form))
- (lambda args
- (for-each
- (lambda (x)
- (run-hook before-print-hook x)
- (pp x))
- args))))
-
-
-;;;
-;;; System commands
-;;;
-
-(define-meta-command (gc repl)
- "gc
-Garbage collection."
- (gc))
-
-(define-meta-command (statistics repl)
- "statistics
-Display statistics."
- (let ((this-tms (times))
- (this-gcs (gc-stats))
- (last-tms (repl-tm-stats repl))
- (last-gcs (repl-gc-stats repl)))
- ;; GC times
- (let ((this-times (assq-ref this-gcs 'gc-times))
- (last-times (assq-ref last-gcs 'gc-times)))
- (display-diff-stat "GC times_" #t this-times last-times "times")
- (newline))
- ;; Memory size
- (let ((this-heap (assq-ref this-gcs 'heap-size))
- (this-free (assq-ref this-gcs 'heap-free-size)))
- (display-stat-title "Memory size_" "current" "limit")
- (display-stat "heap" #f (- this-heap this-free) this-heap "bytes")
- (newline))
- ;; Cells collected
- (let ((this-alloc (assq-ref this-gcs 'heap-total-allocated))
- (last-alloc (assq-ref last-gcs 'heap-total-allocated)))
- (display-stat-title "Bytes allocated_" "diff" "total")
- (display-diff-stat "allocated" #f this-alloc last-alloc "bytes")
- (newline))
- ;; GC time taken
- (let ((this-total (assq-ref this-gcs 'gc-time-taken))
- (last-total (assq-ref last-gcs 'gc-time-taken)))
- (display-stat-title "GC time taken_" "diff" "total")
- (display-time-stat "total" this-total last-total)
- (newline))
- ;; Process time spent
- (let ((this-utime (tms_utime this-tms))
- (last-utime (tms_utime last-tms))
- (this-stime (tms_stime this-tms))
- (last-stime (tms_stime last-tms))
- (this-cutime (tms_cutime this-tms))
- (last-cutime (tms_cutime last-tms))
- (this-cstime (tms_cstime this-tms))
- (last-cstime (tms_cstime last-tms)))
- (display-stat-title "Process time spent_" "diff" "total")
- (display-time-stat "user" this-utime last-utime)
- (display-time-stat "system" this-stime last-stime)
- (display-time-stat "child user" this-cutime last-cutime)
- (display-time-stat "child system" this-cstime last-cstime)
- (newline))
- ;; Save statistics
- ;; Save statistics
- (set! (repl-tm-stats repl) this-tms)
- (set! (repl-gc-stats repl) this-gcs)))
-
-(define (display-stat title flag field1 field2 unit)
- (let ((fmt (format #f "~~20~AA ~~10@A /~~10@A ~~A~~%" (if flag "" "@"))))
- (format #t fmt title field1 field2 unit)))
-
-(define (display-stat-title title field1 field2)
- (display-stat title #t field1 field2 ""))
-
-(define (display-diff-stat title flag this last unit)
- (display-stat title flag (- this last) this unit))
-
-(define (display-time-stat title this last)
- (define (conv num)
- (format #f "~10,2F" (exact->inexact (/ num internal-time-units-per-second))))
- (display-stat title #f (conv (- this last)) (conv this) "s"))
-
-(define (display-mips-stat title this-time this-clock last-time last-clock)
- (define (mips time clock)
- (if (= time 0) "----" (format #f "~10,2F" (/ clock time 1000000.0))))
- (display-stat title #f
- (mips (- this-time last-time) (- this-clock last-clock))
- (mips this-time this-clock) "mips"))
-;;; Repl common routines
-
-;; Copyright (C) 2001, 2008-2016 Free Software Foundation, Inc.
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code_
-
-(define-module (system repl common)
- #\use-module (system base syntax)
- #\use-module (system base compile)
- #\use-module (system base language)
- #\use-module (system base message)
- #\use-module (system vm program)
- #\autoload (language tree-il optimize) (optimize!)
- #\use-module (ice-9 control)
- #\use-module (ice-9 history)
- #\export (<repl> make-repl repl-language repl-options
- repl-tm-stats repl-gc-stats repl-debug
- repl-welcome repl-prompt
- repl-read repl-compile repl-prepare-eval-thunk repl-eval
- repl-expand repl-optimize
- repl-parse repl-print repl-option-ref repl-option-set!
- repl-default-option-set! repl-default-prompt-set!
- puts ->string user-error
- *warranty* *copying* *version*))
-
-(define *version*
- (format #f "GNU Guile ~A
-Copyright (C) 1995-2016 Free Software Foundation, Inc.
-
-Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
-This program is free software, and you are welcome to redistribute it
-under certain conditions; type `,show c' for details." (version)))
-
-(define *copying*
-"Guile is free software_ you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as
-published by the Free Software Foundation, either version 3 of
-the License, or (at your option) any later version.
-
-Guile is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public
-License along with this program. If not, see
-<http_//www.gnu.org/licenses/lgpl.html>.")
-
-(define *warranty*
-"Guile is distributed WITHOUT ANY WARRANTY. The following
-sections from the GNU General Public License, version 3, should
-make that clear.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
-See <http_//www.gnu.org/licenses/lgpl.html>, for more details.")
-
-
-;;;
-;;; Repl type
-;;;
-
-(define-record/keywords <repl>
- language options tm-stats gc-stats debug)
-
-(define repl-default-options
- (copy-tree
- `((compile-options ,%auto-compilation-options #f)
- (trace #f #f)
- (interp #f #f)
- (prompt #f ,(lambda (prompt)
- (cond
- ((not prompt) #f)
- ((string? prompt) (lambda (repl) prompt))
- ((thunk? prompt) (lambda (repl) (prompt)))
- ((procedure? prompt) prompt)
- (else (error "Invalid prompt" prompt)))))
- (print #f ,(lambda (print)
- (cond
- ((not print) #f)
- ((procedure? print) print)
- (else (error "Invalid print procedure" print)))))
- (value-history
- ,(value-history-enabled?)
- ,(lambda (x)
- (if x (enable-value-history!) (disable-value-history!))
- (->bool x)))
- (on-error
- debug
- ,(let ((vals '(debug backtrace report pass)))
- (lambda (x)
- (if (memq x vals)
- x
- (error "Bad on-error value ~a; expected one of ~a" x vals))))))))
-
-(define %make-repl make-repl)
-(define* (make-repl lang #\optional debug)
- (%make-repl #\language (if (language? lang)
- lang
- (lookup-language lang))
- #\options (copy-tree repl-default-options)
- #\tm-stats (times)
- #\gc-stats (gc-stats)
- #\debug debug))
-
-(define (repl-welcome repl)
- (display *version*)
- (newline)
- (newline)
- (display "Enter `,help' for help.\n"))
-
-(define (repl-prompt repl)
- (cond
- ((repl-option-ref repl 'prompt)
- => (lambda (prompt) (prompt repl)))
- (else
- (format #f "~A@~A~A> " (language-name (repl-language repl))
- (module-name (current-module))
- (let ((level (length (cond
- ((fluid-ref *repl-stack*) => cdr)
- (else '())))))
- (if (zero? level) "" (format #f " [~a]" level)))))))
-
-(define (repl-read repl)
- (let ((reader (language-reader (repl-language repl))))
- (reader (current-input-port) (current-module))))
-
-(define (repl-compile-options repl)
- (repl-option-ref repl 'compile-options))
-
-(define (repl-compile repl form)
- (let ((from (repl-language repl))
- (opts (repl-compile-options repl)))
- (compile form #\from from #\to 'objcode #\opts opts
- #\env (current-module))))
-
-(define (repl-expand repl form)
- (let ((from (repl-language repl))
- (opts (repl-compile-options repl)))
- (decompile (compile form #\from from #\to 'tree-il #\opts opts
- #\env (current-module))
- #\from 'tree-il #\to from)))
-
-(define (repl-optimize repl form)
- (let ((from (repl-language repl))
- (opts (repl-compile-options repl)))
- (decompile (optimize! (compile form #\from from #\to 'tree-il #\opts opts
- #\env (current-module))
- (current-module)
- opts)
- #\from 'tree-il #\to from)))
-
-(define (repl-parse repl form)
- (let ((parser (language-parser (repl-language repl))))
- (if parser (parser form) form)))
-
-(define (repl-prepare-eval-thunk repl form)
- (let* ((eval (language-evaluator (repl-language repl))))
- (if (and eval
- (or (null? (language-compilers (repl-language repl)))
- (repl-option-ref repl 'interp)))
- (lambda () (eval form (current-module)))
- (make-program (repl-compile repl form)))))
-
-(define (repl-eval repl form)
- (let ((thunk (repl-prepare-eval-thunk repl form)))
- (% (thunk))))
-
-(define (repl-print repl val)
- (if (not (eq? val *unspecified*))
- (begin
- (run-hook before-print-hook val)
- (cond
- ((repl-option-ref repl 'print)
- => (lambda (print) (print repl val)))
- (else
- ;; The result of an evaluation is representable in scheme, and
- ;; should be printed with the generic printer, `write'. The
- ;; language-printer is something else_ it prints expressions of
- ;; a given language, not the result of evaluation.
- (write val)
- (newline))))))
-
-(define (repl-option-ref repl key)
- (cadr (or (assq key (repl-options repl))
- (error "unknown repl option" key))))
-
-(define (repl-option-set! repl key val)
- (let ((spec (or (assq key (repl-options repl))
- (error "unknown repl option" key))))
- (set-car! (cdr spec)
- (if (procedure? (caddr spec))
- ((caddr spec) val)
- val))))
-
-(define (repl-default-option-set! key val)
- (let ((spec (or (assq key repl-default-options)
- (error "unknown repl option" key))))
- (set-car! (cdr spec)
- (if (procedure? (caddr spec))
- ((caddr spec) val)
- val))))
-
-(define (repl-default-prompt-set! prompt)
- (repl-default-option-set! 'prompt prompt))
-
-
-;;;
-;;; Utilities
-;;;
-
-(define (puts x) (display x) (newline))
-
-(define (->string x)
- (object->string x display))
-
-(define (user-error msg . args)
- (throw 'user-error #f msg args #f))
-;;; Cooperative REPL server
-
-;; Copyright (C) 2014, 2016 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-;; 02110-1301 USA
-
-;;; Code_
-
-(define-module (system repl coop-server)
- #\use-module (ice-9 match)
- #\use-module (ice-9 receive)
- #\use-module (ice-9 threads)
- #\use-module (ice-9 q)
- #\use-module (srfi srfi-9)
- #\use-module ((system repl repl)
- #\select (start-repl* prompting-meta-read))
- #\use-module ((system repl server)
- #\select (run-server* make-tcp-server-socket
- add-open-socket! close-socket!
- guard-against-http-request))
- #\export (spawn-coop-repl-server
- poll-coop-repl-server))
-
-(define-record-type <coop-repl-server>
- (%make-coop-repl-server mutex queue)
- coop-repl-server?
- (mutex coop-repl-server-mutex)
- (queue coop-repl-server-queue))
-
-(define (make-coop-repl-server)
- (%make-coop-repl-server (make-mutex) (make-q)))
-
-(define (coop-repl-server-eval coop-server opcode . args)
- "Queue a new instruction with the symbolic name OPCODE and an arbitrary
-number of arguments, to be processed the next time COOP-SERVER is polled."
- (with-mutex (coop-repl-server-mutex coop-server)
- (enq! (coop-repl-server-queue coop-server)
- (cons opcode args))))
-
-(define-record-type <coop-repl>
- (%make-coop-repl mutex condvar thunk cont)
- coop-repl?
- (mutex coop-repl-mutex)
- (condvar coop-repl-condvar) ; signaled when thunk becomes non-#f
- (thunk coop-repl-read-thunk set-coop-repl-read-thunk!)
- (cont coop-repl-cont set-coop-repl-cont!))
-
-(define (make-coop-repl)
- (%make-coop-repl (make-mutex) (make-condition-variable) #f #f))
-
-(define (coop-repl-read coop-repl)
- "Read an expression via the thunk stored in COOP-REPL."
- (let ((thunk
- (with-mutex (coop-repl-mutex coop-repl)
- (unless (coop-repl-read-thunk coop-repl)
- (wait-condition-variable (coop-repl-condvar coop-repl)
- (coop-repl-mutex coop-repl)))
- (let ((thunk (coop-repl-read-thunk coop-repl)))
- (unless thunk
- (error "coop-repl-read_ condvar signaled, but thunk is #f!"))
- (set-coop-repl-read-thunk! coop-repl #f)
- thunk))))
- (thunk)))
-
-(define (store-repl-cont cont coop-repl)
- "Save the partial continuation CONT within COOP-REPL."
- (set-coop-repl-cont! coop-repl
- (lambda (exp)
- (coop-repl-prompt
- (lambda () (cont exp))))))
-
-(define (coop-repl-prompt thunk)
- "Apply THUNK within a prompt for cooperative REPLs."
- (call-with-prompt 'coop-repl-prompt thunk store-repl-cont))
-
-(define (make-coop-reader coop-repl)
- "Return a new procedure for reading user input from COOP-REPL. The
-generated procedure passes the responsibility of reading input to
-another thread and aborts the cooperative REPL prompt."
- (lambda (repl)
- (let ((read-thunk
- ;; Need to preserve the REPL stack and current module across
- ;; threads.
- (let ((stack (fluid-ref *repl-stack*))
- (module (current-module)))
- (lambda ()
- (with-fluids ((*repl-stack* stack))
- (set-current-module module)
- (prompting-meta-read repl))))))
- (with-mutex (coop-repl-mutex coop-repl)
- (when (coop-repl-read-thunk coop-repl)
- (error "coop-reader_ read-thunk is not #f!"))
- (set-coop-repl-read-thunk! coop-repl read-thunk)
- (signal-condition-variable (coop-repl-condvar coop-repl))))
- (abort-to-prompt 'coop-repl-prompt coop-repl)))
-
-(define (reader-loop coop-server coop-repl)
- "Run an unbounded loop that reads an expression for COOP-REPL and
-stores the expression within COOP-SERVER for later evaluation."
- (coop-repl-server-eval coop-server 'eval coop-repl
- (coop-repl-read coop-repl))
- (reader-loop coop-server coop-repl))
-
-(define (poll-coop-repl-server coop-server)
- "Poll the cooperative REPL server COOP-SERVER and apply a pending
-operation if there is one, such as evaluating an expression typed at the
-REPL prompt. This procedure must be called from the same thread that
-called spawn-coop-repl-server."
- (let ((op (with-mutex (coop-repl-server-mutex coop-server)
- (let ((queue (coop-repl-server-queue coop-server)))
- (and (not (q-empty? queue))
- (deq! queue))))))
- (when op
- (match op
- (('new-repl client)
- (start-repl-client coop-server client))
- (('eval coop-repl exp)
- ((coop-repl-cont coop-repl) exp))))
- *unspecified*))
-
-(define (start-coop-repl coop-server)
- "Start a new cooperative REPL process for COOP-SERVER."
- ;; Calling stop-server-and-clients! from a REPL will cause an
- ;; exception to be thrown when trying to read from the socket that has
- ;; been closed, so we catch that here.
- (false-if-exception
- (let ((coop-repl (make-coop-repl)))
- (make-thread reader-loop coop-server coop-repl)
- (start-repl* (current-language) #f (make-coop-reader coop-repl)))))
-
-(define (run-coop-repl-server coop-server server-socket)
- "Start the cooperative REPL server for COOP-SERVER using the socket
-SERVER-SOCKET."
- (run-server* server-socket (make-coop-client-proc coop-server)))
-
-(define* (spawn-coop-repl-server
- #\optional (server-socket (make-tcp-server-socket)))
- "Create and return a new cooperative REPL server object, and spawn a
-new thread to listen for connections on SERVER-SOCKET. Proper
-functioning of the REPL server requires that poll-coop-repl-server be
-called periodically on the returned server object."
- (let ((coop-server (make-coop-repl-server)))
- (make-thread run-coop-repl-server
- coop-server
- server-socket)
- coop-server))
-
-(define (make-coop-client-proc coop-server)
- "Return a new procedure that is used to schedule the creation of a new
-cooperative REPL for COOP-SERVER."
- (lambda (client addr)
- (coop-repl-server-eval coop-server 'new-repl client)))
-
-(define (start-repl-client coop-server client)
- "Run a cooperative REPL for COOP-SERVER within a prompt. All input
-and output is sent over the socket CLIENT."
-
- ;; Add the client to the list of open sockets, with a 'force-close'
- ;; procedure that closes the underlying file descriptor. We do it
- ;; this way because we cannot close the port itself safely from
- ;; another thread.
- (add-open-socket! client (lambda () (close-fdes (fileno client))))
-
- (guard-against-http-request client)
-
- (with-continuation-barrier
- (lambda ()
- (coop-repl-prompt
- (lambda ()
- (parameterize ((current-input-port client)
- (current-output-port client)
- (current-error-port client)
- (current-warning-port client))
- (with-fluids ((*repl-stack* '()))
- (save-module-excursion
- (lambda ()
- (start-coop-repl coop-server)))))
-
- ;; This may fail if 'stop-server-and-clients!' is called,
- ;; because the 'force-close' procedure above closes the
- ;; underlying file descriptor instead of the port itself.
- (false-if-exception
- (close-socket! client)))))))
-;;; Guile VM debugging facilities
-
-;;; Copyright (C) 2001, 2009, 2010, 2011 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code_
-
-(define-module (system repl debug)
- #\use-module (system base pmatch)
- #\use-module (system base syntax)
- #\use-module (system base language)
- #\use-module (system vm vm)
- #\use-module (system vm frame)
- #\use-module (ice-9 rdelim)
- #\use-module (ice-9 pretty-print)
- #\use-module (ice-9 format)
- #\use-module ((system vm inspect) #\select ((inspect . %inspect)))
- #\use-module (system vm program)
- #\export (<debug>
- make-debug debug?
- debug-frames debug-index debug-error-message debug-for-trap?
- terminal-width
- print-registers print-locals print-frame print-frames frame->module
- stack->vector narrow-stack->vector
- frame->stack-vector))
-
-;; TODO_
-;;
-;; eval expression in context of frame
-;; set local variable in frame
-;; step until greater source line
-;; watch expression
-;; set printing width
-;; disassemble the current function
-;; inspect any object
-
-;;;
-;;; Debugger
-;;;
-;;; The actual interaction loop of the debugger is run by the repl. This module
-;;; simply exports a data structure to hold the debugger state, along with its
-;;; accessors, and provides some helper functions.
-;;;
-
-(define-record <debug> frames index error-message for-trap?)
-
-
-
-;; A fluid, because terminals are usually implicitly associated with
-;; threads.
-;;
-(define terminal-width
- (let ((set-width (make-fluid)))
- (case-lambda
- (()
- (or (fluid-ref set-width)
- (let ((w (false-if-exception (string->number (getenv "COLUMNS")))))
- (and (integer? w) (exact? w) (> w 0) w))
- 72))
- ((w)
- (if (or (not w) (and (integer? w) (exact? w) (> w 0)))
- (fluid-set! set-width w)
- (error "Expected a column number (a positive integer)" w))))))
-
-
-
-
-(define (reverse-hashq h)
- (let ((ret (make-hash-table)))
- (hash-for-each
- (lambda (k v)
- (hashq-set! ret v (cons k (hashq-ref ret v '()))))
- h)
- ret))
-
-(define* (print-registers frame #\optional (port (current-output-port))
- #\key (per-line-prefix " "))
- (define (print fmt val)
- (display per-line-prefix port)
- (run-hook before-print-hook val)
- (format port fmt val))
-
- (format port "~aRegisters_~%" per-line-prefix)
- (print "ip = ~d\n" (frame-instruction-pointer frame))
- (print "sp = #x~x\n" (frame-stack-pointer frame))
- (print "fp = #x~x\n" (frame-address frame)))
-
-(define* (print-locals frame #\optional (port (current-output-port))
- #\key (width (terminal-width)) (per-line-prefix " "))
- (let ((bindings (frame-bindings frame)))
- (cond
- ((null? bindings)
- (format port "~aNo local variables.~%" per-line-prefix))
- (else
- (format port "~aLocal variables_~%" per-line-prefix)
- (for-each
- (lambda (binding)
- (let ((v (let ((x (frame-local-ref frame (binding_index binding))))
- (if (binding_boxed? binding)
- (variable-ref x)
- x))))
- (display per-line-prefix port)
- (run-hook before-print-hook v)
- (format port "~a~_[~; (boxed)~] = ~v_@y\n"
- (binding_name binding) (binding_boxed? binding) width v)))
- (frame-bindings frame))))))
-
-(define* (print-frame frame #\optional (port (current-output-port))
- #\key index (width (terminal-width)) (full? #f)
- (last-source #f) next-source?)
- (define (source_pretty-file source)
- (if source
- (or (source_file source) "current input")
- "unknown file"))
- (let* ((source ((if next-source? frame-next-source frame-source) frame))
- (file (source_pretty-file source))
- (line (and=> source source_line-for-user))
- (col (and=> source source_column)))
- (if (and file (not (equal? file (source_pretty-file last-source))))
- (format port "~&In ~a_~&" file))
- (format port "~9@a~_[~*~3_~;~3d~] ~v_@y~%"
- (if line (format #f "~a_~a" line col) "")
- index index width (frame-call-representation frame))
- (if full?
- (print-locals frame #\width width
- #\per-line-prefix " "))))
-
-(define* (print-frames frames
- #\optional (port (current-output-port))
- #\key (width (terminal-width)) (full? #f)
- (forward? #f) count for-trap?)
- (let* ((len (vector-length frames))
- (lower-idx (if (or (not count) (positive? count))
- 0
- (max 0 (+ len count))))
- (upper-idx (if (and count (negative? count))
- (1- len)
- (1- (if count (min count len) len))))
- (inc (if forward? 1 -1)))
- (let lp ((i (if forward? lower-idx upper-idx))
- (last-source #f))
- (if (<= lower-idx i upper-idx)
- (let* ((frame (vector-ref frames i)))
- (print-frame frame port #\index i #\width width #\full? full?
- #\last-source last-source
- #\next-source? (and (zero? i) for-trap?))
- (lp (+ i inc)
- (if (and (zero? i) for-trap?)
- (frame-next-source frame)
- (frame-source frame))))))))
-
-;; Ideally here we would have something much more syntactic, in that a set! to a
-;; local var that is not settable would raise an error, and export etc forms
-;; would modify the module in question_ but alack, this is what we have now.
-;; Patches welcome!
-(define (frame->module frame)
- (let ((proc (frame-procedure frame)))
- (if (program? proc)
- (let* ((mod (or (program-module proc) (current-module)))
- (mod* (make-module)))
- (module-use! mod* mod)
- (for-each
- (lambda (binding)
- (let* ((x (frame-local-ref frame (binding_index binding)))
- (var (if (binding_boxed? binding) x (make-variable x))))
- (format #t
- "~_[Read-only~;Mutable~] local variable ~a = ~70_@y\n"
- (binding_boxed? binding)
- (binding_name binding)
- (if (variable-bound? var) (variable-ref var) var))
- (module-add! mod* (binding_name binding) var)))
- (frame-bindings frame))
- mod*)
- (current-module))))
-
-
-(define (stack->vector stack)
- (let* ((len (stack-length stack))
- (v (make-vector len)))
- (if (positive? len)
- (let lp ((i 0) (frame (stack-ref stack 0)))
- (if (< i len)
- (begin
- (vector-set! v i frame)
- (lp (1+ i) (frame-previous frame))))))
- v))
-
-(define (narrow-stack->vector stack . args)
- (let ((narrowed (apply make-stack (stack-ref stack 0) args)))
- (if narrowed
- (stack->vector narrowed)
- #()))) ; ? Can be the case for a tail-call to `throw' tho
-
-(define (frame->stack-vector frame)
- (let ((tag (and (pair? (fluid-ref %stacks))
- (cdar (fluid-ref %stacks)))))
- (narrow-stack->vector
- (make-stack frame)
- ;; Take the stack from the given frame, cutting 0
- ;; frames.
- 0
- ;; Narrow the end of the stack to the most recent
- ;; start-stack.
- tag
- ;; And one more frame, because %start-stack
- ;; invoking the start-stack thunk has its own frame
- ;; too.
- 0 (and tag 1))))
-
-;; (define (debug)
-;; (run-debugger
-;; (narrow-stack->vector
-;; (make-stack #t)
-;; ;; Narrow the `make-stack' frame and the `debug' frame
-;; 2
-;; ;; Narrow the end of the stack to the most recent start-stack.
-;; (and (pair? (fluid-ref %stacks))
-;; (cdar (fluid-ref %stacks))))))
-
-;;; Describe objects
-
-;; Copyright (C) 2001, 2009, 2011 Free Software Foundation, Inc.
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code_
-
-(define-module (system repl describe)
- #\use-module (oop goops)
- #\use-module (ice-9 regex)
- #\use-module (ice-9 format)
- #\use-module (ice-9 and-let-star)
- #\export (describe))
-
-(define-method (describe (symbol <symbol>))
- (format #t "`~s' is " symbol)
- (if (not (defined? symbol))
- (display "not defined in the current module.\n")
- (describe-object (module-ref (current-module) symbol))))
-
-
-;;;
-;;; Display functions
-;;;
-
-(define (safe-class-name class)
- (if (slot-bound? class 'name)
- (class-name class)
- class))
-
-(define-method (display-class class . args)
- (let* ((name (safe-class-name class))
- (desc (if (pair? args) (car args) name)))
- (if (eq? *describe-format* 'tag)
- (format #t "@class{~a}{~a}" name desc)
- (format #t "~a" desc))))
-
-(define (display-list title list)
- (if title (begin (display title) (display "_\n\n")))
- (if (null? list)
- (display "(not defined)\n")
- (for-each display-summary list)))
-
-(define (display-slot-list title instance list)
- (if title (begin (display title) (display "_\n\n")))
- (if (null? list)
- (display "(not defined)\n")
- (for-each (lambda (slot)
- (let ((name (slot-definition-name slot)))
- (display "Slot_ ")
- (display name)
- (if (and instance (slot-bound? instance name))
- (begin
- (display " = ")
- (display (slot-ref instance name))))
- (newline)))
- list)))
-
-(define (display-file location)
- (display "Defined in ")
- (if (eq? *describe-format* 'tag)
- (format #t "@location{~a}.\n" location)
- (format #t "`~a'.\n" location)))
-
-(define (format-documentation doc)
- (with-current-buffer (make-buffer #\text doc)
- (lambda ()
- (let ((regexp (make-regexp "@([a-z]*)(\\{([^}]*)\\})?")))
- (do-while (match (re-search-forward regexp))
- (let ((key (string->symbol (match_substring match 1)))
- (value (match_substring match 3)))
- (case key
- ((deffnx)
- (delete-region! (match_start match)
- (begin (forward-line) (point))))
- ((var)
- (replace-match! match 0 (string-upcase value)))
- ((code)
- (replace-match! match 0 (string-append "`" value "'")))))))
- (display (string (current-buffer)))
- (newline))))
-
-
-;;;
-;;; Top
-;;;
-
-(define description-table
- (list
- (cons <boolean> "a boolean")
- (cons <null> "an empty list")
- (cons <integer> "an integer")
- (cons <real> "a real number")
- (cons <complex> "a complex number")
- (cons <char> "a character")
- (cons <symbol> "a symbol")
- (cons <keyword> "a keyword")
- (cons <promise> "a promise")
- (cons <hook> "a hook")
- (cons <fluid> "a fluid")
- (cons <stack> "a stack")
- (cons <variable> "a variable")
- (cons <regexp> "a regexp object")
- (cons <module> "a module object")
- (cons <unknown> "an unknown object")))
-
-(define-generic describe-object)
-(export describe-object)
-
-(define-method (describe-object (obj <top>))
- (display-type obj)
- (display-location obj)
- (newline)
- (display-value obj)
- (newline)
- (display-documentation obj))
-
-(define-generic display-object)
-(define-generic display-summary)
-(define-generic display-type)
-(define-generic display-value)
-(define-generic display-location)
-(define-generic display-description)
-(define-generic display-documentation)
-(export display-object display-summary display-type display-value
- display-location display-description display-documentation)
-
-(define-method (display-object (obj <top>))
- (write obj))
-
-(define-method (display-summary (obj <top>))
- (display "Value_ ")
- (display-object obj)
- (newline))
-
-(define-method (display-type (obj <top>))
- (cond
- ((eof-object? obj) (display "the end-of-file object"))
- ((unspecified? obj) (display "unspecified"))
- (else (let ((class (class-of obj)))
- (display-class class (or (assq-ref description-table class)
- (safe-class-name class))))))
- (display ".\n"))
-
-(define-method (display-value (obj <top>))
- (if (not (unspecified? obj))
- (begin (display-object obj) (newline))))
-
-(define-method (display-location (obj <top>))
- *unspecified*)
-
-(define-method (display-description (obj <top>))
- (let* ((doc (with-output-to-string (lambda () (display-documentation obj))))
- (index (string-index doc #\newline)))
- (display (substring doc 0 (1+ index)))))
-
-(define-method (display-documentation (obj <top>))
- (display "Not documented.\n"))
-
-
-;;;
-;;; Pairs
-;;;
-
-(define-method (display-type (obj <pair>))
- (cond
- ((list? obj) (display-class <list> "a list"))
- ((pair? (cdr obj)) (display "an improper list"))
- (else (display-class <pair> "a pair")))
- (display ".\n"))
-
-
-;;;
-;;; Strings
-;;;
-
-(define-method (display-type (obj <string>))
- (if (read-only-string? 'obj)
- (display "a read-only string")
- (display-class <string> "a string"))
- (display ".\n"))
-
-
-;;;
-;;; Procedures
-;;;
-
-(define-method (display-object (obj <procedure>))
- (cond
- ;; FIXME_ VM programs, ...
- (else
- ;; Primitive procedure. Let's lookup the dictionary.
- (and-let* ((entry (lookup-procedure obj)))
- (let ((name (entry-property entry 'name))
- (print-arg (lambda (arg)
- (display " ")
- (display (string-upcase (symbol->string arg))))))
- (display "(")
- (display name)
- (and-let* ((args (entry-property entry 'args)))
- (for-each print-arg args))
- (and-let* ((opts (entry-property entry 'opts)))
- (display " &optional")
- (for-each print-arg opts))
- (and-let* ((rest (entry-property entry 'rest)))
- (display " &rest")
- (print-arg rest))
- (display ")"))))))
-
-(define-method (display-summary (obj <procedure>))
- (display "Procedure_ ")
- (display-object obj)
- (newline)
- (display " ")
- (display-description obj))
-
-(define-method (display-type (obj <procedure>))
- (cond
- ((and (thunk? obj) (not (procedure-name obj))) (display "a thunk"))
- ((procedure-with-setter? obj)
- (display-class <procedure-with-setter> "a procedure with setter"))
- (else (display-class <procedure> "a procedure")))
- (display ".\n"))
-
-(define-method (display-location (obj <procedure>))
- (and-let* ((entry (lookup-procedure obj)))
- (display-file (entry-file entry))))
-
-(define-method (display-documentation (obj <procedure>))
- (cond ((or (procedure-documentation obj)
- (and=> (lookup-procedure obj) entry-text))
- => format-documentation)
- (else (next-method))))
-
-
-;;;
-;;; Classes
-;;;
-
-(define-method (describe-object (obj <class>))
- (display-type obj)
- (display-location obj)
- (newline)
- (display-documentation obj)
- (newline)
- (display-value obj))
-
-(define-method (display-summary (obj <class>))
- (display "Class_ ")
- (display-class obj)
- (newline)
- (display " ")
- (display-description obj))
-
-(define-method (display-type (obj <class>))
- (display-class <class> "a class")
- (if (not (eq? (class-of obj) <class>))
- (begin (display " of ") (display-class (class-of obj))))
- (display ".\n"))
-
-(define-method (display-value (obj <class>))
- (display-list "Class precedence list" (class-precedence-list obj))
- (newline)
- (display-list "Direct superclasses" (class-direct-supers obj))
- (newline)
- (display-list "Direct subclasses" (class-direct-subclasses obj))
- (newline)
- (display-slot-list "Direct slots" #f (class-direct-slots obj))
- (newline)
- (display-list "Direct methods" (class-direct-methods obj)))
-
-
-;;;
-;;; Instances
-;;;
-
-(define-method (display-type (obj <object>))
- (display-class <object> "an instance")
- (display " of class ")
- (display-class (class-of obj))
- (display ".\n"))
-
-(define-method (display-value (obj <object>))
- (display-slot-list #f obj (class-slots (class-of obj))))
-
-
-;;;
-;;; Generic functions
-;;;
-
-(define-method (display-type (obj <generic>))
- (display-class <generic> "a generic function")
- (display " of class ")
- (display-class (class-of obj))
- (display ".\n"))
-
-(define-method (display-value (obj <generic>))
- (display-list #f (generic-function-methods obj)))
-
-
-;;;
-;;; Methods
-;;;
-
-(define-method (display-object (obj <method>))
- (display "(")
- (let ((gf (method-generic-function obj)))
- (display (if gf (generic-function-name gf) "#<anonymous>")))
- (let loop ((args (method-specializers obj)))
- (cond
- ((null? args))
- ((pair? args)
- (display " ")
- (display-class (car args))
- (loop (cdr args)))
- (else (display " . ") (display-class args))))
- (display ")"))
-
-(define-method (display-summary (obj <method>))
- (display "Method_ ")
- (display-object obj)
- (newline)
- (display " ")
- (display-description obj))
-
-(define-method (display-type (obj <method>))
- (display-class <method> "a method")
- (display " of class ")
- (display-class (class-of obj))
- (display ".\n"))
-
-(define-method (display-documentation (obj <method>))
- (let ((doc (procedure-documentation (method-procedure obj))))
- (if doc (format-documentation doc) (next-method))))
-;;; Error handling in the REPL
-
-;; Copyright (C) 2001, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-;; 02110-1301 USA
-
-;;; Code_
-
-(define-module (system repl error-handling)
- #\use-module (system base pmatch)
- #\use-module (system vm trap-state)
- #\use-module (system repl debug)
- #\use-module (ice-9 format)
- #\export (call-with-error-handling
- with-error-handling))
-
-
-
-
-;;;
-;;; Error handling via repl debugging
-;;;
-
-(define (error-string stack key args)
- (call-with-output-string
- (lambda (port)
- (let ((frame (and (< 0 (vector-length stack)) (vector-ref stack 0))))
- (print-exception port frame key args)))))
-
-(define* (call-with-error-handling thunk #\key
- (on-error 'debug) (post-error 'catch)
- (pass-keys '(quit)) (trap-handler 'debug))
- (let ((in (current-input-port))
- (out (current-output-port))
- (err (current-error-port)))
- (define (with-saved-ports thunk)
- (with-input-from-port in
- (lambda ()
- (with-output-to-port out
- (lambda ()
- (with-error-to-port err
- thunk))))))
-
- (define (debug-trap-handler frame trap-idx trap-name)
- (let* ((tag (and (pair? (fluid-ref %stacks))
- (cdar (fluid-ref %stacks))))
- (stack (narrow-stack->vector
- (make-stack frame)
- ;; Take the stack from the given frame, cutting 0
- ;; frames.
- 0
- ;; Narrow the end of the stack to the most recent
- ;; start-stack.
- tag
- ;; And one more frame, because %start-stack
- ;; invoking the start-stack thunk has its own frame
- ;; too.
- 0 (and tag 1)))
- (error-msg (if trap-idx
- (format #f "Trap ~d_ ~a" trap-idx trap-name)
- trap-name))
- (debug (make-debug stack 0 error-msg #t)))
- (with-saved-ports
- (lambda ()
- (if trap-idx
- (begin
- (format #t "~a~%" error-msg)
- (format #t "Entering a new prompt. ")
- (format #t "Type `,bt' for a backtrace or `,q' to continue.\n")))
- ((@ (system repl repl) start-repl) #\debug debug)))))
-
- (define (null-trap-handler frame trap-idx trap-name)
- #t)
-
- (define le-trap-handler
- (case trap-handler
- ((debug) debug-trap-handler)
- ((pass) null-trap-handler)
- ((disabled) #f)
- (else (error "Unknown trap-handler strategy" trap-handler))))
-
- (catch #t
- (lambda ()
- (with-default-trap-handler le-trap-handler
- (lambda () (%start-stack #t thunk))))
-
- (case post-error
- ((report)
- (lambda (key . args)
- (if (memq key pass-keys)
- (apply throw key args)
- (begin
- (with-saved-ports
- (lambda ()
- (run-hook before-error-hook)
- (print-exception err #f key args)
- (run-hook after-error-hook)
- (force-output err)))
- (if #f #f)))))
- ((catch)
- (lambda (key . args)
- (if (memq key pass-keys)
- (apply throw key args))))
- (else
- (if (procedure? post-error)
- (lambda (k . args)
- (apply (if (memq k pass-keys) throw post-error) k args))
- (error "Unknown post-error strategy" post-error))))
-
- (case on-error
- ((debug)
- (lambda (key . args)
- (if (not (memq key pass-keys))
- (let* ((tag (and (pair? (fluid-ref %stacks))
- (cdar (fluid-ref %stacks))))
- (stack (narrow-stack->vector
- (make-stack #t)
- ;; Cut three frames from the top of the stack_
- ;; make-stack, this one, and the throw handler.
- 3
- ;; Narrow the end of the stack to the most recent
- ;; start-stack.
- tag
- ;; And one more frame, because %start-stack invoking
- ;; the start-stack thunk has its own frame too.
- 0 (and tag 1)))
- (error-msg (error-string stack key args))
- (debug (make-debug stack 0 error-msg #f)))
- (with-saved-ports
- (lambda ()
- (format #t "~a~%" error-msg)
- (format #t "Entering a new prompt. ")
- (format #t "Type `,bt' for a backtrace or `,q' to continue.\n")
- ((@ (system repl repl) start-repl) #\debug debug)))))))
- ((report)
- (lambda (key . args)
- (if (not (memq key pass-keys))
- (begin
- (with-saved-ports
- (lambda ()
- (run-hook before-error-hook)
- (print-exception err #f key args)
- (run-hook after-error-hook)
- (force-output err)))
- (if #f #f)))))
- ((backtrace)
- (lambda (key . args)
- (if (not (memq key pass-keys))
- (let* ((tag (and (pair? (fluid-ref %stacks))
- (cdar (fluid-ref %stacks))))
- (frames (narrow-stack->vector
- (make-stack #t)
- ;; Narrow as above, for the debugging case.
- 3 tag 0 (and tag 1))))
- (with-saved-ports
- (lambda ()
- (print-frames frames)
- (run-hook before-error-hook)
- (print-exception err #f key args)
- (run-hook after-error-hook)
- (force-output err)))
- (if #f #f)))))
- ((pass)
- (lambda (key . args)
- ;; fall through to rethrow
- #t))
- (else
- (if (procedure? on-error)
- (lambda (k . args)
- (apply (if (memq k pass-keys) throw on-error) k args))
- (error "Unknown on-error strategy" on-error)))))))
-
-(define-syntax-rule (with-error-handling form)
- (call-with-error-handling (lambda () form)))
-;;; Read-Eval-Print Loop
-
-;; Copyright (C) 2001, 2009, 2010, 2011, 2013,
-;; 2014 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-;; 02110-1301 USA
-
-;;; Code_
-
-(define-module (system repl repl)
- #\use-module (system base syntax)
- #\use-module (system base pmatch)
- #\use-module (system base compile)
- #\use-module (system base language)
- #\use-module (system vm vm)
- #\use-module (system repl error-handling)
- #\use-module (system repl common)
- #\use-module (system repl command)
- #\use-module (ice-9 control)
- #\export (start-repl run-repl))
-
-
-;;;
-;;; Comments
-;;;
-;;; (You don't want a comment to force a continuation line.)
-;;;
-
-(define (read-scheme-line-comment port)
- (let lp ()
- (let ((ch (read-char port)))
- (or (eof-object? ch)
- (eqv? ch #\newline)
- (lp)))))
-
-(define (read-scheme-datum-comment port)
- (read port))
-
-;; ch is a peeked char
-(define (read-comment lang port ch)
- (and (eq? (language-name lang) 'scheme)
- (case ch
- ((#\;)
- (read-char port)
- (read-scheme-line-comment port)
- #t)
- ((#\#)
- (read-char port)
- (case (peek-char port)
- ((#\;)
- (read-char port)
- (read-scheme-datum-comment port)
- #t)
- ;; Not doing R6RS block comments because of the possibility
- ;; of read-hash extensions. Lame excuse. Not doing scsh
- ;; block comments either, because I don't feel like handling
- ;; .
- (else
- (unread-char #\# port)
- #f)))
- (else
- #f))))
-
-
-
-;;;
-;;; Meta commands
-;;;
-
-(define meta-command-token (cons 'meta 'command))
-
-(define (meta-reader lang env)
- (lambda* (#\optional (port (current-input-port)))
- (with-input-from-port port
- (lambda ()
- (let ((ch (flush-leading-whitespace)))
- (cond ((eof-object? ch)
- (read-char)) ; consume the EOF and return it
- ((eqv? ch #\,)
- (read-char)
- meta-command-token)
- ((read-comment lang port ch)
- *unspecified*)
- (else ((language-reader lang) port env))))))))
-
-(define (flush-all-input)
- (if (and (char-ready?)
- (not (eof-object? (peek-char))))
- (begin
- (read-char)
- (flush-all-input))))
-
-;; repl-reader is a function defined in boot-9.scm, and is replaced by
-;; something else if readline has been activated. much of this hoopla is
-;; to be able to re-use the existing readline machinery.
-;;
-;; Catches read errors, returning *unspecified* in that case.
-;;
-;; Note_ although not exported, this is used by (system repl coop-server)
-(define (prompting-meta-read repl)
- (catch #t
- (lambda ()
- (repl-reader (lambda () (repl-prompt repl))
- (meta-reader (repl-language repl) (current-module))))
- (lambda (key . args)
- (case key
- ((quit)
- (apply throw key args))
- (else
- (format (current-output-port) "While reading expression_\n")
- (print-exception (current-output-port) #f key args)
- (flush-all-input)
- *unspecified*)))))
-
-
-
-;;;
-;;; The repl
-;;;
-
-(define* (start-repl #\optional (lang (current-language)) #\key debug)
- (start-repl* lang debug prompting-meta-read))
-
-;; Note_ although not exported, this is used by (system repl coop-server)
-(define (start-repl* lang debug prompting-meta-read)
- ;; ,language at the REPL will update the current-language. Make
- ;; sure that it does so in a new dynamic scope.
- (parameterize ((current-language lang))
- (run-repl* (make-repl lang debug) prompting-meta-read)))
-
-;; (put 'abort-on-error 'scheme-indent-function 1)
-(define-syntax-rule (abort-on-error string exp)
- (catch #t
- (lambda () exp)
- (lambda (key . args)
- (format #t "While ~A_~%" string)
- (print-exception (current-output-port) #f key args)
- (abort))))
-
-(define (run-repl repl)
- (run-repl* repl prompting-meta-read))
-
-(define (run-repl* repl prompting-meta-read)
- (define (with-stack-and-prompt thunk)
- (call-with-prompt (default-prompt-tag)
- (lambda () (start-stack #t (thunk)))
- (lambda (k proc)
- (with-stack-and-prompt (lambda () (proc k))))))
-
- (% (with-fluids ((*repl-stack*
- (cons repl (or (fluid-ref *repl-stack*) '()))))
- (if (null? (cdr (fluid-ref *repl-stack*)))
- (repl-welcome repl))
- (let prompt-loop ()
- (let ((exp (prompting-meta-read repl)))
- (cond
- ((eqv? exp *unspecified*)) ; read error or comment, pass
- ((eq? exp meta-command-token)
- (catch #t
- (lambda ()
- (meta-command repl))
- (lambda (k . args)
- (if (eq? k 'quit)
- (abort args)
- (begin
- (format #t "While executing meta-command_~%")
- (print-exception (current-output-port) #f k args))))))
- ((eof-object? exp)
- (newline)
- (abort '()))
- (else
- ;; since the input port is line-buffered, consume up to the
- ;; newline
- (flush-to-newline)
- (call-with-error-handling
- (lambda ()
- (catch 'quit
- (lambda ()
- (call-with-values
- (lambda ()
- (% (let ((thunk
- (abort-on-error "compiling expression"
- (repl-prepare-eval-thunk
- repl
- (abort-on-error "parsing expression"
- (repl-parse repl exp))))))
- (run-hook before-eval-hook exp)
- (call-with-error-handling
- (lambda ()
- (with-stack-and-prompt thunk))
- #\on-error (repl-option-ref repl 'on-error)))
- (lambda (k) (values))))
- (lambda l
- (for-each (lambda (v)
- (repl-print repl v))
- l))))
- (lambda (k . args)
- (abort args))))
- #\on-error (repl-option-ref repl 'on-error)
- #\trap-handler 'disabled)))
- (flush-to-newline) ;; consume trailing whitespace
- (prompt-loop))))
- (lambda (k status)
- status)))
-
-;; Returns first non-whitespace char.
-(define (flush-leading-whitespace)
- (let ((ch (peek-char)))
- (cond ((eof-object? ch) ch)
- ((char-whitespace? ch) (read-char) (flush-leading-whitespace))
- (else ch))))
-
-(define (flush-to-newline)
- (if (char-ready?)
- (let ((ch (peek-char)))
- (if (and (not (eof-object? ch)) (char-whitespace? ch))
- (begin
- (read-char)
- (if (not (char=? ch #\newline))
- (flush-to-newline)))))))
-;;; Repl server
-
-;; Copyright (C) 2003, 2010, 2011, 2014, 2016 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-;; 02110-1301 USA
-
-;;; Code_
-
-(define-module (system repl server)
- #\use-module (system repl repl)
- #\use-module (ice-9 threads)
- #\use-module (ice-9 rdelim)
- #\use-module (ice-9 match)
- #\use-module (ice-9 iconv)
- #\use-module (rnrs bytevectors)
- #\use-module (rnrs io ports)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-26) ; cut
- #\export (make-tcp-server-socket
- make-unix-domain-server-socket
- run-server
- spawn-server
- stop-server-and-clients!))
-
-;; List of pairs of the form (SOCKET . FORCE-CLOSE), where SOCKET is a
-;; socket port, and FORCE-CLOSE is a thunk that forcefully shuts down
-;; the socket.
-(define *open-sockets* '())
-
-(define sockets-lock (make-mutex))
-
-;; WARNING_ it is unsafe to call 'close-socket!' from another thread.
-;; Note_ although not exported, this is used by (system repl coop-server)
-(define (close-socket! s)
- (with-mutex sockets-lock
- (set! *open-sockets* (assq-remove! *open-sockets* s)))
- ;; Close-port could block or raise an exception flushing buffered
- ;; output. Hmm.
- (close-port s))
-
-;; Note_ although not exported, this is used by (system repl coop-server)
-(define (add-open-socket! s force-close)
- (with-mutex sockets-lock
- (set! *open-sockets* (acons s force-close *open-sockets*))))
-
-(define (stop-server-and-clients!)
- (cond
- ((with-mutex sockets-lock
- (match *open-sockets*
- (() #f)
- (((s . force-close) . rest)
- (set! *open-sockets* rest)
- force-close)))
- => (lambda (force-close)
- (force-close)
- (stop-server-and-clients!)))))
-
-(define* (make-tcp-server-socket #\key
- (host #f)
- (addr (if host (inet-aton host) INADDR_LOOPBACK))
- (port 37146))
- (let ((sock (socket PF_INET SOCK_STREAM 0)))
- (setsockopt sock SOL_SOCKET SO_REUSEADDR 1)
- (bind sock AF_INET addr port)
- sock))
-
-(define* (make-unix-domain-server-socket #\key (path "/tmp/guile-socket"))
- (let ((sock (socket PF_UNIX SOCK_STREAM 0)))
- (setsockopt sock SOL_SOCKET SO_REUSEADDR 1)
- (bind sock AF_UNIX path)
- sock))
-
-;; List of errno values from 'select' or 'accept' that should lead to a
-;; retry in 'run-server'.
-(define errs-to-retry
- (delete-duplicates
- (filter-map (lambda (name)
- (and=> (module-variable the-root-module name)
- variable-ref))
- '(EINTR EAGAIN EWOULDBLOCK))))
-
-(define* (run-server #\optional (server-socket (make-tcp-server-socket)))
- (run-server* server-socket serve-client))
-
-;; Note_ although not exported, this is used by (system repl coop-server)
-(define (run-server* server-socket serve-client)
- ;; We use a pipe to notify the server when it should shut down.
- (define shutdown-pipes (pipe))
- (define shutdown-read-pipe (car shutdown-pipes))
- (define shutdown-write-pipe (cdr shutdown-pipes))
-
- ;; 'shutdown-server' is called by 'stop-server-and-clients!'.
- (define (shutdown-server)
- (display #\! shutdown-write-pipe)
- (force-output shutdown-write-pipe))
-
- (define monitored-ports
- (list server-socket
- shutdown-read-pipe))
-
- (define (accept-new-client)
- (catch #t
- (lambda ()
- (let ((ready-ports (car (select monitored-ports '() '()))))
- ;; If we've been asked to shut down, return #f.
- (and (not (memq shutdown-read-pipe ready-ports))
- (accept server-socket))))
- (lambda k-args
- (let ((err (system-error-errno k-args)))
- (cond
- ((memv err errs-to-retry)
- (accept-new-client))
- (else
- (warn "Error accepting client" k-args)
- ;; Retry after a timeout.
- (sleep 1)
- (accept-new-client)))))))
-
- ;; Put the socket into non-blocking mode.
- (fcntl server-socket F_SETFL
- (logior O_NONBLOCK
- (fcntl server-socket F_GETFL)))
-
- (sigaction SIGPIPE SIG_IGN)
- (add-open-socket! server-socket shutdown-server)
- (listen server-socket 5)
- (let lp ((client (accept-new-client)))
- ;; If client is false, we are shutting down.
- (if client
- (let ((client-socket (car client))
- (client-addr (cdr client)))
- (make-thread serve-client client-socket client-addr)
- (lp (accept-new-client)))
- (begin (close shutdown-write-pipe)
- (close shutdown-read-pipe)
- (close server-socket)))))
-
-(define* (spawn-server #\optional (server-socket (make-tcp-server-socket)))
- (make-thread run-server server-socket))
-
-(define (serve-client client addr)
-
- (let ((thread (current-thread)))
- ;; Close the socket when this thread exits, even if canceled.
- (set-thread-cleanup! thread (lambda () (close-socket! client)))
- ;; Arrange to cancel this thread to forcefully shut down the socket.
- (add-open-socket! client (lambda () (cancel-thread thread))))
-
- (guard-against-http-request client)
-
- (with-continuation-barrier
- (lambda ()
- (parameterize ((current-input-port client)
- (current-output-port client)
- (current-error-port client)
- (current-warning-port client))
- (with-fluids ((*repl-stack* '()))
- (start-repl))))))
-
-
-;;;
-;;; The following code adds protection to Guile's REPL servers against
-;;; HTTP inter-protocol exploitation attacks, a scenario whereby an
-;;; attacker can, via an HTML page, cause a web browser to send data to
-;;; TCP servers listening on a loopback interface or private network.
-;;; See <https_//en.wikipedia.org/wiki/Inter-protocol_exploitation> and
-;;; <https_//www.jochentopf.com/hfpa/hfpa.pdf>, The HTML Form Protocol
-;;; Attack (2001) by Tochen Topf <jochen@remote.org>.
-;;;
-;;; Here we add a procedure to 'before-read-hook' that looks for a possible
-;;; HTTP request-line in the first line of input from the client socket. If
-;;; present, the socket is drained and closed, and a loud warning is written
-;;; to stderr (POSIX file descriptor 2).
-;;;
-
-(define (with-temporary-port-encoding port encoding thunk)
- "Call THUNK in a dynamic environment in which the encoding of PORT is
-temporarily set to ENCODING."
- (let ((saved-encoding #f))
- (dynamic-wind
- (lambda ()
- (unless (port-closed? port)
- (set! saved-encoding (port-encoding port))
- (set-port-encoding! port encoding)))
- thunk
- (lambda ()
- (unless (port-closed? port)
- (set! encoding (port-encoding port))
- (set-port-encoding! port saved-encoding))))))
-
-(define (with-saved-port-line+column port thunk)
- "Save the line and column of PORT before entering THUNK, and restore
-their previous values upon normal or non-local exit from THUNK."
- (let ((saved-line #f) (saved-column #f))
- (dynamic-wind
- (lambda ()
- (unless (port-closed? port)
- (set! saved-line (port-line port))
- (set! saved-column (port-column port))))
- thunk
- (lambda ()
- (unless (port-closed? port)
- (set-port-line! port saved-line)
- (set-port-column! port saved-column))))))
-
-(define (drain-input-and-close socket)
- "Drain input from SOCKET using ISO-8859-1 encoding until it would block,
-and then close it. Return the drained input as a string."
- (dynamic-wind
- (lambda ()
- ;; Enable full buffering mode on the socket to allow
- ;; 'get-bytevector-some' to return non-trivial chunks.
- (setvbuf socket _IOFBF))
- (lambda ()
- (let loop ((chunks '()))
- (let ((result (and (char-ready? socket)
- (get-bytevector-some socket))))
- (if (bytevector? result)
- (loop (cons (bytevector->string result "ISO-8859-1")
- chunks))
- (string-concatenate-reverse chunks)))))
- (lambda ()
- ;; Close the socket even in case of an exception.
- (close-port socket))))
-
-(define permissive-http-request-line?
- ;; This predicate is deliberately permissive
- ;; when checking the Request-URI component.
- (let ((cs (ucs-range->char-set #x20 #x7E))
- (rx (make-regexp
- (string-append
- "^(OPTIONS|GET|HEAD|POST|PUT|DELETE|TRACE|CONNECT) "
- "[^ ]+ "
- "HTTP/[0-9]+.[0-9]+$"))))
- (lambda (line)
- "Return true if LINE might plausibly be an HTTP request-line,
-otherwise return #f."
- ;; We cannot simplify this to a simple 'regexp-exec', because
- ;; 'regexp-exec' cannot cope with NUL bytes.
- (and (string-every cs line)
- (regexp-exec rx line)))))
-
-(define (check-for-http-request socket)
- "Check for a possible HTTP request in the initial input from SOCKET.
-If one is found, close the socket and print a report to STDERR (fdes 2).
-Otherwise, put back the bytes."
- ;; Temporarily set the port encoding to ISO-8859-1 to allow lossless
- ;; reading and unreading of the first line, regardless of what bytes
- ;; are present. Note that a valid HTTP request-line contains only
- ;; ASCII characters.
- (with-temporary-port-encoding socket "ISO-8859-1"
- (lambda ()
- ;; Save the port 'line' and 'column' counters and later restore
- ;; them, since unreading what we read is not sufficient to do so.
- (with-saved-port-line+column socket
- (lambda ()
- ;; Read up to (but not including) the first CR or LF.
- ;; Although HTTP mandates CRLF line endings, we are permissive
- ;; here to guard against the possibility that in some
- ;; environments CRLF might be converted to LF before it
- ;; reaches us.
- (match (read-delimited "\r\n" socket 'peek)
- ((? eof-object?)
- ;; We found EOF before any input. Nothing to do.
- 'done)
-
- ((? permissive-http-request-line? request-line)
- ;; The input from the socket began with a plausible HTTP
- ;; request-line, which is unlikely to be legitimate and may
- ;; indicate an possible break-in attempt.
-
- ;; First, set the current port parameters to a void-port,
- ;; to avoid sending any more data over the socket, to cause
- ;; the REPL reader to see EOF, and to swallow any remaining
- ;; output gracefully.
- (let ((void-port (%make-void-port "rw")))
- (current-input-port void-port)
- (current-output-port void-port)
- (current-error-port void-port)
- (current-warning-port void-port))
-
- ;; Read from the socket until we would block,
- ;; and then close it.
- (let ((drained-input (drain-input-and-close socket)))
-
- ;; Print a report to STDERR (POSIX file descriptor 2).
- ;; XXX Can we do better here?
- (call-with-port (dup->port 2 "w")
- (cut format <> "
-@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-@@ POSSIBLE BREAK-IN ATTEMPT ON THE REPL SERVER @@
-@@ BY AN HTTP INTER-PROTOCOL EXPLOITATION ATTACK. See_ @@
-@@ <https_//en.wikipedia.org/wiki/Inter-protocol_exploitation> @@
-@@ Possible HTTP request received_ ~S
-@@ The associated socket has been closed. @@
-@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"
- (string-append request-line
- drained-input)))))
-
- (start-line
- ;; The HTTP request-line was not found, so
- ;; 'unread' the characters that we have read.
- (unread-string start-line socket))))))))
-
-(define (guard-against-http-request socket)
- "Arrange for the Guile REPL to check for an HTTP request in the
-initial input from SOCKET, in which case the socket will be closed.
-This guards against HTTP inter-protocol exploitation attacks, a scenario
-whereby an attacker can, via an HTML page, cause a web browser to send
-data to TCP servers listening on a loopback interface or private
-network."
- (%set-port-property! socket 'guard-against-http-request? #t))
-
-(define* (maybe-check-for-http-request
- #\optional (socket (current-input-port)))
- "Apply check-for-http-request to SOCKET if previously requested by
-guard-against-http-request. This procedure is intended to be added to
-before-read-hook."
- (when (%port-property socket 'guard-against-http-request?)
- (check-for-http-request socket)
- (unless (port-closed? socket)
- (%set-port-property! socket 'guard-against-http-request? #f))))
-
-;; Install the hook.
-(add-hook! before-read-hook
- maybe-check-for-http-request)
-
-;;; Local Variables_
-;;; eval_ (put 'with-temporary-port-encoding 'scheme-indent-function 2)
-;;; eval_ (put 'with-saved-port-line+column 'scheme-indent-function 1)
-;;; End_
-;;; -*- mode_ scheme; coding_ utf-8; -*-
-;;;
-;;; Copyright (C) 2010 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (system vm coverage)
- #\use-module (system vm vm)
- #\use-module (system vm frame)
- #\use-module (system vm program)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-9)
- #\use-module (srfi srfi-11)
- #\use-module (srfi srfi-26)
- #\export (with-code-coverage
- coverage-data?
- instrumented-source-files
- instrumented/executed-lines
- line-execution-counts
- procedure-execution-count
- coverage-data->lcov))
-
-;;; Author_ Ludovic Courtès
-;;;
-;;; Commentary_
-;;;
-;;; This module provides support to gather code coverage data by instrumenting
-;;; the VM.
-;;;
-;;; Code_
-
-
-;;;
-;;; Gathering coverage data.
-;;;
-
-(define (hashq-proc proc n)
- ;; Return the hash of PROC's objcode.
- (hashq (program-objcode proc) n))
-
-(define (assq-proc proc alist)
- ;; Instead of really looking for PROC in ALIST, look for the objcode of PROC.
- ;; IOW the alist is indexed by procedures, not objcodes, but those procedures
- ;; are taken as an arbitrary representative of all the procedures (closures)
- ;; sharing that objcode. This can significantly reduce memory consumption.
- (let ((code (program-objcode proc)))
- (find (lambda (pair)
- (eq? code (program-objcode (car pair))))
- alist)))
-
-(define (with-code-coverage vm thunk)
- "Run THUNK, a zero-argument procedure, using VM; instrument VM to collect code
-coverage data. Return code coverage data and the values returned by THUNK."
-
- (define procedure->ip-counts
- ;; Mapping from procedures to hash tables; said hash tables map instruction
- ;; pointers to the number of times they were executed.
- (make-hash-table 500))
-
- (define (collect! frame)
- ;; Update PROCEDURE->IP-COUNTS with info from FRAME.
- (let* ((proc (frame-procedure frame))
- (ip (frame-instruction-pointer frame))
- (proc-entry (hashx-create-handle! hashq-proc assq-proc
- procedure->ip-counts proc #f)))
- (let loop ()
- (define ip-counts (cdr proc-entry))
- (if ip-counts
- (let ((ip-entry (hashv-create-handle! ip-counts ip 0)))
- (set-cdr! ip-entry (+ (cdr ip-entry) 1)))
- (begin
- (set-cdr! proc-entry (make-hash-table))
- (loop))))))
-
- ;; FIXME_ It's unclear what the dynamic-wind is for, given that if the
- ;; VM is different from the current one, continuations will not be
- ;; resumable.
- (call-with-values (lambda ()
- (let ((level (vm-trace-level vm))
- (hook (vm-next-hook vm)))
- (dynamic-wind
- (lambda ()
- (set-vm-trace-level! vm (+ level 1))
- (add-hook! hook collect!))
- (lambda ()
- (call-with-vm vm thunk))
- (lambda ()
- (set-vm-trace-level! vm level)
- (remove-hook! hook collect!)))))
- (lambda args
- (apply values (make-coverage-data procedure->ip-counts) args))))
-
-
-;;;
-;;; Coverage data summary.
-;;;
-
-(define-record-type <coverage-data>
- (%make-coverage-data procedure->ip-counts
- procedure->sources
- file->procedures
- file->line-counts)
- coverage-data?
-
- ;; Mapping from procedures to hash tables; said hash tables map instruction
- ;; pointers to the number of times they were executed.
- (procedure->ip-counts data-procedure->ip-counts)
-
- ;; Mapping from procedures to the result of `program-sources'.
- (procedure->sources data-procedure->sources)
-
- ;; Mapping from source file names to lists of procedures defined in the file.
- (file->procedures data-file->procedures)
-
- ;; Mapping from file names to hash tables, which in turn map from line numbers
- ;; to execution counts.
- (file->line-counts data-file->line-counts))
-
-
-(define (make-coverage-data procedure->ip-counts)
- ;; Return a `coverage-data' object based on the coverage data available in
- ;; PROCEDURE->IP-COUNTS. Precompute the other hash tables that make up
- ;; `coverage-data' objects.
- (let* ((procedure->sources (make-hash-table 500))
- (file->procedures (make-hash-table 100))
- (file->line-counts (make-hash-table 100))
- (data (%make-coverage-data procedure->ip-counts
- procedure->sources
- file->procedures
- file->line-counts)))
- (define (increment-execution-count! file line count)
- ;; Make the execution count of FILE_LINE the maximum of its current value
- ;; and COUNT. This is so that LINE's execution count is correct when
- ;; several instruction pointers map to LINE.
- (let ((file-entry (hash-create-handle! file->line-counts file #f)))
- (if (not (cdr file-entry))
- (set-cdr! file-entry (make-hash-table 500)))
- (let ((line-entry (hashv-create-handle! (cdr file-entry) line 0)))
- (set-cdr! line-entry (max (cdr line-entry) count)))))
-
- ;; Update execution counts for procs that were executed.
- (hash-for-each (lambda (proc ip-counts)
- (let* ((sources (program-sources* data proc))
- (file (and (pair? sources)
- (source_file (car sources)))))
- (and file
- (begin
- ;; Add a zero count for all IPs in SOURCES and in
- ;; the sources of procedures closed over by PROC.
- (for-each
- (lambda (source)
- (let ((file (source_file source))
- (line (source_line source)))
- (increment-execution-count! file line 0)))
- (append-map (cut program-sources* data <>)
- (closed-over-procedures proc)))
-
- ;; Add the actual execution count collected.
- (hash-for-each
- (lambda (ip count)
- (let ((line (closest-source-line sources ip)))
- (increment-execution-count! file line count)))
- ip-counts)))))
- procedure->ip-counts)
-
- ;; Set the execution count to zero for procedures loaded and not executed.
- ;; FIXME_ Traversing thousands of procedures here is inefficient.
- (for-each (lambda (proc)
- (and (not (hashq-ref procedure->sources proc))
- (for-each (lambda (proc)
- (let* ((sources (program-sources* data proc))
- (file (and (pair? sources)
- (source_file (car sources)))))
- (and file
- (for-each
- (lambda (ip)
- (let ((line (closest-source-line sources ip)))
- (increment-execution-count! file line 0)))
- (map source_addr sources)))))
- (closed-over-procedures proc))))
- (append-map module-procedures (loaded-modules)))
-
- data))
-
-(define (procedure-execution-count data proc)
- "Return the number of times PROC's code was executed, according to DATA, or #f
-if PROC was not executed. When PROC is a closure, the number of times its code
-was executed is returned, not the number of times this code associated with this
-particular closure was executed."
- (let ((sources (program-sources* data proc)))
- (and (pair? sources)
- (and=> (hashx-ref hashq-proc assq-proc
- (data-procedure->ip-counts data) proc)
- (lambda (ip-counts)
- ;; FIXME_ broken with lambda*
- (let ((entry-ip (source_addr (car sources))))
- (hashv-ref ip-counts entry-ip 0)))))))
-
-(define (program-sources* data proc)
- ;; A memoizing version of `program-sources'.
- (or (hashq-ref (data-procedure->sources data) proc)
- (and (program? proc)
- (let ((sources (program-sources proc))
- (p->s (data-procedure->sources data))
- (f->p (data-file->procedures data)))
- (if (pair? sources)
- (let* ((file (source_file (car sources)))
- (entry (hash-create-handle! f->p file '())))
- (hashq-set! p->s proc sources)
- (set-cdr! entry (cons proc (cdr entry)))
- sources)
- sources)))))
-
-(define (file-procedures data file)
- ;; Return the list of globally bound procedures defined in FILE.
- (hash-ref (data-file->procedures data) file '()))
-
-(define (instrumented/executed-lines data file)
- "Return the number of instrumented and the number of executed source lines in
-FILE according to DATA."
- (define instr+exec
- (and=> (hash-ref (data-file->line-counts data) file)
- (lambda (line-counts)
- (hash-fold (lambda (line count instr+exec)
- (let ((instr (car instr+exec))
- (exec (cdr instr+exec)))
- (cons (+ 1 instr)
- (if (> count 0)
- (+ 1 exec)
- exec))))
- '(0 . 0)
- line-counts))))
-
- (values (car instr+exec) (cdr instr+exec)))
-
-(define (line-execution-counts data file)
- "Return a list of line number/execution count pairs for FILE, or #f if FILE
-is not among the files covered by DATA."
- (and=> (hash-ref (data-file->line-counts data) file)
- (lambda (line-counts)
- (hash-fold alist-cons '() line-counts))))
-
-(define (instrumented-source-files data)
- "Return the list of `instrumented' source files, i.e., source files whose code
-was loaded at the time DATA was collected."
- (hash-fold (lambda (file counts files)
- (cons file files))
- '()
- (data-file->line-counts data)))
-
-
-;;;
-;;; Helpers.
-;;;
-
-(define (loaded-modules)
- ;; Return the list of all the modules currently loaded.
- (define seen (make-hash-table))
-
- (let loop ((modules (module-submodules (resolve-module '() #f)))
- (result '()))
- (hash-fold (lambda (name module result)
- (if (hashq-ref seen module)
- result
- (begin
- (hashq-set! seen module #t)
- (loop (module-submodules module)
- (cons module result)))))
- result
- modules)))
-
-(define (module-procedures module)
- ;; Return the list of procedures bound globally in MODULE.
- (hash-fold (lambda (binding var result)
- (if (variable-bound? var)
- (let ((value (variable-ref var)))
- (if (procedure? value)
- (cons value result)
- result))
- result))
- '()
- (module-obarray module)))
-
-(define (closest-source-line sources ip)
- ;; Given SOURCES, as returned by `program-sources' for a given procedure,
- ;; return the source line of code that is the closest to IP. This is similar
- ;; to what `program-source' does.
- (let loop ((sources sources)
- (line (and (pair? sources) (source_line (car sources)))))
- (if (null? sources)
- line
- (let ((source (car sources)))
- (if (> (source_addr source) ip)
- line
- (loop (cdr sources) (source_line source)))))))
-
-(define (closed-over-procedures proc)
- ;; Return the list of procedures PROC closes over, PROC included.
- (let loop ((proc proc)
- (result '()))
- (if (and (program? proc) (not (memq proc result)))
- (fold loop (cons proc result)
- (append (vector->list (or (program-objects proc) #()))
- (program-free-variables proc)))
- result)))
-
-
-;;;
-;;; LCOV output.
-;;;
-
-(define* (coverage-data->lcov data port)
- "Traverse code coverage information DATA, as obtained with
-`with-code-coverage', and write coverage information in the LCOV format to PORT.
-The report will include all the modules loaded at the time coverage data was
-gathered, even if their code was not executed."
-
- (define (dump-function proc)
- ;; Dump source location and basic coverage data for PROC.
- (and (program? proc)
- (let ((sources (program-sources* data proc)))
- (and (pair? sources)
- (let* ((line (source_line-for-user (car sources)))
- (name (or (procedure-name proc)
- (format #f "anonymous-l~a" line))))
- (format port "FN_~A,~A~%" line name)
- (and=> (procedure-execution-count data proc)
- (lambda (count)
- (format port "FNDA_~A,~A~%" count name))))))))
-
- ;; Output per-file coverage data.
- (format port "TN_~%")
- (for-each (lambda (file)
- (let ((procs (file-procedures data file))
- (path (search-path %load-path file)))
- (if (string? path)
- (begin
- (format port "SF_~A~%" path)
- (for-each dump-function procs)
- (for-each (lambda (line+count)
- (let ((line (car line+count))
- (count (cdr line+count)))
- (format port "DA_~A,~A~%"
- (+ 1 line) count)))
- (line-execution-counts data file))
- (let-values (((instr exec)
- (instrumented/executed-lines data file)))
- (format port "LH_ ~A~%" exec)
- (format port "LF_ ~A~%" instr))
- (format port "end_of_record~%"))
- (begin
- (format (current-error-port)
- "skipping unknown source file_ ~a~%"
- file)))))
- (instrumented-source-files data)))
-;;; Guile VM frame functions
-
-;;; Copyright (C) 2001, 2005, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code_
-
-(define-module (system vm frame)
- #\use-module (system base pmatch)
- #\use-module (system vm program)
- #\use-module (system vm instruction)
- #\use-module (system vm objcode)
- #\export (frame-bindings
- frame-lookup-binding
- frame-binding-ref frame-binding-set!
- frame-next-source frame-call-representation
- frame-environment
- frame-object-binding frame-object-name
- frame-return-values))
-
-(define (frame-bindings frame)
- (let ((p (frame-procedure frame)))
- (if (program? p)
- (program-bindings-for-ip p (frame-instruction-pointer frame))
- '())))
-
-(define (frame-lookup-binding frame var)
- (let lp ((bindings (frame-bindings frame)))
- (cond ((null? bindings)
- #f)
- ((eq? (binding_name (car bindings)) var)
- (car bindings))
- (else
- (lp (cdr bindings))))))
-
-(define (frame-binding-set! frame var val)
- (frame-local-set! frame
- (binding_index
- (or (frame-lookup-binding frame var)
- (error "variable not bound in frame" var frame)))
- val))
-
-(define (frame-binding-ref frame var)
- (frame-local-ref frame
- (binding_index
- (or (frame-lookup-binding frame var)
- (error "variable not bound in frame" var frame)))))
-
-
-;; This function is always called to get some sort of representation of the
-;; frame to present to the user, so let's do the logical thing and dispatch to
-;; frame-call-representation.
-(define (frame-arguments frame)
- (cdr (frame-call-representation frame)))
-
-
-
-;;;
-;;; Pretty printing
-;;;
-
-(define (frame-next-source frame)
- (let ((proc (frame-procedure frame)))
- (if (program? proc)
- (program-source proc
- (frame-instruction-pointer frame)
- (program-sources-pre-retire proc))
- '())))
-
-
-;; Basically there are two cases to deal with here_
-;;
-;; 1. We've already parsed the arguments, and bound them to local
-;; variables. In a standard (lambda (a b c) ...) call, this doesn't
-;; involve any argument shuffling; but with rest, optional, or
-;; keyword arguments, the arguments as given to the procedure may
-;; not correspond to what's on the stack. We reconstruct the
-;; arguments using e.g. for the case above_ `(,a ,b ,c). This works
-;; for rest arguments too_ (a b . c) => `(,a ,b . ,c)
-;;
-;; 2. We have failed to parse the arguments. Perhaps it's the wrong
-;; number of arguments, or perhaps we're doing a typed dispatch and
-;; the types don't match. In that case the arguments are all on the
-;; stack, and nothing else is on the stack.
-
-(define (frame-call-representation frame)
- (let ((p (frame-procedure frame)))
- (cons
- (or (false-if-exception (procedure-name p)) p)
- (cond
- ((and (program? p)
- (program-arguments-alist p (frame-instruction-pointer frame)))
- ;; case 1
- => (lambda (arguments)
- (define (binding-ref sym i)
- (cond
- ((frame-lookup-binding frame sym)
- => (lambda (b) (frame-local-ref frame (binding_index b))))
- ((< i (frame-num-locals frame))
- (frame-local-ref frame i))
- (else
- ;; let's not error here, as we are called during backtraces...
- '???)))
- (let lp ((req (or (assq-ref arguments 'required) '()))
- (opt (or (assq-ref arguments 'optional) '()))
- (key (or (assq-ref arguments 'keyword) '()))
- (rest (or (assq-ref arguments 'rest) #f))
- (i 0))
- (cond
- ((pair? req)
- (cons (binding-ref (car req) i)
- (lp (cdr req) opt key rest (1+ i))))
- ((pair? opt)
- (cons (binding-ref (car opt) i)
- (lp req (cdr opt) key rest (1+ i))))
- ((pair? key)
- (cons* (caar key)
- (frame-local-ref frame (cdar key))
- (lp req opt (cdr key) rest (1+ i))))
- (rest
- (binding-ref rest i))
- (else
- '())))))
- (else
- ;; case 2
- (map (lambda (i)
- (frame-local-ref frame i))
- (iota (frame-num-locals frame))))))))
-
-
-
-;;; Misc
-;;;
-
-(define (frame-environment frame)
- (map (lambda (binding)
- (cons (binding_name binding) (frame-binding-ref frame binding)))
- (frame-bindings frame)))
-
-(define (frame-object-binding frame obj)
- (do ((bs (frame-bindings frame) (cdr bs)))
- ((or (null? bs) (eq? obj (frame-binding-ref frame (car bs))))
- (and (pair? bs) (car bs)))))
-
-(define (frame-object-name frame obj)
- (cond ((frame-object-binding frame obj) => binding_name)
- (else #f)))
-
-;; Nota bene, only if frame is in a return context (i.e. in a
-;; pop-continuation hook dispatch).
-(define (frame-return-values frame)
- (let* ((len (frame-num-locals frame))
- (nvalues (frame-local-ref frame (1- len))))
- (map (lambda (i)
- (frame-local-ref frame (+ (- len nvalues 1) i)))
- (iota nvalues))))
-;;; Guile VM debugging facilities
-
-;;; Copyright (C) 2001, 2009, 2010, 2011 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code_
-
-(define-module (system vm inspect)
- #\use-module (system base pmatch)
- #\use-module (system base syntax)
- #\use-module (system vm vm)
- #\use-module (system vm frame)
- #\use-module ((language assembly disassemble)
- #\select ((disassemble . %disassemble)))
- #\use-module (ice-9 rdelim)
- #\use-module (ice-9 pretty-print)
- #\use-module (ice-9 format)
- #\use-module (system vm program)
- #\export (inspect))
-
-
-(define (reverse-hashq h)
- (let ((ret (make-hash-table)))
- (hash-for-each
- (lambda (k v)
- (hashq-set! ret v (cons k (hashq-ref ret v '()))))
- h)
- ret))
-
-(define (catch-bad-arguments thunk bad-args-thunk)
- (catch 'wrong-number-of-args
- (lambda ()
- (catch 'keyword-argument-error
- thunk
- (lambda (k . args)
- (bad-args-thunk))))
- (lambda (k . args)
- (bad-args-thunk))))
-
-(define (read-args prompt)
- (define (read* reader)
- (repl-reader prompt reader))
- (define (next)
- (read* read-char))
- (define (cmd chr)
- (cond
- ((eof-object? chr) (list chr))
- ((char=? chr #\newline) (cmd (next)))
- ((char-whitespace? chr) (cmd (next)))
- (else
- (unread-char chr)
- (let ((tok (read* read)))
- (args (list tok) (next))))))
- (define (args out chr)
- (cond
- ((eof-object? chr) (reverse out))
- ((char=? chr #\newline) (reverse out))
- ((char-whitespace? chr) (args out (next)))
- (else
- (unread-char chr)
- (let ((tok (read* read)))
- (args (cons tok out) (next))))))
- (cmd (next)))
-
-
-;;;
-;;; Inspector
-;;;
-
-(define (inspect x)
- (define-syntax-rule (define-command ((mod cname alias ...) . args)
- body ...)
- (define cname
- (let ((c (lambda* args body ...)))
- (set-procedure-property! c 'name 'cname)
- (module-define! mod 'cname c)
- (module-add! mod 'alias (module-local-variable mod 'cname))
- ...
- c)))
-
- (let ((commands (make-module)))
- (define (prompt)
- (format #f "~20@y inspect> " x))
-
- (define-command ((commands quit q continue cont c))
- "Quit the inspector."
- (throw 'quit))
-
- (define-command ((commands print p))
- "Print the current object using `pretty-print'."
- (pretty-print x))
-
- (define-command ((commands write w))
- "Print the current object using `write'."
- (write x))
-
- (define-command ((commands display d))
- "Print the current object using `display'."
- (display x))
-
- (define-command ((commands disassemble x))
- "Disassemble the current object, which should be objcode or a procedure."
- (catch #t
- (lambda ()
- (%disassemble x))
- (lambda args
- (format #t "Error disassembling object_ ~a\n" args))))
-
- (define-command ((commands help h ?) #\optional cmd)
- "Show this help message."
- (let ((rhash (reverse-hashq (module-obarray commands))))
- (define (help-cmd cmd)
- (let* ((v (module-local-variable commands cmd))
- (p (variable-ref v))
- (canonical-name (procedure-name p)))
- ;; la la la
- (format #t "~a~{ ~_@(~a~)~}~?~%~a~&~%"
- canonical-name (program-lambda-list p)
- "~#[~_;~40t(aliases_ ~@{~a~^, ~})~]"
- (delq canonical-name (hashq-ref rhash v))
- (procedure-documentation p))))
- (cond
- (cmd
- (cond
- ((and (symbol? cmd) (module-local-variable commands cmd))
- (help-cmd cmd))
- (else
- (format #t "Invalid command ~s.~%" cmd)
- (format #t "Try `help' for a list of commands~%"))))
- (else
- (let ((names (sort
- (hash-map->list
- (lambda (k v)
- (procedure-name (variable-ref k)))
- rhash)
- (lambda (x y)
- (string<? (symbol->string x)
- (symbol->string y))))))
- (format #t "Available commands_~%~%")
- (for-each help-cmd names))))))
-
- (define (handle cmd . args)
- (cond
- ((and (symbol? cmd)
- (module-local-variable commands cmd))
- => (lambda (var)
- (let ((proc (variable-ref var)))
- (catch-bad-arguments
- (lambda ()
- (apply (variable-ref var) args))
- (lambda ()
- (format (current-error-port)
- "Invalid arguments to ~a. Try `help ~a'.~%"
- (procedure-name proc) (procedure-name proc)))))))
- ; ((and (integer? cmd) (exact? cmd))
- ; (nth cmd))
- ((eof-object? cmd)
- (newline)
- (throw 'quit))
- (else
- (format (current-error-port)
- "~&Unknown command_ ~a. Try `help'.~%" cmd)
- *unspecified*)))
-
- (catch 'quit
- (lambda ()
- (let loop ()
- (apply
- handle
- (save-module-excursion
- (lambda ()
- (set-current-module commands)
- (read-args prompt))))
- (loop)))
- (lambda (k . args)
- (apply values args)))))
-;;; Guile VM instructions
-
-;; Copyright (C) 2001, 2010 Free Software Foundation, Inc.
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code_
-
-(define-module (system vm instruction)
- #\export (instruction-list
- instruction? instruction-length
- instruction-pops instruction-pushes
- instruction->opcode opcode->instruction))
-
-(load-extension (string-append "libguile-" (effective-version))
- "scm_init_instructions")
-;;; Guile VM object code
-
-;; Copyright (C) 2001, 2010 Free Software Foundation, Inc.
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code_
-
-(define-module (system vm objcode)
- #\export (objcode? objcode-meta
- bytecode->objcode objcode->bytecode
- load-objcode write-objcode
- word-size byte-order))
-
-(load-extension (string-append "libguile-" (effective-version))
- "scm_init_objcodes")
-;;; Guile VM program functions
-
-;;; Copyright (C) 2001, 2009, 2010, 2013 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code_
-
-(define-module (system vm program)
- #\use-module (system base pmatch)
- #\use-module (system vm instruction)
- #\use-module (system vm objcode)
- #\use-module (rnrs bytevectors)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-26)
- #\export (make-program
-
- make-binding binding_name binding_boxed? binding_index
- binding_start binding_end
-
- source_addr source_line source_column source_file
- source_line-for-user
- program-sources program-sources-pre-retire program-source
-
- program-bindings program-bindings-by-index program-bindings-for-ip
- program-arities program-arity arity_start arity_end
-
- arity_nreq arity_nopt arity_rest? arity_kw arity_allow-other-keys?
-
- program-arguments-alist program-lambda-list
-
- program-meta
- program-objcode program? program-objects
- program-module program-base
- program-free-variables
- program-num-free-variables
- program-free-variable-ref program-free-variable-set!))
-
-(load-extension (string-append "libguile-" (effective-version))
- "scm_init_programs")
-
-(define (make-binding name boxed? index start end)
- (list name boxed? index start end))
-(define (binding_name b) (list-ref b 0))
-(define (binding_boxed? b) (list-ref b 1))
-(define (binding_index b) (list-ref b 2))
-(define (binding_start b) (list-ref b 3))
-(define (binding_end b) (list-ref b 4))
-
-(define (source_addr source)
- (car source))
-(define (source_file source)
- (cadr source))
-(define (source_line source)
- (caddr source))
-(define (source_column source)
- (cdddr source))
-
-;; Lines are zero-indexed inside Guile, but users expect them to be
-;; one-indexed. Columns, on the other hand, are zero-indexed to both. Go
-;; figure.
-(define (source_line-for-user source)
- (1+ (source_line source)))
-
-;; FIXME_ pull this definition from elsewhere.
-(define *bytecode-header-len* 8)
-
-;; We could decompile the program to get this, but that seems like a
-;; waste.
-(define (bytecode-instruction-length bytecode ip)
- (let* ((idx (+ ip *bytecode-header-len*))
- (inst (opcode->instruction (bytevector-u8-ref bytecode idx))))
- ;; 1+ for the instruction itself.
- (1+ (cond
- ((eq? inst 'load-program)
- (+ (bytevector-u32-native-ref bytecode (+ idx 1))
- (bytevector-u32-native-ref bytecode (+ idx 5))))
- ((< (instruction-length inst) 0)
- ;; variable length instruction -- the length is encoded in the
- ;; instruction stream.
- (+ (ash (bytevector-u8-ref bytecode (+ idx 1)) 16)
- (ash (bytevector-u8-ref bytecode (+ idx 2)) 8)
- (bytevector-u8-ref bytecode (+ idx 3))))
- (else
- ;; fixed length
- (instruction-length inst))))))
-
-;; Source information could in theory be correlated with the ip of the
-;; instruction, or the ip just after the instruction is retired. Guile
-;; does the latter, to make backtraces easy -- an error produced while
-;; running an opcode always happens after it has retired its arguments.
-;;
-;; But for breakpoints and such, we need the ip before the instruction
-;; is retired -- before it has had a chance to do anything. So here we
-;; change from the post-retire addresses given by program-sources to
-;; pre-retire addresses.
-;;
-(define (program-sources-pre-retire proc)
- (let ((bv (objcode->bytecode (program-objcode proc))))
- (let lp ((in (program-sources proc))
- (out '())
- (ip 0))
- (cond
- ((null? in)
- (reverse out))
- (else
- (pmatch (car in)
- ((,post-ip . ,source)
- (let lp2 ((ip ip)
- (next ip))
- (if (< next post-ip)
- (lp2 next (+ next (bytecode-instruction-length bv next)))
- (lp (cdr in)
- (acons ip source out)
- next))))
- (else
- (error "unexpected"))))))))
-
-(define (collapse-locals locs)
- (let lp ((ret '()) (locs locs))
- (if (null? locs)
- (map cdr (sort! ret
- (lambda (x y) (< (car x) (car y)))))
- (let ((b (car locs)))
- (cond
- ((assv-ref ret (binding_index b))
- => (lambda (bindings)
- (append! bindings (list b))
- (lp ret (cdr locs))))
- (else
- (lp (acons (binding_index b) (list b) ret)
- (cdr locs))))))))
-
-;; returns list of list of bindings
-;; (list-ref ret N) == bindings bound to the Nth local slot
-(define (program-bindings-by-index prog)
- (cond ((program-bindings prog) => collapse-locals)
- (else '())))
-
-(define (program-bindings-for-ip prog ip)
- (let lp ((in (program-bindings-by-index prog)) (out '()))
- (if (null? in)
- (reverse out)
- (lp (cdr in)
- (let inner ((binds (car in)))
- (cond ((null? binds) out)
- ((<= (binding_start (car binds))
- ip
- (binding_end (car binds)))
- (cons (car binds) out))
- (else (inner (cdr binds)))))))))
-
-(define (arity_start a)
- (pmatch a ((,start ,end . _) start) (else (error "bad arity" a))))
-(define (arity_end a)
- (pmatch a ((,start ,end . _) end) (else (error "bad arity" a))))
-(define (arity_nreq a)
- (pmatch a ((_ _ ,nreq . _) nreq) (else 0)))
-(define (arity_nopt a)
- (pmatch a ((_ _ ,nreq ,nopt . _) nopt) (else 0)))
-(define (arity_rest? a)
- (pmatch a ((_ _ ,nreq ,nopt ,rest? . _) rest?) (else #f)))
-(define (arity_kw a)
- (pmatch a ((_ _ ,nreq ,nopt ,rest? (_ . ,kw)) kw) (else '())))
-(define (arity_allow-other-keys? a)
- (pmatch a ((_ _ ,nreq ,nopt ,rest? (,aok . ,kw)) aok) (else #f)))
-
-(define (program-arity prog ip)
- (let ((arities (program-arities prog)))
- (and arities
- (let lp ((arities arities))
- (cond ((null? arities) #f)
- ((not ip) (car arities)) ; take the first one
- ((and (< (arity_start (car arities)) ip)
- (<= ip (arity_end (car arities))))
- (car arities))
- (else (lp (cdr arities))))))))
-
-(define (arglist->arguments-alist arglist)
- (pmatch arglist
- ((,req ,opt ,keyword ,allow-other-keys? ,rest . ,extents)
- `((required . ,req)
- (optional . ,opt)
- (keyword . ,keyword)
- (allow-other-keys? . ,allow-other-keys?)
- (rest . ,rest)
- (extents . ,extents)))
- (else #f)))
-
-(define* (arity->arguments-alist prog arity
- #\optional
- (make-placeholder
- (lambda (i) (string->symbol "_"))))
- (define var-by-index
- (let ((rbinds (map (lambda (x)
- (cons (binding_index x) (binding_name x)))
- (program-bindings-for-ip prog
- (arity_start arity)))))
- (lambda (i)
- (or (assv-ref rbinds i)
- ;; if we don't know the name, return a placeholder
- (make-placeholder i)))))
-
- (let lp ((nreq (arity_nreq arity)) (req '())
- (nopt (arity_nopt arity)) (opt '())
- (rest? (arity_rest? arity)) (rest #f)
- (n 0))
- (cond
- ((< 0 nreq)
- (lp (1- nreq) (cons (var-by-index n) req)
- nopt opt rest? rest (1+ n)))
- ((< 0 nopt)
- (lp nreq req
- (1- nopt) (cons (var-by-index n) opt)
- rest? rest (1+ n)))
- (rest?
- (lp nreq req nopt opt
- #f (var-by-index (+ n (length (arity_kw arity))))
- (1+ n)))
- (else
- `((required . ,(reverse req))
- (optional . ,(reverse opt))
- (keyword . ,(arity_kw arity))
- (allow-other-keys? . ,(arity_allow-other-keys? arity))
- (rest . ,rest))))))
-
-;; the name "program-arguments" is taken by features.c...
-(define* (program-arguments-alist prog #\optional ip)
- "Returns the signature of the given procedure in the form of an association list."
- (let ((arity (program-arity prog ip)))
- (and arity
- (arity->arguments-alist prog arity))))
-
-(define* (program-lambda-list prog #\optional ip)
- "Returns the signature of the given procedure in the form of an argument list."
- (and=> (program-arguments-alist prog ip) arguments-alist->lambda-list))
-
-(define (arguments-alist->lambda-list arguments-alist)
- (let ((req (or (assq-ref arguments-alist 'required) '()))
- (opt (or (assq-ref arguments-alist 'optional) '()))
- (key (map keyword->symbol
- (map car (or (assq-ref arguments-alist 'keyword) '()))))
- (rest (or (assq-ref arguments-alist 'rest) '())))
- `(,@req
- ,@(if (pair? opt) (cons #\optional opt) '())
- ,@(if (pair? key) (cons #\key key) '())
- . ,rest)))
-
-(define (program-free-variables prog)
- "Return the list of free variables of PROG."
- (let ((count (program-num-free-variables prog)))
- (unfold (lambda (i) (>= i count))
- (cut program-free-variable-ref prog <>)
- 1+
- 0)))
-
-(define (write-program prog port)
- (format port "#<procedure ~a~a>"
- (or (procedure-name prog)
- (and=> (program-source prog 0)
- (lambda (s)
- (format #f "~a at ~a_~a_~a"
- (number->string (object-address prog) 16)
- (or (source_file s)
- (if s "<current input>" "<unknown port>"))
- (source_line-for-user s) (source_column s))))
- (number->string (object-address prog) 16))
- (let ((arities (program-arities prog)))
- (if (or (not arities) (null? arities))
- ""
- (string-append
- " " (string-join (map (lambda (a)
- (object->string
- (arguments-alist->lambda-list
- (arity->arguments-alist prog a))))
- arities)
- " | "))))))
-
-;;; Guile VM tracer
-
-;; Copyright (C) 2001, 2009, 2010, 2013 Free Software Foundation, Inc.
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code_
-
-(define-module (system vm trace)
- #\use-module (system base syntax)
- #\use-module (system vm vm)
- #\use-module (system vm frame)
- #\use-module (system vm program)
- #\use-module (system vm objcode)
- #\use-module (system vm traps)
- #\use-module (rnrs bytevectors)
- #\use-module (system vm instruction)
- #\use-module (ice-9 format)
- #\export (trace-calls-in-procedure
- trace-calls-to-procedure
- trace-instructions-in-procedure
- call-with-trace))
-
-;; FIXME_ this constant needs to go in system vm objcode
-(define *objcode-header-len* 8)
-
-(define (build-prefix prefix depth infix numeric-format max-indent)
- (let lp ((indent "") (n 0))
- (cond
- ((= n depth)
- (string-append prefix indent))
- ((< (+ (string-length indent) (string-length infix)) max-indent)
- (lp (string-append indent infix) (1+ n)))
- (else
- (string-append prefix indent (format #f numeric-format depth))))))
-
-(define (print-application frame depth width prefix max-indent)
- (let ((prefix (build-prefix prefix depth "| " "~d> " max-indent)))
- (format (current-error-port) "~a~v_@y\n"
- prefix
- width
- (frame-call-representation frame))))
-
-(define* (print-return frame depth width prefix max-indent)
- (let* ((len (frame-num-locals frame))
- (nvalues (frame-local-ref frame (1- len)))
- (prefix (build-prefix prefix depth "| " "~d< "max-indent)))
- (case nvalues
- ((0)
- (format (current-error-port) "~ano values\n" prefix))
- ((1)
- (format (current-error-port) "~a~v_@y\n"
- prefix
- width
- (frame-local-ref frame (- len 2))))
- (else
- ;; this should work, but there appears to be a bug
- ;; "~a~d values_~_{ ~v_@y~}\n"
- (format (current-error-port) "~a~d values_~{ ~a~}\n"
- prefix nvalues
- (map (lambda (val)
- (format #f "~v_@y" width val))
- (frame-return-values frame)))))))
-
-(define* (trace-calls-to-procedure proc #\key (width 80) (vm (the-vm))
- (prefix "trace_ ")
- (max-indent (- width 40)))
- (define (apply-handler frame depth)
- (print-application frame depth width prefix max-indent))
- (define (return-handler frame depth)
- (print-return frame depth width prefix max-indent))
- (trap-calls-to-procedure proc apply-handler return-handler
- #\vm vm))
-
-(define* (trace-calls-in-procedure proc #\key (width 80) (vm (the-vm))
- (prefix "trace_ ")
- (max-indent (- width 40)))
- (define (apply-handler frame depth)
- (print-application frame depth width prefix max-indent))
- (define (return-handler frame depth)
- (print-return frame depth width prefix max-indent))
- (trap-calls-in-dynamic-extent proc apply-handler return-handler
- #\vm vm))
-
-(define* (trace-instructions-in-procedure proc #\key (width 80) (vm (the-vm))
- (max-indent (- width 40)))
- (define (trace-next frame)
- (let* ((ip (frame-instruction-pointer frame))
- (objcode (program-objcode (frame-procedure frame)))
- (opcode (bytevector-u8-ref (objcode->bytecode objcode)
- (+ ip *objcode-header-len*))))
- (format #t "~8d_ ~a\n" ip (opcode->instruction opcode))))
-
- (trap-instructions-in-dynamic-extent proc trace-next
- #\vm vm))
-
-;; Note that because this procedure manipulates the VM trace level
-;; directly, it doesn't compose well with traps at the REPL.
-;;
-(define* (call-with-trace thunk #\key (calls? #t) (instructions? #f)
- (width 80) (vm (the-vm)) (max-indent (- width 40)))
- (let ((call-trap #f)
- (inst-trap #f))
- (dynamic-wind
- (lambda ()
- (if calls?
- (set! call-trap
- (trace-calls-in-procedure thunk #\vm vm #\width width
- #\max-indent max-indent)))
- (if instructions?
- (set! inst-trap
- (trace-instructions-in-procedure thunk #\vm vm #\width width
- #\max-indent max-indent)))
- (set-vm-trace-level! vm (1+ (vm-trace-level vm))))
- thunk
- (lambda ()
- (set-vm-trace-level! vm (1- (vm-trace-level vm)))
- (if call-trap (call-trap))
- (if inst-trap (inst-trap))
- (set! call-trap #f)
- (set! inst-trap #f)))))
-;;; trap-state.scm_ a set of traps
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Commentary_
-;;;
-;;; Code_
-
-(define-module (system vm trap-state)
- #\use-module (system base syntax)
- #\use-module ((srfi srfi-1) #\select (fold))
- #\use-module (system vm vm)
- #\use-module (system vm traps)
- #\use-module (system vm trace)
- #\use-module (system vm frame)
- #\use-module (system vm program)
- #\export (add-trap!
- list-traps
- trap-enabled?
- trap-name
- enable-trap!
- disable-trap!
- delete-trap!
-
- with-default-trap-handler
- install-trap-handler!
-
- add-trap-at-procedure-call!
- add-trace-at-procedure-call!
- add-trap-at-source-location!
- add-ephemeral-trap-at-frame-finish!
- add-ephemeral-stepping-trap!))
-
-(define %default-trap-handler (make-fluid))
-
-(define (default-trap-handler frame idx trap-name)
- (let ((default-handler (fluid-ref %default-trap-handler)))
- (if default-handler
- (default-handler frame idx trap-name)
- (warn "Trap with no handler installed" frame idx trap-name))))
-
-(define-record <trap-wrapper>
- index
- enabled?
- trap
- name)
-
-(define-record <trap-state>
- (handler default-trap-handler)
- (next-idx 0)
- (next-ephemeral-idx -1)
- (wrappers '()))
-
-(define (trap-wrapper<? t1 t2)
- (< (trap-wrapper-index t1) (trap-wrapper-index t2)))
-
-;; The interface that a trap provides to the outside world is that of a
-;; procedure, which when called disables the trap, and returns a
-;; procedure to enable the trap. Perhaps this is a bit too odd and we
-;; should fix this.
-(define (enable-trap-wrapper! wrapper)
- (if (trap-wrapper-enabled? wrapper)
- (error "Trap already enabled" (trap-wrapper-index wrapper))
- (let ((trap (trap-wrapper-trap wrapper)))
- (set! (trap-wrapper-trap wrapper) (trap))
- (set! (trap-wrapper-enabled? wrapper) #t))))
-
-(define (disable-trap-wrapper! wrapper)
- (if (not (trap-wrapper-enabled? wrapper))
- (error "Trap already disabled" (trap-wrapper-index wrapper))
- (let ((trap (trap-wrapper-trap wrapper)))
- (set! (trap-wrapper-trap wrapper) (trap))
- (set! (trap-wrapper-enabled? wrapper) #f))))
-
-(define (add-trap-wrapper! trap-state wrapper)
- (set! (trap-state-wrappers trap-state)
- (append (trap-state-wrappers trap-state) (list wrapper)))
- (trap-wrapper-index wrapper))
-
-(define (remove-trap-wrapper! trap-state wrapper)
- (set! (trap-state-wrappers trap-state)
- (delq wrapper (trap-state-wrappers trap-state))))
-
-(define (trap-state->trace-level trap-state)
- (fold (lambda (wrapper level)
- (if (trap-wrapper-enabled? wrapper)
- (1+ level)
- level))
- 0
- (trap-state-wrappers trap-state)))
-
-(define (wrapper-at-index trap-state idx)
- (let lp ((wrappers (trap-state-wrappers trap-state)))
- (cond
- ((null? wrappers)
- (warn "no wrapper found with index in trap-state" idx)
- #f)
- ((eqv? (trap-wrapper-index (car wrappers)) idx)
- (car wrappers))
- (else
- (lp (cdr wrappers))))))
-
-(define (next-index! trap-state)
- (let ((idx (trap-state-next-idx trap-state)))
- (set! (trap-state-next-idx trap-state) (1+ idx))
- idx))
-
-(define (next-ephemeral-index! trap-state)
- (let ((idx (trap-state-next-ephemeral-idx trap-state)))
- (set! (trap-state-next-ephemeral-idx trap-state) (1- idx))
- idx))
-
-(define (handler-for-index trap-state idx)
- (lambda (frame)
- (let ((wrapper (wrapper-at-index trap-state idx))
- (handler (trap-state-handler trap-state)))
- (if wrapper
- (handler frame
- (trap-wrapper-index wrapper)
- (trap-wrapper-name wrapper))))))
-
-(define (ephemeral-handler-for-index trap-state idx handler)
- (lambda (frame)
- (let ((wrapper (wrapper-at-index trap-state idx)))
- (if wrapper
- (begin
- (if (trap-wrapper-enabled? wrapper)
- (disable-trap-wrapper! wrapper))
- (remove-trap-wrapper! trap-state wrapper)
- (handler frame))))))
-
-
-
-;;;
-;;; VM-local trap states
-;;;
-
-(define *trap-states* (make-weak-key-hash-table))
-
-(define (trap-state-for-vm vm)
- (or (hashq-ref *trap-states* vm)
- (let ((ts (make-trap-state)))
- (hashq-set! *trap-states* vm ts)
- (trap-state-for-vm vm))))
-
-(define (the-trap-state)
- (trap-state-for-vm (the-vm)))
-
-
-
-;;;
-;;; API
-;;;
-
-(define* (with-default-trap-handler handler thunk
- #\optional (trap-state (the-trap-state)))
- (with-fluids ((%default-trap-handler handler))
- (dynamic-wind
- (lambda ()
- ;; Don't enable hooks if the handler is #f.
- (if handler
- (set-vm-trace-level! (the-vm) (trap-state->trace-level trap-state))))
- thunk
- (lambda ()
- (if handler
- (set-vm-trace-level! (the-vm) 0))))))
-
-(define* (list-traps #\optional (trap-state (the-trap-state)))
- (map trap-wrapper-index (trap-state-wrappers trap-state)))
-
-(define* (trap-name idx #\optional (trap-state (the-trap-state)))
- (and=> (wrapper-at-index trap-state idx)
- trap-wrapper-name))
-
-(define* (trap-enabled? idx #\optional (trap-state (the-trap-state)))
- (and=> (wrapper-at-index trap-state idx)
- trap-wrapper-enabled?))
-
-(define* (enable-trap! idx #\optional (trap-state (the-trap-state)))
- (and=> (wrapper-at-index trap-state idx)
- enable-trap-wrapper!))
-
-(define* (disable-trap! idx #\optional (trap-state (the-trap-state)))
- (and=> (wrapper-at-index trap-state idx)
- disable-trap-wrapper!))
-
-(define* (delete-trap! idx #\optional (trap-state (the-trap-state)))
- (and=> (wrapper-at-index trap-state idx)
- (lambda (wrapper)
- (if (trap-wrapper-enabled? wrapper)
- (disable-trap-wrapper! wrapper))
- (remove-trap-wrapper! trap-state wrapper))))
-
-(define* (install-trap-handler! handler #\optional (trap-state (the-trap-state)))
- (set! (trap-state-handler trap-state) handler))
-
-(define* (add-trap-at-procedure-call! proc #\optional (trap-state (the-trap-state)))
- (let* ((idx (next-index! trap-state))
- (trap (trap-at-procedure-call
- proc
- (handler-for-index trap-state idx))))
- (add-trap-wrapper!
- trap-state
- (make-trap-wrapper
- idx #t trap
- (format #f "Breakpoint at ~a" proc)))))
-
-(define* (add-trace-at-procedure-call! proc
- #\optional (trap-state (the-trap-state)))
- (let* ((idx (next-index! trap-state))
- (trap (trace-calls-to-procedure
- proc
- #\prefix (format #f "Trap ~a_ " idx))))
- (add-trap-wrapper!
- trap-state
- (make-trap-wrapper
- idx #t trap
- (format #f "Tracepoint at ~a" proc)))))
-
-(define* (add-trap-at-source-location! file user-line
- #\optional (trap-state (the-trap-state)))
- (let* ((idx (next-index! trap-state))
- (trap (trap-at-source-location file user-line
- (handler-for-index trap-state idx))))
- (add-trap-wrapper!
- trap-state
- (make-trap-wrapper
- idx #t trap
- (format #f "Breakpoint at ~a_~a" file user-line)))))
-
-;; handler _= frame -> nothing
-(define* (add-ephemeral-trap-at-frame-finish! frame handler
- #\optional (trap-state
- (the-trap-state)))
- (let* ((idx (next-ephemeral-index! trap-state))
- (trap (trap-frame-finish
- frame
- (ephemeral-handler-for-index trap-state idx handler)
- (lambda (frame) (delete-trap! idx trap-state)))))
- (add-trap-wrapper!
- trap-state
- (make-trap-wrapper
- idx #t trap
- (format #f "Return from ~a" frame)))))
-
-(define (source-string source)
- (if source
- (format #f "~a_~a_~a" (or (source_file source) "unknown file")
- (source_line-for-user source) (source_column source))
- "unknown source location"))
-
-(define* (add-ephemeral-stepping-trap! frame handler
- #\optional (trap-state
- (the-trap-state))
- #\key (into? #t) (instruction? #f))
- (define (wrap-predicate-according-to-into predicate)
- (if into?
- predicate
- (let ((fp (frame-address frame)))
- (lambda (f)
- (and (<= (frame-address f) fp)
- (predicate f))))))
-
- (let* ((source (frame-next-source frame))
- (idx (next-ephemeral-index! trap-state))
- (trap (trap-matching-instructions
- (wrap-predicate-according-to-into
- (if instruction?
- (lambda (f) #t)
- (lambda (f) (not (equal? (frame-next-source f) source)))))
- (ephemeral-handler-for-index trap-state idx handler))))
- (add-trap-wrapper!
- trap-state
- (make-trap-wrapper
- idx #t trap
- (if instruction?
- (if into?
- "Step to different instruction"
- (format #f "Step to different instruction in ~a" frame))
- (if into?
- (format #f "Step into ~a" (source-string source))
- (format #f "Step out of ~a" (source-string source))))))))
-
-(define* (add-trap! trap name #\optional (trap-state (the-trap-state)))
- (let* ((idx (next-index! trap-state)))
- (add-trap-wrapper!
- trap-state
- (make-trap-wrapper idx #t trap name))))
-;;; Traps_ stepping, breakpoints, and such.
-
-;; Copyright (C) 2010 Free Software Foundation, Inc.
-
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Commentary_
-;;;
-;;; Guile's debugging capabilities come from the hooks that its VM
-;;; provides. For example, there is a hook that is fired when a function
-;;; is called, and even a hook that gets fired at every retired
-;;; instruction.
-;;;
-;;; But as the firing of these hooks is interleaved with the program
-;;; execution, if we want to debug a program, we have to write an
-;;; imperative program that mutates the state of these hooks, and to
-;;; dispatch the hooks to a more semantic context.
-;;;
-;;; For example if we have placed a breakpoint at foo.scm_38, and
-;;; determined that that location maps to the 18th instruction in
-;;; procedure `bar', then we will need per-instruction hooks within
-;;; `bar' -- but when running other procedures, we can have the
-;;; per-instruction hooks off.
-;;;
-;;; Our approach is to define "traps". The behavior of a trap is
-;;; specified when the trap is created. After creation, traps expose a
-;;; limited, uniform interface_ they are either on or off.
-;;;
-;;; To take our foo.scm_38 example again, we can define a trap that
-;;; calls a function when control transfers to that source line --
-;;; trap-at-source-location below. Calling the trap-at-source-location
-;;; function adds to the VM hooks in such at way that it can do its job.
-;;; The result of calling the function is a "disable-hook" closure that,
-;;; when called, will turn off that trap.
-;;;
-;;; The result of calling the "disable-hook" closure, in turn, is an
-;;; "enable-hook" closure, which when called turns the hook back on, and
-;;; returns a "disable-hook" closure.
-;;;
-;;; It's a little confusing. The summary is, call these functions to add
-;;; a trap; and call their return value to disable the trap.
-;;;
-;;; Code_
-
-(define-module (system vm traps)
- #\use-module (system base pmatch)
- #\use-module (system vm vm)
- #\use-module (system vm frame)
- #\use-module (system vm program)
- #\use-module (system vm objcode)
- #\use-module (system vm instruction)
- #\use-module (system xref)
- #\use-module (rnrs bytevectors)
- #\export (trap-at-procedure-call
- trap-in-procedure
- trap-instructions-in-procedure
- trap-at-procedure-ip-in-range
- trap-at-source-location
- trap-frame-finish
- trap-in-dynamic-extent
- trap-calls-in-dynamic-extent
- trap-instructions-in-dynamic-extent
- trap-calls-to-procedure
- trap-matching-instructions))
-
-(define-syntax arg-check
- (syntax-rules ()
- ((_ arg predicate? message)
- (if (not (predicate? arg))
- (error "bad argument ~a_ ~a" 'arg message)))
- ((_ arg predicate?)
- (if (not (predicate? arg))
- (error "bad argument ~a_ expected ~a" 'arg 'predicate?)))))
-
-(define (new-disabled-trap vm enable disable)
- (let ((enabled? #f))
- (define-syntax disabled?
- (identifier-syntax
- (disabled? (not enabled?))
- ((set! disabled? val) (set! enabled? (not val)))))
-
- (define* (enable-trap #\optional frame)
- (if enabled? (error "trap already enabled"))
- (enable frame)
- (set! enabled? #t)
- disable-trap)
-
- (define* (disable-trap #\optional frame)
- (if disabled? (error "trap already disabled"))
- (disable frame)
- (set! disabled? #t)
- enable-trap)
-
- enable-trap))
-
-(define (new-enabled-trap vm frame enable disable)
- ((new-disabled-trap vm enable disable) frame))
-
-(define (frame-matcher proc match-objcode?)
- (let ((proc (if (struct? proc)
- (procedure proc)
- proc)))
- (if match-objcode?
- (lambda (frame)
- (let ((frame-proc (frame-procedure frame)))
- (or (eq? frame-proc proc)
- (and (program? frame-proc)
- (eq? (program-objcode frame-proc)
- (program-objcode proc))))))
- (lambda (frame)
- (eq? (frame-procedure frame) proc)))))
-
-;; A basic trap, fires when a procedure is called.
-;;
-(define* (trap-at-procedure-call proc handler #\key (vm (the-vm))
- (closure? #f)
- (our-frame? (frame-matcher proc closure?)))
- (arg-check proc procedure?)
- (arg-check handler procedure?)
- (let ()
- (define (apply-hook frame)
- (if (our-frame? frame)
- (handler frame)))
-
- (new-enabled-trap
- vm #f
- (lambda (frame)
- (add-hook! (vm-apply-hook vm) apply-hook))
- (lambda (frame)
- (remove-hook! (vm-apply-hook vm) apply-hook)))))
-
-;; A more complicated trap, traps when control enters a procedure.
-;;
-;; Control can enter a procedure via_
-;; * A procedure call.
-;; * A return to a procedure's frame on the stack.
-;; * A continuation returning directly to an application of this
-;; procedure.
-;;
-;; Control can leave a procedure via_
-;; * A normal return from the procedure.
-;; * An application of another procedure.
-;; * An invocation of a continuation.
-;; * An abort.
-;;
-(define* (trap-in-procedure proc enter-handler exit-handler
- #\key current-frame (vm (the-vm))
- (closure? #f)
- (our-frame? (frame-matcher proc closure?)))
- (arg-check proc procedure?)
- (arg-check enter-handler procedure?)
- (arg-check exit-handler procedure?)
- (let ((in-proc? #f))
- (define (enter-proc frame)
- (if in-proc?
- (warn "already in proc" frame)
- (begin
- (enter-handler frame)
- (set! in-proc? #t))))
-
- (define (exit-proc frame)
- (if in-proc?
- (begin
- (exit-handler frame)
- (set! in-proc? #f))
- (warn "not in proc" frame)))
-
- (define (apply-hook frame)
- (if in-proc?
- (exit-proc frame))
- (if (our-frame? frame)
- (enter-proc frame)))
-
- (define (push-cont-hook frame)
- (if in-proc?
- (exit-proc frame)))
-
- (define (pop-cont-hook frame)
- (if in-proc?
- (exit-proc frame))
- (if (our-frame? (frame-previous frame))
- (enter-proc (frame-previous frame))))
-
- (define (abort-hook frame)
- (if in-proc?
- (exit-proc frame))
- (if (our-frame? frame)
- (enter-proc frame)))
-
- (define (restore-hook frame)
- (if in-proc?
- (exit-proc frame))
- (if (our-frame? frame)
- (enter-proc frame)))
-
- (new-enabled-trap
- vm current-frame
- (lambda (frame)
- (add-hook! (vm-apply-hook vm) apply-hook)
- (add-hook! (vm-push-continuation-hook vm) push-cont-hook)
- (add-hook! (vm-pop-continuation-hook vm) pop-cont-hook)
- (add-hook! (vm-abort-continuation-hook vm) abort-hook)
- (add-hook! (vm-restore-continuation-hook vm) restore-hook)
- (if (and frame (our-frame? frame))
- (enter-proc frame)))
- (lambda (frame)
- (if in-proc?
- (exit-proc frame))
- (remove-hook! (vm-apply-hook vm) apply-hook)
- (remove-hook! (vm-push-continuation-hook vm) push-cont-hook)
- (remove-hook! (vm-pop-continuation-hook vm) pop-cont-hook)
- (remove-hook! (vm-abort-continuation-hook vm) abort-hook)
- (remove-hook! (vm-restore-continuation-hook vm) restore-hook)))))
-
-;; Building on trap-in-procedure, we have trap-instructions-in-procedure
-;;
-(define* (trap-instructions-in-procedure proc next-handler exit-handler
- #\key current-frame (vm (the-vm))
- (closure? #f)
- (our-frame?
- (frame-matcher proc closure?)))
- (arg-check proc procedure?)
- (arg-check next-handler procedure?)
- (arg-check exit-handler procedure?)
- (let ()
- (define (next-hook frame)
- (if (our-frame? frame)
- (next-handler frame)))
-
- (define (enter frame)
- (add-hook! (vm-next-hook vm) next-hook)
- (if frame (next-hook frame)))
-
- (define (exit frame)
- (exit-handler frame)
- (remove-hook! (vm-next-hook vm) next-hook))
-
- (trap-in-procedure proc enter exit
- #\current-frame current-frame #\vm vm
- #\our-frame? our-frame?)))
-
-(define (non-negative-integer? x)
- (and (number? x) (integer? x) (exact? x) (not (negative? x))))
-
-(define (positive-integer? x)
- (and (number? x) (integer? x) (exact? x) (positive? x)))
-
-(define (range? x)
- (and (list? x)
- (and-map (lambda (x)
- (and (pair? x)
- (non-negative-integer? (car x))
- (non-negative-integer? (cdr x))))
- x)))
-
-(define (in-range? range i)
- (or-map (lambda (bounds)
- (and (<= (car bounds) i)
- (< i (cdr bounds))))
- range))
-
-;; Building on trap-instructions-in-procedure, we have
-;; trap-at-procedure-ip-in-range.
-;;
-(define* (trap-at-procedure-ip-in-range proc range handler
- #\key current-frame (vm (the-vm))
- (closure? #f)
- (our-frame?
- (frame-matcher proc closure?)))
- (arg-check proc procedure?)
- (arg-check range range?)
- (arg-check handler procedure?)
- (let ((fp-stack '()))
- (define (cull-frames! fp)
- (let lp ((frames fp-stack))
- (if (and (pair? frames) (< (car frames) fp))
- (lp (cdr frames))
- (set! fp-stack frames))))
-
- (define (next-handler frame)
- (let ((fp (frame-address frame))
- (ip (frame-instruction-pointer frame)))
- (cull-frames! fp)
- (let ((now-in-range? (in-range? range ip))
- (was-in-range? (and (pair? fp-stack) (= (car fp-stack) fp))))
- (cond
- (was-in-range?
- (if (not now-in-range?)
- (set! fp-stack (cdr fp-stack))))
- (now-in-range?
- (set! fp-stack (cons fp fp-stack))
- (handler frame))))))
-
- (define (exit-handler frame)
- (if (and (pair? fp-stack)
- (= (car fp-stack) (frame-address frame)))
- (set! fp-stack (cdr fp-stack))))
-
- (trap-instructions-in-procedure proc next-handler exit-handler
- #\current-frame current-frame #\vm vm
- #\our-frame? our-frame?)))
-
-;; FIXME_ define this in objcode somehow. We are reffing the first
-;; uint32 in the objcode, which is the length of the program (without
-;; the meta).
-(define (program-last-ip prog)
- (bytevector-u32-native-ref (objcode->bytecode (program-objcode prog)) 0))
-
-(define (program-sources-by-line proc file)
- (let lp ((sources (program-sources-pre-retire proc))
- (out '()))
- (if (pair? sources)
- (lp (cdr sources)
- (pmatch (car sources)
- ((,start-ip ,start-file ,start-line . ,start-col)
- (if (equal? start-file file)
- (cons (cons start-line
- (if (pair? (cdr sources))
- (pmatch (cadr sources)
- ((,end-ip . _)
- (cons start-ip end-ip))
- (else (error "unexpected")))
- (cons start-ip (program-last-ip proc))))
- out)
- out))
- (else (error "unexpected"))))
- (let ((alist '()))
- (for-each
- (lambda (pair)
- (set! alist
- (assv-set! alist (car pair)
- (cons (cdr pair)
- (or (assv-ref alist (car pair))
- '())))))
- out)
- (sort! alist (lambda (x y) (< (car x) (car y))))
- alist))))
-
-(define (source->ip-range proc file line)
- (or (or-map (lambda (line-and-ranges)
- (cond
- ((= (car line-and-ranges) line)
- (cdr line-and-ranges))
- ((> (car line-and-ranges) line)
- (warn "no instructions found at" file "_" line
- "; using line" (car line-and-ranges) "instead")
- (cdr line-and-ranges))
- (else #f)))
- (program-sources-by-line proc file))
- (begin
- (warn "no instructions found for" file "_" line)
- '())))
-
-(define (source-closures-or-procedures file line)
- (let ((closures (source-closures file line)))
- (if (pair? closures)
- (values closures #t)
- (values (source-procedures file line) #f))))
-
-;; Building on trap-on-instructions-in-procedure, we have
-;; trap-at-source-location. The parameter `user-line' is one-indexed, as
-;; a user counts lines, instead of zero-indexed, as Guile counts lines.
-;;
-(define* (trap-at-source-location file user-line handler
- #\key current-frame (vm (the-vm)))
- (arg-check file string?)
- (arg-check user-line positive-integer?)
- (arg-check handler procedure?)
- (let ((traps #f))
- (call-with-values
- (lambda () (source-closures-or-procedures file (1- user-line)))
- (lambda (procs closures?)
- (new-enabled-trap
- vm current-frame
- (lambda (frame)
- (set! traps
- (map
- (lambda (proc)
- (let ((range (source->ip-range proc file (1- user-line))))
- (trap-at-procedure-ip-in-range proc range handler
- #\current-frame current-frame
- #\vm vm
- #\closure? closures?)))
- procs))
- (if (null? traps)
- (error "No procedures found at ~a_~a." file user-line)))
- (lambda (frame)
- (for-each (lambda (trap) (trap frame)) traps)
- (set! traps #f)))))))
-
-
-
-;; On a different tack, now we're going to build up a set of traps that
-;; do useful things during the dynamic extent of a procedure's
-;; application. First, a trap for when a frame returns.
-;;
-(define* (trap-frame-finish frame return-handler abort-handler
- #\key (vm (the-vm)))
- (arg-check frame frame?)
- (arg-check return-handler procedure?)
- (arg-check abort-handler procedure?)
- (let ((fp (frame-address frame)))
- (define (pop-cont-hook frame)
- (if (and fp (eq? (frame-address frame) fp))
- (begin
- (set! fp #f)
- (return-handler frame))))
-
- (define (abort-hook frame)
- (if (and fp (< (frame-address frame) fp))
- (begin
- (set! fp #f)
- (abort-handler frame))))
-
- (new-enabled-trap
- vm frame
- (lambda (frame)
- (if (not fp)
- (error "return-or-abort traps may only be enabled once"))
- (add-hook! (vm-pop-continuation-hook vm) pop-cont-hook)
- (add-hook! (vm-abort-continuation-hook vm) abort-hook)
- (add-hook! (vm-restore-continuation-hook vm) abort-hook))
- (lambda (frame)
- (set! fp #f)
- (remove-hook! (vm-pop-continuation-hook vm) pop-cont-hook)
- (remove-hook! (vm-abort-continuation-hook vm) abort-hook)
- (remove-hook! (vm-restore-continuation-hook vm) abort-hook)))))
-
-;; A more traditional dynamic-wind trap. Perhaps this should not be
-;; based on the above trap-frame-finish?
-;;
-(define* (trap-in-dynamic-extent proc enter-handler return-handler abort-handler
- #\key current-frame (vm (the-vm))
- (closure? #f)
- (our-frame? (frame-matcher proc closure?)))
- (arg-check proc procedure?)
- (arg-check enter-handler procedure?)
- (arg-check return-handler procedure?)
- (arg-check abort-handler procedure?)
- (let ((exit-trap #f))
- (define (return-hook frame)
- (exit-trap frame) ; disable the return/abort trap.
- (set! exit-trap #f)
- (return-handler frame))
-
- (define (abort-hook frame)
- (exit-trap frame) ; disable the return/abort trap.
- (set! exit-trap #f)
- (abort-handler frame))
-
- (define (apply-hook frame)
- (if (and (not exit-trap) (our-frame? frame))
- (begin
- (enter-handler frame)
- (set! exit-trap
- (trap-frame-finish frame return-hook abort-hook
- #\vm vm)))))
-
- (new-enabled-trap
- vm current-frame
- (lambda (frame)
- (add-hook! (vm-apply-hook vm) apply-hook))
- (lambda (frame)
- (if exit-trap
- (abort-hook frame))
- (set! exit-trap #f)
- (remove-hook! (vm-apply-hook vm) apply-hook)))))
-
-;; Trapping all procedure calls within a dynamic extent, recording the
-;; depth of the call stack relative to the original procedure.
-;;
-(define* (trap-calls-in-dynamic-extent proc apply-handler return-handler
- #\key current-frame (vm (the-vm))
- (closure? #f)
- (our-frame?
- (frame-matcher proc closure?)))
- (arg-check proc procedure?)
- (arg-check apply-handler procedure?)
- (arg-check return-handler procedure?)
- (let ((*call-depth* 0))
- (define (trace-push frame)
- (set! *call-depth* (1+ *call-depth*)))
-
- (define (trace-pop frame)
- (return-handler frame *call-depth*)
- (set! *call-depth* (1- *call-depth*)))
-
- (define (trace-apply frame)
- (apply-handler frame *call-depth*))
-
- ;; FIXME_ recalc depth on abort
-
- (define (enter frame)
- (add-hook! (vm-push-continuation-hook vm) trace-push)
- (add-hook! (vm-pop-continuation-hook vm) trace-pop)
- (add-hook! (vm-apply-hook vm) trace-apply))
-
- (define (leave frame)
- (remove-hook! (vm-push-continuation-hook vm) trace-push)
- (remove-hook! (vm-pop-continuation-hook vm) trace-pop)
- (remove-hook! (vm-apply-hook vm) trace-apply))
-
- (define (return frame)
- (leave frame))
-
- (define (abort frame)
- (leave frame))
-
- (trap-in-dynamic-extent proc enter return abort
- #\current-frame current-frame #\vm vm
- #\our-frame? our-frame?)))
-
-;; Trapping all retired intructions within a dynamic extent.
-;;
-(define* (trap-instructions-in-dynamic-extent proc next-handler
- #\key current-frame (vm (the-vm))
- (closure? #f)
- (our-frame?
- (frame-matcher proc closure?)))
- (arg-check proc procedure?)
- (arg-check next-handler procedure?)
- (let ()
- (define (trace-next frame)
- (next-handler frame))
-
- (define (enter frame)
- (add-hook! (vm-next-hook vm) trace-next))
-
- (define (leave frame)
- (remove-hook! (vm-next-hook vm) trace-next))
-
- (define (return frame)
- (leave frame))
-
- (define (abort frame)
- (leave frame))
-
- (trap-in-dynamic-extent proc enter return abort
- #\current-frame current-frame #\vm vm
- #\our-frame? our-frame?)))
-
-;; Traps calls and returns for a given procedure, keeping track of the call depth.
-;;
-(define* (trap-calls-to-procedure proc apply-handler return-handler
- #\key (vm (the-vm)))
- (arg-check proc procedure?)
- (arg-check apply-handler procedure?)
- (arg-check return-handler procedure?)
- (let ((pending-finish-traps '())
- (last-fp #f))
- (define (apply-hook frame)
- (let ((depth (length pending-finish-traps)))
-
- (apply-handler frame depth)
-
- (if (not (eq? (frame-address frame) last-fp))
- (let ((finish-trap #f))
- (define (frame-finished frame)
- (finish-trap frame) ;; disables the trap.
- (set! pending-finish-traps
- (delq finish-trap pending-finish-traps))
- (set! finish-trap #f))
-
- (define (return-hook frame)
- (frame-finished frame)
- (return-handler frame depth))
-
- ;; FIXME_ abort handler?
- (define (abort-hook frame)
- (frame-finished frame))
-
- (set! finish-trap
- (trap-frame-finish frame return-hook abort-hook #\vm vm))
- (set! pending-finish-traps
- (cons finish-trap pending-finish-traps))))))
-
- ;; The basic idea is that we install one trap that fires for calls,
- ;; but that each call installs its own finish trap. Those finish
- ;; traps remove themselves as their frames finish or abort.
- ;;
- ;; However since to the outside world we present the interface of
- ;; just being one trap, disabling this calls-to-procedure trap
- ;; should take care of disabling all of the pending finish traps. We
- ;; keep track of pending traps through the pending-finish-traps
- ;; list.
- ;;
- ;; So since we know that the trap-at-procedure will be enabled, and
- ;; thus returning a disable closure, we make sure to wrap that
- ;; closure in something that will disable pending finish traps.
- (define (with-pending-finish-disablers trap)
- (define (with-pending-finish-enablers trap)
- (lambda* (#\optional frame)
- (with-pending-finish-disablers (trap frame))))
-
- (lambda* (#\optional frame)
- (for-each (lambda (disable) (disable frame))
- pending-finish-traps)
- (set! pending-finish-traps '())
- (with-pending-finish-enablers (trap frame))))
-
- (with-pending-finish-disablers
- (trap-at-procedure-call proc apply-hook #\vm vm))))
-
-;; Trap when the source location changes.
-;;
-(define* (trap-matching-instructions frame-pred handler
- #\key (vm (the-vm)))
- (arg-check frame-pred procedure?)
- (arg-check handler procedure?)
- (let ()
- (define (next-hook frame)
- (if (frame-pred frame)
- (handler frame)))
-
- (new-enabled-trap
- vm #f
- (lambda (frame)
- (add-hook! (vm-next-hook vm) next-hook))
- (lambda (frame)
- (remove-hook! (vm-next-hook vm) next-hook)))))
-;;; Guile VM core
-
-;;; Copyright (C) 2001, 2009, 2010 Free Software Foundation, Inc.
-;;;
-;;; This library is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public
-;;; License as published by the Free Software Foundation; either
-;;; version 3 of the License, or (at your option) any later version.
-;;;
-;;; This library is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public
-;;; License along with this library; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code_
-
-(define-module (system vm vm)
- #\export (vm?
- make-vm the-vm call-with-vm
- vm_ip vm_sp vm_fp
-
- vm-trace-level set-vm-trace-level!
- vm-engine set-vm-engine! set-default-vm-engine!
- vm-push-continuation-hook vm-pop-continuation-hook
- vm-apply-hook
- vm-next-hook
- vm-abort-continuation-hook vm-restore-continuation-hook))
-
-(load-extension (string-append "libguile-" (effective-version))
- "scm_init_vm")
-;;;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 2.1 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-
-(define-module (system xref)
- #\use-module (system base pmatch)
- #\use-module (system base compile)
- #\use-module (system vm program)
- #\use-module (srfi srfi-1)
- #\export (*xref-ignored-modules*
- procedure-callees
- procedure-callers
- source-closures
- source-procedures))
-
-;;;
-;;; The cross-reference database_ who calls whom.
-;;;
-
-(define (program-callee-rev-vars prog)
- (define (cons-uniq x y)
- (if (memq x y) y (cons x y)))
- (cond
- ((program-objects prog)
- => (lambda (objects)
- (let ((n (vector-length objects))
- (progv (make-vector (vector-length objects) #f))
- (asm (decompile (program-objcode prog) #\to 'assembly)))
- (pmatch asm
- ((load-program ,labels ,len . ,body)
- (for-each
- (lambda (x)
- (pmatch x
- ((toplevel-ref ,n) (vector-set! progv n #t))
- ((toplevel-set ,n) (vector-set! progv n #t))))
- body)))
- (let lp ((i 0) (out '()))
- (cond
- ((= i n) out)
- ((program? (vector-ref objects i))
- (lp (1+ i)
- (fold cons-uniq out
- (program-callee-rev-vars (vector-ref objects i)))))
- ((vector-ref progv i)
- (let ((obj (vector-ref objects i)))
- (if (variable? obj)
- (lp (1+ i) (cons-uniq obj out))
- ;; otherwise it's an unmemoized binding
- (pmatch obj
- (,sym (guard (symbol? sym))
- (let ((v (module-variable (or (program-module prog)
- the-root-module)
- sym)))
- (lp (1+ i) (if v (cons-uniq v out) out))))
- ((,mod ,sym ,public?)
- ;; hm, hacky.
- (let* ((m (nested-ref-module (resolve-module '() #f)
- mod))
- (v (and m
- (module-variable
- (if public?
- (module-public-interface m)
- m)
- sym))))
- (lp (1+ i)
- (if v (cons-uniq v out) out))))))))
- (else (lp (1+ i) out)))))))
- (else '())))
-
-(define (procedure-callee-rev-vars proc)
- (cond
- ((program? proc) (program-callee-rev-vars proc))
- (else '())))
-
-(define (procedure-callees prog)
- "Evaluates to a list of the given program callees."
- (let lp ((in (procedure-callee-rev-vars prog)) (out '()))
- (cond ((null? in) out)
- ((variable-bound? (car in))
- (lp (cdr in) (cons (variable-ref (car in)) out)))
- (else (lp (cdr in) out)))))
-
-;; var -> ((module-name caller ...) ...)
-(define *callers-db* #f)
-;; module-name -> (callee ...)
-(define *module-callees-db* (make-hash-table))
-;; (module-name ...)
-(define *tainted-modules* '())
-
-(define *xref-ignored-modules* '((value-history)))
-(define (on-module-modified m)
- (let ((name (module-name m)))
- (if (and (not (member name *xref-ignored-modules*))
- (not (member name *tainted-modules*))
- (pair? name))
- (set! *tainted-modules* (cons name *tainted-modules*)))))
-
-(define (add-caller callee caller mod-name)
- (let ((all-callers (hashq-ref *callers-db* callee)))
- (if (not all-callers)
- (hashq-set! *callers-db* callee `((,mod-name ,caller)))
- (let ((callers (assoc mod-name all-callers)))
- (if callers
- (if (not (member caller callers))
- (set-cdr! callers (cons caller (cdr callers))))
- (hashq-set! *callers-db* callee
- (cons `(,mod-name ,caller) all-callers)))))))
-
-(define (forget-callers callee mod-name)
- (hashq-set! *callers-db* callee
- (assoc-remove! (hashq-ref *callers-db* callee '()) mod-name)))
-
-(define (add-callees callees mod-name)
- (hash-set! *module-callees-db* mod-name
- (append callees (hash-ref *module-callees-db* mod-name '()))))
-
-(define (untaint-modules)
- (define (untaint m)
- (for-each (lambda (callee) (forget-callers callee m))
- (hash-ref *module-callees-db* m '()))
- (ensure-callers-db m))
- (ensure-callers-db #f)
- (for-each untaint *tainted-modules*)
- (set! *tainted-modules* '()))
-
-(define (ensure-callers-db mod-name)
- (let ((mod (and mod-name (resolve-module mod-name)))
- (visited #f))
- (define (visit-variable var mod-name)
- (if (variable-bound? var)
- (let ((x (variable-ref var)))
- (cond
- ((and visited (hashq-ref visited x)))
- ((procedure? x)
- (if visited (hashq-set! visited x #t))
- (let ((callees (filter variable-bound?
- (procedure-callee-rev-vars x))))
- (for-each (lambda (callee)
- (add-caller callee x mod-name))
- callees)
- (add-callees callees mod-name)))))))
-
- (define (visit-module mod)
- (if visited (hashq-set! visited mod #t))
- (if (not (memq on-module-modified (module-observers mod)))
- (module-observe mod on-module-modified))
- (let ((name (module-name mod)))
- (module-for-each (lambda (sym var)
- (visit-variable var name))
- mod)))
-
- (define (visit-submodules mod)
- (hash-for-each
- (lambda (name sub)
- (if (not (and visited (hashq-ref visited sub)))
- (begin
- (visit-module sub)
- (visit-submodules sub))))
- (module-submodules mod)))
-
- (cond ((and (not mod-name) (not *callers-db*))
- (set! *callers-db* (make-hash-table 1000))
- (set! visited (make-hash-table 1000))
- (visit-submodules (resolve-module '() #f)))
- (mod-name (visit-module mod)))))
-
-(define (procedure-callers var)
- "Returns an association list, keyed by module name, of known callers
-of the given procedure. The latter can specified directly as a
-variable, a symbol (which gets resolved in the current module) or a
-pair of the form (module-name . variable-name), "
- (let ((v (cond ((variable? var) var)
- ((symbol? var) (module-variable (current-module) var))
- (else
- (pmatch var
- ((,modname . ,sym)
- (module-variable (resolve-module modname) sym))
- (else
- (error "expected a variable, symbol, or (modname . sym)" var)))))))
- (untaint-modules)
- (hashq-ref *callers-db* v '())))
-
-
-
-;;;
-;;; The source database_ procedures defined at a given source location.
-;;;
-
-;; FIXME_ refactor to share code with the xref database.
-
-;; ((ip file line . col) ...)
-(define (procedure-sources proc)
- (cond
- ((program? proc) (program-sources proc))
- (else '())))
-
-;; file -> line -> (proc ...)
-(define *closure-sources-db* #f)
-;; file -> line -> (proc ...)
-(define *sources-db* #f)
-;; module-name -> proc -> sources
-(define *module-sources-db* (make-hash-table))
-;; (module-name ...)
-(define *tainted-sources* '())
-
-(define (on-source-modified m)
- (let ((name (module-name m)))
- (if (and (not (member name *xref-ignored-modules*))
- (not (member name *tainted-sources*))
- (pair? name))
- (set! *tainted-sources* (cons name *tainted-sources*)))))
-
-(define (add-source proc file line db)
- (let ((file-table (or (hash-ref db file)
- (let ((table (make-hash-table)))
- (hash-set! db file table)
- table))))
- (hashv-set! file-table
- line
- (cons proc (hashv-ref file-table line '())))))
-
-(define (forget-source proc file line db)
- (let ((file-table (hash-ref db file)))
- (if file-table
- (let ((procs (delq proc (hashv-ref file-table line '()))))
- (if (pair? procs)
- (hashv-set! file-table line procs)
- (hashv-remove! file-table line))))))
-
-(define (add-sources proc mod-name db)
- (let ((sources (procedure-sources proc)))
- (if (pair? sources)
- (begin
- ;; Add proc to *module-sources-db*, for book-keeping.
- (hashq-set! (or (hash-ref *module-sources-db* mod-name)
- (let ((table (make-hash-table)))
- (hash-set! *module-sources-db* mod-name table)
- table))
- proc
- sources)
- ;; Actually add the source entries.
- (for-each (lambda (source)
- (pmatch source
- ((,ip ,file ,line . ,col)
- (add-source proc file line db))
- (else (error "unexpected source format" source))))
- sources)))
- ;; Add source entries for nested procedures.
- (for-each (lambda (obj)
- (if (procedure? obj)
- (add-sources obj mod-name *closure-sources-db*)))
- (or (and (program? proc)
- (and=> (program-objects proc) vector->list))
- '()))))
-
-(define (forget-sources proc mod-name db)
- (let ((mod-table (hash-ref *module-sources-db* mod-name)))
- (if mod-table
- (begin
- ;; Forget source entries.
- (for-each (lambda (source)
- (pmatch source
- ((,ip ,file ,line . ,col)
- (forget-source proc file line db))
- (else (error "unexpected source format" source))))
- (hashq-ref mod-table proc '()))
- ;; Forget the proc.
- (hashq-remove! mod-table proc)
- ;; Forget source entries for nested procedures.
- (for-each (lambda (obj)
- (if (procedure? obj)
- (forget-sources obj mod-name *closure-sources-db*)))
- (or (and (program? proc)
- (and=> (program-objects proc) vector->list))
- '()))))))
-
-(define (untaint-sources)
- (define (untaint m)
- (for-each (lambda (proc) (forget-sources proc m *sources-db*))
- (cond
- ((hash-ref *module-sources-db* m)
- => (lambda (table)
- (hash-for-each (lambda (proc sources) proc) table)))
- (else '())))
- (ensure-sources-db m))
- (ensure-sources-db #f)
- (for-each untaint *tainted-sources*)
- (set! *tainted-sources* '()))
-
-(define (ensure-sources-db mod-name)
- (define (visit-module mod)
- (if (not (memq on-source-modified (module-observers mod)))
- (module-observe mod on-source-modified))
- (let ((name (module-name mod)))
- (module-for-each
- (lambda (sym var)
- (if (variable-bound? var)
- (let ((x (variable-ref var)))
- (if (procedure? x)
- (add-sources x name *sources-db*)))))
- mod)))
-
- (define visit-submodules
- (let ((visited #f))
- (lambda (mod)
- (if (not visited)
- (set! visited (make-hash-table)))
- (hash-for-each
- (lambda (name sub)
- (if (not (hashq-ref visited sub))
- (begin
- (hashq-set! visited sub #t)
- (visit-module sub)
- (visit-submodules sub))))
- (module-submodules mod)))))
-
- (cond ((and (not mod-name) (not *sources-db*) (not *closure-sources-db*))
- (set! *closure-sources-db* (make-hash-table 1000))
- (set! *sources-db* (make-hash-table 1000))
- (visit-submodules (resolve-module '() #f)))
- (mod-name (visit-module (resolve-module mod-name)))))
-
-(define (lines->ranges file-table)
- (let ((ranges (make-hash-table)))
- (hash-for-each
- (lambda (line procs)
- (for-each
- (lambda (proc)
- (cond
- ((hashq-ref ranges proc)
- => (lambda (pair)
- (if (< line (car pair))
- (set-car! pair line))
- (if (> line (cdr pair))
- (set-cdr! pair line))))
- (else
- (hashq-set! ranges proc (cons line line)))))
- procs))
- file-table)
- (sort! (hash-map->list cons ranges)
- (lambda (x y) (< (cadr x) (cadr y))))))
-
-(define* (lookup-source-procedures canon-file line db)
- (let ((file-table (hash-ref db canon-file)))
- (let lp ((ranges (if file-table (lines->ranges file-table) '()))
- (procs '()))
- (cond
- ((null? ranges) (reverse procs))
- ((<= (cadar ranges) line (cddar ranges))
- (lp (cdr ranges) (cons (caar ranges) procs)))
- (else
- (lp (cdr ranges) procs))))))
-
-(define* (source-closures file line #\key (canonicalization 'relative))
- (ensure-sources-db #f)
- (let* ((port (with-fluids ((%file-port-name-canonicalization canonicalization))
- (false-if-exception (open-input-file file))))
- (file (if port (port-filename port) file)))
- (lookup-source-procedures file line *closure-sources-db*)))
-
-(define* (source-procedures file line #\key (canonicalization 'relative))
- (ensure-sources-db #f)
- (let* ((port (with-fluids ((%file-port-name-canonicalization canonicalization))
- (false-if-exception (open-input-file file))))
- (file (if port (port-filename port) file)))
- (lookup-source-procedures file line *sources-db*)))
-;;;; (texinfo) -- parsing of texinfo into SXML
-;;;;
-;;;; Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
-;;;; Copyright (C) 2004, 2009 Andy Wingo <wingo at pobox dot com>
-;;;; Copyright (C) 2001,2002 Oleg Kiselyov <oleg at pobox dot com>
-;;;;
-;;;; This file is based on SSAX's SSAX.scm.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Commentary_
-;;
-;; @subheading Texinfo processing in scheme
-;;
-;; This module parses texinfo into SXML. TeX will always be the
-;; processor of choice for print output, of course. However, although
-;; @code{makeinfo} works well for info, its output in other formats is
-;; not very customizable, and the program is not extensible as a whole.
-;; This module aims to provide an extensible framework for texinfo
-;; processing that integrates texinfo into the constellation of SXML
-;; processing tools.
-;;
-;; @subheading Notes on the SXML vocabulary
-;;
-;; Consider the following texinfo fragment_
-;;
-;;@example
-;; @@deffn Primitive set-car! pair value
-;; This function...
-;; @@end deffn
-;;@end example
-;;
-;; Logically, the category (Primitive), name (set-car!), and arguments
-;; (pair value) are ``attributes'' of the deffn, with the description as
-;; the content. However, texinfo allows for @@-commands within the
-;; arguments to an environment, like @code{@@deffn}, which means that
-;; texinfo ``attributes'' are PCDATA. XML attributes, on the other hand,
-;; are CDATA. For this reason, ``attributes'' of texinfo @@-commands are
-;; called ``arguments'', and are grouped under the special element, `%'.
-;;
-;; Because `%' is not a valid NCName, stexinfo is a superset of SXML. In
-;; the interests of interoperability, this module provides a conversion
-;; function to replace the `%' with `texinfo-arguments'.
-;;
-;;; Code_
-
-;; Comparison to xml output of texinfo (which is rather undocumented)_
-;; Doesn't conform to texinfo dtd
-;; No DTD at all, in fact _-/
-;; Actually outputs valid xml, after transforming %
-;; Slower (although with caching the SXML that problem can go away)
-;; Doesn't parse menus (although menus are shite)
-;; Args go in a dedicated element, FBOFW
-;; Definitions are handled a lot better
-;; Does parse comments
-;; Outputs only significant line breaks (a biggie!)
-;; Nodes are treated as anchors, rather than content organizers (a biggie)
-;; (more book-like, less info-like)
-
-;; TODO
-;; Integration_ help, indexing, plain text
-
-(define-module (texinfo)
- #\use-module (sxml simple)
- #\use-module (sxml transform)
- #\use-module (sxml ssax input-parse)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-11)
- #\use-module (srfi srfi-13)
- #\export (call-with-file-and-dir
- texi-command-specs
- texi-command-depth
- texi-fragment->stexi
- texi->stexi
- stexi->sxml))
-
-;; Some utilities
-
-(define (parser-error port message . rest)
- (apply throw 'parser-error port message rest))
-
-(define (call-with-file-and-dir filename proc)
- "Call the one-argument procedure @var{proc} with an input port that
-reads from @var{filename}. During the dynamic extent of @var{proc}'s
-execution, the current directory will be @code{(dirname
-@var{filename})}. This is useful for parsing documents that can include
-files by relative path name."
- (let ((current-dir (getcwd)))
- (dynamic-wind
- (lambda () (chdir (dirname filename)))
- (lambda ()
- (call-with-input-file (basename filename) proc))
- (lambda () (chdir current-dir)))))
-
-;;========================================================================
-;; Reflection on the XML vocabulary
-
-(define texi-command-specs
- ;~
-"A list of (@var{name} @var{content-model} . @var{args})
-
-@table @var
-@item name
-The name of an @@-command, as a symbol.
-
-@item content-model
-A symbol indicating the syntactic type of the @@-command_
-@table @code
-@item EMPTY-COMMAND
-No content, and no @code{@@end} is coming
-@item EOL-ARGS
-Unparsed arguments until end of line
-@item EOL-TEXT
-Parsed arguments until end of line
-@item INLINE-ARGS
-Unparsed arguments ending with @code{#\\@}}
-@item INLINE-TEXT
-Parsed arguments ending with @code{#\\@}}
-@item INLINE-TEXT-ARGS
-Parsed arguments ending with @code{#\\@}}
-@item ENVIRON
-The tag is an environment tag, expect @code{@@end foo}.
-@item TABLE-ENVIRON
-Like ENVIRON, but with special parsing rules for its arguments.
-@item FRAGMENT
-For @code{*fragment*}, the command used for parsing fragments of
-texinfo documents.
-@end table
-
-@code{INLINE-TEXT} commands will receive their arguments within their
-bodies, whereas the @code{-ARGS} commands will receive them in their
-attribute list.
-
-@code{EOF-TEXT} receives its arguments in its body.
-
-@code{ENVIRON} commands have both_ parsed arguments until the end of
-line, received through their attribute list, and parsed text until the
-@code{@@end}, received in their bodies.
-
-@code{EOF-TEXT-ARGS} receives its arguments in its attribute list, as in
-@code{ENVIRON}.
-
-In addition, @code{ALIAS} can alias one command to another. The alias
-will never be seen in parsed stexinfo.
-
-There are four @@-commands that are treated specially. @code{@@include}
-is a low-level token that will not be seen by higher-level parsers, so
-it has no content-model. @code{@@para} is the paragraph command, which
-is only implicit in the texinfo source. @code{@@item} has special
-syntax, as noted above, and @code{@@entry} is how this parser treats
-@code{@@item} commands within @code{@@table}, @code{@@ftable}, and
-@code{@@vtable}.
-
-Also, indexing commands (@code{@@cindex}, etc.) are treated specially.
-Their arguments are parsed, but they are needed before entering the
-element so that an anchor can be inserted into the text before the index
-entry.
-
-@item args
-Named arguments to the command, in the same format as the formals for a
-lambda. Only present for @code{INLINE-ARGS}, @code{EOL-ARGS},
-@code{INLINE-TEXT-ARGS}, @code{ENVIRON}, @code{TABLE-ENVIRON} commands.
-@end table"
- '(;; Special commands
- (include #f) ;; this is a low-level token
- (para PARAGRAPH)
- (item ITEM)
- (entry ENTRY . heading)
- (noindent EMPTY-COMMAND)
- (*fragment* FRAGMENT)
-
- ;; Inline text commands
- (*braces* INLINE-TEXT) ;; FIXME_ make me irrelevant
- (bold INLINE-TEXT)
- (sample INLINE-TEXT)
- (samp INLINE-TEXT)
- (code INLINE-TEXT)
- (math INLINE-TEXT)
- (kbd INLINE-TEXT)
- (key INLINE-TEXT)
- (var INLINE-TEXT)
- (env INLINE-TEXT)
- (file INLINE-TEXT)
- (command INLINE-TEXT)
- (option INLINE-TEXT)
- (dfn INLINE-TEXT)
- (cite INLINE-TEXT)
- (acro INLINE-TEXT)
- (email INLINE-TEXT)
- (emph INLINE-TEXT)
- (strong INLINE-TEXT)
- (sample INLINE-TEXT)
- (sc INLINE-TEXT)
- (titlefont INLINE-TEXT)
- (asis INLINE-TEXT)
- (b INLINE-TEXT)
- (i INLINE-TEXT)
- (r INLINE-TEXT)
- (sansserif INLINE-TEXT)
- (slanted INLINE-TEXT)
- (t INLINE-TEXT)
-
- ;; Inline args commands
- (value INLINE-ARGS . (key))
- (ref INLINE-ARGS . (node #\opt name section info-file manual))
- (xref INLINE-ARGS . (node #\opt name section info-file manual))
- (pxref INLINE-TEXT-ARGS
- . (node #\opt name section info-file manual))
- (url ALIAS . uref)
- (uref INLINE-ARGS . (url #\opt title replacement))
- (anchor INLINE-ARGS . (name))
- (dots INLINE-ARGS . ())
- (result INLINE-ARGS . ())
- (bullet INLINE-ARGS . ())
- (copyright INLINE-ARGS . ())
- (tie INLINE-ARGS . ())
- (image INLINE-ARGS . (file #\opt width height alt-text extension))
-
- ;; Inline parsed args commands
- (acronym INLINE-TEXT-ARGS . (acronym #\opt meaning))
-
- ;; EOL args elements
- (node EOL-ARGS . (name #\opt next previous up))
- (c EOL-ARGS . all)
- (comment EOL-ARGS . all)
- (setchapternewpage EOL-ARGS . all)
- (sp EOL-ARGS . all)
- (page EOL-ARGS . ())
- (vskip EOL-ARGS . all)
- (syncodeindex EOL-ARGS . all)
- (contents EOL-ARGS . ())
- (shortcontents EOL-ARGS . ())
- (summarycontents EOL-ARGS . ())
- (insertcopying EOL-ARGS . ())
- (dircategory EOL-ARGS . (category))
- (top EOL-ARGS . (title))
- (printindex EOL-ARGS . (type))
- (paragraphindent EOL-ARGS . (indent))
-
- ;; EOL text commands
- (*ENVIRON-ARGS* EOL-TEXT)
- (itemx EOL-TEXT)
- (set EOL-TEXT)
- (center EOL-TEXT)
- (title EOL-TEXT)
- (subtitle EOL-TEXT)
- (author EOL-TEXT)
- (chapter EOL-TEXT)
- (section EOL-TEXT)
- (appendix EOL-TEXT)
- (appendixsec EOL-TEXT)
- (unnumbered EOL-TEXT)
- (unnumberedsec EOL-TEXT)
- (subsection EOL-TEXT)
- (subsubsection EOL-TEXT)
- (appendixsubsec EOL-TEXT)
- (appendixsubsubsec EOL-TEXT)
- (unnumberedsubsec EOL-TEXT)
- (unnumberedsubsubsec EOL-TEXT)
- (chapheading EOL-TEXT)
- (majorheading EOL-TEXT)
- (heading EOL-TEXT)
- (subheading EOL-TEXT)
- (subsubheading EOL-TEXT)
-
- (deftpx EOL-TEXT-ARGS . (category name . attributes))
- (defcvx EOL-TEXT-ARGS . (category class name))
- (defivarx EOL-TEXT-ARGS . (class name))
- (deftypeivarx EOL-TEXT-ARGS . (class data-type name))
- (defopx EOL-TEXT-ARGS . (category class name . arguments))
- (deftypeopx EOL-TEXT-ARGS . (category class data-type name . arguments))
- (defmethodx EOL-TEXT-ARGS . (class name . arguments))
- (deftypemethodx EOL-TEXT-ARGS . (class data-type name . arguments))
- (defoptx EOL-TEXT-ARGS . (name))
- (defvrx EOL-TEXT-ARGS . (category name))
- (defvarx EOL-TEXT-ARGS . (name))
- (deftypevrx EOL-TEXT-ARGS . (category data-type name))
- (deftypevarx EOL-TEXT-ARGS . (data-type name))
- (deffnx EOL-TEXT-ARGS . (category name . arguments))
- (deftypefnx EOL-TEXT-ARGS . (category data-type name . arguments))
- (defspecx EOL-TEXT-ARGS . (name . arguments))
- (defmacx EOL-TEXT-ARGS . (name . arguments))
- (defunx EOL-TEXT-ARGS . (name . arguments))
- (deftypefunx EOL-TEXT-ARGS . (data-type name . arguments))
-
- ;; Indexing commands
- (cindex INDEX . entry)
- (findex INDEX . entry)
- (vindex INDEX . entry)
- (kindex INDEX . entry)
- (pindex INDEX . entry)
- (tindex INDEX . entry)
-
- ;; Environment commands (those that need @end)
- (texinfo ENVIRON . title)
- (ignore ENVIRON . ())
- (ifinfo ENVIRON . ())
- (iftex ENVIRON . ())
- (ifhtml ENVIRON . ())
- (ifxml ENVIRON . ())
- (ifplaintext ENVIRON . ())
- (ifnotinfo ENVIRON . ())
- (ifnottex ENVIRON . ())
- (ifnothtml ENVIRON . ())
- (ifnotxml ENVIRON . ())
- (ifnotplaintext ENVIRON . ())
- (titlepage ENVIRON . ())
- (menu ENVIRON . ())
- (direntry ENVIRON . ())
- (copying ENVIRON . ())
- (example ENVIRON . ())
- (smallexample ENVIRON . ())
- (display ENVIRON . ())
- (smalldisplay ENVIRON . ())
- (verbatim ENVIRON . ())
- (format ENVIRON . ())
- (smallformat ENVIRON . ())
- (lisp ENVIRON . ())
- (smalllisp ENVIRON . ())
- (cartouche ENVIRON . ())
- (quotation ENVIRON . ())
-
- (deftp ENVIRON . (category name . attributes))
- (defcv ENVIRON . (category class name))
- (defivar ENVIRON . (class name))
- (deftypeivar ENVIRON . (class data-type name))
- (defop ENVIRON . (category class name . arguments))
- (deftypeop ENVIRON . (category class data-type name . arguments))
- (defmethod ENVIRON . (class name . arguments))
- (deftypemethod ENVIRON . (class data-type name . arguments))
- (defopt ENVIRON . (name))
- (defvr ENVIRON . (category name))
- (defvar ENVIRON . (name))
- (deftypevr ENVIRON . (category data-type name))
- (deftypevar ENVIRON . (data-type name))
- (deffn ENVIRON . (category name . arguments))
- (deftypefn ENVIRON . (category data-type name . arguments))
- (defspec ENVIRON . (name . arguments))
- (defmac ENVIRON . (name . arguments))
- (defun ENVIRON . (name . arguments))
- (deftypefun ENVIRON . (data-type name . arguments))
-
- (table TABLE-ENVIRON . (formatter))
- (itemize TABLE-ENVIRON . (formatter))
- (enumerate TABLE-ENVIRON . (start))
- (ftable TABLE-ENVIRON . (formatter))
- (vtable TABLE-ENVIRON . (formatter))))
-
-(define command-depths
- '((chapter . 1) (section . 2) (subsection . 3) (subsubsection . 4)
- (top . 0) (unnumbered . 1) (unnumberedsec . 2)
- (unnumberedsubsec . 3) (unnumberedsubsubsec . 4)
- (appendix . 1) (appendixsec . 2) (appendixsection . 2)
- (appendixsubsec . 3) (appendixsubsubsec . 4)))
-(define (texi-command-depth command max-depth)
- "Given the texinfo command @var{command}, return its nesting level, or
-@code{#f} if it nests too deep for @var{max-depth}.
-
-Examples_
-@example
- (texi-command-depth 'chapter 4) @result{} 1
- (texi-command-depth 'top 4) @result{} 0
- (texi-command-depth 'subsection 4) @result{} 3
- (texi-command-depth 'appendixsubsec 4) @result{} 3
- (texi-command-depth 'subsection 2) @result{} #f
-@end example"
- (let ((depth (and=> (assq command command-depths) cdr)))
- (and depth (<= depth max-depth) depth)))
-
-;; The % is for arguments
-(define (space-significant? command)
- (memq command
- '(example smallexample verbatim lisp smalllisp menu %)))
-
-;; Like a DTD for texinfo
-(define (command-spec command)
- (let ((spec (assq command texi-command-specs)))
- (cond
- ((not spec)
- (parser-error #f "Unknown command" command))
- ((eq? (cadr spec) 'ALIAS)
- (command-spec (cddr spec)))
- (else
- spec))))
-
-(define (inline-content? content)
- (case content
- ((INLINE-TEXT INLINE-ARGS INLINE-TEXT-ARGS) #t)
- (else #f)))
-
-
-;;========================================================================
-;; Lower-level parsers and scanners
-;;
-;; They deal with primitive lexical units (Names, whitespaces, tags) and
-;; with pieces of more generic productions. Most of these parsers must
-;; be called in appropriate context. For example, complete-start-command
-;; must be called only when the @-command start has been detected and
-;; its name token has been read.
-
-;; Test if a string is made of only whitespace
-;; An empty string is considered made of whitespace as well
-(define (string-whitespace? str)
- (or (string-null? str)
- (string-every char-whitespace? str)))
-
-;; Like read-text-line, but allows EOF.
-(define read-eof-breaks '(*eof* #\return #\newline))
-(define (read-eof-line port)
- (if (eof-object? (peek-char port))
- (peek-char port)
- (let* ((line (next-token '() read-eof-breaks
- "reading a line" port))
- (c (read-char port))) ; must be either \n or \r or EOF
- (if (and (eq? c #\return) (eq? (peek-char port) #\newline))
- (read-char port)) ; skip \n that follows \r
- line)))
-
-(define (skip-whitespace port)
- (skip-while '(#\space #\tab #\return #\newline) port))
-
-(define (skip-horizontal-whitespace port)
- (skip-while '(#\space #\tab) port))
-
-;; command __= Letter+
-
-;; procedure_ read-command PORT
-;;
-;; Read a command starting from the current position in the PORT and
-;; return it as a symbol.
-(define (read-command port)
- (let ((first-char (peek-char port)))
- (or (char-alphabetic? first-char)
- (parser-error port "Nonalphabetic @-command char_ '" first-char "'")))
- (string->symbol
- (next-token-of
- (lambda (c)
- (cond
- ((eof-object? c) #f)
- ((char-alphabetic? c) c)
- (else #f)))
- port)))
-
-;; A token is a primitive lexical unit. It is a record with two fields,
-;; token-head and token-kind.
-;;
-;; Token types_
-;; END The end of a texinfo command. If the command is ended by },
-;; token-head will be #f. Otherwise if the command is ended by
-;; @end COMMAND, token-head will be COMMAND. As a special case,
-;; @bye is the end of a special @texinfo command.
-;; START The start of a texinfo command. The token-head will be a
-;; symbol of the @-command name.
-;; INCLUDE An @include directive. The token-head will be empty -- the
-;; caller is responsible for reading the include file name.
-;; ITEM @item commands have an irregular syntax. They end at the
-;; next @item, or at the end of the environment. For that
-;; read-command-token treats them specially.
-
-(define (make-token kind head) (cons kind head))
-(define token? pair?)
-(define token-kind car)
-(define token-head cdr)
-
-;; procedure_ read-command-token PORT
-;;
-;; This procedure starts parsing of a command token. The current
-;; position in the stream must be #\@. This procedure scans enough of
-;; the input stream to figure out what kind of a command token it is
-;; seeing. The procedure returns a token structure describing the token.
-
-(define (read-command-token port)
- (assert-curr-char '(#\@) "start of the command" port)
- (let ((peeked (peek-char port)))
- (cond
- ((memq peeked '(#\! #\_ #\. #\? #\@ #\\ #\{ #\}))
- ;; @-commands that escape characters
- (make-token 'STRING (string (read-char port))))
- (else
- (let ((name (read-command port)))
- (case name
- ((end)
- ;; got an ending tag
- (let ((command (string-trim-both
- (read-eof-line port))))
- (or (and (not (string-null? command))
- (string-every char-alphabetic? command))
- (parser-error port "malformed @end" command))
- (make-token 'END (string->symbol command))))
- ((bye)
- ;; the end of the top
- (make-token 'END 'texinfo))
- ((item)
- (make-token 'ITEM 'item))
- ((include)
- (make-token 'INCLUDE #f))
- (else
- (make-token 'START name))))))))
-
-;; procedure+_ read-verbatim-body PORT STR-HANDLER SEED
-;;
-;; This procedure must be called after we have read a string
-;; "@verbatim\n" that begins a verbatim section. The current position
-;; must be the first position of the verbatim body. This function reads
-;; _lines_ of the verbatim body and passes them to a STR-HANDLER, a
-;; character data consumer.
-;;
-;; The str-handler is a STR-HANDLER, a procedure STRING1 STRING2 SEED.
-;; The first STRING1 argument to STR-HANDLER never contains a newline.
-;; The second STRING2 argument often will. On the first invocation of the
-;; STR-HANDLER, the seed is the one passed to read-verbatim-body
-;; as the third argument. The result of this first invocation will be
-;; passed as the seed argument to the second invocation of the line
-;; consumer, and so on. The result of the last invocation of the
-;; STR-HANDLER is returned by the read-verbatim-body. Note a
-;; similarity to the fundamental 'fold' iterator.
-;;
-;; Within a verbatim section all characters are taken at their face
-;; value. It ends with "\n@end verbatim(\r)?\n".
-
-;; Must be called right after the newline after @verbatim.
-(define (read-verbatim-body port str-handler seed)
- (let loop ((seed seed))
- (let ((fragment (next-token '() '(#\newline)
- "reading verbatim" port)))
- ;; We're reading the char after the 'fragment', which is
- ;; #\newline.
- (read-char port)
- (if (string=? fragment "@end verbatim")
- seed
- (loop (str-handler fragment "\n" seed))))))
-
-;; procedure+_ read-arguments PORT
-;;
-;; This procedure reads and parses a production ArgumentList.
-;; ArgumentList __= S* Argument (S* , S* Argument)* S*
-;; Argument __= ([^@{},])*
-;;
-;; Arguments are the things in braces, i.e @ref{my node} has one
-;; argument, "my node". Most commands taking braces actually don't have
-;; arguments, they process text. For example, in
-;; @emph{@strong{emphasized}}, the emph takes text, because the parse
-;; continues into the braces.
-;;
-;; Any whitespace within Argument is replaced with a single space.
-;; Whitespace around an Argument is trimmed.
-;;
-;; The procedure returns a list of arguments. Afterwards the current
-;; character will be after the final #\}.
-
-(define (read-arguments port stop-char)
- (define (split str)
- (read-char port) ;; eat the delimiter
- (let ((ret (map (lambda (x) (if (string-null? x) #f x))
- (map string-trim-both (string-split str #\,)))))
- (if (and (pair? ret) (eq? (car ret) #f) (null? (cdr ret)))
- '()
- ret)))
- (split (next-token '() (list stop-char)
- "arguments of @-command" port)))
-
-;; procedure+_ complete-start-command COMMAND PORT
-;;
-;; This procedure is to complete parsing of an @-command. The procedure
-;; must be called after the command token has been read. COMMAND is a
-;; TAG-NAME.
-;;
-;; This procedure returns several values_
-;; COMMAND_ a symbol.
-;; ARGUMENTS_ command's arguments, as an alist.
-;; CONTENT-MODEL_ the content model of the command.
-;;
-;; On exit, the current position in PORT will depend on the CONTENT-MODEL.
-;;
-;; Content model Port position
-;; ============= =============
-;; INLINE-TEXT One character after the #\{.
-;; INLINE-TEXT-ARGS One character after the #\{.
-;; INLINE-ARGS The first character after the #\}.
-;; EOL-TEXT The first non-whitespace character after the command.
-;; ENVIRON, TABLE-ENVIRON, EOL-ARGS, EOL-TEXT
-;; The first character on the next line.
-;; PARAGRAPH, ITEM, EMPTY-COMMAND
-;; The first character after the command.
-
-(define (arguments->attlist port args arg-names)
- (let loop ((in args) (names arg-names) (opt? #f) (out '()))
- (cond
- ((symbol? names) ;; a rest arg
- (reverse (if (null? in) out (acons names in out))))
- ((and (not (null? names)) (eq? (car names) #\opt))
- (loop in (cdr names) #t out))
- ((null? in)
- (if (or (null? names) opt?)
- (reverse out)
- (parser-error port "@-command expected more arguments_"
- args arg-names names)))
- ((null? names)
- (parser-error port "@-command didn't expect more arguments_" in))
- ((not (car in))
- (or (and opt? (loop (cdr in) (cdr names) opt? out))
- (parser-error "@-command missing required argument"
- (car names))))
- (else
- (loop (cdr in) (cdr names) opt?
- (acons (car names)
- (if (list? (car in)) (car in) (list (car in)))
- out))))))
-
-(define (parse-table-args command port)
- (let* ((line (string-trim-both (read-text-line port)))
- (length (string-length line)))
- (define (get-formatter)
- (or (and (not (zero? length))
- (eq? (string-ref line 0) #\@)
- (let ((f (string->symbol (substring line 1))))
- (or (inline-content? (cadr (command-spec f)))
- (parser-error
- port "@item formatter must be INLINE" f))
- f))
- (parser-error port "Invalid @item formatter" line)))
- (case command
- ((enumerate)
- (if (zero? length)
- '()
- `((start
- ,(if (or (and (eq? length 1)
- (char-alphabetic? (string-ref line 0)))
- (string-every char-numeric? line))
- line
- (parser-error
- port "Invalid enumerate start" line))))))
- ((itemize)
- `((bullet
- ,(or (and (eq? length 1) line)
- (and (string-null? line) '(bullet))
- (list (get-formatter))))))
- (else ;; tables of various varieties
- `((formatter (,(get-formatter))))))))
-
-(define (complete-start-command command port)
- (define (get-arguments type arg-names stop-char)
- (arguments->attlist port (read-arguments port stop-char) arg-names))
-
- (let* ((spec (command-spec command))
- (command (car spec))
- (type (cadr spec))
- (arg-names (cddr spec)))
- (case type
- ((INLINE-TEXT)
- (assert-curr-char '(#\{) "Inline element lacks {" port)
- (values command '() type))
- ((INLINE-ARGS)
- (assert-curr-char '(#\{) "Inline element lacks {" port)
- (values command (get-arguments type arg-names #\}) type))
- ((INLINE-TEXT-ARGS)
- (assert-curr-char '(#\{) "Inline element lacks {" port)
- (values command '() type))
- ((EOL-ARGS)
- (values command (get-arguments type arg-names #\newline) type))
- ((ENVIRON ENTRY INDEX)
- (skip-horizontal-whitespace port)
- (values command (parse-environment-args command port) type))
- ((TABLE-ENVIRON)
- (skip-horizontal-whitespace port)
- (values command (parse-table-args command port) type))
- ((EOL-TEXT)
- (skip-horizontal-whitespace port)
- (values command '() type))
- ((EOL-TEXT-ARGS)
- (skip-horizontal-whitespace port)
- (values command (parse-eol-text-args command port) type))
- ((PARAGRAPH EMPTY-COMMAND ITEM FRAGMENT)
- (values command '() type))
- (else ;; INCLUDE shouldn't get here
- (parser-error port "can't happen")))))
-
-;;-----------------------------------------------------------------------------
-;; Higher-level parsers and scanners
-;;
-;; They parse productions corresponding entire @-commands.
-
-;; Only reads @settitle, leaves it to the command parser to finish
-;; reading the title.
-(define (take-until-settitle port)
- (or (find-string-from-port? "\n@settitle " port)
- (parser-error port "No \\n@settitle found"))
- (skip-horizontal-whitespace port)
- (and (eq? (peek-char port) #\newline)
- (parser-error port "You have a @settitle, but no title")))
-
-;; procedure+_ read-char-data PORT EXPECT-EOF? STR-HANDLER SEED
-;;
-;; This procedure is to read the CharData of a texinfo document.
-;;
-;; text __= (CharData | Command)*
-;;
-;; The procedure reads CharData and stops at @-commands (or
-;; environments). It also stops at an open or close brace.
-;;
-;; port
-;; a PORT to read
-;; expect-eof?
-;; a boolean indicating if EOF is normal, i.e., the character
-;; data may be terminated by the EOF. EOF is normal
-;; while processing the main document.
-;; preserve-ws?
-;; a boolean indicating if we are within a whitespace-preserving
-;; environment. If #t, suppress paragraph detection.
-;; str-handler
-;; a STR-HANDLER, see read-verbatim-body
-;; seed
-;; an argument passed to the first invocation of STR-HANDLER.
-;;
-;; The procedure returns two results_ SEED and TOKEN. The SEED is the
-;; result of the last invocation of STR-HANDLER, or the original seed if
-;; STR-HANDLER was never called.
-;;
-;; TOKEN can be either an eof-object (this can happen only if expect-eof?
-;; was #t), or a texinfo token denoting the start or end of a tag.
-
-;; read-char-data port expect-eof? preserve-ws? str-handler seed
-(define read-char-data
- (let* ((end-chars-eof '(*eof* #\{ #\} #\@ #\newline)))
- (define (handle str-handler str1 str2 seed)
- (if (and (string-null? str1) (string-null? str2))
- seed
- (str-handler str1 str2 seed)))
-
- (lambda (port expect-eof? preserve-ws? str-handler seed)
- (let ((end-chars ((if expect-eof? identity cdr) end-chars-eof)))
- (let loop ((seed seed))
- (let* ((fragment (next-token '() end-chars "reading char data" port))
- (term-char (peek-char port))) ; one of end-chars
- (cond
- ((eof-object? term-char) ; only if expect-eof?
- (values (handle str-handler fragment "" seed) term-char))
- ((memq term-char '(#\@ #\{ #\}))
- (values (handle str-handler fragment "" seed)
- (case term-char
- ((#\@) (read-command-token port))
- ((#\{) (make-token 'START '*braces*))
- ((#\}) (read-char port) (make-token 'END #f)))))
- ((eq? term-char #\newline)
- ;; Always significant, unless directly before an end token.
- (let ((c (peek-next-char port)))
- (cond
- ((eof-object? c)
- (or expect-eof?
- (parser-error port "EOF while reading char data"))
- (values (handle str-handler fragment "" seed) c))
- ((eq? c #\@)
- (let* ((token (read-command-token port))
- (end? (eq? (token-kind token) 'END)))
- (values
- (handle str-handler fragment
- (if end? "" (if preserve-ws? "\n" " "))
- seed)
- token)))
- ((and (not preserve-ws?) (eq? c #\newline))
- ;; paragraph-separator __= #\newline #\newline+
- (skip-while '(#\newline) port)
- (skip-horizontal-whitespace port)
- (values (handle str-handler fragment "" seed)
- (make-token 'PARA 'para)))
- (else
- (loop (handle str-handler fragment
- (if preserve-ws? "\n" " ") seed)))))))))))))
-
-; procedure+_ assert-token TOKEN KIND NAME
-; Make sure that TOKEN is of anticipated KIND and has anticipated NAME
-(define (assert-token token kind name)
- (or (and (token? token)
- (eq? kind (token-kind token))
- (equal? name (token-head token)))
- (parser-error #f "Expecting @end for " name ", got " token)))
-
-;;========================================================================
-;; Highest-level parsers_ Texinfo to SXML
-
-;; These parsers are a set of syntactic forms to instantiate a SSAX
-;; parser. The user tells what to do with the parsed character and
-;; element data. These latter handlers determine if the parsing follows a
-;; SAX or a DOM model.
-
-;; syntax_ make-command-parser fdown fup str-handler
-
-;; Create a parser to parse and process one element, including its
-;; character content or children elements. The parser is typically
-;; applied to the root element of a document.
-
-;; fdown
-;; procedure COMMAND ARGUMENTS EXPECTED-CONTENT SEED
-;;
-;; This procedure is to generate the seed to be passed to handlers
-;; that process the content of the element. This is the function
-;; identified as 'fdown' in the denotational semantics of the XML
-;; parser given in the title comments to (sxml ssax).
-;;
-;; fup
-;; procedure COMMAND ARGUMENTS PARENT-SEED SEED
-;;
-;; This procedure is called when parsing of COMMAND is finished.
-;; The SEED is the result from the last content parser (or from
-;; fdown if the element has the empty content). PARENT-SEED is the
-;; same seed as was passed to fdown. The procedure is to generate a
-;; seed that will be the result of the element parser. This is the
-;; function identified as 'fup' in the denotational semantics of
-;; the XML parser given in the title comments to (sxml ssax).
-;;
-;; str-handler
-;; A STR-HANDLER, see read-verbatim-body
-;;
-
-;; The generated parser is a
-;; procedure COMMAND PORT SEED
-;;
-;; The procedure must be called *after* the command token has been read.
-
-(define (read-include-file-name port)
- (let ((x (string-trim-both (read-eof-line port))))
- (if (string-null? x)
- (error "no file listed")
- x))) ;; fixme_ should expand @value{} references
-
-(define (sxml->node-name sxml)
- "Turn some sxml string into a valid node name."
- (let loop ((in (string->list (sxml->string sxml))) (out '()))
- (if (null? in)
- (apply string (reverse out))
- (if (memq (car in) '(#\{ #\} #\@ #\,))
- (loop (cdr in) out)
- (loop (cdr in) (cons (car in) out))))))
-
-(define (index command arguments fdown fup parent-seed)
- (case command
- ((deftp defcv defivar deftypeivar defop deftypeop defmethod
- deftypemethod defopt defvr defvar deftypevr deftypevar deffn
- deftypefn defspec defmac defun deftypefun)
- (let ((args `((name ,(string-append (symbol->string command) "-"
- (cadr (assq 'name arguments)))))))
- (fup 'anchor args parent-seed
- (fdown 'anchor args 'INLINE-ARGS '()))))
- ((cindex findex vindex kindex pindex tindex)
- (let ((args `((name ,(string-append (symbol->string command) "-"
- (sxml->node-name
- (assq 'entry arguments)))))))
- (fup 'anchor args parent-seed
- (fdown 'anchor args 'INLINE-ARGS '()))))
- (else parent-seed)))
-
-(define (make-command-parser fdown fup str-handler)
- (lambda (command port seed)
- (let visit ((command command) (port port) (sig-ws? #f) (parent-seed seed))
- (let*-values (((command arguments expected-content)
- (complete-start-command command port)))
- (let* ((parent-seed (index command arguments fdown fup parent-seed))
- (seed (fdown command arguments expected-content parent-seed))
- (eof-closes? (or (memq command '(texinfo para *fragment*))
- (eq? expected-content 'EOL-TEXT)))
- (sig-ws? (or sig-ws? (space-significant? command)))
- (up (lambda (s) (fup command arguments parent-seed s)))
- (new-para (lambda (s) (fdown 'para '() 'PARAGRAPH s)))
- (make-end-para (lambda (p) (lambda (s) (fup 'para '() p s)))))
-
- (define (port-for-content)
- (if (eq? expected-content 'EOL-TEXT)
- (call-with-input-string (read-text-line port) identity)
- port))
-
- (cond
- ((memq expected-content '(EMPTY-COMMAND INLINE-ARGS EOL-ARGS INDEX
- EOL-TEXT-ARGS))
- ;; empty or finished by complete-start-command
- (up seed))
- ((eq? command 'verbatim)
- (up (read-verbatim-body port str-handler seed)))
- (else
- (let loop ((port (port-for-content))
- (expect-eof? eof-closes?)
- (end-para identity)
- (need-break? (and (not sig-ws?)
- (memq expected-content
- '(ENVIRON TABLE-ENVIRON
- ENTRY ITEM FRAGMENT))))
- (seed seed))
- (cond
- ((and need-break? (or sig-ws? (skip-whitespace port))
- (not (memq (peek-char port) '(#\@ #\})))
- (not (eof-object? (peek-char port))))
- ;; Even if we have an @, it might be inline -- check
- ;; that later
- (let ((seed (end-para seed)))
- (loop port expect-eof? (make-end-para seed) #f
- (new-para seed))))
- (else
- (let*-values (((seed token)
- (read-char-data
- port expect-eof? sig-ws? str-handler seed)))
- (cond
- ((eof-object? token)
- (case expect-eof?
- ((include #f) (end-para seed))
- (else (up (end-para seed)))))
- (else
- (case (token-kind token)
- ((STRING)
- ;; this is only @-commands that escape
- ;; characters_ @}, @@, @{ -- new para if need-break
- (let ((seed ((if need-break? end-para identity) seed)))
- (loop port expect-eof?
- (if need-break? (make-end-para seed) end-para) #f
- (str-handler (token-head token) ""
- ((if need-break? new-para identity)
- seed)))))
- ((END)
- ;; The end will only have a name if it's for an
- ;; environment
- (cond
- ((memq command '(item entry))
- (let ((spec (command-spec (token-head token))))
- (or (eq? (cadr spec) 'TABLE-ENVIRON)
- (parser-error
- port "@item not ended by @end table/enumerate/itemize"
- token))))
- ((eq? expected-content 'ENVIRON)
- (assert-token token 'END command)))
- (up (end-para seed)))
- ((ITEM)
- (cond
- ((memq command '(enumerate itemize))
- (up (visit 'item port sig-ws? (end-para seed))))
- ((eq? expected-content 'TABLE-ENVIRON)
- (up (visit 'entry port sig-ws? (end-para seed))))
- ((memq command '(item entry))
- (visit command port sig-ws? (up (end-para seed))))
- (else
- (parser-error
- port "@item must be within a table environment"
- command))))
- ((PARA)
- ;; examine valid paragraphs?
- (loop port expect-eof? end-para (not sig-ws?) seed))
- ((INCLUDE)
- ;; Recurse for include files
- (let ((seed (call-with-file-and-dir
- (read-include-file-name port)
- (lambda (port)
- (loop port 'include end-para
- need-break? seed)))))
- (loop port expect-eof? end-para need-break? seed)))
- ((START) ; Start of an @-command
- (let* ((head (token-head token))
- (spec (command-spec head))
- (head (car spec))
- (type (cadr spec))
- (inline? (inline-content? type))
- (seed ((if (and inline? (not need-break?))
- identity end-para) seed))
- (end-para (if inline?
- (if need-break? (make-end-para seed)
- end-para)
- identity))
- (new-para (if (and inline? need-break?)
- new-para identity)))
- (loop port expect-eof? end-para (not inline?)
- (visit head port sig-ws? (new-para seed)))))
- (else
- (parser-error port "Unknown token type" token))))))))))))))))
-
-;; procedure_ reverse-collect-str-drop-ws fragments
-;;
-;; Given the list of fragments (some of which are text strings), reverse
-;; the list and concatenate adjacent text strings. We also drop
-;; "unsignificant" whitespace, that is, whitespace in front, behind and
-;; between elements. The whitespace that is included in character data
-;; is not affected.
-(define (reverse-collect-str-drop-ws fragments)
- (cond
- ((null? fragments) ; a shortcut
- '())
- ((and (string? (car fragments)) ; another shortcut
- (null? (cdr fragments)) ; remove single ws-only string
- (string-whitespace? (car fragments)))
- '())
- (else
- (let loop ((fragments fragments) (result '()) (strs '())
- (all-whitespace? #t))
- (cond
- ((null? fragments)
- (if all-whitespace?
- result ; remove leading ws
- (cons (apply string-append strs) result)))
- ((string? (car fragments))
- (loop (cdr fragments) result (cons (car fragments) strs)
- (and all-whitespace?
- (string-whitespace? (car fragments)))))
- (else
- (loop (cdr fragments)
- (cons
- (car fragments)
- (cond
- ((null? strs) result)
- (all-whitespace?
- (if (null? result)
- result ; remove trailing whitespace
- (cons " " result))); replace interstitial ws with
- ; one space
- (else
- (cons (apply string-append strs) result))))
- '() #t)))))))
-
-(define (parse-inline-text-args port spec text)
- (let lp ((in text) (cur '()) (out '()))
- (cond
- ((null? in)
- (if (and (pair? cur)
- (string? (car cur))
- (string-whitespace? (car cur)))
- (lp in (cdr cur) out)
- (let ((args (reverse (if (null? cur)
- out
- (cons (reverse cur) out)))))
- (arguments->attlist port args (cddr spec)))))
- ((pair? (car in))
- (lp (cdr in) (cons (car in) cur) out))
- ((string-index (car in) #\,)
- (let* ((parts (string-split (car in) #\,))
- (head (string-trim-right (car parts)))
- (rev-tail (reverse (cdr parts)))
- (last (string-trim (car rev-tail))))
- (lp (cdr in)
- (if (string-null? last) cur (cons last cur))
- (append (cdr rev-tail)
- (cons (reverse (if (string-null? head) cur (cons head cur)))
- out)))))
- (else
- (lp (cdr in)
- (cons (if (null? cur) (string-trim (car in)) (car in)) cur)
- out)))))
-
-(define (make-dom-parser)
- (make-command-parser
- (lambda (command args content seed) ; fdown
- '())
- (lambda (command args parent-seed seed) ; fup
- (let* ((seed (reverse-collect-str-drop-ws seed))
- (spec (command-spec command))
- (command (car spec)))
- (if (eq? (cadr spec) 'INLINE-TEXT-ARGS)
- (cons (list command (cons '% (parse-inline-text-args #f spec seed)))
- parent-seed)
- (acons command
- (if (null? args) seed (acons '% args seed))
- parent-seed))))
- (lambda (string1 string2 seed) ; str-handler
- (if (string-null? string2)
- (cons string1 seed)
- (cons* string2 string1 seed)))))
-
-(define parse-environment-args
- (let ((parser (make-dom-parser)))
- ;; duplicate arguments->attlist to avoid unnecessary splitting
- (lambda (command port)
- (let* ((args (cdar (parser '*ENVIRON-ARGS* port '())))
- (spec (command-spec command))
- (command (car spec))
- (arg-names (cddr spec)))
- (cond
- ((not arg-names)
- (if (null? args) '()
- (parser-error port "@-command doesn't take args" command)))
- ((eq? arg-names #t)
- (list (cons 'arguments args)))
- (else
- (let loop ((args args) (arg-names arg-names) (out '()))
- (cond
- ((null? arg-names)
- (if (null? args) (reverse! out)
- (parser-error port "@-command didn't expect more args"
- command args)))
- ((symbol? arg-names)
- (reverse! (acons arg-names args out)))
- ((null? args)
- (parser-error port "@-command expects more args"
- command arg-names))
- ((and (string? (car args)) (string-index (car args) #\space))
- => (lambda (i)
- (let ((rest (substring/shared (car args) (1+ i))))
- (if (zero? i)
- (loop (cons rest (cdr args)) arg-names out)
- (loop (cons rest (cdr args)) (cdr arg-names)
- (cons (list (car arg-names)
- (substring (car args) 0 i))
- out))))))
- (else
- (loop (cdr args) (cdr arg-names)
- (if (and (pair? (car args)) (eq? (caar args) '*braces*))
- (acons (car arg-names) (cdar args) out)
- (cons (list (car arg-names) (car args)) out))))))))))))
-
-(define (parse-eol-text-args command port)
- ;; perhaps parse-environment-args should be named more
- ;; generically.
- (parse-environment-args command port))
-
-;; procedure_ texi-fragment->stexi STRING
-;;
-;; A DOM parser for a texinfo fragment STRING.
-;;
-;; The procedure returns an SXML tree headed by the special tag,
-;; *fragment*.
-
-(define (texi-fragment->stexi string-or-port)
- "Parse the texinfo commands in @var{string-or-port}, and return the
-resultant stexi tree. The head of the tree will be the special command,
-@code{*fragment*}."
- (define (parse port)
- (postprocess (car ((make-dom-parser) '*fragment* port '()))))
- (if (input-port? string-or-port)
- (parse string-or-port)
- (call-with-input-string string-or-port parse)))
-
-;; procedure_ texi->stexi PORT
-;;
-;; This is an instance of a SSAX parser above that returns an SXML
-;; representation of the texinfo document ready to be read at PORT.
-;;
-;; The procedure returns an SXML tree. The port points to the
-;; first character after the @bye, or to the end of the file.
-
-(define (texi->stexi port)
- "Read a full texinfo document from @var{port} and return the parsed
-stexi tree. The parsing will start at the @code{@@settitle} and end at
-@code{@@bye} or EOF."
- (let ((parser (make-dom-parser)))
- (take-until-settitle port)
- (postprocess (car (parser 'texinfo port '())))))
-
-(define (car-eq? x y) (and (pair? x) (eq? (car x) y)))
-(define (make-contents tree)
- (define (lp in out depth)
- (cond
- ((null? in) (values in (cons 'enumerate (reverse! out))))
- ((and (pair? (cdr in)) (texi-command-depth (caadr in) 4))
- => (lambda (new-depth)
- (let ((node-name (and (car-eq? (car in) 'node)
- (cadr (assq 'name (cdadar in))))))
- (cond
- ((< new-depth depth)
- (values in (cons 'enumerate (reverse! out))))
- ((> new-depth depth)
- (let ((out-cdr (if (null? out) '() (cdr out)))
- (out-car (if (null? out) (list 'item) (car out))))
- (let*-values (((new-in new-out) (lp in '() (1+ depth))))
- (lp new-in
- (cons (append out-car (list new-out)) out-cdr)
- depth))))
- (else ;; same depth
- (lp (cddr in)
- (cons
- `(item (para
- ,@(if node-name
- `((ref (% (node ,node-name))))
- (cdadr in))))
- out)
- depth))))))
- (else (lp (cdr in) out depth))))
- (let*-values (((_ contents) (lp tree '() 1)))
- `((chapheading "Table of Contents") ,contents)))
-
-(define (trim-whitespace str trim-left? trim-right?)
- (let* ((left-space? (and (not trim-left?)
- (string-prefix? " " str)))
- (right-space? (and (not trim-right?)
- (string-suffix? " " str)))
- (tail (append! (string-tokenize str)
- (if right-space? '("") '()))))
- (string-join (if left-space? (cons "" tail) tail))))
-
-(define (postprocess tree)
- (define (loop in out state first? sig-ws?)
- (cond
- ((null? in)
- (values (reverse! out) state))
- ((string? (car in))
- (loop (cdr in)
- (cons (if sig-ws? (car in)
- (trim-whitespace (car in) first? (null? (cdr in))))
- out)
- state #f sig-ws?))
- ((pair? (car in))
- (case (caar in)
- ((set)
- (if (null? (cdar in)) (error "@set missing arguments" in))
- (if (string? (cadar in))
- (let ((i (string-index (cadar in) #\space)))
- (if i
- (loop (cdr in) out
- (acons (substring (cadar in) 0 i)
- (cons (substring (cadar in) (1+ i)) (cddar in))
- state)
- #f sig-ws?)
- (loop (cdr in) out (acons (cadar in) (cddar in) state)
- #f sig-ws?)))
- (error "expected a constant to define for @set" in)))
- ((value)
- (loop (fold-right cons (cdr in)
- (or (and=>
- (assoc (cadr (assq 'key (cdadar in))) state) cdr)
- (error "unknown value" (cdadar in) state)))
- out
- state #f sig-ws?))
- ((copying)
- (loop (cdr in) out (cons (car in) state) #f sig-ws?))
- ((insertcopying)
- (loop (fold-right cons (cdr in)
- (or (cdr (assoc 'copying state))
- (error "copying isn't set yet")))
- out
- state #f sig-ws?))
- ((contents)
- (loop (cdr in) (fold cons out (make-contents tree)) state #f sig-ws?))
- (else
- (let*-values (((kid-out state)
- (loop (car in) '() state #t
- (or sig-ws? (space-significant? (caar in))))))
- (loop (cdr in) (cons kid-out out) state #f sig-ws?)))))
- (else ; a symbol
- (loop (cdr in) (cons (car in) out) state #t sig-ws?))))
-
- (call-with-values
- (lambda () (loop tree '() '() #t #f))
- (lambda (out state) out)))
-
-;; Replace % with texinfo-arguments.
-(define (stexi->sxml tree)
- "Transform the stexi tree @var{tree} into sxml. This involves
-replacing the @code{%} element that keeps the texinfo arguments with an
-element for each argument.
-
-FIXME_ right now it just changes % to @code{texinfo-arguments} -- that
-doesn't hang with the idea of making a dtd at some point"
- (pre-post-order
- tree
- `((% . ,(lambda (x . t) (cons 'texinfo-arguments t)))
- (*text* . ,(lambda (x t) t))
- (*default* . ,(lambda (x . t) (cons x t))))))
-
-;;; arch-tag_ 73890afa-597c-4264-ae70-46fe7756ffb5
-;;; texinfo.scm ends here
-;;;; (texinfo docbook) -- translating sdocbook into stexinfo
-;;;;
-;;;; Copyright (C) 2009, 2010, 2012 Free Software Foundation, Inc.
-;;;; Copyright (C) 2007, 2009 Andy Wingo <wingo at pobox dot com>
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary_
-;;
-;; @c
-;; This module exports procedures for transforming a limited subset of
-;; the SXML representation of docbook into stexi. It is not complete by
-;; any means. The intention is to gather a number of routines and
-;; stylesheets so that external modules can parse specific subsets of
-;; docbook, for example that set generated by certain tools.
-;;
-;;; Code_
-
-(define-module (texinfo docbook)
- #\use-module (sxml fold)
- #\use-module ((srfi srfi-1) #\select (fold))
- #\export (*sdocbook->stexi-rules*
- *sdocbook-block-commands*
- sdocbook-flatten
- filter-empty-elements
- replace-titles))
-
-(define (identity . args)
- args)
-
-(define (identity-deattr tag . body)
- `(,tag ,@(if (and (pair? body) (pair? (car body))
- (eq? (caar body) '@))
- (cdr body)
- body)))
-
-(define (detag-one tag body)
- body)
-
-(define tag-replacements
- '((parameter var)
- (replaceable var)
- (type code)
- (function code)
- (literal samp)
- (emphasis emph)
- (simpara para)
- (programlisting example)
- (firstterm dfn)
- (filename file)
- (quote cite)
- (application cite)
- (symbol code)
- (note cartouche)
- (envar env)))
-
-(define ignore-list '())
-
-(define (stringify exp)
- (with-output-to-string (lambda () (write exp))))
-
-(define *sdocbook->stexi-rules*
- ;~
- "A stylesheet for use with SSAX's @code{pre-post-order}, which defines
-a number of generic rules for transforming docbook into texinfo."
- `((@ *preorder* . ,identity)
- (% *preorder* . ,identity)
- (para . ,identity-deattr)
- (orderedlist ((listitem
- . ,(lambda (tag . body)
- `(item ,@body))))
- . ,(lambda (tag . body)
- `(enumerate ,@body)))
- (itemizedlist ((listitem
- . ,(lambda (tag . body)
- `(item ,@body))))
- . ,(lambda (tag . body)
- `(itemize ,@body)))
- (acronym . ,(lambda (tag . body)
- `(acronym (% (acronym . ,body)))))
- (term . ,detag-one)
- (informalexample . ,detag-one)
- (section . ,identity)
- (subsection . ,identity)
- (subsubsection . ,identity)
- (ulink . ,(lambda (tag attrs . body)
- (cond
- ((assq 'url (cdr attrs))
- => (lambda (url)
- `(uref (% ,url (title ,@body)))))
- (else
- (car body)))))
- (*text* . ,detag-one)
- (*default* . ,(lambda (tag . body)
- (let ((subst (assq tag tag-replacements)))
- (cond
- (subst
- (if (and (pair? body) (pair? (car body)) (eq? (caar body) '@))
- (begin
- (warn "Ignoring" tag "attributes" (car body))
- (append (cdr subst) (cdr body)))
- (append (cdr subst) body)))
- ((memq tag ignore-list) #f)
- (else
- (warn "Don't know how to convert" tag "to stexi")
- `(c (% (all ,(stringify (cons tag body))))))))))))
-
-;; (variablelist
-;; ((varlistentry
-;; . ,(lambda (tag term . body)
-;; `(entry (% (heading ,@(cdr term))) ,@body)))
-;; (listitem
-;; . ,(lambda (tag simpara)
-;; simpara)))
-;; . ,(lambda (tag attrs . body)
-;; `(table (% (formatter (var))) ,@body)))
-
-(define *sdocbook-block-commands*
- ;~
- "The set of sdocbook element tags that should not be nested inside
-each other. @xref{texinfo docbook sdocbook-flatten,,sdocbook-flatten},
-for more information."
- '(para programlisting informalexample indexterm variablelist
- orderedlist refsect1 refsect2 refsect3 refsect4 title example
- note itemizedlist informaltable))
-
-(define (inline-command? command)
- (not (memq command *sdocbook-block-commands*)))
-
-(define (sdocbook-flatten sdocbook)
- "\"Flatten\" a fragment of sdocbook so that block elements do not nest
-inside each other.
-
-Docbook is a nested format, where e.g. a @code{refsect2} normally
-appears inside a @code{refsect1}. Logical divisions in the document are
-represented via the tree topology; a @code{refsect2} element
-@emph{contains} all of the elements in its section.
-
-On the contrary, texinfo is a flat format, in which sections are marked
-off by standalone section headers like @code{@@chapter}, and block
-elements do not nest inside each other.
-
-This function takes a nested sdocbook fragment @var{sdocbook} and
-flattens all of the sections, such that e.g.
-@example
- (refsect1 (refsect2 (para \"Hello\")))
-@end example
-becomes
-@example
- ((refsect1) (refsect2) (para \"Hello\"))
-@end example
-
-Oftentimes (always?) sectioning elements have @code{<title>} as their
-first element child; users interested in processing the @code{refsect*}
-elements into proper sectioning elements like @code{chapter} might be
-interested in @code{replace-titles} and @code{filter-empty-elements}.
-@xref{texinfo docbook replace-titles,,replace-titles}, and @ref{texinfo
-docbook filter-empty-elements,,filter-empty-elements}.
-
-Returns a nodeset, as described in @ref{sxml xpath}. That is to say,
-this function returns an untagged list of stexi elements."
- (define (fhere str accum block cont)
- (values (cons str accum)
- block
- cont))
- (define (fdown node accum block cont)
- (let ((command (car node))
- (attrs (and (pair? (cdr node)) (pair? (cadr node))
- (eq? (caadr node) '%)
- (cadr node))))
- (values (if attrs (cddr node) (cdr node))
- '()
- '()
- (lambda (accum block)
- (values
- `(,command ,@(if attrs (list attrs) '())
- ,@(reverse accum))
- block)))))
- (define (fup node paccum pblock pcont kaccum kblock kcont)
- (call-with-values (lambda () (kcont kaccum kblock))
- (lambda (ret block)
- (if (inline-command? (car ret))
- (values (cons ret paccum) (append kblock pblock) pcont)
- (values paccum (append kblock (cons ret pblock)) pcont)))))
- (call-with-values
- (lambda () (foldts*-values fdown fup fhere sdocbook '() '() #f))
- (lambda (accum block cont)
- (reverse block))))
-
-(define (filter-empty-elements sdocbook)
- "Filters out empty elements in an sdocbook nodeset. Mostly useful
-after running @code{sdocbook-flatten}."
- (reverse
- (fold
- (lambda (x rest)
- (if (and (pair? x) (null? (cdr x)))
- rest
- (cons x rest)))
- '()
- sdocbook)))
-
-(define (replace-titles sdocbook-fragment)
- "Iterate over the sdocbook nodeset @var{sdocbook-fragment},
-transforming contiguous @code{refsect} and @code{title} elements into
-the appropriate texinfo sectioning command. Most useful after having run
-@code{sdocbook-flatten}.
-
-For example_
-@example
- (replace-titles '((refsect1) (title \"Foo\") (para \"Bar.\")))
- @result{} '((chapter \"Foo\") (para \"Bar.\"))
-@end example
-"
- (define sections '((refsect1 . chapter)
- (refsect2 . section)
- (refsect3 . subsection)
- (refsect4 . subsubsection)))
- (let lp ((in sdocbook-fragment) (out '()))
- (cond
- ((null? in)
- (reverse out))
- ((and (pair? (car in)) (assq (caar in) sections))
- ;; pull out the title
- => (lambda (pair)
- (lp (cddr in) (cons `(,(cdr pair) ,@(cdadr in)) out))))
- (else
- (lp (cdr in) (cons (car in) out))))))
-;;;; (texinfo html) -- translating stexinfo into shtml
-;;;;
-;;;; Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
-;;;; Copyright (C) 2003,2004,2009 Andy Wingo <wingo at pobox dot com>
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary_
-;;
-;;This module implements transformation from @code{stexi} to HTML. Note
-;;that the output of @code{stexi->shtml} is actually SXML with the HTML
-;;vocabulary. This means that the output can be further processed, and
-;;that it must eventually be serialized by
-;;@ref{sxml simple sxml->xml,sxml->xml}.
-;;
-;;References (i.e., the @code{@@ref} family of commands) are resolved by
-;;a @dfn{ref-resolver}.
-;;@xref{texinfo html add-ref-resolver!,add-ref-resolver!}, for more
-;;information.
-;;
-;;; Code_
-
-;; TODO_ nice ref resolving API, default CSS stylesheet (esp. to remove
-;; margin-top on dd > p)
-
-(define-module (texinfo html)
- #\use-module (texinfo)
- #\use-module (sxml transform)
- #\use-module (ice-9 match)
- #\use-module (srfi srfi-13)
- #\export (stexi->shtml add-ref-resolver! urlify))
-
-;; The caller is responsible for carring the returned list.
-(define (arg-ref key %-args)
- (and=> (assq key (cdr %-args)) (lambda (x) (stexi->shtml (cdr x)))))
-(define (arg-req key %-args)
- (or (arg-ref key %-args)
- (error "Missing argument_" key %-args)))
-(define (car* x) (and x (car x)))
-
-(define (urlify str)
- (string-downcase
- (string-map
- (lambda (c)
- (case c
- ((#\space #\/ #\_) #\-)
- (else c)))
- str)))
-
-(define ref-resolvers
- (list
- (lambda (node-name manual-name) ;; the default
- (urlify (string-append (or manual-name "") "#" node-name)))))
-
-(define (add-ref-resolver! proc)
- "Add @var{proc} to the head of the list of ref-resolvers. @var{proc}
-will be expected to take the name of a node and the name of a manual and
-return the URL of the referent, or @code{#f} to pass control to the next
-ref-resolver in the list.
-
-The default ref-resolver will return the concatenation of the manual
-name, @code{#}, and the node name."
- (set! ref-resolvers (cons proc ref-resolvers)))
-
-(define (resolve-ref node manual)
- (or (or-map (lambda (x) (x node manual)) ref-resolvers)
- (error "Could not resolve reference" node manual)))
-
-(define (ref tag args)
- (let* ((node (car (arg-req 'node args)))
- (section (or (car* (arg-ref 'section args)) node))
- (manual (car* (arg-ref 'manual args)))
- (target (resolve-ref node manual)))
- `(span ,(and=> (assq tag '((xref "See ") (pxref "see "))) cdr)
- (a (@ (href ,target)) ,section))))
-
-(define (uref tag args)
- (let ((url (car (arg-req 'url args))))
- `(a (@ (href ,url)) ,(or (car* (arg-ref 'title args)) url))))
-
-;; @!*&%( Mozilla gets confused at an empty ("<a .. />") a tag. Put an
-;; empty string here to placate the reptile.
-(define (node tag args)
- `(a (@ (name ,(urlify (car (arg-req 'name args))))) ""))
-
-(define (def tag args . body)
- (define (code x) (and x (cons 'code x)))
- (define (var x) (and x (cons 'var x)))
- (define (b x) (and x (cons 'b x)))
- (define (list/spaces . elts)
- (let lp ((in elts) (out '()))
- (cond ((null? in) (reverse! out))
- ((null? (car in)) (lp (cdr in) out))
- (else (lp (cdr in)
- (cons (car in)
- (if (null? out) out (cons " " out))))))))
- (define (left-td-contents)
- (list/spaces (code (arg-ref 'data-type args))
- (b (list (code (arg-ref 'class args)))) ;; is this right?
- (b (list (code (arg-ref 'name args))))
- (if (memq tag '(deftypeop deftypefn deftypefun))
- (code (arg-ref 'arguments args))
- (var (list (code (arg-ref 'arguments args)))))))
-
- (let* ((category (case tag
- ((defun) "Function")
- ((defspec) "Special Form")
- ((defvar) "Variable")
- (else (car (arg-req 'category args))))))
- `(div
- (table
- (@ (cellpadding "0") (cellspacing "0") (width "100%") (class "def"))
- (tr (td ,@(left-td-contents))
- (td (div (@ (class "right")) "[" ,category "]"))))
- (div (@ (class "description")) ,@body))))
-
-(define (enumerate tag . elts)
- (define (tonumber start)
- (let ((c (string-ref start 0)))
- (cond ((number? c) (string->number start))
- (else (1+ (- (char->integer c)
- (char->integer (if (char-upper-case? c) #\A #\a))))))))
- `(ol ,@(if (and (pair? elts) (pair? (car elts)) (eq? (caar elts) '%))
- (cons `(@ (start ,@(tonumber (arg-req 'start (car elts)))))
- ;; (type ,(type (arg-ref 'start (car elts)))))
- (cdr elts))
- elts)))
-
-(define (itemize tag . elts)
- `(ul ,@(match elts
- ;; Strip `bullet' attribute.
- ((('% . attrs) . elts) elts)
- (elts elts))))
-
-(define (acronym tag . elts)
- (match elts
- ;; FIXME_ Need attribute matcher that doesn't depend on attribute
- ;; order.
- ((('% ('acronym text) . _)) `(acronym ,text))))
-
-(define (table tag args . body)
- (let ((formatter (caar (arg-req 'formatter args))))
- (cons 'dl
- (map (lambda (x)
- (cond ((and (pair? x) (eq? (car x) 'dt))
- (list (car x) (cons formatter (cdr x))))
- (else x)))
- (apply append body)))))
-
-(define (entry tag args . body)
- (let lp ((out `((dt ,@(arg-req 'heading args))))
- (body body))
- (if (and (pair? body) (pair? (car body)) (eq? (caar body) 'itemx))
- (lp (append out `(dt ,@(map stexi->shtml (cdar body))))
- (cdr body))
- (append out `((dd ,@(map stexi->shtml body)))))))
-
-(define tag-replacements
- '((titlepage div (@ (class "titlepage")))
- (title h2 (@ (class "title")))
- (subtitle h3 (@ (class "subtitle")))
- (author h3 (@ (class "author")))
- (example pre)
- (lisp pre)
- (smallexample pre (@ (class "smaller")))
- (smalllisp pre (@ (class "smaller")))
- (cartouche div (@ (class "cartouche")))
- (verbatim pre (@ (class "verbatim")))
- (chapter h2)
- (section h3)
- (subsection h4)
- (subsubsection h5)
- (appendix h2)
- (appendixsec h3)
- (appendixsubsec h4)
- (appendixsubsubsec h5)
- (unnumbered h2)
- (unnumberedsec h3)
- (unnumberedsubsec h4)
- (unnumberedsubsubsec h5)
- (majorheading h2)
- (chapheading h2)
- (heading h3)
- (subheading h4)
- (subsubheading h5)
- (quotation blockquote)
- (item li) ;; itemx ?
- (para p)
- (*fragment* div) ;; should be ok
-
- (asis span)
- (bold b)
- (sample samp)
- (samp samp)
- (code code)
- (kbd kbd)
- (key code (@ (class "key")))
- (var var)
- (env code (@ (class "env")))
- (file code (@ (class "file")))
- (command code (@ (class "command")))
- (option code (@ (class "option")))
- (url code (@ (class "url")))
- (dfn dfn)
- (cite cite)
- (acro acronym)
- (email code (@ (class "email")))
- (emph em)
- (strong strong)
- (sc span (@ (class "small-caps")))))
-
-(define ignore-list
- '(page setfilename setchapternewpage iftex ifinfo ifplaintext ifxml sp vskip
- menu ignore syncodeindex comment c dircategory direntry top shortcontents
- cindex printindex))
-
-(define rules
- `((% *preorder* . ,(lambda args args)) ;; Keep these around...
- (texinfo . ,(lambda (tag args . body)
- (pre-post-order
- `(html
- (@ (xmlns "http_//www.w3.org/1999/xhtml"))
- (head (title ,(car (arg-req 'title args))))
- (body ,@body))
- `((% *preorder* . ,(lambda args #f)) ;; ... filter out.
- (*text* . ,(lambda (tag x) x))
- (*default* . ,(lambda (tag . body)
- (cons tag body)))))))
- (copyright . ,(lambda args '(*ENTITY* "copy")))
- (result . ,(lambda args '(*ENTITY* "rArr")))
- (xref . ,ref) (ref . ,ref) (pxref . ,ref)
- (uref . ,uref)
- (node . ,node) (anchor . ,node)
- (table . ,table)
- (enumerate . ,enumerate)
- (itemize . ,itemize)
- (acronym . ,acronym)
- (entry *preorder* . ,entry)
-
- (deftp . ,def) (defcv . ,def) (defivar . ,def) (deftypeivar . ,def)
- (defop . ,def) (deftypeop . ,def) (defmethod . ,def)
- (deftypemethod . ,def) (defopt . ,def) (defvr . ,def) (defvar . ,def)
- (deftypevr . ,def) (deftypevar . ,def) (deffn . ,def)
- (deftypefn . ,def) (defmac . ,def) (defspec . ,def) (defun . ,def)
- (deftypefun . ,def)
- (ifnottex . ,(lambda (tag . body) body))
- (*text* . ,(lambda (tag x) x))
- (*default* . ,(lambda (tag . body)
- (let ((subst (assq tag tag-replacements)))
- (cond
- (subst (append (cdr subst) body))
- ((memq tag ignore-list) #f)
- (else
- (warn "Don't know how to convert" tag "to HTML")
- body)))))))
-
-(define (stexi->shtml tree)
- "Transform the stexi @var{tree} into shtml, resolving references via
-ref-resolvers. See the module commentary for more details."
- (pre-post-order tree rules))
-
-;;; arch-tag_ ab05f3fe-9981-4a78-b64c-48efcd9983a6
-;;;; (texinfo indexing) -- indexing stexinfo
-;;;;
-;;;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
-;;;; Copyright (C) 2003,2004,2009 Andy Wingo <wingo at pobox dot com>
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary_
-;;
-;;@c texinfo formatting
-;;Given a piece of stexi, return an index of a specified variety.
-;;
-;;Note that currently, @code{stexi-extract-index} doesn't differentiate
-;;between different kinds of index entries. That's a bug ;)
-;;; Code_
-
-(define-module (texinfo indexing)
- #\use-module (sxml simple)
- #\use-module (srfi srfi-13)
- #\export (stexi-extract-index))
-
-(define defines
- '(deftp defcv defivar deftypeivar defop deftypeop defmethod
- deftypemethod defopt defvr defvar deftypevr deftypevar deffn
- deftypefn defspec defmac defun deftypefun))
-
-(define indices
- '(cindex findex vindex kindex pindex tindex))
-
-(define (stexi-extract-index tree manual-name kind)
- "Given an stexi tree @var{tree}, index all of the entries of type
-@var{kind}. @var{kind} can be one of the predefined texinfo indices
-(@code{concept}, @code{variable}, @code{function}, @code{key},
-@code{program}, @code{type}) or one of the special symbols @code{auto}
-or @code{all}. @code{auto} will scan the stext for a @code{(printindex)}
-statement, and @code{all} will generate an index from all entries,
-regardless of type.
-
-The returned index is a list of pairs, the @sc{car} of which is the
-entry (a string) and the @sc{cdr} of which is a node name (a string)."
- (let loop ((in tree) (entries '()))
- (cond
- ((null? in)
- entries)
- ((pair? (car in))
- (cond
- ((and (pair? (cdr in)) (pair? (cadr in))
- (eq? (caar in) 'anchor) (memq (caadr in) defines))
- (loop (cddr in) (acons (cadr (assq 'name (cdr (cadadr in))))
- (cadr (assq 'name (cdadar in)))
- entries)))
- ((and (pair? (cdr in)) (pair? (cadr in))
- (eq? (caar in) 'anchor) (memq (caadr in) indices))
- (loop (cddr in) (acons (sxml->string (cadr in))
- (cadr (assq 'name (cdadar in)))
- entries)))
- (else
- (loop (cdr in) (loop (car in) entries)))))
- (else
- (loop (cdr in) entries)))))
-
-;;; arch-tag_ 216d29d3-1ed9-433f-9c19-0dc4d6b439b6
-;;;; (texinfo plain-text) -- rendering stexinfo as plain text
-;;;;
-;;;; Copyright (C) 2009, 2010, 2011, 2013 Free Software Foundation, Inc.
-;;;; Copyright (C) 2003,2004,2009 Andy Wingo <wingo at pobox dot com>
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary_
-;;
-;;Transformation from stexi to plain-text. Strives to re-create the
-;;output from @code{info}; comes pretty damn close.
-;;
-;;; Code_
-
-(define-module (texinfo plain-text)
- #\use-module (texinfo)
- #\use-module (texinfo string-utils)
- #\use-module (sxml transform)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-13)
- #\use-module (ice-9 match)
- #\export (stexi->plain-text))
-
-;; The return value is a string.
-(define (arg-ref key %-args)
- (and=> (and=> (assq key (cdr %-args)) cdr)
- stexi->plain-text))
-(define (arg-req key %-args)
- (or (arg-ref key %-args)
- (error "Missing argument_" key %-args)))
-
-(define (make-ticker str)
- (lambda () str))
-(define (make-enumerator n)
- (lambda ()
- (let ((last n))
- (set! n (1+ n))
- (format #f "~A. " last))))
-
-(define *indent* (make-fluid ""))
-(define *itemizer* (make-fluid (make-ticker "* ")))
-
-(define-macro (with-indent n . body)
- `(with-fluids ((*indent* (string-append (fluid-ref *indent*)
- (make-string ,n #\space))))
- ,@body))
-
-(define (make-indenter n proc)
- (lambda args (with-indent n (apply proc args))))
-
-(define (string-indent str)
- (string-append (fluid-ref *indent*) str "\n"))
-
-(define-macro (with-itemizer itemizer . body)
- `(with-fluids ((*itemizer* ,itemizer))
- ,@body))
-
-(define (wrap* . strings)
- (let ((indent (fluid-ref *indent*)))
- (fill-string (string-concatenate strings)
- #\line-width 72 #\initial-indent indent
- #\subsequent-indent indent)))
-(define (wrap . strings)
- (string-append (apply wrap* strings) "\n\n"))
-(define (wrap-heading . strings)
- (string-append (apply wrap* strings) "\n"))
-
-(define (ref tag args)
- (let* ((node (arg-req 'node args))
- (name (or (arg-ref 'name args) node))
- (manual (arg-ref 'manual args)))
- (string-concatenate
- (cons*
- (or (and=> (assq tag '((xref "See ") (pxref "see "))) cadr) "")
- name
- (if manual `(" in manual " ,manual) '())))))
-
-(define (uref tag args)
- (let ((url (arg-req 'url args))
- (title (arg-ref 'title args)))
- (if title
- (string-append title " (" url ")")
- (string-append "`" url "'"))))
-
-(define (def tag args . body)
- (define (first-line)
- (string-join
- (filter identity
- (map (lambda (x) (arg-ref x args))
- '(data-type class name arguments)))
- " "))
-
- (let* ((category (case tag
- ((defun) "Function")
- ((defspec) "Special Form")
- ((defvar) "Variable")
- (else (arg-req 'category args)))))
- (string-append
- (wrap-heading (string-append " - " category "_ " (first-line)))
- (with-indent 5 (stexi->plain-text body)))))
-
-(define (enumerate tag . elts)
- (define (tonumber start)
- (let ((c (string-ref start 0)))
- (cond ((number? c) (string->number start))
- (else (1+ (- (char->integer c)
- (char->integer (if (char-upper-case? c) #\A #\a))))))))
- (let* ((args? (and (pair? elts) (pair? (car elts))
- (eq? (caar elts) '%)))
- (start (and args? (arg-ref 'start (car elts)))))
- (with-itemizer (make-enumerator (if start (tonumber start) 1))
- (with-indent 5
- (stexi->plain-text (if start (cdr elts) elts))))))
-
-(define (itemize tag args . elts)
- (with-itemizer (make-ticker "* ")
- (with-indent 5
- (stexi->plain-text elts))))
-
-(define (item tag . elts)
- (let* ((ret (stexi->plain-text elts))
- (tick ((fluid-ref *itemizer*)))
- (tick-pos (- (string-length (fluid-ref *indent*))
- (string-length tick))))
- (if (and (not (string-null? ret)) (not (negative? tick-pos)))
- (string-copy! ret tick-pos tick))
- ret))
-
-(define (table tag args . body)
- (stexi->plain-text body))
-
-(define (entry tag args . body)
- (let ((heading (wrap-heading
- (stexi->plain-text (arg-req 'heading args)))))
- (string-append heading
- (with-indent 5 (stexi->plain-text body)))))
-
-(define (make-underliner char)
- (lambda (tag . body)
- (let ((str (stexi->plain-text body)))
- (string-append
- "\n"
- (string-indent str)
- (string-indent (make-string (string-length str) char))
- "\n"))))
-
-(define chapter (make-underliner #\*))
-(define section (make-underliner #\=))
-(define subsection (make-underliner #\-))
-(define subsubsection (make-underliner #\.))
-
-(define (example tag . body)
- (let ((ret (stexi->plain-text body)))
- (string-append
- (string-concatenate
- (with-indent 5 (map string-indent (string-split ret #\newline))))
- "\n")))
-
-(define (verbatim tag . body)
- (let ((ret (stexi->plain-text body)))
- (string-append
- (string-concatenate
- (map string-indent (string-split ret #\newline)))
- "\n")))
-
-(define (fragment tag . body)
- (string-concatenate (map-in-order stexi->plain-text body)))
-
-(define (para tag . body)
- (wrap (stexi->plain-text body)))
-
-(define (make-surrounder str)
- (lambda (tag . body)
- (string-append str (stexi->plain-text body) str)))
-
-(define (code tag . body)
- (string-append "`" (stexi->plain-text body) "'"))
-
-(define (key tag . body)
- (string-append "<" (stexi->plain-text body) ">"))
-
-(define (var tag . body)
- (string-upcase (stexi->plain-text body)))
-
-(define (passthrough tag . body)
- (stexi->plain-text body))
-
-(define (texinfo tag args . body)
- (let ((title (chapter 'foo (arg-req 'title args))))
- (string-append title (stexi->plain-text body))))
-
-(define ignore-list
- '(page setfilename setchapternewpage iftex ifinfo ifplaintext ifxml sp vskip
- menu ignore syncodeindex comment c % node anchor))
-(define (ignored? tag)
- (memq tag ignore-list))
-
-(define tag-handlers
- `((title ,chapter)
- (chapter ,chapter)
- (section ,section)
- (subsection ,subsection)
- (subsubsection ,subsubsection)
- (appendix ,chapter)
- (appendixsec ,section)
- (appendixsubsec ,subsection)
- (appendixsubsubsec ,subsubsection)
- (unnumbered ,chapter)
- (unnumberedsec ,section)
- (unnumberedsubsec ,subsection)
- (unnumberedsubsubsec ,subsubsection)
- (majorheading ,chapter)
- (chapheading ,chapter)
- (heading ,section)
- (subheading ,subsection)
- (subsubheading ,subsubsection)
-
- (strong ,(make-surrounder "*"))
- (sample ,code)
- (samp ,code)
- (code ,code)
- (math ,passthrough)
- (kbd ,code)
- (key ,key)
- (var ,var)
- (env ,code)
- (file ,code)
- (command ,code)
- (option ,code)
- (url ,code)
- (dfn ,(make-surrounder "\""))
- (cite ,(make-surrounder "\""))
- (acro ,passthrough)
- (email ,key)
- (emph ,(make-surrounder "_"))
- (sc ,var)
- (copyright ,(lambda args "(C)"))
- (result ,(lambda args "==>"))
- (dots ,(lambda args "..."))
- (xref ,ref)
- (ref ,ref)
- (pxref ,ref)
- (uref ,uref)
-
- (texinfo ,texinfo)
- (quotation ,(make-indenter 5 para))
- (itemize ,itemize)
- (enumerate ,enumerate)
- (item ,item)
- (table ,table)
- (entry ,entry)
- (example ,example)
- (lisp ,example)
- (smallexample ,example)
- (smalllisp ,example)
- (verbatim ,verbatim)
- (*fragment* ,fragment)
-
- (deftp ,def)
- (defcv ,def)
- (defivar ,def)
- (deftypeivar ,def)
- (defop ,def)
- (deftypeop ,def)
- (defmethod ,def)
- (deftypemethod ,def)
- (defopt ,def)
- (defvr ,def)
- (defvar ,def)
- (deftypevr ,def)
- (deftypevar ,def)
- (deffn ,def)
- (deftypefn ,def)
- (defmac ,def)
- (defspec ,def)
- (defun ,def)
- (deftypefun ,def)))
-
-(define (stexi->plain-text tree)
- "Transform @var{tree} into plain text. Returns a string."
- (match tree
- (() "")
- ((? string?) tree)
- (((? symbol? tag) body ...)
- (let ((handler (and (not (ignored? tag))
- (or (and=> (assq tag tag-handlers) cadr)
- para))))
- (if handler
- (apply handler tree)
- "")))
- ((tree ...)
- (string-concatenate (map-in-order stexi->plain-text tree)))
- (_ "")))
-
-;;; arch-tag_ f966c3f6-3b46-4790-bbf9-3ad27e4917c2
-;;;; (texinfo reflection) -- documenting Scheme as stexinfo
-;;;;
-;;;; Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
-;;;; Copyright (C) 2003,2004,2009 Andy Wingo <wingo at pobox dot com>
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary_
-;;
-;;Routines to generare @code{stexi} documentation for objects and
-;;modules.
-;;
-;;Note that in this context, an @dfn{object} is just a value associated
-;;with a location. It has nothing to do with GOOPS.
-;;
-;;; Code_
-
-(define-module (texinfo reflection)
- #\use-module ((srfi srfi-1) #\select (append-map))
- #\use-module (oop goops)
- #\use-module (texinfo)
- #\use-module (texinfo plain-text)
- #\use-module (srfi srfi-13)
- #\use-module (ice-9 session)
- #\use-module (ice-9 documentation)
- #\use-module (ice-9 optargs)
- #\use-module ((sxml transform) #\select (pre-post-order))
- #\export (module-stexi-documentation
- script-stexi-documentation
- object-stexi-documentation
- package-stexi-standard-copying
- package-stexi-standard-titlepage
- package-stexi-generic-menu
- package-stexi-standard-menu
- package-stexi-extended-menu
- package-stexi-standard-prologue
- package-stexi-documentation
- package-stexi-documentation-for-include))
-
-;; List for sorting the definitions in a module
-(define defs
- '(deftp defcv defivar deftypeivar defop deftypeop defmethod
- deftypemethod defopt defvr defvar deftypevr deftypevar deffn
- deftypefn defmac defspec defun deftypefun))
-
-(define (sort-defs ordering a b)
- (define (def x)
- ;; a and b are lists of the form ((anchor ...) (def* ...)...)
- (cadr x))
- (define (name x)
- (cadr (assq 'name (cdadr (def x)))))
- (define (priority x)
- (list-index defs (car (def x))))
- (define (order x)
- (or (list-index ordering (string->symbol (name x)))
- ;; if the def is not in the list, a big number
- 1234567890))
- (define (compare-in-order proc eq? < . args)
- (if (not (eq? (proc a) (proc b)))
- (< (proc a) (proc b))
- (or (null? args)
- (apply compare-in-order args))))
- (compare-in-order order = <
- priority = <
- name string=? string<=?))
-
-(define (list*-join l infix restfix)
- (let lp ((in l) (out '()))
- (cond ((null? in) (reverse! out))
- ((symbol? in) (reverse! (cons* in restfix out)))
- (else (lp (cdr in) (if (null? out)
- (list (car in))
- (cons* (car in) infix out)))))))
-
-(define (process-args args)
- (map (lambda (x) (if (string? x) x (object->string x)))
- (list*-join (or args '())
- " " " . ")))
-
-(define (get-proc-args proc)
- (cond
- ((procedure-arguments proc)
- => (lambda (args)
- (let ((required-args (assq-ref args 'required))
- (optional-args (assq-ref args 'optional))
- (keyword-args (assq-ref args 'keyword))
- (rest-arg (assq-ref args 'rest)))
- (process-args
- (append
- ;; start with the required args...
- (map symbol->string required-args)
-
- ;; add any optional args if needed...
- (map (lambda (a)
- (if (list? a)
- (format #f "[~a = ~s]" (car a) (cadr a))
- (format #f "[~a]" a)))
- optional-args)
-
- ;; now the keyword args..
- (map (lambda (a)
- (if (pair? a)
- (format #f "[~a]" (car a))
- (format #f "[#:~a]" a)))
- keyword-args)
-
- ;; now the rest arg...
- (if rest-arg
- (list "." (symbol->string rest-arg))
- '()))))))))
-
-(define (macro-arguments name type transformer)
- (process-args
- (case type
- ((syntax-rules)
- (let ((patterns (procedure-property transformer 'patterns)))
- (if (pair? patterns)
- (car patterns)
- '())))
- ((identifier-syntax)
- '())
- ((defmacro)
- (or (procedure-property transformer 'defmacro-args)
- '()))
- (else
- ;; a procedural (syntax-case) macro. how to document these?
- '()))))
-
-(define (macro-additional-stexi name type transformer)
- (case type
- ((syntax-rules)
- (let ((patterns (procedure-property transformer 'patterns)))
- (if (pair? patterns)
- (map (lambda (x)
- `(defspecx (% (name ,name)
- (arguments ,@(process-args x)))))
- (cdr patterns))
- '())))
- (else
- '())))
-
-(define many-space? (make-regexp "[[_space_]][[_space_]][[_space_]]"))
-(define initial-space? (make-regexp "^[[_space_]]"))
-(define (string->stexi str)
- (or (and (or (not str) (string-null? str))
- '(*fragment*))
- (and (or (string-index str #\@)
- (and (not (regexp-exec many-space? str))
- (not (regexp-exec initial-space? str))))
- (false-if-exception
- (texi-fragment->stexi str)))
- `(*fragment* (verbatim ,str))))
-
-(define method-formals
- (and (defined? 'method-formals) method-formals))
-
-(define (method-stexi-arguments method)
- (cond
- (method-formals
- (let lp ((formals (method-formals method))
- (specializers (method-specializers method))
- (out '()))
- (define (arg-texinfo formal specializer)
- `(" (" (var ,(symbol->string formal)) " "
- (code ,(symbol->string (class-name specializer))) ")"))
- (cond
- ((null? formals) (reverse out))
- ((pair? formals)
- (lp (cdr formals) (cdr specializers)
- (append (reverse (arg-texinfo (car formals) (car specializers)))
- out)))
- (else
- (append (reverse out) (arg-texinfo formals specializers)
- (list "..."))))))
- ((method-source method)
- (let lp ((bindings (cadr (method-source method))) (out '()))
- (define (arg-texinfo arg)
- `(" (" (var ,(symbol->string (car arg))) " "
- (code ,(symbol->string (cadr arg))) ")"))
- (cond
- ((null? bindings)
- (reverse out))
- ((not (pair? (car bindings)))
- (append (reverse out) (arg-texinfo bindings) (list "...")))
- (else
- (lp (cdr bindings)
- (append (reverse (arg-texinfo (car bindings))) out))))))
- (else (warn method) '())))
-
-(define* (object-stexi-documentation object #\optional (name "[unknown]")
- #\key (force #f))
- (if (symbol? name)
- (set! name (symbol->string name)))
- (let ((stexi ((lambda (x)
- (cond ((string? x) (string->stexi x))
- ((and (pair? x) (eq? (car x) '*fragment*)) x)
- (force `(*fragment*))
- (else #f)))
- (object-documentation
- (if (is-a? object <method>)
- (method-procedure object)
- object)))))
- (define (make-def type args)
- `(,type (% ,@args) ,@(cdr stexi)))
- (cond
- ((not stexi) #f)
- ;; stexi is now a list, headed by *fragment*.
- ((and (pair? (cdr stexi)) (pair? (cadr stexi))
- (memq (caadr stexi) defs))
- ;; it's already a deffoo.
- stexi)
- ((is-a? object <class>)
- (make-def 'deftp `((name ,name)
- (category "Class"))))
- ((is-a? object <macro>)
- (let* ((proc (macro-transformer object))
- (type (and proc (procedure-property proc 'macro-type))))
- `(defspec (% (name ,name)
- (arguments ,@(macro-arguments name type proc)))
- ,@(macro-additional-stexi name type proc)
- ,@(cdr stexi))))
-
- ((is-a? object <procedure>)
- (make-def 'defun `((name ,name)
- (arguments ,@(get-proc-args object)))))
- ((is-a? object <method>)
- (make-def 'deffn `((category "Method")
- (name ,name)
- (arguments ,@(method-stexi-arguments object)))))
- ((is-a? object <generic>)
- `(*fragment*
- ,(make-def 'deffn `((name ,name)
- (category "Generic")))
- ,@(map
- (lambda (method)
- (object-stexi-documentation method name #\force force))
- (generic-function-methods object))))
- (else
- (make-def 'defvar `((name ,name)))))))
-
-(define (module-name->node-name sym-name)
- (string-join (map symbol->string sym-name) " "))
-
-;; this copied from (ice-9 session); need to find a better way
-(define (module-filename name)
- (let* ((name (map symbol->string name))
- (reverse-name (reverse name))
- (leaf (car reverse-name))
- (dir-hint-module-name (reverse (cdr reverse-name)))
- (dir-hint (apply string-append
- (map (lambda (elt)
- (string-append elt "/"))
- dir-hint-module-name))))
- (%search-load-path (in-vicinity dir-hint leaf))))
-
-(define (read-module name)
- (let ((filename (module-filename name)))
- (if filename
- (let ((port (open-input-file filename)))
- (let lp ((out '()) (form (read port)))
- (if (eof-object? form)
- (reverse out)
- (lp (cons form out) (read port)))))
- '())))
-
-(define (module-export-list sym-name)
- (define (module-form-export-list form)
- (and (pair? form)
- (eq? (car form) 'define-module)
- (equal? (cadr form) sym-name)
- (and=> (memq #\export (cddr form)) cadr)))
- (let lp ((forms (read-module sym-name)))
- (cond ((null? forms) '())
- ((module-form-export-list (car forms)) => identity)
- (else (lp (cdr forms))))))
-
-(define* (module-stexi-documentation sym-name
- #\optional %docs-resolver
- #\key (docs-resolver
- (or %docs-resolver
- (lambda (name def) def))))
- "Return documentation for the module named @var{sym-name}. The
-documentation will be formatted as @code{stexi}
- (@pxref{texinfo,texinfo})."
- (if %docs-resolver
- (issue-deprecation-warning
- "module-stexi-documentation_ use #:docs-resolver instead of a positional argument."))
- (let* ((commentary (and=> (module-commentary sym-name)
- (lambda (x) (string-trim-both x #\newline))))
- (stexi (string->stexi commentary))
- (node-name (module-name->node-name sym-name))
- (name-str (with-output-to-string
- (lambda () (display sym-name))))
- (module (resolve-interface sym-name))
- (export-list (module-export-list sym-name)))
- (define (anchor-name sym)
- (string-append node-name " " (symbol->string sym)))
- (define (make-defs)
- (sort!
- (module-map
- (lambda (sym var)
- `((anchor (% (name ,(anchor-name sym))))
- ,@((lambda (x)
- (if (eq? (car x) '*fragment*)
- (cdr x)
- (list x)))
- (if (variable-bound? var)
- (docs-resolver
- sym
- (object-stexi-documentation (variable-ref var) sym
- #\force #t))
- (begin
- (warn "variable unbound!" sym)
- `(defvar (% (name ,(symbol->string sym)))
- "[unbound!]"))))))
- module)
- (lambda (a b) (sort-defs export-list a b))))
-
- `(texinfo (% (title ,name-str))
- (node (% (name ,node-name)))
- (section "Overview")
- ,@(cdr stexi)
- (section "Usage")
- ,@(apply append! (make-defs)))))
-
-(define (script-stexi-documentation scriptpath)
- "Return documentation for given script. The documentation will be
-taken from the script's commentary, and will be returned in the
-@code{stexi} format (@pxref{texinfo,texinfo})."
- (let ((commentary (file-commentary scriptpath)))
- `(texinfo (% (title ,(basename scriptpath)))
- (node (% (name ,(basename scriptpath))))
- ,@(if commentary
- (cdr
- (string->stexi
- (string-trim-both commentary #\newline)))
- '()))))
-
-(cond
- ((defined? 'add-value-help-handler!)
- (add-value-help-handler!
- (lambda (name value)
- (stexi->plain-text
- (object-stexi-documentation value name #\force #t))))
- (add-name-help-handler!
- (lambda (name)
- (and (list? name)
- (and-map symbol? name)
- (stexi->plain-text (module-stexi-documentation name)))))))
-
-;; we could be dealing with an old (ice-9 session); fondle it to get
-;; module-commentary
-(define module-commentary (@@ (ice-9 session) module-commentary))
-
-(define (package-stexi-standard-copying name version updated years
- copyright-holder permissions)
- "Create a standard texinfo @code{copying} section.
-
-@var{years} is a list of years (as integers) in which the modules
-being documented were released. All other arguments are strings."
- `(copying
- (para "This manual is for " ,name
- " (version " ,version ", updated " ,updated ")")
- (para "Copyright " ,(string-join (map number->string years) ",")
- " " ,copyright-holder)
- (quotation
- (para ,permissions))))
-
-(define (package-stexi-standard-titlepage name version updated authors)
- "Create a standard GNU title page.
-
-@var{authors} is a list of @code{(@var{name} . @var{email})}
-pairs. All other arguments are strings.
-
-Here is an example of the usage of this procedure_
-
-@smallexample
- (package-stexi-standard-titlepage
- \"Foolib\"
- \"3.2\"
- \"26 September 2006\"
- '((\"Alyssa P Hacker\" . \"alyssa@@example.com\"))
- '(2004 2005 2006)
- \"Free Software Foundation, Inc.\"
- \"Standard GPL permissions blurb goes here\")
-@end smallexample
-"
- `(;(setchapternewpage (% (all "odd"))) makes manuals too long
- (titlepage
- (title ,name)
- (subtitle "version " ,version ", updated " ,updated)
- ,@(map (lambda (pair)
- `(author ,(car pair)
- " (" (email ,(cdr pair)) ")"))
- authors)
- (page)
- (vskip (% (all "0pt plus 1filll")))
- (insertcopying))))
-
-(define (package-stexi-generic-menu name entries)
- "Create a menu from a generic alist of entries, the car of which
-should be the node name, and the cdr the description. As an exception,
-an entry of @code{#f} will produce a separator."
- (define (make-entry node description)
- `("* " ,node "__"
- ,(make-string (max (- 21 (string-length node)) 2) #\space)
- ,@description "\n"))
- `((ifnottex
- (node (% (name "Top")))
- (top (% (title ,name)))
- (insertcopying)
- (menu
- ,@(apply
- append
- (map
- (lambda (entry)
- (if entry
- (make-entry (car entry) (cdr entry))
- '("\n")))
- entries))))
- (iftex
- (shortcontents))))
-
-
-(define (package-stexi-standard-menu name modules module-descriptions
- extra-entries)
- "Create a standard top node and menu, suitable for processing
-by makeinfo."
- (package-stexi-generic-menu
- name
- (let ((module-entries (map cons
- (map module-name->node-name modules)
- module-descriptions))
- (separate-sections (lambda (x) (if (null? x) x (cons #f x)))))
- `(,@module-entries
- ,@(separate-sections extra-entries)))))
-
-(define (package-stexi-extended-menu name module-pairs script-pairs
- extra-entries)
- "Create an \"extended\" menu, like the standard menu but with a
-section for scripts."
- (package-stexi-generic-menu
- name
- (let ((module-entries (map cons
- (map module-name->node-name
- (map car module-pairs))
- (map cdr module-pairs)))
- (script-entries (map cons
- (map basename (map car script-pairs))
- (map cdr script-pairs)))
- (separate-sections (lambda (x) (if (null? x) x (cons #f x)))))
- `(,@module-entries
- ,@(separate-sections script-entries)
- ,@(separate-sections extra-entries)))))
-
-(define (package-stexi-standard-prologue name filename category
- description copying titlepage
- menu)
- "Create a standard prologue, suitable for later serialization
-to texinfo and .info creation with makeinfo.
-
-Returns a list of stexinfo forms suitable for passing to
-@code{package-stexi-documentation} as the prologue. @xref{texinfo
-reflection package-stexi-documentation}, @ref{texinfo reflection
-package-stexi-standard-titlepage,package-stexi-standard-titlepage},
-@ref{texinfo reflection
-package-stexi-standard-copying,package-stexi-standard-copying},
-and @ref{texinfo reflection
-package-stexi-standard-menu,package-stexi-standard-menu}."
- `(,copying
- (dircategory (% (category ,category)))
- (direntry
- "* " ,name "_ (" ,filename "). " ,description ".")
- ,@titlepage
- ,@menu))
-
-(define (stexi->chapter stexi)
- (pre-post-order
- stexi
- `((texinfo . ,(lambda (tag attrs node . body)
- `(,node
- (chapter ,@(assq-ref (cdr attrs) 'title))
- ,@body)))
- (*text* . ,(lambda (tag text) text))
- (*default* . ,(lambda args args)))))
-
-(define* (package-stexi-documentation modules name filename
- prologue epilogue
- #\key
- (module-stexi-documentation-args
- '())
- (scripts '()))
- "Create stexi documentation for a @dfn{package}, where a
-package is a set of modules that is released together.
-
-@var{modules} is expected to be a list of module names, where a
-module name is a list of symbols. The stexi that is returned will
-be titled @var{name} and a texinfo filename of @var{filename}.
-
-@var{prologue} and @var{epilogue} are lists of stexi forms that
-will be spliced into the output document before and after the
-generated modules documentation, respectively.
-@xref{texinfo reflection package-stexi-standard-prologue}, to
-create a conventional GNU texinfo prologue.
-
-@var{module-stexi-documentation-args} is an optional argument that, if
-given, will be added to the argument list when
-@code{module-texi-documentation} is called. For example, it might be
-useful to define a @code{#:docs-resolver} argument."
- (define (verify-modules-list l)
- (define (all pred l)
- (and (pred (car l))
- (or (null? (cdr l)) (all pred (cdr l)))))
- (false-if-exception
- (all (lambda (x) (all symbol? x)) modules)))
- (if (not (verify-modules-list modules))
- (error "expected modules to be a list of a list of symbols"
- modules))
-
- `(texinfo
- (% (title ,name)
- (filename ,filename))
- ,@prologue
- ,@(append-map (lambda (mod)
- (stexi->chapter
- (apply module-stexi-documentation
- mod module-stexi-documentation-args)))
- modules)
- ,@(append-map (lambda (script)
- (stexi->chapter
- (script-stexi-documentation script)))
- scripts)
- ,@epilogue))
-
-(define* (package-stexi-documentation-for-include modules module-descriptions
- #\key
- (module-stexi-documentation-args '()))
- "Create stexi documentation for a @dfn{package}, where a
-package is a set of modules that is released together.
-
-@var{modules} is expected to be a list of module names, where a
-module name is a list of symbols. Returns an stexinfo fragment.
-
-Unlike @code{package-stexi-documentation}, this function simply produces
-a menu and the module documentations instead of producing a full texinfo
-document. This can be useful if you write part of your manual by hand,
-and just use @code{@@include} to pull in the automatically generated
-parts.
-
-@var{module-stexi-documentation-args} is an optional argument that, if
-given, will be added to the argument list when
-@code{module-texi-documentation} is called. For example, it might be
-useful to define a @code{#:docs-resolver} argument."
- (define (make-entry node description)
- `("* " ,node "__"
- ,(make-string (max (- 21 (string-length node)) 2) #\space)
- ,@description "\n"))
- `(*fragment*
- (menu
- ,@(append-map (lambda (modname desc)
- (make-entry (module-name->node-name modname)
- desc))
- modules
- module-descriptions))
- ,@(append-map (lambda (modname)
- (stexi->chapter
- (apply module-stexi-documentation
- modname
- module-stexi-documentation-args)))
- modules)))
-
-;;; arch-tag_ bbe2bc03-e16d-4a9e-87b9-55225dc9836c
-;;;; (texinfo serialize) -- rendering stexinfo as texinfo
-;;;;
-;;;; Copyright (C) 2009, 2012, 2013 Free Software Foundation, Inc.
-;;;; Copyright (C) 2003,2004,2009 Andy Wingo <wingo at pobox dot com>
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary_
-;;
-;;Serialization of @code{stexi} to plain texinfo.
-;;
-;;; Code_
-
-(define-module (texinfo serialize)
- #\use-module (texinfo)
- #\use-module (texinfo string-utils)
- #\use-module (sxml transform)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-13)
- #\export (stexi->texi))
-
-(define (list-intersperse src-l elem)
- (if (null? src-l) src-l
- (let loop ((l (cdr src-l)) (dest (cons (car src-l) '())))
- (if (null? l) (reverse dest)
- (loop (cdr l) (cons (car l) (cons elem dest)))))))
-
-;; converts improper lists to proper lists.
-(define (filter* pred l)
- (let lp ((in l) (out '()))
- (cond ((null? in)
- (reverse! out))
- ((pair? in)
- (lp (cdr in) (if (pred (car in)) (cons (car in) out) out)))
- (else
- (lp '() (if (pred in) (cons in out) out))))))
-
-;; (list* 'a '(b c) 'd '(e f g)) => '(a b c d e f g)
-(define (list* . args)
- (let* ((args (reverse args))
- (tail (car args)))
- (let lp ((in (cdr args)) (out tail))
- (cond ((null? in) out)
- ((pair? (car in)) (lp (cdr in) (append (car in) out)))
- ((null? (car in)) (lp (cdr in) out))
- (else (lp (cdr in) (cons (car in) out)))))))
-
-;; Why? Well, because syntax-case defines `include', and carps about its
-;; wrong usage below...
-(eval-when (expand load eval)
- (define (include exp lp command type formals args accum)
- (list* "\n"
- (list-intersperse
- args
- " ")
- " " command "@" accum)))
-
-(define (empty-command exp lp command type formals args accum)
- (list* " " command "@" accum))
-
-(define (inline-text exp lp command type formals args accum)
- (if (not (string=? command "*braces*")) ;; fixme _(
- (list* "}"
- (append-map (lambda (x) (lp x '())) (reverse (cdr exp)))
- "{" command "@" accum)
- (list* "@}"
- (append-map (lambda (x) (lp x '())) (reverse (cdr exp)))
- "@{" accum)))
-
-(define (inline-args exp lp command type formals args accum)
- (list* "}"
- (if (not args) ""
- (list-intersperse
- (map
- (lambda (x)
- (cond ((not x) "")
- ((pair? x)
- (if (pair? (cdr x))
- (warn "Strange inline-args!" args))
- (car x))
- (else (error "Invalid inline-args" args))))
- (drop-while not
- (map (lambda (x) (assq-ref args x))
- (reverse formals))))
- ","))
- "{" command "@" accum))
-
-(define (inline-text-args exp lp command type formals args accum)
- (list* "}"
- (if (not args) ""
- (apply
- append
- (list-intersperse
- (map
- (lambda (x) (append-map (lambda (x) (lp x '())) (reverse x)))
- (drop-while not
- (map (lambda (x) (assq-ref args x))
- (reverse formals))))
- '(","))))
- "{" command "@" accum))
-
-(define (serialize-text-args lp formals args)
- (apply
- append
- (list-intersperse
- (map (lambda (arg) (append-map (lambda (x) (lp x '())) arg))
- (map
- reverse
- (drop-while
- not (map (lambda (x) (assq-ref args x))
- (reverse formals)))))
- '(" "))))
-
-(define (eol-text-args exp lp command type formals args accum)
- (list* "\n"
- (serialize-text-args lp formals args)
- " " command "@" accum))
-
-(define (eol-text exp lp command type formals args accum)
- (list* "\n"
- (append-map (lambda (x) (lp x '()))
- (reverse (if args (cddr exp) (cdr exp))))
- " " command "@" accum))
-
-(define (eol-args exp lp command type formals args accum)
- (list* "\n"
- (list-intersperse
- (apply append
- (drop-while not
- (map (lambda (x) (assq-ref args x))
- (reverse formals))))
- ", ")
- " " command "@" accum))
-
-(define (environ exp lp command type formals args accum)
- (case (car exp)
- ((texinfo)
- (list* "@bye\n"
- (append-map (lambda (x) (lp x '())) (reverse (cddr exp)))
- "\n@c %**end of header\n\n"
- (reverse (assq-ref args 'title)) "@settitle "
- (or (and=> (assq-ref args 'filename)
- (lambda (filename)
- (cons "\n" (reverse (cons "@setfilename " filename)))))
- "")
- "\\input texinfo @c -*-texinfo-*-\n@c %**start of header\n"
- accum))
- (else
- (list* "\n\n" command "@end "
- (let ((body (append-map (lambda (x) (lp x '()))
- (reverse (if args (cddr exp) (cdr exp))))))
- (if (or (null? body)
- (eqv? (string-ref (car body)
- (1- (string-length (car body))))
- #\newline))
- body
- (cons "\n" body)))
- "\n"
- (serialize-text-args lp formals args)
- " " command "@" accum))))
-
-(define (table-environ exp lp command type formals args accum)
- (list* "\n\n" command "@end "
- (append-map (lambda (x) (lp x '()))
- (reverse (if args (cddr exp) (cdr exp))))
- "\n"
- (let* ((arg (if args (cadar args) ""))) ;; zero or one args
- (if (pair? arg)
- (list (symbol->string (car arg)) "@")
- arg))
- " " command "@" accum))
-
-(define (wrap strings)
- (fill-string (string-concatenate strings)
- #\line-width 72
- #\break-long-words? #f))
-
-(define (paragraph exp lp command type formals args accum)
- (list* "\n\n"
- (wrap
- (reverse
- (append-map (lambda (x) (lp x '())) (reverse (cdr exp)))))
- accum))
-
-(define (item exp lp command type formals args accum)
- (list* (append-map (lambda (x) (lp x '())) (reverse (cdr exp)))
- "@item\n"
- accum))
-
-(define (entry exp lp command type formals args accum)
- (list* (append-map (lambda (x) (lp x '())) (reverse (cddr exp)))
- "\n"
- (append-map (lambda (x) (lp x '())) (reverse (cdar args)))
- "@item "
- accum))
-
-(define (fragment exp lp command type formals args accum)
- (list* "\n@c %end of fragment\n"
- (append-map (lambda (x) (lp x '())) (reverse (cdr exp)))
- "\n@c %start of fragment\n\n"
- accum))
-
-(define serializers
- `((EMPTY-COMMAND . ,empty-command)
- (INLINE-TEXT . ,inline-text)
- (INLINE-ARGS . ,inline-args)
- (INLINE-TEXT-ARGS . ,inline-text-args)
- (EOL-TEXT . ,eol-text)
- (EOL-TEXT-ARGS . ,eol-text-args)
- (INDEX . ,eol-text-args)
- (EOL-ARGS . ,eol-args)
- (ENVIRON . ,environ)
- (TABLE-ENVIRON . ,table-environ)
- (ENTRY . ,entry)
- (ITEM . ,item)
- (PARAGRAPH . ,paragraph)
- (FRAGMENT . ,fragment)
- (#f . ,include))) ; support writing include statements
-
-(define (serialize exp lp command type formals args accum)
- ((or (assq-ref serializers type)
- (error "Unknown command type" exp type))
- exp lp command type formals args accum))
-
-(define escaped-chars '(#\} #\{ #\@))
-(define (escape str)
- "Escapes any illegal texinfo characters (currently @{, @}, and @@)."
- (let loop ((in (string->list str)) (out '()))
- (if (null? in)
- (apply string (reverse out))
- (if (memq (car in) escaped-chars)
- (loop (cdr in) (cons* (car in) #\@ out))
- (loop (cdr in) (cons (car in) out))))))
-
-(define (stexi->texi tree)
- "Serialize the stexi @var{tree} into plain texinfo."
- (string-concatenate-reverse
- (let lp ((in tree) (out '()))
- (cond
- ((or (not in) (null? in)) out)
- ((string? in) (cons (escape in) out))
- ((pair? in)
- (let ((command-spec (assq (car in) texi-command-specs)))
- (if (not command-spec)
- (begin
- (warn "Unknown stexi command, not rendering" in)
- out)
- (serialize in
- lp
- (symbol->string (car in))
- (cadr command-spec)
- (filter* symbol? (cddr command-spec))
- (cond
- ((and (pair? (cdr in)) (pair? (cadr in))
- (eq? (caadr in) '%))
- (cdadr in))
- ((not (cadr command-spec))
- ;; include
- (cdr in))
- (else
- #f))
- out))))
- (else
- (error "Invalid stexi" in))))))
-
-;;; arch-tag_ d3fa16ea-0bf7-4ec5-ab9f-3f08490f77f5
-;;;; (texinfo string-utils) -- text filling and wrapping
-;;;;
-;;;; Copyright (C) 2009, 2013 Free Software Foundation, Inc.
-;;;; Copyright (C) 2003 Richard Todd
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary_
-;; Module @samp{(texinfo string-utils)} provides various string-related
-;; functions useful to Guile's texinfo support.
-;;; Code_
-
-(define-module (texinfo string-utils)
- #\use-module (srfi srfi-13)
- #\use-module (srfi srfi-14)
- #\export (escape-special-chars
- transform-string
- expand-tabs
- center-string
- left-justify-string
- right-justify-string
- collapse-repeated-chars
- make-text-wrapper
- fill-string
- string->wrapped-lines))
-
-(define* (transform-string str match? replace #\optional (start #f) (end #f))
-"Uses @var{match?} against each character in @var{str}, and performs a
-replacement on each character for which matches are found.
-
-@var{match?} may either be a function, a character, a string, or
-@code{#t}. If @var{match?} is a function, then it takes a single
-character as input, and should return @samp{#t} for matches.
-@var{match?} is a character, it is compared to each string character
-using @code{char=?}. If @var{match?} is a string, then any character
-in that string will be considered a match. @code{#t} will cause
-every character to be a match.
-
-If @var{replace} is a function, it is called with the matched
-character as an argument, and the returned value is sent to the output
-string via @samp{display}. If @var{replace} is anything else, it is
-sent through the output string via @samp{display}.
-
-Note that te replacement for the matched characters does not need to
-be a single character. That is what differentiates this function from
-@samp{string-map}, and what makes it useful for applications such as
-converting @samp{#\\&} to @samp{\"&amp;\"} in web page text. Some other
-functions in this module are just wrappers around common uses of
-@samp{transform-string}. Transformations not possible with this
-function should probably be done with regular expressions.
-
-If @var{start} and @var{end} are given, they control which portion
-of the string undergoes transformation. The entire input string
-is still output, though. So, if @var{start} is @samp{5}, then the
-first five characters of @var{str} will still appear in the returned
-string.
-
-@lisp
-; these two are equivalent...
- (transform-string str #\\space #\\-) ; change all spaces to -'s
- (transform-string str (lambda (c) (char=? #\\space c)) #\\-)
-@end lisp"
- ;; I had implemented this with string-fold, but it was
- ;; slower...
- (let* ((os (open-output-string))
- (matcher (cond ((char? match?)
- (lambda (c) (char=? match? c)))
- ((procedure? match?)
- match?)
- ((string? match?)
- (lambda (c) (string-index match? c)))
- ((boolean? match?)
- (lambda (c) match?))
- (else (throw 'bad-type "expected #t, char, string, or procedure"))))
- (replacer (if (procedure? replace)
- (lambda (c) (display (replace c) os))
- (lambda (c) (display replace os)))))
-
- ;; put the first part in, un-transformed if they asked for it...
- (if (and start (<= start (string-length str)))
- (display (substring str 0 start) os))
-
- ;; process the portion they want processed....
- (string-for-each
- (lambda (c)
- (if (matcher c)
- ;; we have a match! replace the char as directed...
- (replacer c)
-
- ;; not a match, just insert the character itself...
- (write-char c os)))
- str
- (or start 0)
- (or end (string-length str)))
-
- ;; if there was any at the end, tack it on...
- (if (and end (< end (string-length str)))
- (display (substring str end) os))
-
- (get-output-string os)))
-
-(define* (expand-tabs str #\optional (tab-size 8))
-"Returns a copy of @var{str} with all tabs expanded to spaces. @var{tab-size} defaults to 8.
-
-Assuming tab size of 8, this is equivalent to_ @lisp
- (transform-string str #\\tab \" \")
-@end lisp"
- (transform-string str
- #\tab
- (make-string tab-size #\space)))
-
-(define (escape-special-chars str special-chars escape-char)
-"Returns a copy of @var{str} with all given special characters preceded
-by the given @var{escape-char}.
-
-@var{special-chars} can either be a single character, or a string consisting
-of all the special characters.
-
-@lisp
-;; make a string regexp-safe...
- (escape-special-chars \"***(Example String)***\"
- \"[]()/*.\"
- #\\\\)
-=> \"\\\\*\\\\*\\\\*\\\\(Example String\\\\)\\\\*\\\\*\\\\*\"
-
-;; also can escape a singe char...
- (escape-special-chars \"richardt@@vzavenue.net\"
- #\\@@
- #\\@@)
-=> \"richardt@@@@vzavenue.net\"
-@end lisp"
- (transform-string str
- (if (char? special-chars)
- ;; if they gave us a char, use char=?
- (lambda (c) (char=? c special-chars))
-
- ;; if they gave us a string, see if our character is in it
- (lambda (c) (string-index special-chars c)))
-
- ;; replace matches with the character preceded by the escape character
- (lambda (c) (string escape-char c))))
-
-(define* (center-string str #\optional (width 80) (chr #\space) (rchr #f))
-"Returns a copy of @var{str} centered in a field of @var{width}
-characters. Any needed padding is done by character @var{chr}, which
-defaults to @samp{#\\space}. If @var{rchr} is provided, then the
-padding to the right will use it instead. See the examples below.
-left and @var{rchr} on the right. The default @var{width} is 80. The
-default @var{chr} and @var{rchr} is @samp{#\\space}. The string is
-never truncated.
-@lisp
- (center-string \"Richard Todd\" 24)
-=> \" Richard Todd \"
-
- (center-string \" Richard Todd \" 24 #\\=)
-=> \"===== Richard Todd =====\"
-
- (center-string \" Richard Todd \" 24 #\\< #\\>)
-=> \"<<<<< Richard Todd >>>>>\"
-@end lisp"
- (let* ((len (string-length str))
- (lpad (make-string (max (quotient (- width len) 2) 0) chr))
- ;; right-char == char unless it has been provided by the user
- (right-chr (or rchr chr))
- (rpad (if (char=? right-chr chr)
- lpad
- (make-string (max (quotient (- width len) 2) 0) right-chr))))
- (if (>= len width)
- str
- (string-append lpad str rpad (if (odd? (- width len)) (string right-chr) "")))))
-
-(define* (left-justify-string str #\optional (width 80) (chr #\space))
-"@code{left-justify-string str [width chr]}.
-Returns a copy of @var{str} padded with @var{chr} such that it is left
-justified in a field of @var{width} characters. The default
-@var{width} is 80. Unlike @samp{string-pad} from srfi-13, the string
-is never truncated."
- (let* ((len (string-length str))
- (pad (make-string (max (- width len) 0) chr)))
- (if (>= len width)
- str
- (string-append str pad))))
-
-(define* (right-justify-string str #\optional (width 80) (chr #\space))
-"Returns a copy of @var{str} padded with @var{chr} such that it is
-right justified in a field of @var{width} characters. The default
-@var{width} is 80. The default @var{chr} is @samp{#\\space}. Unlike
-@samp{string-pad} from srfi-13, the string is never truncated."
- (let* ((len (string-length str))
- (pad (make-string (max (- width len) 0) chr)))
- (if (>= len width)
- str
- (string-append pad str))))
-
- (define* (collapse-repeated-chars str #\optional (chr #\space) (num 1))
-"Returns a copy of @var{str} with all repeated instances of
-@var{chr} collapsed down to at most @var{num} instances.
-The default value for @var{chr} is @samp{#\\space}, and
-the default value for @var{num} is 1.
-
-@lisp
- (collapse-repeated-chars \"H e l l o\")
-=> \"H e l l o\"
- (collapse-repeated-chars \"H--e--l--l--o\" #\\-)
-=> \"H-e-l-l-o\"
- (collapse-repeated-chars \"H-e--l---l----o\" #\\- 2)
-=> \"H-e--l--l--o\"
-@end lisp"
- ;; define repeat-locator as a stateful match? function which remembers
- ;; the last character it had seen.
- (let ((repeat-locator
- ;; initialize prev-chr to something other than what we're seeking...
- (let ((prev-chr (if (char=? chr #\space) #\A #\space))
- (match-count 0))
- (lambda (c)
- (if (and (char=? c prev-chr)
- (char=? prev-chr chr))
- ;; found enough duplicates if the match-count is high enough
- (begin
- (set! match-count (+ 1 match-count))
- (>= match-count num))
-
- ;; did not find a duplicate
- (begin (set! match-count 0)
- (set! prev-chr c)
- #f))))))
-
- ;; transform the string with our stateful matcher...
- ;; deleting matches...
- (transform-string str repeat-locator "")))
-
-;; split a text string into segments that have the form...
-;; <ws non-ws> <ws non-ws> etc..
-(define (split-by-single-words str)
- (let ((non-wschars (char-set-complement char-set_whitespace)))
- (let loop ((ans '())
- (index 0))
- (let ((next-non-ws (string-index str non-wschars index)))
- (if next-non-ws
- ;; found non-ws...look for ws following...
- (let ((next-ws (string-index str char-set_whitespace next-non-ws)))
- (if next-ws
- ;; found the ws following...
- (loop (cons (substring str index next-ws) ans)
- next-ws)
- ;; did not find ws...must be the end...
- (reverse (cons (substring str index) ans))))
- ;; did not find non-ws... only ws at end of the string...
- (reverse ans))))))
-
-(define (end-of-sentence? str)
- "Return #t when STR likely denotes the end of sentence."
- (let ((len (string-length str)))
- (and (> len 1)
- (eqv? #\. (string-ref str (- len 1)))
- (not (eqv? #\. (string-ref str (- len 2)))))))
-
-(define* (make-text-wrapper #\key
- (line-width 80)
- (expand-tabs? #t)
- (tab-width 8)
- (collapse-whitespace? #t)
- (subsequent-indent "")
- (initial-indent "")
- (break-long-words? #t))
- "Returns a procedure that will split a string into lines according to the
-given parameters.
-
-@table @code
-@item #:line-width
-This is the target length used when deciding where to wrap lines.
-Default is 80.
-
-@item #:expand-tabs?
-Boolean describing whether tabs in the input should be expanded. Default
-is #t.
-
-@item #:tab-width
-If tabs are expanded, this will be the number of spaces to which they
-expand. Default is 8.
-
-@item #:collapse-whitespace?
-Boolean describing whether the whitespace inside the existing text
-should be removed or not. Default is #t.
-
-If text is already well-formatted, and is just being wrapped to fit in a
-different width, then set this to @samp{#f}. This way, many common text
-conventions (such as two spaces between sentences) can be preserved if
-in the original text. If the input text spacing cannot be trusted, then
-leave this setting at the default, and all repeated whitespace will be
-collapsed down to a single space.
-
-@item #:initial-indent
-Defines a string that will be put in front of the first line of wrapped
-text. Default is the empty string, ``''.
-
-@item #:subsequent-indent
-Defines a string that will be put in front of all lines of wrapped
-text, except the first one. Default is the empty string, ``''.
-
-@item #:break-long-words?
-If a single word is too big to fit on a line, this setting tells the
-wrapper what to do. Defaults to #t, which will break up long words.
-When set to #f, the line will be allowed, even though it is longer
-than the defined @code{#:line-width}.
-@end table
-
-The return value is a procedure of one argument, the input string, which
-returns a list of strings, where each element of the list is one line."
- (lambda (str)
- ;; replace newlines with spaces
- (set! str (transform-string str (lambda (c) (char=? c #\nl)) #\space))
-
- ;; expand tabs if they wanted us to...
- (if expand-tabs?
- (set! str (expand-tabs str tab-width)))
-
- ;; collapse whitespace if they wanted us to...
- (if collapse-whitespace?
- (set! str (collapse-repeated-chars str)))
-
- ;; drop any whitespace from the front...
- (set! str (string-trim str))
-
- ;; now start breaking the text into lines...
- (let loop ((ans '())
- (words (split-by-single-words str))
- (line initial-indent)
- (count 0))
- (if (null? words)
- ;; out of words? ...done!
- (reverse (if (> count 0)
- (cons line ans)
- ans))
-
- ;; not out of words...keep going...
- (let ((length-left (- line-width
- (string-length line)))
- (next-word (if (= count 0)
- (string-trim (car words))
- (car words))))
- (cond
- ;; does the next entry fit?
- ((<= (string-length next-word)
- length-left)
- (loop ans
- (cdr words)
- (if (and collapse-whitespace?
- (end-of-sentence? line))
- ;; Add an extra space after the period.
- (string-append line " " next-word)
- (string-append line next-word))
- (+ count 1)))
-
- ;; ok, it didn't fit...is there already at least one word on the line?
- ((> count 0)
- ;; try to use it for the next line, then...
- (loop (cons line ans)
- words
- subsequent-indent
- 0))
-
- ;; ok, it didn't fit...and it's the first word.
- ;; were we told to break up long words?
- (break-long-words?
- ;; break the like at the limit, since the user wants us to...
- (loop (cons (string-append line (substring next-word 0 length-left))
- ans)
- (cons (substring next-word length-left)
- (cdr words))
- subsequent-indent
- 0))
-
- ;; well, then is it the first word and we *shouldn't* break long words, then...
- (else
- (loop (cons (string-append line next-word)
- ans)
- (cdr words)
- subsequent-indent
- 0))))))))
-
-(define (string->wrapped-lines str . kwargs)
- "@code{string->wrapped-lines str keywds ...}. Wraps the text given in
-string @var{str} according to the parameters provided in @var{keywds},
-or the default setting if they are not given. Returns a list of strings
-representing the formatted lines. Valid keyword arguments are discussed
-in @code{make-text-wrapper}."
- ((apply make-text-wrapper kwargs) str))
-
-(define (fill-string str . kwargs)
- "Wraps the text given in string @var{str} according to the parameters
-provided in @var{kwargs}, or the default setting if they are not
-given. Returns a single string with the wrapped text. Valid keyword
-arguments are discussed in @code{make-text-wrapper}."
- (string-join (apply string->wrapped-lines str kwargs)
- "\n"
- 'infix))
-;;; Web client
-
-;; Copyright (C) 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-;; 02110-1301 USA
-
-;;; Commentary_
-;;;
-;;; (web client) is a simple HTTP URL fetcher for Guile.
-;;;
-;;; In its current incarnation, (web client) is synchronous. If you
-;;; want to fetch a number of URLs at once, probably the best thing to
-;;; do is to write an event-driven URL fetcher, similar in structure to
-;;; the web server.
-;;;
-;;; Another option, good but not as performant, would be to use threads,
-;;; possibly via a thread pool.
-;;;
-;;; Code_
-
-(define-module (web client)
- #\use-module (rnrs bytevectors)
- #\use-module (ice-9 binary-ports)
- #\use-module (ice-9 iconv)
- #\use-module (ice-9 rdelim)
- #\use-module (web request)
- #\use-module (web response)
- #\use-module (web uri)
- #\use-module (web http)
- #\use-module (srfi srfi-1)
- #\use-module (srfi srfi-9)
- #\use-module (srfi srfi-9 gnu)
- #\export (current-http-proxy
- open-socket-for-uri
- http-get
- http-get*
- http-head
- http-post
- http-put
- http-delete
- http-trace
- http-options))
-
-(define current-http-proxy
- (make-parameter (let ((proxy (getenv "http_proxy")))
- (and (not (equal? proxy ""))
- proxy))))
-
-(define (ensure-uri uri-or-string)
- (cond
- ((string? uri-or-string) (string->uri uri-or-string))
- ((uri? uri-or-string) uri-or-string)
- (else (error "Invalid URI" uri-or-string))))
-
-(define (open-socket-for-uri uri-or-string)
- "Return an open input/output port for a connection to URI."
- (define http-proxy (current-http-proxy))
- (define uri (ensure-uri (or http-proxy uri-or-string)))
- (define addresses
- (let ((port (uri-port uri)))
- (delete-duplicates
- (getaddrinfo (uri-host uri)
- (cond (port => number->string)
- (else (symbol->string (uri-scheme uri))))
- (if port
- AI_NUMERICSERV
- 0))
- (lambda (ai1 ai2)
- (equal? (addrinfo_addr ai1) (addrinfo_addr ai2))))))
-
- (let loop ((addresses addresses))
- (let* ((ai (car addresses))
- (s (with-fluids ((%default-port-encoding #f))
- ;; Restrict ourselves to TCP.
- (socket (addrinfo_fam ai) SOCK_STREAM IPPROTO_IP))))
- (catch 'system-error
- (lambda ()
- (connect s (addrinfo_addr ai))
-
- ;; Buffer input and output on this port.
- (setvbuf s _IOFBF)
- ;; If we're using a proxy, make a note of that.
- (when http-proxy (set-http-proxy-port?! s #t))
- s)
- (lambda args
- ;; Connection failed, so try one of the other addresses.
- (close s)
- (if (null? (cdr addresses))
- (apply throw args)
- (loop (cdr addresses))))))))
-
-(define (extend-request r k v . additional)
- (let ((r (set-field r (request-headers)
- (assoc-set! (copy-tree (request-headers r))
- k v))))
- (if (null? additional)
- r
- (apply extend-request r additional))))
-
-;; -> request body
-(define (sanitize-request request body)
- "\"Sanitize\" the given request and body, ensuring that they are
-complete and coherent. This method is most useful for methods that send
-data to the server, like POST, but can be used for any method. Return
-two values_ a request and a bytevector, possibly the same ones that were
-passed as arguments.
-
-If BODY is a string, encodes the string to a bytevector, in an encoding
-appropriate for REQUEST. Adds a ‘content-length’ and ‘content-type’
-header, as necessary.
-
-If BODY is a procedure, it is called with a port as an argument, and the
-output collected as a bytevector. In the future we might try to instead
-use a compressing, chunk-encoded port, and call this procedure later.
-Authors are advised not to rely on the procedure being called at any
-particular time.
-
-Note that we rely on the request itself already having been validated,
-as is the case by default with a request returned by `build-request'."
- (cond
- ((not body)
- (let ((length (request-content-length request)))
- (if length
- ;; FIXME make this stricter_ content-length header should be
- ;; prohibited if there's no body, even if the content-length
- ;; is 0.
- (unless (zero? length)
- (error "content-length, but no body"))
- (when (assq 'transfer-encoding (request-headers request))
- (error "transfer-encoding not allowed with no body")))
- (values request #vu8())))
- ((string? body)
- (let* ((type (request-content-type request '(text/plain)))
- (declared-charset (assq-ref (cdr type) 'charset))
- (charset (or declared-charset "utf-8")))
- (sanitize-request
- (if declared-charset
- request
- (extend-request request 'content-type
- `(,@type (charset . ,charset))))
- (string->bytevector body charset))))
- ((procedure? body)
- (let* ((type (request-content-type request
- '(text/plain)))
- (declared-charset (assq-ref (cdr type) 'charset))
- (charset (or declared-charset "utf-8")))
- (sanitize-request
- (if declared-charset
- request
- (extend-request request 'content-type
- `(,@type (charset . ,charset))))
- (call-with-encoded-output-string charset body))))
- ((not (bytevector? body))
- (error "unexpected body type"))
- (else
- (values (let ((rlen (request-content-length request))
- (blen (bytevector-length body)))
- (cond
- (rlen (if (= rlen blen)
- request
- (error "bad content-length" rlen blen)))
- (else (extend-request request 'content-length blen))))
- body))))
-
-(define (decode-response-body response body)
- ;; `body' is either #f or a bytevector.
- (cond
- ((not body) body)
- ((bytevector? body)
- (let ((rlen (response-content-length response))
- (blen (bytevector-length body)))
- (cond
- ((and rlen (not (= rlen blen)))
- (error "bad content-length" rlen blen))
- ((response-content-type response)
- => (lambda (type)
- (cond
- ((text-content-type? (car type))
- ;; RFC 2616 3.7.1_ "When no explicit charset parameter is
- ;; provided by the sender, media subtypes of the "text"
- ;; type are defined to have a default charset value of
- ;; "ISO-8859-1" when received via HTTP."
- (bytevector->string body (or (assq-ref (cdr type) 'charset)
- "iso-8859-1")))
- (else body))))
- (else body))))
- (else
- (error "unexpected body type" body))))
-
-;; We could expose this to user code if there is demand.
-(define* (request uri #\key
- (body #f)
- (port (open-socket-for-uri uri))
- (method 'GET)
- (version '(1 . 1))
- (keep-alive? #f)
- (headers '())
- (decode-body? #t)
- (streaming? #f)
- (request
- (build-request
- (ensure-uri uri)
- #\method method
- #\version version
- #\headers (if keep-alive?
- headers
- (cons '(connection close) headers))
- #\port port)))
- (call-with-values (lambda () (sanitize-request request body))
- (lambda (request body)
- (let ((request (write-request request port)))
- (when body
- (write-request-body request body))
- (force-output (request-port request))
- (let ((response (read-response port)))
- (cond
- ((eq? (request-method request) 'HEAD)
- (unless keep-alive?
- (close-port port))
- (values response #f))
- (streaming?
- (values response
- (response-body-port response
- #\keep-alive? keep-alive?
- #\decode? decode-body?)))
- (else
- (let ((body (read-response-body response)))
- (unless keep-alive?
- (close-port port))
- (values response
- (if decode-body?
- (decode-response-body response body)
- body))))))))))
-
-(define* (http-get uri #\key
- (body #f)
- (port (open-socket-for-uri uri))
- (version '(1 . 1)) (keep-alive? #f)
- ;; #\headers is the new name of #\extra-headers.
- (extra-headers #f) (headers (or extra-headers '()))
- (decode-body? #t) (streaming? #f))
- "Connect to the server corresponding to URI and ask for the
-resource, using the ‘GET’ method. If you already have a port open,
-pass it as PORT. The port will be closed at the end of the
-request unless KEEP-ALIVE? is true. Any extra headers in the
-alist HEADERS will be added to the request.
-
-If BODY is not ‘#f’, a message body will also be sent with the HTTP
-request. If BODY is a string, it is encoded according to the
-content-type in HEADERS, defaulting to UTF-8. Otherwise BODY should be
-a bytevector, or ‘#f’ for no body. Although it's allowed to send a
-message body along with any request, usually only POST and PUT requests
-have bodies. See ‘http-put’ and ‘http-post’ documentation, for more.
-
-If DECODE-BODY? is true, as is the default, the body of the
-response will be decoded to string, if it is a textual content-type.
-Otherwise it will be returned as a bytevector.
-
-However, if STREAMING? is true, instead of eagerly reading the response
-body from the server, this function only reads off the headers. The
-response body will be returned as a port on which the data may be read.
-Unless KEEP-ALIVE? is true, the port will be closed after the full
-response body has been read.
-
-Returns two values_ the response read from the server, and the response
-body as a string, bytevector, #f value, or as a port (if STREAMING? is
-true)."
- (when extra-headers
- (issue-deprecation-warning
- "The #\extra-headers argument to http-get has been renamed to #:headers. "
- "Please update your code."))
- (request uri #\method 'GET #\body body
- #\port port #\version version #\keep-alive? keep-alive?
- #\headers headers #\decode-body? decode-body?
- #\streaming? streaming?))
-
-(define* (http-get* uri #\key
- (body #f)
- (port (open-socket-for-uri uri))
- (version '(1 . 1)) (keep-alive? #f)
- ;; #\headers is the new name of #\extra-headers.
- (extra-headers #f) (headers (or extra-headers '()))
- (decode-body? #t))
- "Deprecated in favor of (http-get #:streaming? #t)."
- (issue-deprecation-warning
- "`http-get*' has been deprecated. "
- "Instead, use `http-get' with the #:streaming? #t keyword argument.")
- (http-get uri #\body body
- #\port port #\version version #\keep-alive? keep-alive?
- #\headers headers #\decode-body? #t #\streaming? #t))
-
-(define-syntax-rule (define-http-verb http-verb method doc)
- (define* (http-verb uri #\key
- (body #f)
- (port (open-socket-for-uri uri))
- (version '(1 . 1))
- (keep-alive? #f)
- (headers '())
- (decode-body? #t)
- (streaming? #f))
- doc
- (request uri
- #\body body #\method method
- #\port port #\version version #\keep-alive? keep-alive?
- #\headers headers #\decode-body? decode-body?
- #\streaming? streaming?)))
-
-(define-http-verb http-head
- 'HEAD
- "Fetch message headers for the given URI using the HTTP \"HEAD\"
-method.
-
-This function is similar to ‘http-get’, except it uses the \"HEAD\"
-method. See ‘http-get’ for full documentation on the various keyword
-arguments that are accepted by this function.
-
-Returns two values_ the resulting response, and ‘#f’. Responses to HEAD
-requests do not have a body. The second value is only returned so that
-other procedures can treat all of the http-foo verbs identically.")
-
-(define-http-verb http-post
- 'POST
- "Post data to the given URI using the HTTP \"POST\" method.
-
-This function is similar to ‘http-get’, except it uses the \"POST\"
-method. See ‘http-get’ for full documentation on the various keyword
-arguments that are accepted by this function.
-
-Returns two values_ the resulting response, and the response body.")
-
-(define-http-verb http-put
- 'PUT
- "Put data at the given URI using the HTTP \"PUT\" method.
-
-This function is similar to ‘http-get’, except it uses the \"PUT\"
-method. See ‘http-get’ for full documentation on the various keyword
-arguments that are accepted by this function.
-
-Returns two values_ the resulting response, and the response body.")
-
-(define-http-verb http-delete
- 'DELETE
- "Delete data at the given URI using the HTTP \"DELETE\" method.
-
-This function is similar to ‘http-get’, except it uses the \"DELETE\"
-method. See ‘http-get’ for full documentation on the various keyword
-arguments that are accepted by this function.
-
-Returns two values_ the resulting response, and the response body.")
-
-(define-http-verb http-trace
- 'TRACE
- "Send an HTTP \"TRACE\" request.
-
-This function is similar to ‘http-get’, except it uses the \"TRACE\"
-method. See ‘http-get’ for full documentation on the various keyword
-arguments that are accepted by this function.
-
-Returns two values_ the resulting response, and the response body.")
-
-(define-http-verb http-options
- 'OPTIONS
- "Query characteristics of an HTTP resource using the HTTP \"OPTIONS\"
-method.
-
-This function is similar to ‘http-get’, except it uses the \"OPTIONS\"
-method. See ‘http-get’ for full documentation on the various keyword
-arguments that are accepted by this function.
-
-Returns two values_ the resulting response, and the response body.")
-;;; HTTP messages
-
-;; Copyright (C) 2010-2016 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-;; 02110-1301 USA
-
-;;; Commentary_
-;;;
-;;; This module has a number of routines to parse textual
-;;; representations of HTTP data into native Scheme data structures.
-;;;
-;;; It tries to follow RFCs fairly strictly---the road to perdition
-;;; being paved with compatibility hacks---though some allowances are
-;;; made for not-too-divergent texts (like a quality of .2 which should
-;;; be 0.2, etc).
-;;;
-;;; Code_
-
-(define-module (web http)
- #\use-module ((srfi srfi-1) #\select (append-map! map!))
- #\use-module (srfi srfi-9)
- #\use-module (srfi srfi-19)
- #\use-module (ice-9 rdelim)
- #\use-module (ice-9 match)
- #\use-module (ice-9 q)
- #\use-module (ice-9 binary-ports)
- #\use-module (rnrs bytevectors)
- #\use-module (web uri)
- #\export (string->header
- header->string
-
- declare-header!
- declare-opaque-header!
- known-header?
- header-parser
- header-validator
- header-writer
-
- read-header
- parse-header
- valid-header?
- write-header
-
- read-headers
- write-headers
-
- parse-http-method
- parse-http-version
- parse-request-uri
-
- read-request-line
- write-request-line
- read-response-line
- write-response-line
-
- make-chunked-input-port
- make-chunked-output-port
-
- http-proxy-port?
- set-http-proxy-port?!))
-
-
-(define (string->header name)
- "Parse NAME to a symbolic header name."
- (string->symbol (string-downcase name)))
-
-(define-record-type <header-decl>
- (make-header-decl name parser validator writer multiple?)
- header-decl?
- (name header-decl-name)
- (parser header-decl-parser)
- (validator header-decl-validator)
- (writer header-decl-writer)
- (multiple? header-decl-multiple?))
-
-;; sym -> header
-(define *declared-headers* (make-hash-table))
-
-(define (lookup-header-decl sym)
- (hashq-ref *declared-headers* sym))
-
-(define* (declare-header! name
- parser
- validator
- writer
- #\key multiple?)
- "Declare a parser, validator, and writer for a given header."
- (if (and (string? name) parser validator writer)
- (let ((decl (make-header-decl name parser validator writer multiple?)))
- (hashq-set! *declared-headers* (string->header name) decl)
- decl)
- (error "bad header decl" name parser validator writer multiple?)))
-
-(define (header->string sym)
- "Return the string form for the header named SYM."
- (let ((decl (lookup-header-decl sym)))
- (if decl
- (header-decl-name decl)
- (string-titlecase (symbol->string sym)))))
-
-(define (known-header? sym)
- "Return ‘#t’ iff SYM is a known header, with associated
-parsers and serialization procedures."
- (and (lookup-header-decl sym) #t))
-
-(define (header-parser sym)
- "Return the value parser for headers named SYM. The result is a
-procedure that takes one argument, a string, and returns the parsed
-value. If the header isn't known to Guile, a default parser is returned
-that passes through the string unchanged."
- (let ((decl (lookup-header-decl sym)))
- (if decl
- (header-decl-parser decl)
- (lambda (x) x))))
-
-(define (header-validator sym)
- "Return a predicate which returns ‘#t’ if the given value is valid
-for headers named SYM. The default validator for unknown headers
-is ‘string?’."
- (let ((decl (lookup-header-decl sym)))
- (if decl
- (header-decl-validator decl)
- string?)))
-
-(define (header-writer sym)
- "Return a procedure that writes values for headers named SYM to a
-port. The resulting procedure takes two arguments_ a value and a port.
-The default writer is ‘display’."
- (let ((decl (lookup-header-decl sym)))
- (if decl
- (header-decl-writer decl)
- display)))
-
-(define (read-header-line port)
- "Read an HTTP header line and return it without its final CRLF or LF.
-Raise a 'bad-header' exception if the line does not end in CRLF or LF,
-or if EOF is reached."
- (match (%read-line port)
- (((? string? line) . #\newline)
- ;; '%read-line' does not consider #\return a delimiter; so if it's
- ;; there, remove it. We are more tolerant than the RFC in that we
- ;; tolerate LF-only endings.
- (if (string-suffix? "\r" line)
- (string-drop-right line 1)
- line))
- ((line . _) ;EOF or missing delimiter
- (bad-header 'read-header-line line))))
-
-(define (read-continuation-line port val)
- (if (or (eqv? (peek-char port) #\space)
- (eqv? (peek-char port) #\tab))
- (read-continuation-line port
- (string-append val
- (read-header-line port)))
- val))
-
-(define *eof* (call-with-input-string "" read))
-
-(define (read-header port)
- "Read one HTTP header from PORT. Return two values_ the header
-name and the parsed Scheme value. May raise an exception if the header
-was known but the value was invalid.
-
-Returns the end-of-file object for both values if the end of the message
-body was reached (i.e., a blank line)."
- (let ((line (read-header-line port)))
- (if (or (string-null? line)
- (string=? line "\r"))
- (values *eof* *eof*)
- (let* ((delim (or (string-index line #\_)
- (bad-header '%read line)))
- (sym (string->header (substring line 0 delim))))
- (values
- sym
- (parse-header
- sym
- (read-continuation-line
- port
- (string-trim-both line char-set_whitespace (1+ delim)))))))))
-
-(define (parse-header sym val)
- "Parse VAL, a string, with the parser registered for the header
-named SYM. Returns the parsed value."
- ((header-parser sym) val))
-
-(define (valid-header? sym val)
- "Returns a true value iff VAL is a valid Scheme value for the
-header with name SYM."
- (if (symbol? sym)
- ((header-validator sym) val)
- (error "header name not a symbol" sym)))
-
-(define (write-header sym val port)
- "Write the given header name and value to PORT, using the writer
-from ‘header-writer’."
- (display (header->string sym) port)
- (display "_ " port)
- ((header-writer sym) val port)
- (display "\r\n" port))
-
-(define (read-headers port)
- "Read the headers of an HTTP message from PORT, returning them
-as an ordered alist."
- (let lp ((headers '()))
- (call-with-values (lambda () (read-header port))
- (lambda (k v)
- (if (eof-object? k)
- (reverse! headers)
- (lp (acons k v headers)))))))
-
-(define (write-headers headers port)
- "Write the given header alist to PORT. Doesn't write the final
-‘\\r\\n’, as the user might want to add another header."
- (let lp ((headers headers))
- (if (pair? headers)
- (begin
- (write-header (caar headers) (cdar headers) port)
- (lp (cdr headers))))))
-
-
-
-
-;;;
-;;; Utilities
-;;;
-
-(define (bad-header sym val)
- (throw 'bad-header sym val))
-(define (bad-header-component sym val)
- (throw 'bad-header-component sym val))
-
-(define (bad-header-printer port key args default-printer)
- (apply (case-lambda
- ((sym val)
- (format port "Bad ~a header_ ~a\n" (header->string sym) val))
- (_ (default-printer)))
- args))
-(define (bad-header-component-printer port key args default-printer)
- (apply (case-lambda
- ((sym val)
- (format port "Bad ~a header component_ ~a\n" sym val))
- (_ (default-printer)))
- args))
-(set-exception-printer! 'bad-header bad-header-printer)
-(set-exception-printer! 'bad-header-component bad-header-component-printer)
-
-(define (parse-opaque-string str)
- str)
-(define (validate-opaque-string val)
- (string? val))
-(define (write-opaque-string val port)
- (display val port))
-
-(define separators-without-slash
- (string->char-set "[^][()<>@,;_\\\"?= \t]"))
-(define (validate-media-type str)
- (let ((idx (string-index str #\/)))
- (and idx (= idx (string-rindex str #\/))
- (not (string-index str separators-without-slash)))))
-(define (parse-media-type str)
- (if (validate-media-type str)
- (string->symbol str)
- (bad-header-component 'media-type str)))
-
-(define* (skip-whitespace str #\optional (start 0) (end (string-length str)))
- (let lp ((i start))
- (if (and (< i end) (char-whitespace? (string-ref str i)))
- (lp (1+ i))
- i)))
-
-(define* (trim-whitespace str #\optional (start 0) (end (string-length str)))
- (let lp ((i end))
- (if (and (< start i) (char-whitespace? (string-ref str (1- i))))
- (lp (1- i))
- i)))
-
-(define* (split-and-trim str #\optional (delim #\,)
- (start 0) (end (string-length str)))
- (let lp ((i start))
- (if (< i end)
- (let* ((idx (string-index str delim i end))
- (tok (string-trim-both str char-set_whitespace i (or idx end))))
- (cons tok (split-and-trim str delim (if idx (1+ idx) end) end)))
- '())))
-
-(define (list-of-strings? val)
- (list-of? val string?))
-
-(define (write-list-of-strings val port)
- (write-list val port display ", "))
-
-(define (split-header-names str)
- (map string->header (split-and-trim str)))
-
-(define (list-of-header-names? val)
- (list-of? val symbol?))
-
-(define (write-header-list val port)
- (write-list val port
- (lambda (x port)
- (display (header->string x) port))
- ", "))
-
-(define (collect-escaped-string from start len escapes)
- (let ((to (make-string len)))
- (let lp ((start start) (i 0) (escapes escapes))
- (if (null? escapes)
- (begin
- (substring-move! from start (+ start (- len i)) to i)
- to)
- (let* ((e (car escapes))
- (next-start (+ start (- e i) 2)))
- (substring-move! from start (- next-start 2) to i)
- (string-set! to e (string-ref from (- next-start 1)))
- (lp next-start (1+ e) (cdr escapes)))))))
-
-;; in incremental mode, returns two values_ the string, and the index at
-;; which the string ended
-(define* (parse-qstring str #\optional
- (start 0) (end (trim-whitespace str start))
- #\key incremental?)
- (if (and (< start end) (eqv? (string-ref str start) #\"))
- (let lp ((i (1+ start)) (qi 0) (escapes '()))
- (if (< i end)
- (case (string-ref str i)
- ((#\\)
- (lp (+ i 2) (1+ qi) (cons qi escapes)))
- ((#\")
- (let ((out (collect-escaped-string str (1+ start) qi escapes)))
- (if incremental?
- (values out (1+ i))
- (if (= (1+ i) end)
- out
- (bad-header-component 'qstring str)))))
- (else
- (lp (1+ i) (1+ qi) escapes)))
- (bad-header-component 'qstring str)))
- (bad-header-component 'qstring str)))
-
-(define (write-list l port write-item delim)
- (if (pair? l)
- (let lp ((l l))
- (write-item (car l) port)
- (if (pair? (cdr l))
- (begin
- (display delim port)
- (lp (cdr l)))))))
-
-(define (write-qstring str port)
- (display #\" port)
- (if (string-index str #\")
- ;; optimize me
- (write-list (string-split str #\") port display "\\\"")
- (display str port))
- (display #\" port))
-
-(define* (parse-quality str #\optional (start 0) (end (string-length str)))
- (define (char->decimal c)
- (let ((i (- (char->integer c) (char->integer #\0))))
- (if (and (<= 0 i) (< i 10))
- i
- (bad-header-component 'quality str))))
- (cond
- ((not (< start end))
- (bad-header-component 'quality str))
- ((eqv? (string-ref str start) #\1)
- (if (or (string= str "1" start end)
- (string= str "1." start end)
- (string= str "1.0" start end)
- (string= str "1.00" start end)
- (string= str "1.000" start end))
- 1000
- (bad-header-component 'quality str)))
- ((eqv? (string-ref str start) #\0)
- (if (or (string= str "0" start end)
- (string= str "0." start end))
- 0
- (if (< 2 (- end start) 6)
- (let lp ((place 1) (i (+ start 4)) (q 0))
- (if (= i (1+ start))
- (if (eqv? (string-ref str (1+ start)) #\.)
- q
- (bad-header-component 'quality str))
- (lp (* 10 place) (1- i)
- (if (< i end)
- (+ q (* place (char->decimal (string-ref str i))))
- q))))
- (bad-header-component 'quality str))))
- ;; Allow the nonstandard .2 instead of 0.2.
- ((and (eqv? (string-ref str start) #\.)
- (< 1 (- end start) 5))
- (let lp ((place 1) (i (+ start 3)) (q 0))
- (if (= i start)
- q
- (lp (* 10 place) (1- i)
- (if (< i end)
- (+ q (* place (char->decimal (string-ref str i))))
- q)))))
- (else
- (bad-header-component 'quality str))))
-
-(define (valid-quality? q)
- (and (non-negative-integer? q) (<= q 1000)))
-
-(define (write-quality q port)
- (define (digit->char d)
- (integer->char (+ (char->integer #\0) d)))
- (display (digit->char (modulo (quotient q 1000) 10)) port)
- (display #\. port)
- (display (digit->char (modulo (quotient q 100) 10)) port)
- (display (digit->char (modulo (quotient q 10) 10)) port)
- (display (digit->char (modulo q 10)) port))
-
-(define (list-of? val pred)
- (or (null? val)
- (and (pair? val)
- (pred (car val))
- (list-of? (cdr val) pred))))
-
-(define* (parse-quality-list str)
- (map (lambda (part)
- (cond
- ((string-rindex part #\;)
- => (lambda (idx)
- (let ((qpart (string-trim-both part char-set_whitespace (1+ idx))))
- (if (string-prefix? "q=" qpart)
- (cons (parse-quality qpart 2)
- (string-trim-both part char-set_whitespace 0 idx))
- (bad-header-component 'quality qpart)))))
- (else
- (cons 1000 (string-trim-both part char-set_whitespace)))))
- (string-split str #\,)))
-
-(define (validate-quality-list l)
- (list-of? l
- (lambda (elt)
- (and (pair? elt)
- (valid-quality? (car elt))
- (string? (cdr elt))))))
-
-(define (write-quality-list l port)
- (write-list l port
- (lambda (x port)
- (let ((q (car x))
- (str (cdr x)))
- (display str port)
- (if (< q 1000)
- (begin
- (display ";q=" port)
- (write-quality q port)))))
- ","))
-
-(define* (parse-non-negative-integer val #\optional (start 0)
- (end (string-length val)))
- (define (char->decimal c)
- (let ((i (- (char->integer c) (char->integer #\0))))
- (if (and (<= 0 i) (< i 10))
- i
- (bad-header-component 'non-negative-integer val))))
- (if (not (< start end))
- (bad-header-component 'non-negative-integer val)
- (let lp ((i start) (out 0))
- (if (< i end)
- (lp (1+ i)
- (+ (* out 10) (char->decimal (string-ref val i))))
- out))))
-
-(define (non-negative-integer? code)
- (and (number? code) (>= code 0) (exact? code) (integer? code)))
-
-(define (default-val-parser k val)
- val)
-
-(define (default-val-validator k val)
- (or (not val) (string? val)))
-
-(define (default-val-writer k val port)
- (if (or (string-index val #\;)
- (string-index val #\,)
- (string-index val #\"))
- (write-qstring val port)
- (display val port)))
-
-(define* (parse-key-value-list str #\optional
- (val-parser default-val-parser)
- (start 0) (end (string-length str)))
- (let lp ((i start) (out '()))
- (if (not (< i end))
- (reverse! out)
- (let* ((i (skip-whitespace str i end))
- (eq (string-index str #\= i end))
- (comma (string-index str #\, i end))
- (delim (min (or eq end) (or comma end)))
- (k (string->symbol
- (substring str i (trim-whitespace str i delim)))))
- (call-with-values
- (lambda ()
- (if (and eq (or (not comma) (< eq comma)))
- (let ((i (skip-whitespace str (1+ eq) end)))
- (if (and (< i end) (eqv? (string-ref str i) #\"))
- (parse-qstring str i end #\incremental? #t)
- (values (substring str i
- (trim-whitespace str i
- (or comma end)))
- (or comma end))))
- (values #f delim)))
- (lambda (v-str next-i)
- (let ((v (val-parser k v-str))
- (i (skip-whitespace str next-i end)))
- (if (or (= i end) (eqv? (string-ref str i) #\,))
- (lp (1+ i) (cons (if v (cons k v) k) out))
- (bad-header-component 'key-value-list
- (substring str start end))))))))))
-
-(define* (key-value-list? list #\optional
- (valid? default-val-validator))
- (list-of? list
- (lambda (elt)
- (cond
- ((pair? elt)
- (let ((k (car elt))
- (v (cdr elt)))
- (and (symbol? k)
- (valid? k v))))
- ((symbol? elt)
- (valid? elt #f))
- (else #f)))))
-
-(define* (write-key-value-list list port #\optional
- (val-writer default-val-writer) (delim ", "))
- (write-list
- list port
- (lambda (x port)
- (let ((k (if (pair? x) (car x) x))
- (v (if (pair? x) (cdr x) #f)))
- (display k port)
- (if v
- (begin
- (display #\= port)
- (val-writer k v port)))))
- delim))
-
-;; param-component = token [ "=" (token | quoted-string) ] \
-;; *(";" token [ "=" (token | quoted-string) ])
-;;
-(define param-delimiters (char-set #\, #\; #\=))
-(define param-value-delimiters (char-set-adjoin char-set_whitespace #\, #\;))
-(define* (parse-param-component str #\optional
- (val-parser default-val-parser)
- (start 0) (end (string-length str)))
- (let lp ((i start) (out '()))
- (if (not (< i end))
- (values (reverse! out) end)
- (let ((delim (string-index str param-delimiters i)))
- (let ((k (string->symbol
- (substring str i (trim-whitespace str i (or delim end)))))
- (delimc (and delim (string-ref str delim))))
- (case delimc
- ((#\=)
- (call-with-values
- (lambda ()
- (let ((i (skip-whitespace str (1+ delim) end)))
- (if (and (< i end) (eqv? (string-ref str i) #\"))
- (parse-qstring str i end #\incremental? #t)
- (let ((delim
- (or (string-index str param-value-delimiters
- i end)
- end)))
- (values (substring str i delim)
- delim)))))
- (lambda (v-str next-i)
- (let* ((v (val-parser k v-str))
- (x (if v (cons k v) k))
- (i (skip-whitespace str next-i end)))
- (case (and (< i end) (string-ref str i))
- ((#f)
- (values (reverse! (cons x out)) end))
- ((#\;)
- (lp (skip-whitespace str (1+ i) end)
- (cons x out)))
- (else ; including #\,
- (values (reverse! (cons x out)) i)))))))
- ((#\;)
- (let ((v (val-parser k #f)))
- (lp (skip-whitespace str (1+ delim) end)
- (cons (if v (cons k v) k) out))))
-
- (else ;; either the end of the string or a #\,
- (let ((v (val-parser k #f)))
- (values (reverse! (cons (if v (cons k v) k) out))
- (or delim end))))))))))
-
-(define* (parse-param-list str #\optional
- (val-parser default-val-parser)
- (start 0) (end (string-length str)))
- (let lp ((i start) (out '()))
- (call-with-values
- (lambda () (parse-param-component str val-parser i end))
- (lambda (item i)
- (if (< i end)
- (if (eqv? (string-ref str i) #\,)
- (lp (skip-whitespace str (1+ i) end)
- (cons item out))
- (bad-header-component 'param-list str))
- (reverse! (cons item out)))))))
-
-(define* (validate-param-list list #\optional
- (valid? default-val-validator))
- (list-of? list
- (lambda (elt)
- (key-value-list? elt valid?))))
-
-(define* (write-param-list list port #\optional
- (val-writer default-val-writer))
- (write-list
- list port
- (lambda (item port)
- (write-key-value-list item port val-writer ";"))
- ","))
-
-(define-syntax string-match?
- (lambda (x)
- (syntax-case x ()
- ((_ str pat) (string? (syntax->datum #'pat))
- (let ((p (syntax->datum #'pat)))
- #`(let ((s str))
- (and
- (= (string-length s) #,(string-length p))
- #,@(let lp ((i 0) (tests '()))
- (if (< i (string-length p))
- (let ((c (string-ref p i)))
- (lp (1+ i)
- (case c
- ((#\.) ; Whatever.
- tests)
- ((#\d) ; Digit.
- (cons #`(char-numeric? (string-ref s #,i))
- tests))
- ((#\a) ; Alphabetic.
- (cons #`(char-alphabetic? (string-ref s #,i))
- tests))
- (else ; Literal.
- (cons #`(eqv? (string-ref s #,i) #,c)
- tests)))))
- tests)))))))))
-
-;; "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun"
-;; "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec"
-
-(define (parse-month str start end)
- (define (bad)
- (bad-header-component 'month (substring str start end)))
- (if (not (= (- end start) 3))
- (bad)
- (let ((a (string-ref str (+ start 0)))
- (b (string-ref str (+ start 1)))
- (c (string-ref str (+ start 2))))
- (case a
- ((#\J)
- (case b
- ((#\a) (case c ((#\n) 1) (else (bad))))
- ((#\u) (case c ((#\n) 6) ((#\l) 7) (else (bad))))
- (else (bad))))
- ((#\F)
- (case b
- ((#\e) (case c ((#\b) 2) (else (bad))))
- (else (bad))))
- ((#\M)
- (case b
- ((#\a) (case c ((#\r) 3) ((#\y) 5) (else (bad))))
- (else (bad))))
- ((#\A)
- (case b
- ((#\p) (case c ((#\r) 4) (else (bad))))
- ((#\u) (case c ((#\g) 8) (else (bad))))
- (else (bad))))
- ((#\S)
- (case b
- ((#\e) (case c ((#\p) 9) (else (bad))))
- (else (bad))))
- ((#\O)
- (case b
- ((#\c) (case c ((#\t) 10) (else (bad))))
- (else (bad))))
- ((#\N)
- (case b
- ((#\o) (case c ((#\v) 11) (else (bad))))
- (else (bad))))
- ((#\D)
- (case b
- ((#\e) (case c ((#\c) 12) (else (bad))))
- (else (bad))))
- (else (bad))))))
-
-;; "GMT" | "+" 4DIGIT | "-" 4DIGIT
-;;
-;; RFC 2616 requires date values to use "GMT", but recommends accepting
-;; the others as they are commonly generated by e.g. RFC 822 sources.
-(define (parse-zone-offset str start)
- (let ((s (substring str start)))
- (define (bad)
- (bad-header-component 'zone-offset s))
- (cond
- ((string=? s "GMT")
- 0)
- ((string=? s "UTC")
- 0)
- ((string-match? s ".dddd")
- (let ((sign (case (string-ref s 0)
- ((#\+) +1)
- ((#\-) -1)
- (else (bad))))
- (hours (parse-non-negative-integer s 1 3))
- (minutes (parse-non-negative-integer s 3 5)))
- (* sign 60 (+ (* 60 hours) minutes)))) ; seconds east of Greenwich
- (else (bad)))))
-
-;; RFC 822, updated by RFC 1123
-;;
-;; Sun, 06 Nov 1994 08_49_37 GMT
-;; 01234567890123456789012345678
-;; 0 1 2
-(define (parse-rfc-822-date str space zone-offset)
- ;; We could verify the day of the week but we don't.
- (cond ((string-match? (substring str 0 space) "aaa, dd aaa dddd dd_dd_dd")
- (let ((date (parse-non-negative-integer str 5 7))
- (month (parse-month str 8 11))
- (year (parse-non-negative-integer str 12 16))
- (hour (parse-non-negative-integer str 17 19))
- (minute (parse-non-negative-integer str 20 22))
- (second (parse-non-negative-integer str 23 25)))
- (make-date 0 second minute hour date month year zone-offset)))
- ((string-match? (substring str 0 space) "aaa, d aaa dddd dd_dd_dd")
- (let ((date (parse-non-negative-integer str 5 6))
- (month (parse-month str 7 10))
- (year (parse-non-negative-integer str 11 15))
- (hour (parse-non-negative-integer str 16 18))
- (minute (parse-non-negative-integer str 19 21))
- (second (parse-non-negative-integer str 22 24)))
- (make-date 0 second minute hour date month year zone-offset)))
-
- ;; The next two clauses match dates that have a space instead of
- ;; a leading zero for hours, like " 8_49_37".
- ((string-match? (substring str 0 space) "aaa, dd aaa dddd d_dd_dd")
- (let ((date (parse-non-negative-integer str 5 7))
- (month (parse-month str 8 11))
- (year (parse-non-negative-integer str 12 16))
- (hour (parse-non-negative-integer str 18 19))
- (minute (parse-non-negative-integer str 20 22))
- (second (parse-non-negative-integer str 23 25)))
- (make-date 0 second minute hour date month year zone-offset)))
- ((string-match? (substring str 0 space) "aaa, d aaa dddd d_dd_dd")
- (let ((date (parse-non-negative-integer str 5 6))
- (month (parse-month str 7 10))
- (year (parse-non-negative-integer str 11 15))
- (hour (parse-non-negative-integer str 17 18))
- (minute (parse-non-negative-integer str 19 21))
- (second (parse-non-negative-integer str 22 24)))
- (make-date 0 second minute hour date month year zone-offset)))
-
- (else
- (bad-header 'date str) ; prevent tail call
- #f)))
-
-;; RFC 850, updated by RFC 1036
-;; Sunday, 06-Nov-94 08_49_37 GMT
-;; 0123456789012345678901
-;; 0 1 2
-(define (parse-rfc-850-date str comma space zone-offset)
- ;; We could verify the day of the week but we don't.
- (let ((tail (substring str (1+ comma) space)))
- (if (not (string-match? tail " dd-aaa-dd dd_dd_dd"))
- (bad-header 'date str))
- (let ((date (parse-non-negative-integer tail 1 3))
- (month (parse-month tail 4 7))
- (year (parse-non-negative-integer tail 8 10))
- (hour (parse-non-negative-integer tail 11 13))
- (minute (parse-non-negative-integer tail 14 16))
- (second (parse-non-negative-integer tail 17 19)))
- (make-date 0 second minute hour date month
- (let* ((now (date-year (current-date)))
- (then (+ now year (- (modulo now 100)))))
- (cond ((< (+ then 50) now) (+ then 100))
- ((< (+ now 50) then) (- then 100))
- (else then)))
- zone-offset))))
-
-;; ANSI C's asctime() format
-;; Sun Nov 6 08_49_37 1994
-;; 012345678901234567890123
-;; 0 1 2
-(define (parse-asctime-date str)
- (if (not (string-match? str "aaa aaa .d dd_dd_dd dddd"))
- (bad-header 'date str))
- (let ((date (parse-non-negative-integer
- str
- (if (eqv? (string-ref str 8) #\space) 9 8)
- 10))
- (month (parse-month str 4 7))
- (year (parse-non-negative-integer str 20 24))
- (hour (parse-non-negative-integer str 11 13))
- (minute (parse-non-negative-integer str 14 16))
- (second (parse-non-negative-integer str 17 19)))
- (make-date 0 second minute hour date month year 0)))
-
-;; Convert all date values to GMT time zone, as per RFC 2616 appendix C.
-(define (normalize-date date)
- (if (zero? (date-zone-offset date))
- date
- (time-utc->date (date->time-utc date) 0)))
-
-(define (parse-date str)
- (let* ((space (string-rindex str #\space))
- (zone-offset (and space (false-if-exception
- (parse-zone-offset str (1+ space))))))
- (normalize-date
- (if zone-offset
- (let ((comma (string-index str #\,)))
- (cond ((not comma) (bad-header 'date str))
- ((= comma 3) (parse-rfc-822-date str space zone-offset))
- (else (parse-rfc-850-date str comma space zone-offset))))
- (parse-asctime-date str)))))
-
-(define (write-date date port)
- (define (display-digits n digits port)
- (define zero (char->integer #\0))
- (let lp ((tens (expt 10 (1- digits))))
- (if (> tens 0)
- (begin
- (display (integer->char (+ zero (modulo (truncate/ n tens) 10)))
- port)
- (lp (floor/ tens 10))))))
- (let ((date (if (zero? (date-zone-offset date))
- date
- (time-tai->date (date->time-tai date) 0))))
- (display (case (date-week-day date)
- ((0) "Sun, ") ((1) "Mon, ") ((2) "Tue, ")
- ((3) "Wed, ") ((4) "Thu, ") ((5) "Fri, ")
- ((6) "Sat, ") (else (error "bad date" date)))
- port)
- (display-digits (date-day date) 2 port)
- (display (case (date-month date)
- ((1) " Jan ") ((2) " Feb ") ((3) " Mar ")
- ((4) " Apr ") ((5) " May ") ((6) " Jun ")
- ((7) " Jul ") ((8) " Aug ") ((9) " Sep ")
- ((10) " Oct ") ((11) " Nov ") ((12) " Dec ")
- (else (error "bad date" date)))
- port)
- (display-digits (date-year date) 4 port)
- (display #\space port)
- (display-digits (date-hour date) 2 port)
- (display #\_ port)
- (display-digits (date-minute date) 2 port)
- (display #\_ port)
- (display-digits (date-second date) 2 port)
- (display " GMT" port)))
-
-;; Following https_//tools.ietf.org/html/rfc7232#section-2.3, an entity
-;; tag should really be a qstring. However there are a number of
-;; servers that emit etags as unquoted strings. Assume that if the
-;; value doesn't start with a quote, it's an unquoted strong etag.
-(define (parse-entity-tag val)
- (cond
- ((string-prefix? "W/" val) (cons (parse-qstring val 2) #f))
- ((string-prefix? "\"" val) (cons (parse-qstring val) #t))
- (else (cons val #t))))
-
-(define (entity-tag? val)
- (and (pair? val)
- (string? (car val))))
-
-(define (write-entity-tag val port)
- (if (not (cdr val))
- (display "W/" port))
- (write-qstring (car val) port))
-
-(define* (parse-entity-tag-list val #\optional
- (start 0) (end (string-length val)))
- (let ((strong? (not (string-prefix? "W/" val 0 2 start end))))
- (call-with-values (lambda ()
- (parse-qstring val (if strong? start (+ start 2))
- end #\incremental? #t))
- (lambda (tag next)
- (acons tag strong?
- (let ((next (skip-whitespace val next end)))
- (if (< next end)
- (if (eqv? (string-ref val next) #\,)
- (parse-entity-tag-list
- val
- (skip-whitespace val (1+ next) end)
- end)
- (bad-header-component 'entity-tag-list val))
- '())))))))
-
-(define (entity-tag-list? val)
- (list-of? val entity-tag?))
-
-(define (write-entity-tag-list val port)
- (write-list val port write-entity-tag ", "))
-
-;; credentials = auth-scheme #auth-param
-;; auth-scheme = token
-;; auth-param = token "=" ( token | quoted-string )
-;;
-;; That's what the spec says. In reality the Basic scheme doesn't have
-;; k-v pairs, just one auth token, so we give that token as a string.
-;;
-(define* (parse-credentials str #\optional (val-parser default-val-parser)
- (start 0) (end (string-length str)))
- (let* ((start (skip-whitespace str start end))
- (delim (or (string-index str char-set_whitespace start end) end)))
- (if (= start end)
- (bad-header-component 'authorization str))
- (let ((scheme (string->symbol
- (string-downcase (substring str start (or delim end))))))
- (case scheme
- ((basic)
- (let* ((start (skip-whitespace str delim end)))
- (if (< start end)
- (cons scheme (substring str start end))
- (bad-header-component 'credentials str))))
- (else
- (cons scheme (parse-key-value-list str default-val-parser delim end)))))))
-
-(define (validate-credentials val)
- (and (pair? val) (symbol? (car val))
- (case (car val)
- ((basic) (string? (cdr val)))
- (else (key-value-list? (cdr val))))))
-
-(define (write-credentials val port)
- (display (car val) port)
- (display #\space port)
- (case (car val)
- ((basic) (display (cdr val) port))
- (else (write-key-value-list (cdr val) port))))
-
-;; challenges = 1#challenge
-;; challenge = auth-scheme 1*SP 1#auth-param
-;;
-;; A pain to parse, as both challenges and auth params are delimited by
-;; commas, and qstrings can contain anything. We rely on auth params
-;; necessarily having "=" in them.
-;;
-(define* (parse-challenge str #\optional
- (start 0) (end (string-length str)))
- (let* ((start (skip-whitespace str start end))
- (sp (string-index str #\space start end))
- (scheme (if sp
- (string->symbol (string-downcase (substring str start sp)))
- (bad-header-component 'challenge str))))
- (let lp ((i sp) (out (list scheme)))
- (if (not (< i end))
- (values (reverse! out) end)
- (let* ((i (skip-whitespace str i end))
- (eq (string-index str #\= i end))
- (comma (string-index str #\, i end))
- (delim (min (or eq end) (or comma end)))
- (token-end (trim-whitespace str i delim)))
- (if (string-index str #\space i token-end)
- (values (reverse! out) i)
- (let ((k (string->symbol (substring str i token-end))))
- (call-with-values
- (lambda ()
- (if (and eq (or (not comma) (< eq comma)))
- (let ((i (skip-whitespace str (1+ eq) end)))
- (if (and (< i end) (eqv? (string-ref str i) #\"))
- (parse-qstring str i end #\incremental? #t)
- (values (substring
- str i
- (trim-whitespace str i
- (or comma end)))
- (or comma end))))
- (values #f delim)))
- (lambda (v next-i)
- (let ((i (skip-whitespace str next-i end)))
- (if (or (= i end) (eqv? (string-ref str i) #\,))
- (lp (1+ i) (cons (if v (cons k v) k) out))
- (bad-header-component
- 'challenge
- (substring str start end)))))))))))))
-
-(define* (parse-challenges str #\optional (val-parser default-val-parser)
- (start 0) (end (string-length str)))
- (let lp ((i start) (ret '()))
- (let ((i (skip-whitespace str i end)))
- (if (< i end)
- (call-with-values (lambda () (parse-challenge str i end))
- (lambda (challenge i)
- (lp i (cons challenge ret))))
- (reverse ret)))))
-
-(define (validate-challenges val)
- (list-of? val (lambda (x)
- (and (pair? x) (symbol? (car x))
- (key-value-list? (cdr x))))))
-
-(define (write-challenge val port)
- (display (car val) port)
- (display #\space port)
- (write-key-value-list (cdr val) port))
-
-(define (write-challenges val port)
- (write-list val port write-challenge ", "))
-
-
-
-
-;;;
-;;; Request-Line and Response-Line
-;;;
-
-;; Hmm.
-(define (bad-request message . args)
- (throw 'bad-request message args))
-(define (bad-response message . args)
- (throw 'bad-response message args))
-
-(define *known-versions* '())
-
-(define* (parse-http-version str #\optional (start 0) (end (string-length str)))
- "Parse an HTTP version from STR, returning it as a major–minor
-pair. For example, ‘HTTP/1.1’ parses as the pair of integers,
-‘(1 . 1)’."
- (or (let lp ((known *known-versions*))
- (and (pair? known)
- (if (string= str (caar known) start end)
- (cdar known)
- (lp (cdr known)))))
- (let ((dot-idx (string-index str #\. start end)))
- (if (and (string-prefix? "HTTP/" str 0 5 start end)
- dot-idx
- (= dot-idx (string-rindex str #\. start end)))
- (cons (parse-non-negative-integer str (+ start 5) dot-idx)
- (parse-non-negative-integer str (1+ dot-idx) end))
- (bad-header-component 'http-version (substring str start end))))))
-
-(define (write-http-version val port)
- "Write the given major-minor version pair to PORT."
- (display "HTTP/" port)
- (display (car val) port)
- (display #\. port)
- (display (cdr val) port))
-
-(for-each
- (lambda (v)
- (set! *known-versions*
- (acons v (parse-http-version v 0 (string-length v))
- *known-versions*)))
- '("HTTP/1.0" "HTTP/1.1"))
-
-
-;; Request-URI = "*" | absoluteURI | abs_path | authority
-;;
-;; The `authority' form is only permissible for the CONNECT method, so
-;; because we don't expect people to implement CONNECT, we save
-;; ourselves the trouble of that case, and disallow the CONNECT method.
-;;
-(define* (parse-http-method str #\optional (start 0) (end (string-length str)))
- "Parse an HTTP method from STR. The result is an upper-case
-symbol, like ‘GET’."
- (cond
- ((string= str "GET" start end) 'GET)
- ((string= str "HEAD" start end) 'HEAD)
- ((string= str "POST" start end) 'POST)
- ((string= str "PUT" start end) 'PUT)
- ((string= str "DELETE" start end) 'DELETE)
- ((string= str "OPTIONS" start end) 'OPTIONS)
- ((string= str "TRACE" start end) 'TRACE)
- (else (bad-request "Invalid method_ ~a" (substring str start end)))))
-
-(define* (parse-request-uri str #\optional (start 0) (end (string-length str)))
- "Parse a URI from an HTTP request line. Note that URIs in requests do
-not have to have a scheme or host name. The result is a URI object."
- (cond
- ((= start end)
- (bad-request "Missing Request-URI"))
- ((string= str "*" start end)
- #f)
- ((eqv? (string-ref str start) #\/)
- (let* ((q (string-index str #\? start end))
- (f (string-index str #\# start end))
- (q (and q (or (not f) (< q f)) q)))
- (build-uri 'http
- #\path (substring str start (or q f end))
- #\query (and q (substring str (1+ q) (or f end)))
- #\fragment (and f (substring str (1+ f) end)))))
- (else
- (or (string->uri (substring str start end))
- (bad-request "Invalid URI_ ~a" (substring str start end))))))
-
-(define (read-request-line port)
- "Read the first line of an HTTP request from PORT, returning
-three values_ the method, the URI, and the version."
- (let* ((line (read-header-line port))
- (d0 (string-index line char-set_whitespace)) ; "delimiter zero"
- (d1 (string-rindex line char-set_whitespace)))
- (if (and d0 d1 (< d0 d1))
- (values (parse-http-method line 0 d0)
- (parse-request-uri line (skip-whitespace line (1+ d0) d1) d1)
- (parse-http-version line (1+ d1) (string-length line)))
- (bad-request "Bad Request-Line_ ~s" line))))
-
-(define (write-uri uri port)
- (if (uri-host uri)
- (begin
- (display (uri-scheme uri) port)
- (display "_//" port)
- (if (uri-userinfo uri)
- (begin
- (display (uri-userinfo uri) port)
- (display #\@ port)))
- (display (uri-host uri) port)
- (let ((p (uri-port uri)))
- (if (and p (not (eqv? p 80)))
- (begin
- (display #\_ port)
- (display p port))))))
- (let* ((path (uri-path uri))
- (len (string-length path)))
- (cond
- ((and (> len 0) (not (eqv? (string-ref path 0) #\/)))
- (bad-request "Non-absolute URI path_ ~s" path))
- ((and (zero? len) (not (uri-host uri)))
- (bad-request "Empty path and no host for URI_ ~s" uri))
- (else
- (display path port))))
- (if (uri-query uri)
- (begin
- (display #\? port)
- (display (uri-query uri) port))))
-
-(define (write-request-line method uri version port)
- "Write the first line of an HTTP request to PORT."
- (display method port)
- (display #\space port)
- (when (http-proxy-port? port)
- (let ((scheme (uri-scheme uri))
- (host (uri-host uri))
- (host-port (uri-port uri)))
- (when (and scheme host)
- (display scheme port)
- (display "_//" port)
- (if (string-index host #\_)
- (begin (display #\[ port)
- (display host port)
- (display #\] port))
- (display host port))
- (unless ((@@ (web uri) default-port?) scheme host-port)
- (display #\_ port)
- (display host-port port)))))
- (let ((path (uri-path uri))
- (query (uri-query uri)))
- (if (string-null? path)
- (display "/" port)
- (display path port))
- (if query
- (begin
- (display "?" port)
- (display query port))))
- (display #\space port)
- (write-http-version version port)
- (display "\r\n" port))
-
-(define (read-response-line port)
- "Read the first line of an HTTP response from PORT, returning three
-values_ the HTTP version, the response code, and the (possibly empty)
-\"reason phrase\"."
- (let* ((line (read-header-line port))
- (d0 (string-index line char-set_whitespace)) ; "delimiter zero"
- (d1 (and d0 (string-index line char-set_whitespace
- (skip-whitespace line d0)))))
- (if (and d0 d1)
- (values (parse-http-version line 0 d0)
- (parse-non-negative-integer line (skip-whitespace line d0 d1)
- d1)
- (string-trim-both line char-set_whitespace d1))
- (bad-response "Bad Response-Line_ ~s" line))))
-
-(define (write-response-line version code reason-phrase port)
- "Write the first line of an HTTP response to PORT."
- (write-http-version version port)
- (display #\space port)
- (display code port)
- (display #\space port)
- (display reason-phrase port)
- (display "\r\n" port))
-
-
-
-
-;;;
-;;; Helpers for declaring headers
-;;;
-
-;; emacs_ (put 'declare-header! 'scheme-indent-function 1)
-;; emacs_ (put 'declare-opaque!-header 'scheme-indent-function 1)
-(define (declare-opaque-header! name)
- "Declares a given header as \"opaque\", meaning that its value is not
-treated specially, and is just returned as a plain string."
- (declare-header! name
- parse-opaque-string validate-opaque-string write-opaque-string))
-
-;; emacs_ (put 'declare-date-header! 'scheme-indent-function 1)
-(define (declare-date-header! name)
- (declare-header! name
- parse-date date? write-date))
-
-;; emacs_ (put 'declare-string-list-header! 'scheme-indent-function 1)
-(define (declare-string-list-header! name)
- (declare-header! name
- split-and-trim list-of-strings? write-list-of-strings))
-
-;; emacs_ (put 'declare-symbol-list-header! 'scheme-indent-function 1)
-(define (declare-symbol-list-header! name)
- (declare-header! name
- (lambda (str)
- (map string->symbol (split-and-trim str)))
- (lambda (v)
- (list-of? v symbol?))
- (lambda (v port)
- (write-list v port display ", "))))
-
-;; emacs_ (put 'declare-header-list-header! 'scheme-indent-function 1)
-(define (declare-header-list-header! name)
- (declare-header! name
- split-header-names list-of-header-names? write-header-list))
-
-;; emacs_ (put 'declare-integer-header! 'scheme-indent-function 1)
-(define (declare-integer-header! name)
- (declare-header! name
- parse-non-negative-integer non-negative-integer? display))
-
-;; emacs_ (put 'declare-uri-header! 'scheme-indent-function 1)
-(define (declare-uri-header! name)
- (declare-header! name
- (lambda (str) (or (string->uri str) (bad-header-component 'uri str)))
- (@@ (web uri) absolute-uri?)
- write-uri))
-
-;; emacs_ (put 'declare-relative-uri-header! 'scheme-indent-function 1)
-(define (declare-relative-uri-header! name)
- (declare-header! name
- (lambda (str)
- (or ((@@ (web uri) string->uri*) str)
- (bad-header-component 'uri str)))
- uri?
- write-uri))
-
-;; emacs_ (put 'declare-quality-list-header! 'scheme-indent-function 1)
-(define (declare-quality-list-header! name)
- (declare-header! name
- parse-quality-list validate-quality-list write-quality-list))
-
-;; emacs_ (put 'declare-param-list-header! 'scheme-indent-function 1)
-(define* (declare-param-list-header! name #\optional
- (val-parser default-val-parser)
- (val-validator default-val-validator)
- (val-writer default-val-writer))
- (declare-header! name
- (lambda (str) (parse-param-list str val-parser))
- (lambda (val) (validate-param-list val val-validator))
- (lambda (val port) (write-param-list val port val-writer))))
-
-;; emacs_ (put 'declare-key-value-list-header! 'scheme-indent-function 1)
-(define* (declare-key-value-list-header! name #\optional
- (val-parser default-val-parser)
- (val-validator default-val-validator)
- (val-writer default-val-writer))
- (declare-header! name
- (lambda (str) (parse-key-value-list str val-parser))
- (lambda (val) (key-value-list? val val-validator))
- (lambda (val port) (write-key-value-list val port val-writer))))
-
-;; emacs_ (put 'declare-entity-tag-list-header! 'scheme-indent-function 1)
-(define (declare-entity-tag-list-header! name)
- (declare-header! name
- (lambda (str) (if (string=? str "*") '* (parse-entity-tag-list str)))
- (lambda (val) (or (eq? val '*) (entity-tag-list? val)))
- (lambda (val port)
- (if (eq? val '*)
- (display "*" port)
- (write-entity-tag-list val port)))))
-
-;; emacs_ (put 'declare-credentials-header! 'scheme-indent-function 1)
-(define (declare-credentials-header! name)
- (declare-header! name
- parse-credentials validate-credentials write-credentials))
-
-;; emacs_ (put 'declare-challenge-list-header! 'scheme-indent-function 1)
-(define (declare-challenge-list-header! name)
- (declare-header! name
- parse-challenges validate-challenges write-challenges))
-
-
-
-
-;;;
-;;; General headers
-;;;
-
-;; Cache-Control = 1#(cache-directive)
-;; cache-directive = cache-request-directive | cache-response-directive
-;; cache-request-directive =
-;; "no-cache" ; Section 14.9.1
-;; | "no-store" ; Section 14.9.2
-;; | "max-age" "=" delta-seconds ; Section 14.9.3, 14.9.4
-;; | "max-stale" [ "=" delta-seconds ] ; Section 14.9.3
-;; | "min-fresh" "=" delta-seconds ; Section 14.9.3
-;; | "no-transform" ; Section 14.9.5
-;; | "only-if-cached" ; Section 14.9.4
-;; | cache-extension ; Section 14.9.6
-;; cache-response-directive =
-;; "public" ; Section 14.9.1
-;; | "private" [ "=" <"> 1#field-name <"> ] ; Section 14.9.1
-;; | "no-cache" [ "=" <"> 1#field-name <"> ]; Section 14.9.1
-;; | "no-store" ; Section 14.9.2
-;; | "no-transform" ; Section 14.9.5
-;; | "must-revalidate" ; Section 14.9.4
-;; | "proxy-revalidate" ; Section 14.9.4
-;; | "max-age" "=" delta-seconds ; Section 14.9.3
-;; | "s-maxage" "=" delta-seconds ; Section 14.9.3
-;; | cache-extension ; Section 14.9.6
-;; cache-extension = token [ "=" ( token | quoted-string ) ]
-;;
-(declare-key-value-list-header! "Cache-Control"
- (lambda (k v-str)
- (case k
- ((max-age min-fresh s-maxage)
- (parse-non-negative-integer v-str))
- ((max-stale)
- (and v-str (parse-non-negative-integer v-str)))
- ((private no-cache)
- (and v-str (split-header-names v-str)))
- (else v-str)))
- (lambda (k v)
- (case k
- ((max-age min-fresh s-maxage)
- (non-negative-integer? v))
- ((max-stale)
- (or (not v) (non-negative-integer? v)))
- ((private no-cache)
- (or (not v) (list-of-header-names? v)))
- ((no-store no-transform only-if-cache must-revalidate proxy-revalidate)
- (not v))
- (else
- (or (not v) (string? v)))))
- (lambda (k v port)
- (cond
- ((string? v) (default-val-writer k v port))
- ((pair? v)
- (display #\" port)
- (write-header-list v port)
- (display #\" port))
- ((integer? v)
- (display v port))
- (else
- (bad-header-component 'cache-control v)))))
-
-;; Connection = "Connection" "_" 1#(connection-token)
-;; connection-token = token
-;; e.g.
-;; Connection_ close, Foo-Header
-;;
-(declare-header! "Connection"
- split-header-names
- list-of-header-names?
- (lambda (val port)
- (write-list val port
- (lambda (x port)
- (display (if (eq? x 'close)
- "close"
- (header->string x))
- port))
- ", ")))
-
-;; Date = "Date" "_" HTTP-date
-;; e.g.
-;; Date_ Tue, 15 Nov 1994 08_12_31 GMT
-;;
-(declare-date-header! "Date")
-
-;; Pragma = "Pragma" "_" 1#pragma-directive
-;; pragma-directive = "no-cache" | extension-pragma
-;; extension-pragma = token [ "=" ( token | quoted-string ) ]
-;;
-(declare-key-value-list-header! "Pragma")
-
-;; Trailer = "Trailer" "_" 1#field-name
-;;
-(declare-header-list-header! "Trailer")
-
-;; Transfer-Encoding = "Transfer-Encoding" "_" 1#transfer-coding
-;;
-(declare-param-list-header! "Transfer-Encoding")
-
-;; Upgrade = "Upgrade" "_" 1#product
-;;
-(declare-string-list-header! "Upgrade")
-
-;; Via = "Via" "_" 1#( received-protocol received-by [ comment ] )
-;; received-protocol = [ protocol-name "/" ] protocol-version
-;; protocol-name = token
-;; protocol-version = token
-;; received-by = ( host [ "_" port ] ) | pseudonym
-;; pseudonym = token
-;;
-(declare-header! "Via"
- split-and-trim
- list-of-strings?
- write-list-of-strings
- #\multiple? #t)
-
-;; Warning = "Warning" "_" 1#warning-value
-;;
-;; warning-value = warn-code SP warn-agent SP warn-text
-;; [SP warn-date]
-;;
-;; warn-code = 3DIGIT
-;; warn-agent = ( host [ "_" port ] ) | pseudonym
-;; ; the name or pseudonym of the server adding
-;; ; the Warning header, for use in debugging
-;; warn-text = quoted-string
-;; warn-date = <"> HTTP-date <">
-(declare-header! "Warning"
- (lambda (str)
- (let ((len (string-length str)))
- (let lp ((i (skip-whitespace str 0)))
- (let* ((idx1 (string-index str #\space i))
- (idx2 (string-index str #\space (1+ idx1))))
- (if (and idx1 idx2)
- (let ((code (parse-non-negative-integer str i idx1))
- (agent (substring str (1+ idx1) idx2)))
- (call-with-values
- (lambda () (parse-qstring str (1+ idx2) #\incremental? #t))
- (lambda (text i)
- (call-with-values
- (lambda ()
- (let ((c (and (< i len) (string-ref str i))))
- (case c
- ((#\space)
- ;; we have a date.
- (call-with-values
- (lambda () (parse-qstring str (1+ i)
- #\incremental? #t))
- (lambda (date i)
- (values text (parse-date date) i))))
- (else
- (values text #f i)))))
- (lambda (text date i)
- (let ((w (list code agent text date))
- (c (and (< i len) (string-ref str i))))
- (case c
- ((#f) (list w))
- ((#\,) (cons w (lp (skip-whitespace str (1+ i)))))
- (else (bad-header 'warning str))))))))))))))
- (lambda (val)
- (list-of? val
- (lambda (elt)
- (and (list? elt)
- (= (length elt) 4)
- (apply (lambda (code host text date)
- (and (non-negative-integer? code) (< code 1000)
- (string? host)
- (string? text)
- (or (not date) (date? date))))
- elt)))))
- (lambda (val port)
- (write-list
- val port
- (lambda (w port)
- (apply
- (lambda (code host text date)
- (display code port)
- (display #\space port)
- (display host port)
- (display #\space port)
- (write-qstring text port)
- (if date
- (begin
- (display #\space port)
- (write-date date port))))
- w))
- ", "))
- #\multiple? #t)
-
-
-
-
-;;;
-;;; Entity headers
-;;;
-
-;; Allow = #Method
-;;
-(declare-symbol-list-header! "Allow")
-
-;; Content-Disposition = disposition-type *( ";" disposition-parm )
-;; disposition-type = "attachment" | disp-extension-token
-;; disposition-parm = filename-parm | disp-extension-parm
-;; filename-parm = "filename" "=" quoted-string
-;; disp-extension-token = token
-;; disp-extension-parm = token "=" ( token | quoted-string )
-;;
-(declare-header! "Content-Disposition"
- (lambda (str)
- (let ((disposition (parse-param-list str default-val-parser)))
- ;; Lazily reuse the param list parser.
- (unless (and (pair? disposition)
- (null? (cdr disposition)))
- (bad-header-component 'content-disposition str))
- (car disposition)))
- (lambda (val)
- (and (pair? val)
- (symbol? (car val))
- (list-of? (cdr val)
- (lambda (x)
- (and (pair? x) (symbol? (car x)) (string? (cdr x)))))))
- (lambda (val port)
- (write-param-list (list val) port)))
-
-;; Content-Encoding = 1#content-coding
-;;
-(declare-symbol-list-header! "Content-Encoding")
-
-;; Content-Language = 1#language-tag
-;;
-(declare-string-list-header! "Content-Language")
-
-;; Content-Length = 1*DIGIT
-;;
-(declare-integer-header! "Content-Length")
-
-;; Content-Location = ( absoluteURI | relativeURI )
-;;
-(declare-relative-uri-header! "Content-Location")
-
-;; Content-MD5 = <base64 of 128 bit MD5 digest as per RFC 1864>
-;;
-(declare-opaque-header! "Content-MD5")
-
-;; Content-Range = content-range-spec
-;; content-range-spec = byte-content-range-spec
-;; byte-content-range-spec = bytes-unit SP
-;; byte-range-resp-spec "/"
-;; ( instance-length | "*" )
-;; byte-range-resp-spec = (first-byte-pos "-" last-byte-pos)
-;; | "*"
-;; instance-length = 1*DIGIT
-;;
-(declare-header! "Content-Range"
- (lambda (str)
- (let ((dash (string-index str #\-))
- (slash (string-index str #\/)))
- (if (and (string-prefix? "bytes " str) slash)
- (list 'bytes
- (cond
- (dash
- (cons
- (parse-non-negative-integer str 6 dash)
- (parse-non-negative-integer str (1+ dash) slash)))
- ((string= str "*" 6 slash)
- '*)
- (else
- (bad-header 'content-range str)))
- (if (string= str "*" (1+ slash))
- '*
- (parse-non-negative-integer str (1+ slash))))
- (bad-header 'content-range str))))
- (lambda (val)
- (and (list? val) (= (length val) 3)
- (symbol? (car val))
- (let ((x (cadr val)))
- (or (eq? x '*)
- (and (pair? x)
- (non-negative-integer? (car x))
- (non-negative-integer? (cdr x)))))
- (let ((x (caddr val)))
- (or (eq? x '*)
- (non-negative-integer? x)))))
- (lambda (val port)
- (display (car val) port)
- (display #\space port)
- (if (eq? (cadr val) '*)
- (display #\* port)
- (begin
- (display (caadr val) port)
- (display #\- port)
- (display (caadr val) port)))
- (if (eq? (caddr val) '*)
- (display #\* port)
- (display (caddr val) port))))
-
-;; Content-Type = media-type
-;;
-(declare-header! "Content-Type"
- (lambda (str)
- (let ((parts (string-split str #\;)))
- (cons (parse-media-type (car parts))
- (map (lambda (x)
- (let ((eq (string-index x #\=)))
- (if (and eq (= eq (string-rindex x #\=)))
- (cons
- (string->symbol
- (string-trim x char-set_whitespace 0 eq))
- (string-trim-right x char-set_whitespace (1+ eq)))
- (bad-header 'content-type str))))
- (cdr parts)))))
- (lambda (val)
- (and (pair? val)
- (symbol? (car val))
- (list-of? (cdr val)
- (lambda (x)
- (and (pair? x) (symbol? (car x)) (string? (cdr x)))))))
- (lambda (val port)
- (display (car val) port)
- (if (pair? (cdr val))
- (begin
- (display ";" port)
- (write-list
- (cdr val) port
- (lambda (pair port)
- (display (car pair) port)
- (display #\= port)
- (display (cdr pair) port))
- ";")))))
-
-;; Expires = HTTP-date
-;;
-(define *date-in-the-past* (parse-date "Thu, 01 Jan 1970 00_00_00 GMT"))
-
-(declare-header! "Expires"
- (lambda (str)
- (if (member str '("0" "-1"))
- *date-in-the-past*
- (parse-date str)))
- date?
- write-date)
-
-;; Last-Modified = HTTP-date
-;;
-(declare-date-header! "Last-Modified")
-
-
-
-
-;;;
-;;; Request headers
-;;;
-
-;; Accept = #( media-range [ accept-params ] )
-;; media-range = ( "*/*" | ( type "/" "*" ) | ( type "/" subtype ) )
-;; *( ";" parameter )
-;; accept-params = ";" "q" "=" qvalue *( accept-extension )
-;; accept-extension = ";" token [ "=" ( token | quoted-string ) ]
-;;
-(declare-param-list-header! "Accept"
- ;; -> (type/subtype (sym-prop . str-val) ...) ...)
- ;;
- ;; with the exception of prop `q', in which case the val will be a
- ;; valid quality value
- ;;
- (lambda (k v)
- (if (eq? k 'q)
- (parse-quality v)
- v))
- (lambda (k v)
- (if (eq? k 'q)
- (valid-quality? v)
- (or (not v) (string? v))))
- (lambda (k v port)
- (if (eq? k 'q)
- (write-quality v port)
- (default-val-writer k v port))))
-
-;; Accept-Charset = 1#( ( charset | "*" )[ ";" "q" "=" qvalue ] )
-;;
-(declare-quality-list-header! "Accept-Charset")
-
-;; Accept-Encoding = 1#( codings [ ";" "q" "=" qvalue ] )
-;; codings = ( content-coding | "*" )
-;;
-(declare-quality-list-header! "Accept-Encoding")
-
-;; Accept-Language = 1#( language-range [ ";" "q" "=" qvalue ] )
-;; language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
-;;
-(declare-quality-list-header! "Accept-Language")
-
-;; Authorization = credentials
-;; credentials = auth-scheme #auth-param
-;; auth-scheme = token
-;; auth-param = token "=" ( token | quoted-string )
-;;
-(declare-credentials-header! "Authorization")
-
-;; Expect = 1#expectation
-;; expectation = "100-continue" | expectation-extension
-;; expectation-extension = token [ "=" ( token | quoted-string )
-;; *expect-params ]
-;; expect-params = ";" token [ "=" ( token | quoted-string ) ]
-;;
-(declare-param-list-header! "Expect")
-
-;; From = mailbox
-;;
-;; Should be an email address; we just pass on the string as-is.
-;;
-(declare-opaque-header! "From")
-
-;; Host = host [ "_" port ]
-;;
-(declare-header! "Host"
- (lambda (str)
- (let* ((rbracket (string-index str #\]))
- (colon (string-index str #\_ (or rbracket 0)))
- (host (cond
- (rbracket
- (unless (eqv? (string-ref str 0) #\[)
- (bad-header 'host str))
- (substring str 1 rbracket))
- (colon
- (substring str 0 colon))
- (else
- str)))
- (port (and colon
- (parse-non-negative-integer str (1+ colon)))))
- (cons host port)))
- (lambda (val)
- (and (pair? val)
- (string? (car val))
- (or (not (cdr val))
- (non-negative-integer? (cdr val)))))
- (lambda (val port)
- (if (string-index (car val) #\_)
- (begin
- (display #\[ port)
- (display (car val) port)
- (display #\] port))
- (display (car val) port))
- (if (cdr val)
- (begin
- (display #\_ port)
- (display (cdr val) port)))))
-
-;; If-Match = ( "*" | 1#entity-tag )
-;;
-(declare-entity-tag-list-header! "If-Match")
-
-;; If-Modified-Since = HTTP-date
-;;
-(declare-date-header! "If-Modified-Since")
-
-;; If-None-Match = ( "*" | 1#entity-tag )
-;;
-(declare-entity-tag-list-header! "If-None-Match")
-
-;; If-Range = ( entity-tag | HTTP-date )
-;;
-(declare-header! "If-Range"
- (lambda (str)
- (if (or (string-prefix? "\"" str)
- (string-prefix? "W/" str))
- (parse-entity-tag str)
- (parse-date str)))
- (lambda (val)
- (or (date? val) (entity-tag? val)))
- (lambda (val port)
- (if (date? val)
- (write-date val port)
- (write-entity-tag val port))))
-
-;; If-Unmodified-Since = HTTP-date
-;;
-(declare-date-header! "If-Unmodified-Since")
-
-;; Max-Forwards = 1*DIGIT
-;;
-(declare-integer-header! "Max-Forwards")
-
-;; Proxy-Authorization = credentials
-;;
-(declare-credentials-header! "Proxy-Authorization")
-
-;; Range = "Range" "_" ranges-specifier
-;; ranges-specifier = byte-ranges-specifier
-;; byte-ranges-specifier = bytes-unit "=" byte-range-set
-;; byte-range-set = 1#( byte-range-spec | suffix-byte-range-spec )
-;; byte-range-spec = first-byte-pos "-" [last-byte-pos]
-;; first-byte-pos = 1*DIGIT
-;; last-byte-pos = 1*DIGIT
-;; suffix-byte-range-spec = "-" suffix-length
-;; suffix-length = 1*DIGIT
-;;
-(declare-header! "Range"
- (lambda (str)
- (if (string-prefix? "bytes=" str)
- (cons
- 'bytes
- (map (lambda (x)
- (let ((dash (string-index x #\-)))
- (cond
- ((not dash)
- (bad-header 'range str))
- ((zero? dash)
- (cons #f (parse-non-negative-integer x 1)))
- ((= dash (1- (string-length x)))
- (cons (parse-non-negative-integer x 0 dash) #f))
- (else
- (cons (parse-non-negative-integer x 0 dash)
- (parse-non-negative-integer x (1+ dash)))))))
- (string-split (substring str 6) #\,)))
- (bad-header 'range str)))
- (lambda (val)
- (and (pair? val)
- (symbol? (car val))
- (list-of? (cdr val)
- (lambda (elt)
- (and (pair? elt)
- (let ((x (car elt)) (y (cdr elt)))
- (and (or x y)
- (or (not x) (non-negative-integer? x))
- (or (not y) (non-negative-integer? y)))))))))
- (lambda (val port)
- (display (car val) port)
- (display #\= port)
- (write-list
- (cdr val) port
- (lambda (pair port)
- (if (car pair)
- (display (car pair) port))
- (display #\- port)
- (if (cdr pair)
- (display (cdr pair) port)))
- ",")))
-
-;; Referer = ( absoluteURI | relativeURI )
-;;
-(declare-relative-uri-header! "Referer")
-
-;; TE = #( t-codings )
-;; t-codings = "trailers" | ( transfer-extension [ accept-params ] )
-;;
-(declare-param-list-header! "TE")
-
-;; User-Agent = 1*( product | comment )
-;;
-(declare-opaque-header! "User-Agent")
-
-
-
-
-;;;
-;;; Reponse headers
-;;;
-
-;; Accept-Ranges = acceptable-ranges
-;; acceptable-ranges = 1#range-unit | "none"
-;;
-(declare-symbol-list-header! "Accept-Ranges")
-
-;; Age = age-value
-;; age-value = delta-seconds
-;;
-(declare-integer-header! "Age")
-
-;; ETag = entity-tag
-;;
-(declare-header! "ETag"
- parse-entity-tag
- entity-tag?
- write-entity-tag)
-
-;; Location = URI-reference
-;;
-(declare-relative-uri-header! "Location")
-
-;; Proxy-Authenticate = 1#challenge
-;;
-(declare-challenge-list-header! "Proxy-Authenticate")
-
-;; Retry-After = ( HTTP-date | delta-seconds )
-;;
-(declare-header! "Retry-After"
- (lambda (str)
- (if (and (not (string-null? str))
- (char-numeric? (string-ref str 0)))
- (parse-non-negative-integer str)
- (parse-date str)))
- (lambda (val)
- (or (date? val) (non-negative-integer? val)))
- (lambda (val port)
- (if (date? val)
- (write-date val port)
- (display val port))))
-
-;; Server = 1*( product | comment )
-;;
-(declare-opaque-header! "Server")
-
-;; Vary = ( "*" | 1#field-name )
-;;
-(declare-header! "Vary"
- (lambda (str)
- (if (equal? str "*")
- '*
- (split-header-names str)))
- (lambda (val)
- (or (eq? val '*) (list-of-header-names? val)))
- (lambda (val port)
- (if (eq? val '*)
- (display "*" port)
- (write-header-list val port))))
-
-;; WWW-Authenticate = 1#challenge
-;;
-(declare-challenge-list-header! "WWW-Authenticate")
-
-
-;; Chunked Responses
-(define (read-chunk-header port)
- "Read a chunk header from PORT and return the size in bytes of the
-upcoming chunk."
- (match (read-line port)
- ((? eof-object?)
- ;; Connection closed prematurely_ there's nothing left to read.
- 0)
- (str
- (let ((extension-start (string-index str
- (lambda (c)
- (or (char=? c #\;)
- (char=? c #\return))))))
- (string->number (if extension-start ; unnecessary?
- (substring str 0 extension-start)
- str)
- 16)))))
-
-(define* (make-chunked-input-port port #\key (keep-alive? #f))
- "Returns a new port which translates HTTP chunked transfer encoded
-data from PORT into a non-encoded format. Returns eof when it has
-read the final chunk from PORT. This does not necessarily mean
-that there is no more data on PORT. When the returned port is
-closed it will also close PORT, unless the KEEP-ALIVE? is true."
- (define (close)
- (unless keep-alive?
- (close-port port)))
-
- (define chunk-size 0) ;size of the current chunk
- (define remaining 0) ;number of bytes left from the current chunk
- (define finished? #f) ;did we get all the chunks?
-
- (define (read! bv idx to-read)
- (define (loop to-read num-read)
- (cond ((or finished? (zero? to-read))
- num-read)
- ((zero? remaining) ;get a new chunk
- (let ((size (read-chunk-header port)))
- (set! chunk-size size)
- (set! remaining size)
- (if (zero? size)
- (begin
- (set! finished? #t)
- num-read)
- (loop to-read num-read))))
- (else ;read from the current chunk
- (let* ((ask-for (min to-read remaining))
- (read (get-bytevector-n! port bv (+ idx num-read)
- ask-for)))
- (if (eof-object? read)
- (begin ;premature termination
- (set! finished? #t)
- num-read)
- (let ((left (- remaining read)))
- (set! remaining left)
- (when (zero? left)
- ;; We're done with this chunk; read CR and LF.
- (get-u8 port) (get-u8 port))
- (loop (- to-read read)
- (+ num-read read))))))))
- (loop to-read 0))
-
- (make-custom-binary-input-port "chunked input port" read! #f #f close))
-
-(define* (make-chunked-output-port port #\key (keep-alive? #f))
- "Returns a new port which translates non-encoded data into a HTTP
-chunked transfer encoded data and writes this to PORT. Data
-written to this port is buffered until the port is flushed, at which
-point it is all sent as one chunk. Take care to close the port when
-done, as it will output the remaining data, and encode the final zero
-chunk. When the port is closed it will also close PORT, unless
-KEEP-ALIVE? is true."
- (define (q-for-each f q)
- (while (not (q-empty? q))
- (f (deq! q))))
- (define queue (make-q))
- (define (put-char c)
- (enq! queue c))
- (define (put-string s)
- (string-for-each (lambda (c) (enq! queue c))
- s))
- (define (flush)
- ;; It is important that we do _not_ write a chunk if the queue is
- ;; empty, since it will be treated as the final chunk.
- (unless (q-empty? queue)
- (let ((len (q-length queue)))
- (display (number->string len 16) port)
- (display "\r\n" port)
- (q-for-each (lambda (elem) (write-char elem port))
- queue)
- (display "\r\n" port))))
- (define (close)
- (flush)
- (display "0\r\n" port)
- (force-output port)
- (unless keep-alive?
- (close-port port)))
- (make-soft-port (vector put-char put-string flush #f close) "w"))
-
-(define %http-proxy-port? (make-object-property))
-(define (http-proxy-port? port) (%http-proxy-port? port))
-(define (set-http-proxy-port?! port flag)
- (set! (%http-proxy-port? port) flag))
-;;; HTTP request objects
-
-;; Copyright (C) 2010, 2011, 2012 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-;; 02110-1301 USA
-
-;;; Code_
-
-(define-module (web request)
- #\use-module (rnrs bytevectors)
- #\use-module (ice-9 binary-ports)
- #\use-module (ice-9 rdelim)
- #\use-module (srfi srfi-9)
- #\use-module (web uri)
- #\use-module (web http)
- #\export (request?
- request-method
- request-uri
- request-version
- request-headers
- request-meta
- request-port
-
- read-request
- build-request
- write-request
-
- read-request-body
- write-request-body
-
- ;; General headers
- ;;
- request-cache-control
- request-connection
- request-date
- request-pragma
- request-trailer
- request-transfer-encoding
- request-upgrade
- request-via
- request-warning
-
- ;; Entity headers
- ;;
- request-allow
- request-content-encoding
- request-content-language
- request-content-length
- request-content-location
- request-content-md5
- request-content-range
- request-content-type
- request-expires
- request-last-modified
-
- ;; Request headers
- ;;
- request-accept
- request-accept-charset
- request-accept-encoding
- request-accept-language
- request-authorization
- request-expect
- request-from
- request-host
- request-if-match
- request-if-modified-since
- request-if-none-match
- request-if-range
- request-if-unmodified-since
- request-max-forwards
- request-proxy-authorization
- request-range
- request-referer
- request-te
- request-user-agent
-
- ;; Misc
- request-absolute-uri))
-
-
-;;; {Character Encodings, Strings, and Bytevectors}
-;;;
-;;; Requests are read from over the wire, and as such have to be treated
-;;; very carefully.
-;;;
-;;; The header portion of the message is defined to be in a subset of
-;;; ASCII, and may be processed either byte-wise (using bytevectors and
-;;; binary I/O) or as characters in a single-byte ASCII-compatible
-;;; encoding.
-;;;
-;;; We choose the latter, processing as strings in the latin-1
-;;; encoding. This allows us to use all the read-delimited machinery,
-;;; character sets, and regular expressions, shared substrings, etc.
-;;;
-;;; The characters in the header values may themselves encode other
-;;; bytes or characters -- basically each header has its own parser. We
-;;; leave that as a header-specific topic.
-;;;
-;;; The body is present if the content-length header is present. Its
-;;; format and, if textual, encoding is determined by the headers, but
-;;; its length is encoded in bytes. So we just slurp that number of
-;;; characters in latin-1, knowing that the number of characters
-;;; corresponds to the number of bytes, and then convert to a
-;;; bytevector, perhaps for later decoding.
-;;;
-
-(define-record-type <request>
- (make-request method uri version headers meta port)
- request?
- (method request-method)
- (uri request-uri)
- (version request-version)
- (headers request-headers)
- (meta request-meta)
- (port request-port))
-
-(define (bad-request message . args)
- (throw 'bad-request message args))
-
-(define (bad-request-printer port key args default-printer)
- (apply (case-lambda
- ((msg args)
- (display "Bad request_ " port)
- (apply format port msg args)
- (newline port))
- (_ (default-printer)))
- args))
-
-(set-exception-printer! 'bad-request bad-request-printer)
-
-(define (non-negative-integer? n)
- (and (number? n) (>= n 0) (exact? n) (integer? n)))
-
-(define (validate-headers headers)
- (if (pair? headers)
- (let ((h (car headers)))
- (if (pair? h)
- (let ((k (car h)) (v (cdr h)))
- (if (valid-header? k v)
- (validate-headers (cdr headers))
- (bad-request "Bad value for header ~a_ ~s" k v)))
- (bad-request "Header not a pair_ ~a" h)))
- (if (not (null? headers))
- (bad-request "Headers not a list_ ~a" headers))))
-
-(define* (build-request uri #\key (method 'GET) (version '(1 . 1))
- (headers '()) port (meta '())
- (validate-headers? #t))
- "Construct an HTTP request object. If VALIDATE-HEADERS? is true,
-the headers are each run through their respective validators."
- (let ((needs-host? (and (equal? version '(1 . 1))
- (not (assq-ref headers 'host)))))
- (cond
- ((not (and (pair? version)
- (non-negative-integer? (car version))
- (non-negative-integer? (cdr version))))
- (bad-request "Bad version_ ~a" version))
- ((not (uri? uri))
- (bad-request "Bad uri_ ~a" uri))
- ((and (not port) (memq method '(POST PUT)))
- (bad-request "Missing port for message ~a" method))
- ((not (list? meta))
- (bad-request "Bad metadata alist" meta))
- ((and needs-host? (not (uri-host uri)))
- (bad-request "HTTP/1.1 request without Host header and no host in URI_ ~a"
- uri))
- (else
- (if validate-headers?
- (validate-headers headers))))
- (make-request method uri version
- (if needs-host?
- (acons 'host (cons (uri-host uri) (uri-port uri))
- headers)
- headers)
- meta port)))
-
-(define* (read-request port #\optional (meta '()))
- "Read an HTTP request from PORT, optionally attaching the given
-metadata, META.
-
-As a side effect, sets the encoding on PORT to
-ISO-8859-1 (latin-1), so that reading one character reads one byte. See
-the discussion of character sets in \"HTTP Requests\" in the manual, for
-more information.
-
-Note that the body is not part of the request. Once you have read a
-request, you may read the body separately, and likewise for writing
-requests."
- (set-port-encoding! port "ISO-8859-1")
- (call-with-values (lambda () (read-request-line port))
- (lambda (method uri version)
- (make-request method uri version (read-headers port) meta port))))
-
-;; FIXME_ really return a new request?
-(define (write-request r port)
- "Write the given HTTP request to PORT.
-
-Return a new request, whose ‘request-port’ will continue writing
-on PORT, perhaps using some transfer encoding."
- (write-request-line (request-method r) (request-uri r)
- (request-version r) port)
- (write-headers (request-headers r) port)
- (display "\r\n" port)
- (if (eq? port (request-port r))
- r
- (make-request (request-method r) (request-uri r) (request-version r)
- (request-headers r) (request-meta r) port)))
-
-(define (read-request-body r)
- "Reads the request body from R, as a bytevector. Return ‘#f’
-if there was no request body."
- (let ((nbytes (request-content-length r)))
- (and nbytes
- (let ((bv (get-bytevector-n (request-port r) nbytes)))
- (if (= (bytevector-length bv) nbytes)
- bv
- (bad-request "EOF while reading request body_ ~a bytes of ~a"
- (bytevector-length bv) nbytes))))))
-
-(define (write-request-body r bv)
- "Write BV, a bytevector, to the port corresponding to the HTTP
-request R."
- (put-bytevector (request-port r) bv))
-
-(define-syntax define-request-accessor
- (lambda (x)
- (syntax-case x ()
- ((_ field)
- #'(define-request-accessor field #f))
- ((_ field def) (identifier? #'field)
- #`(define* (#,(datum->syntax
- #'field
- (symbol-append 'request- (syntax->datum #'field)))
- request
- #\optional (default def))
- (cond
- ((assq 'field (request-headers request)) => cdr)
- (else default)))))))
-
-;; General headers
-;;
-(define-request-accessor cache-control '())
-(define-request-accessor connection '())
-(define-request-accessor date #f)
-(define-request-accessor pragma '())
-(define-request-accessor trailer '())
-(define-request-accessor transfer-encoding '())
-(define-request-accessor upgrade '())
-(define-request-accessor via '())
-(define-request-accessor warning '())
-
-;; Entity headers
-;;
-(define-request-accessor allow '())
-(define-request-accessor content-encoding '())
-(define-request-accessor content-language '())
-(define-request-accessor content-length #f)
-(define-request-accessor content-location #f)
-(define-request-accessor content-md5 #f)
-(define-request-accessor content-range #f)
-(define-request-accessor content-type #f)
-(define-request-accessor expires #f)
-(define-request-accessor last-modified #f)
-
-;; Request headers
-;;
-(define-request-accessor accept '())
-(define-request-accessor accept-charset '())
-(define-request-accessor accept-encoding '())
-(define-request-accessor accept-language '())
-(define-request-accessor authorization #f)
-(define-request-accessor expect '())
-(define-request-accessor from #f)
-(define-request-accessor host #f)
-;; Absence of an if-directive appears to be different from `*'.
-(define-request-accessor if-match #f)
-(define-request-accessor if-modified-since #f)
-(define-request-accessor if-none-match #f)
-(define-request-accessor if-range #f)
-(define-request-accessor if-unmodified-since #f)
-(define-request-accessor max-forwards #f)
-(define-request-accessor proxy-authorization #f)
-(define-request-accessor range #f)
-(define-request-accessor referer #f)
-(define-request-accessor te '())
-(define-request-accessor user-agent #f)
-
-;; Misc accessors
-(define* (request-absolute-uri r #\optional default-host default-port)
- "A helper routine to determine the absolute URI of a request, using the
-‘host’ header and the default host and port."
- (let ((uri (request-uri r)))
- (if (uri-host uri)
- uri
- (let ((host
- (or (request-host r)
- (if default-host
- (cons default-host default-port)
- (bad-request
- "URI not absolute, no Host header, and no default_ ~s"
- uri)))))
- (build-uri (uri-scheme uri)
- #\host (car host)
- #\port (cdr host)
- #\path (uri-path uri)
- #\query (uri-query uri)
- #\fragment (uri-fragment uri))))))
-;;; HTTP response objects
-
-;; Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-;; 02110-1301 USA
-
-;;; Code_
-
-(define-module (web response)
- #\use-module (rnrs bytevectors)
- #\use-module (ice-9 binary-ports)
- #\use-module (ice-9 rdelim)
- #\use-module (ice-9 match)
- #\use-module (srfi srfi-9)
- #\use-module (web http)
- #\export (response?
- response-version
- response-code
- response-reason-phrase
- response-headers
- response-port
- read-response
- build-response
- adapt-response-version
- write-response
-
- response-must-not-include-body?
- response-body-port
- read-response-body
- write-response-body
-
- ;; General headers
- ;;
- response-cache-control
- response-connection
- response-date
- response-pragma
- response-trailer
- response-transfer-encoding
- response-upgrade
- response-via
- response-warning
-
- ;; Entity headers
- ;;
- response-allow
- response-content-encoding
- response-content-language
- response-content-length
- response-content-location
- response-content-md5
- response-content-range
- response-content-type
- text-content-type?
- response-expires
- response-last-modified
-
- ;; Response headers
- ;;
- response-accept-ranges
- response-age
- response-etag
- response-location
- response-proxy-authenticate
- response-retry-after
- response-server
- response-vary
- response-www-authenticate))
-
-
-(define-record-type <response>
- (make-response version code reason-phrase headers port)
- response?
- (version response-version)
- (code response-code)
- (reason-phrase %response-reason-phrase)
- (headers response-headers)
- (port response-port))
-
-(define (bad-response message . args)
- (throw 'bad-response message args))
-
-(define (non-negative-integer? n)
- (and (number? n) (>= n 0) (exact? n) (integer? n)))
-
-(define (validate-headers headers)
- (if (pair? headers)
- (let ((h (car headers)))
- (if (pair? h)
- (let ((k (car h)) (v (cdr h)))
- (if (valid-header? k v)
- (validate-headers (cdr headers))
- (bad-response "Bad value for header ~a_ ~s" k v)))
- (bad-response "Header not a pair_ ~a" h)))
- (if (not (null? headers))
- (bad-response "Headers not a list_ ~a" headers))))
-
-(define* (build-response #\key (version '(1 . 1)) (code 200) reason-phrase
- (headers '()) port (validate-headers? #t))
- "Construct an HTTP response object. If VALIDATE-HEADERS? is true,
-the headers are each run through their respective validators."
- (cond
- ((not (and (pair? version)
- (non-negative-integer? (car version))
- (non-negative-integer? (cdr version))))
- (bad-response "Bad version_ ~a" version))
- ((not (and (non-negative-integer? code) (< code 600)))
- (bad-response "Bad code_ ~a" code))
- ((and reason-phrase (not (string? reason-phrase)))
- (bad-response "Bad reason phrase" reason-phrase))
- (else
- (if validate-headers?
- (validate-headers headers))))
- (make-response version code reason-phrase headers port))
-
-(define *reason-phrases*
- '((100 . "Continue")
- (101 . "Switching Protocols")
- (200 . "OK")
- (201 . "Created")
- (202 . "Accepted")
- (203 . "Non-Authoritative Information")
- (204 . "No Content")
- (205 . "Reset Content")
- (206 . "Partial Content")
- (300 . "Multiple Choices")
- (301 . "Moved Permanently")
- (302 . "Found")
- (303 . "See Other")
- (304 . "Not Modified")
- (305 . "Use Proxy")
- (307 . "Temporary Redirect")
- (400 . "Bad Request")
- (401 . "Unauthorized")
- (402 . "Payment Required")
- (403 . "Forbidden")
- (404 . "Not Found")
- (405 . "Method Not Allowed")
- (406 . "Not Acceptable")
- (407 . "Proxy Authentication Required")
- (408 . "Request Timeout")
- (409 . "Conflict")
- (410 . "Gone")
- (411 . "Length Required")
- (412 . "Precondition Failed")
- (413 . "Request Entity Too Large")
- (414 . "Request-URI Too Long")
- (415 . "Unsupported Media Type")
- (416 . "Requested Range Not Satisfiable")
- (417 . "Expectation Failed")
- (500 . "Internal Server Error")
- (501 . "Not Implemented")
- (502 . "Bad Gateway")
- (503 . "Service Unavailable")
- (504 . "Gateway Timeout")
- (505 . "HTTP Version Not Supported")))
-
-(define (code->reason-phrase code)
- (or (assv-ref *reason-phrases* code)
- "(Unknown)"))
-
-(define (response-reason-phrase response)
- "Return the reason phrase given in RESPONSE, or the standard
-reason phrase for the response's code."
- (or (%response-reason-phrase response)
- (code->reason-phrase (response-code response))))
-
-(define (text-content-type? type)
- "Return #t if TYPE, a symbol as returned by `response-content-type',
-represents a textual type such as `text/plain'."
- (let ((type (symbol->string type)))
- (or (string-prefix? "text/" type)
- (string-suffix? "/xml" type)
- (string-suffix? "+xml" type))))
-
-(define (read-response port)
- "Read an HTTP response from PORT.
-
-As a side effect, sets the encoding on PORT to
-ISO-8859-1 (latin-1), so that reading one character reads one byte. See
-the discussion of character sets in \"HTTP Responses\" in the manual,
-for more information."
- (set-port-encoding! port "ISO-8859-1")
- (call-with-values (lambda () (read-response-line port))
- (lambda (version code reason-phrase)
- (make-response version code reason-phrase (read-headers port) port))))
-
-(define (adapt-response-version response version)
- "Adapt the given response to a different HTTP version. Returns a new
-HTTP response.
-
-The idea is that many applications might just build a response for the
-default HTTP version, and this method could handle a number of
-programmatic transformations to respond to older HTTP versions (0.9 and
-1.0). But currently this function is a bit heavy-handed, just updating
-the version field."
- (build-response #\code (response-code response)
- #\version version
- #\headers (response-headers response)
- #\port (response-port response)))
-
-(define (write-response r port)
- "Write the given HTTP response to PORT.
-
-Returns a new response, whose ‘response-port’ will continue writing
-on PORT, perhaps using some transfer encoding."
- (write-response-line (response-version r) (response-code r)
- (response-reason-phrase r) port)
- (write-headers (response-headers r) port)
- (display "\r\n" port)
- (if (eq? port (response-port r))
- r
- (make-response (response-version r) (response-code r)
- (response-reason-phrase r) (response-headers r) port)))
-
-(define (response-must-not-include-body? r)
- "Returns ‘#t’ if the response R is not permitted to have a body.
-
-This is true for some response types, like those with code 304."
- ;; RFC 2616, section 4.3.
- (or (<= 100 (response-code r) 199)
- (= (response-code r) 204)
- (= (response-code r) 304)))
-
-(define (make-delimited-input-port port len keep-alive?)
- "Return an input port that reads from PORT, and makes sure that
-exactly LEN bytes are available from PORT. Closing the returned port
-closes PORT, unless KEEP-ALIVE? is true."
- (define bytes-read 0)
-
- (define (fail)
- (bad-response "EOF while reading response body_ ~a bytes of ~a"
- bytes-read len))
-
- (define (read! bv start count)
- ;; Read at most LEN bytes in total. HTTP/1.1 doesn't say what to do
- ;; when a server provides more than the Content-Length, but it seems
- ;; wise to just stop reading at LEN.
- (let ((count (min count (- len bytes-read))))
- (let loop ((ret (get-bytevector-n! port bv start count)))
- (cond ((eof-object? ret)
- (if (= bytes-read len)
- 0 ; EOF
- (fail)))
- ((and (zero? ret) (> count 0))
- ;; Do not return zero since zero means EOF, so try again.
- (loop (get-bytevector-n! port bv start count)))
- (else
- (set! bytes-read (+ bytes-read ret))
- ret)))))
-
- (define close
- (and (not keep-alive?)
- (lambda ()
- (close-port port))))
-
- (make-custom-binary-input-port "delimited input port" read! #f #f close))
-
-(define* (response-body-port r #\key (decode? #t) (keep-alive? #t))
- "Return an input port from which the body of R can be read. The
-encoding of the returned port is set according to R's ‘content-type’
-header, when it's textual, except if DECODE? is ‘#f’. Return #f when
-no body is available.
-
-When KEEP-ALIVE? is ‘#f’, closing the returned port also closes R's
-response port."
- (define port
- (cond
- ((member '(chunked) (response-transfer-encoding r))
- (make-chunked-input-port (response-port r)
- #\keep-alive? keep-alive?))
- ((response-content-length r)
- => (lambda (len)
- (make-delimited-input-port (response-port r)
- len keep-alive?)))
- ((response-must-not-include-body? r)
- #f)
- ((or (memq 'close (response-connection r))
- (and (equal? (response-version r) '(1 . 0))
- (not (memq 'keep-alive (response-connection r)))))
- (response-port r))
- (else
- ;; Here we have a message with no transfer encoding, no
- ;; content-length, and a response that won't necessarily be closed
- ;; by the server. Not much we can do; assume that the client
- ;; knows how to handle it.
- (response-port r))))
-
- (when (and decode? port)
- (match (response-content-type r)
- (((? text-content-type?) . props)
- (set-port-encoding! port
- (or (assq-ref props 'charset)
- "ISO-8859-1")))
- (_ #f)))
-
- port)
-
-(define (read-response-body r)
- "Reads the response body from R, as a bytevector. Returns
-‘#f’ if there was no response body."
- (let ((body (and=> (response-body-port r #\decode? #f)
- get-bytevector-all)))
- ;; Reading a body of length 0 will result in get-bytevector-all
- ;; returning the EOF object.
- (if (eof-object? body)
- #vu8()
- body)))
-
-(define (write-response-body r bv)
- "Write BV, a bytevector, to the port corresponding to the HTTP
-response R."
- (put-bytevector (response-port r) bv))
-
-(define-syntax define-response-accessor
- (lambda (x)
- (syntax-case x ()
- ((_ field)
- #'(define-response-accessor field #f))
- ((_ field def) (identifier? #'field)
- #`(define* (#,(datum->syntax
- #'field
- (symbol-append 'response- (syntax->datum #'field)))
- response
- #\optional (default def))
- (cond
- ((assq 'field (response-headers response)) => cdr)
- (else default)))))))
-
-;; General headers
-;;
-(define-response-accessor cache-control '())
-(define-response-accessor connection '())
-(define-response-accessor date #f)
-(define-response-accessor pragma '())
-(define-response-accessor trailer '())
-(define-response-accessor transfer-encoding '())
-(define-response-accessor upgrade '())
-(define-response-accessor via '())
-(define-response-accessor warning '())
-
-;; Entity headers
-;;
-(define-response-accessor allow '())
-(define-response-accessor content-encoding '())
-(define-response-accessor content-language '())
-(define-response-accessor content-length #f)
-(define-response-accessor content-location #f)
-(define-response-accessor content-md5 #f)
-(define-response-accessor content-range #f)
-(define-response-accessor content-type #f)
-(define-response-accessor expires #f)
-(define-response-accessor last-modified #f)
-
-;; Response headers
-;;
-(define-response-accessor accept-ranges #f)
-(define-response-accessor age #f)
-(define-response-accessor etag #f)
-(define-response-accessor location #f)
-(define-response-accessor proxy-authenticate #f)
-(define-response-accessor retry-after #f)
-(define-response-accessor server #f)
-(define-response-accessor vary '())
-(define-response-accessor www-authenticate #f)
-;;; Web server
-
-;; Copyright (C) 2010, 2011, 2012, 2013, 2015 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-;; 02110-1301 USA
-
-;;; Commentary_
-;;;
-;;; (web server) is a generic web server interface, along with a main
-;;; loop implementation for web servers controlled by Guile.
-;;;
-;;; The lowest layer is the <server-impl> object, which defines a set of
-;;; hooks to open a server, read a request from a client, write a
-;;; response to a client, and close a server. These hooks -- open,
-;;; read, write, and close, respectively -- are bound together in a
-;;; <server-impl> object. Procedures in this module take a
-;;; <server-impl> object, if needed.
-;;;
-;;; A <server-impl> may also be looked up by name. If you pass the
-;;; `http' symbol to `run-server', Guile looks for a variable named
-;;; `http' in the `(web server http)' module, which should be bound to a
-;;; <server-impl> object. Such a binding is made by instantiation of
-;;; the `define-server-impl' syntax. In this way the run-server loop can
-;;; automatically load other backends if available.
-;;;
-;;; The life cycle of a server goes as follows_
-;;;
-;;; * The `open' hook is called, to open the server. `open' takes 0 or
-;;; more arguments, depending on the backend, and returns an opaque
-;;; server socket object, or signals an error.
-;;;
-;;; * The `read' hook is called, to read a request from a new client.
-;;; The `read' hook takes one arguments, the server socket. It
-;;; should return three values_ an opaque client socket, the
-;;; request, and the request body. The request should be a
-;;; `<request>' object, from `(web request)'. The body should be a
-;;; string or a bytevector, or `#f' if there is no body.
-;;;
-;;; If the read failed, the `read' hook may return #f for the client
-;;; socket, request, and body.
-;;;
-;;; * A user-provided handler procedure is called, with the request
-;;; and body as its arguments. The handler should return two
-;;; values_ the response, as a `<response>' record from `(web
-;;; response)', and the response body as a string, bytevector, or
-;;; `#f' if not present. We also allow the reponse to be simply an
-;;; alist of headers, in which case a default response object is
-;;; constructed with those headers.
-;;;
-;;; * The `write' hook is called with three arguments_ the client
-;;; socket, the response, and the body. The `write' hook returns no
-;;; values.
-;;;
-;;; * At this point the request handling is complete. For a loop, we
-;;; loop back and try to read a new request.
-;;;
-;;; * If the user interrupts the loop, the `close' hook is called on
-;;; the server socket.
-;;;
-;;; Code_
-
-(define-module (web server)
- #\use-module (srfi srfi-9)
- #\use-module (srfi srfi-9 gnu)
- #\use-module (rnrs bytevectors)
- #\use-module (ice-9 binary-ports)
- #\use-module (web request)
- #\use-module (web response)
- #\use-module (system repl error-handling)
- #\use-module (ice-9 control)
- #\use-module (ice-9 iconv)
- #\export (define-server-impl
- lookup-server-impl
-
- make-server-impl
- server-impl?
- server-impl-name
- server-impl-open
- server-impl-read
- server-impl-write
- server-impl-close
-
- open-server
- read-client
- handle-request
- sanitize-response
- write-client
- close-server
- serve-one-client
- run-server))
-
-(define *timer* (gettimeofday))
-(define (print-elapsed who)
- (let ((t (gettimeofday)))
- (pk who (+ (* (- (car t) (car *timer*)) 1000000)
- (- (cdr t) (cdr *timer*))))
- (set! *timer* t)))
-
-(eval-when (expand)
- (define *time-debug?* #f))
-
-(define-syntax debug-elapsed
- (lambda (x)
- (syntax-case x ()
- ((_ who)
- (if *time-debug?*
- #'(print-elapsed who)
- #'*unspecified*)))))
-
-(define-record-type server-impl
- (make-server-impl name open read write close)
- server-impl?
- (name server-impl-name)
- (open server-impl-open)
- (read server-impl-read)
- (write server-impl-write)
- (close server-impl-close))
-
-(define-syntax-rule (define-server-impl name open read write close)
- (define name
- (make-server-impl 'name open read write close)))
-
-(define (lookup-server-impl impl)
- "Look up a server implementation. If IMPL is a server
-implementation already, it is returned directly. If it is a symbol, the
-binding named IMPL in the ‘(web server IMPL)’ module is
-looked up. Otherwise an error is signaled.
-
-Currently a server implementation is a somewhat opaque type, useful only
-for passing to other procedures in this module, like
-‘read-client’."
- (cond
- ((server-impl? impl) impl)
- ((symbol? impl)
- (let ((impl (module-ref (resolve-module `(web server ,impl)) impl)))
- (if (server-impl? impl)
- impl
- (error "expected a server impl in module" `(web server ,impl)))))
- (else
- (error "expected a server-impl or a symbol" impl))))
-
-;; -> server
-(define (open-server impl open-params)
- "Open a server for the given implementation. Return one value, the
-new server object. The implementation's ‘open’ procedure is
-applied to OPEN-PARAMS, which should be a list."
- (apply (server-impl-open impl) open-params))
-
-;; -> (client request body | #f #f #f)
-(define (read-client impl server)
- "Read a new client from SERVER, by applying the implementation's
-‘read’ procedure to the server. If successful, return three
-values_ an object corresponding to the client, a request object, and the
-request body. If any exception occurs, return ‘#f’ for all three
-values."
- (call-with-error-handling
- (lambda ()
- ((server-impl-read impl) server))
- #\pass-keys '(quit interrupt)
- #\on-error (if (batch-mode?) 'backtrace 'debug)
- #\post-error (lambda _ (values #f #f #f))))
-
-(define (extend-response r k v . additional)
- (let ((r (set-field r (response-headers)
- (assoc-set! (copy-tree (response-headers r))
- k v))))
- (if (null? additional)
- r
- (apply extend-response r additional))))
-
-;; -> response body
-(define (sanitize-response request response body)
- "\"Sanitize\" the given response and body, making them appropriate for
-the given request.
-
-As a convenience to web handler authors, RESPONSE may be given as
-an alist of headers, in which case it is used to construct a default
-response. Ensures that the response version corresponds to the request
-version. If BODY is a string, encodes the string to a bytevector,
-in an encoding appropriate for RESPONSE. Adds a
-‘content-length’ and ‘content-type’ header, as necessary.
-
-If BODY is a procedure, it is called with a port as an argument,
-and the output collected as a bytevector. In the future we might try to
-instead use a compressing, chunk-encoded port, and call this procedure
-later, in the write-client procedure. Authors are advised not to rely
-on the procedure being called at any particular time."
- (cond
- ((list? response)
- (sanitize-response request
- (build-response #\version (request-version request)
- #\headers response)
- body))
- ((not (equal? (request-version request) (response-version response)))
- (sanitize-response request
- (adapt-response-version response
- (request-version request))
- body))
- ((not body)
- (values response #vu8()))
- ((string? body)
- (let* ((type (response-content-type response
- '(text/plain)))
- (declared-charset (assq-ref (cdr type) 'charset))
- (charset (or declared-charset "utf-8")))
- (sanitize-response
- request
- (if declared-charset
- response
- (extend-response response 'content-type
- `(,@type (charset . ,charset))))
- (string->bytevector body charset))))
- ((procedure? body)
- (let* ((type (response-content-type response
- '(text/plain)))
- (declared-charset (assq-ref (cdr type) 'charset))
- (charset (or declared-charset "utf-8")))
- (sanitize-response
- request
- (if declared-charset
- response
- (extend-response response 'content-type
- `(,@type (charset . ,charset))))
- (call-with-encoded-output-string charset body))))
- ((not (bytevector? body))
- (error "unexpected body type"))
- ((and (response-must-not-include-body? response)
- body
- ;; FIXME make this stricter_ even an empty body should be prohibited.
- (not (zero? (bytevector-length body))))
- (error "response with this status code must not include body" response))
- (else
- ;; check length; assert type; add other required fields?
- (values (let ((rlen (response-content-length response))
- (blen (bytevector-length body)))
- (cond
- (rlen (if (= rlen blen)
- response
- (error "bad content-length" rlen blen)))
- (else (extend-response response 'content-length blen))))
- (if (eq? (request-method request) 'HEAD)
- ;; Responses to HEAD requests must not include bodies.
- ;; We could raise an error here, but it seems more
- ;; appropriate to just do something sensible.
- #f
- body)))))
-
-;; -> response body state
-(define (handle-request handler request body state)
- "Handle a given request, returning the response and body.
-
-The response and response body are produced by calling the given
-HANDLER with REQUEST and BODY as arguments.
-
-The elements of STATE are also passed to HANDLER as
-arguments, and may be returned as additional values. The new
-STATE, collected from the HANDLER's return values, is then
-returned as a list. The idea is that a server loop receives a handler
-from the user, along with whatever state values the user is interested
-in, allowing the user's handler to explicitly manage its state."
- (call-with-error-handling
- (lambda ()
- (call-with-values (lambda ()
- (with-stack-and-prompt
- (lambda ()
- (apply handler request body state))))
- (lambda (response body . state)
- (call-with-values (lambda ()
- (debug-elapsed 'handler)
- (sanitize-response request response body))
- (lambda (response body)
- (debug-elapsed 'sanitize)
- (values response body state))))))
- #\pass-keys '(quit interrupt)
- #\on-error (if (batch-mode?) 'backtrace 'debug)
- #\post-error (lambda _
- (values (build-response #\code 500) #f state))))
-
-;; -> unspecified values
-(define (write-client impl server client response body)
- "Write an HTTP response and body to CLIENT. If the server and
-client support persistent connections, it is the implementation's
-responsibility to keep track of the client thereafter, presumably by
-attaching it to the SERVER argument somehow."
- (call-with-error-handling
- (lambda ()
- ((server-impl-write impl) server client response body))
- #\pass-keys '(quit interrupt)
- #\on-error (if (batch-mode?) 'backtrace 'debug)
- #\post-error (lambda _ (values))))
-
-;; -> unspecified values
-(define (close-server impl server)
- "Release resources allocated by a previous invocation of
-‘open-server’."
- ((server-impl-close impl) server))
-
-(define call-with-sigint
- (if (not (provided? 'posix))
- (lambda (thunk handler-thunk) (thunk))
- (lambda (thunk handler-thunk)
- (let ((handler #f))
- (catch 'interrupt
- (lambda ()
- (dynamic-wind
- (lambda ()
- (set! handler
- (sigaction SIGINT (lambda (sig) (throw 'interrupt)))))
- thunk
- (lambda ()
- (if handler
- ;; restore Scheme handler, SIG_IGN or SIG_DFL.
- (sigaction SIGINT (car handler) (cdr handler))
- ;; restore original C handler.
- (sigaction SIGINT #f)))))
- (lambda (k . _) (handler-thunk)))))))
-
-(define (with-stack-and-prompt thunk)
- (call-with-prompt (default-prompt-tag)
- (lambda () (start-stack #t (thunk)))
- (lambda (k proc)
- (with-stack-and-prompt (lambda () (proc k))))))
-
-;; -> new-state
-(define (serve-one-client handler impl server state)
- "Read one request from SERVER, call HANDLER on the request
-and body, and write the response to the client. Return the new state
-produced by the handler procedure."
- (debug-elapsed 'serve-again)
- (call-with-values
- (lambda ()
- (read-client impl server))
- (lambda (client request body)
- (debug-elapsed 'read-client)
- (if client
- (call-with-values
- (lambda ()
- (handle-request handler request body state))
- (lambda (response body state)
- (debug-elapsed 'handle-request)
- (write-client impl server client response body)
- (debug-elapsed 'write-client)
- state))
- state))))
-
-(define* (run-server handler #\optional (impl 'http) (open-params '())
- . state)
- "Run Guile's built-in web server.
-
-HANDLER should be a procedure that takes two or more arguments,
-the HTTP request and request body, and returns two or more values, the
-response and response body.
-
-For example, here is a simple \"Hello, World!\" server_
-
-@example
- (define (handler request body)
- (values '((content-type . (text/plain)))
- \"Hello, World!\"))
- (run-server handler)
-@end example
-
-The response and body will be run through ‘sanitize-response’
-before sending back to the client.
-
-Additional arguments to HANDLER are taken from
-STATE. Additional return values are accumulated into a new
-STATE, which will be used for subsequent requests. In this way a
-handler can explicitly manage its state.
-
-The default server implementation is ‘http’, which accepts
-OPEN-PARAMS like ‘(#:port 8081)’, among others. See \"Web
-Server\" in the manual, for more information."
- (let* ((impl (lookup-server-impl impl))
- (server (open-server impl open-params)))
- (call-with-sigint
- (lambda ()
- (let lp ((state state))
- (lp (serve-one-client handler impl server state))))
- (lambda ()
- (close-server impl server)
- (values)))))
-;;; Web I/O_ HTTP
-
-;; Copyright (C) 2010, 2011, 2012, 2015 Free Software Foundation, Inc.
-
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-;; 02110-1301 USA
-
-;;; Commentary_
-;;;
-;;; This is the HTTP implementation of the (web server) interface.
-;;;
-;;; `read-request' sets the character encoding on the new port to
-;;; latin-1. See the note in request.scm regarding character sets,
-;;; strings, and bytevectors for more information.
-;;;
-;;; Code_
-
-(define-module (web server http)
- #\use-module ((srfi srfi-1) #\select (fold))
- #\use-module (srfi srfi-9)
- #\use-module (rnrs bytevectors)
- #\use-module (web request)
- #\use-module (web response)
- #\use-module (web server)
- #\use-module (ice-9 poll)
- #\export (http))
-
-
-(define (make-default-socket family addr port)
- (let ((sock (socket PF_INET SOCK_STREAM 0)))
- (setsockopt sock SOL_SOCKET SO_REUSEADDR 1)
- (bind sock family addr port)
- sock))
-
-(define-record-type <http-server>
- (make-http-server socket poll-idx poll-set)
- http-server?
- (socket http-socket)
- (poll-idx http-poll-idx set-http-poll-idx!)
- (poll-set http-poll-set))
-
-(define *error-events* (logior POLLHUP POLLERR))
-(define *read-events* POLLIN)
-(define *events* (logior *error-events* *read-events*))
-
-;; -> server
-(define* (http-open #\key
- (host #f)
- (family AF_INET)
- (addr (if host
- (inet-pton family host)
- INADDR_LOOPBACK))
- (port 8080)
- (socket (make-default-socket family addr port)))
- (listen socket 128)
- (sigaction SIGPIPE SIG_IGN)
- (let ((poll-set (make-empty-poll-set)))
- (poll-set-add! poll-set socket *events*)
- (make-http-server socket 0 poll-set)))
-
-(define (bad-request port)
- (write-response (build-response #\version '(1 . 0) #\code 400
- #\headers '((content-length . 0)))
- port))
-
-;; -> (client request body | #f #f #f)
-(define (http-read server)
- (let* ((poll-set (http-poll-set server)))
- (let lp ((idx (http-poll-idx server)))
- (let ((revents (poll-set-revents poll-set idx)))
- (cond
- ((zero? idx)
- ;; The server socket, and the end of our downward loop.
- (cond
- ((zero? revents)
- ;; No client ready, and no error; poll and loop.
- (poll poll-set)
- (lp (1- (poll-set-nfds poll-set))))
- ((not (zero? (logand revents *error-events*)))
- ;; An error.
- (set-http-poll-idx! server idx)
- (throw 'interrupt))
- (else
- ;; A new client. Add to set, poll, and loop.
- ;;
- ;; FIXME_ preserve meta-info.
- (let ((client (accept (poll-set-port poll-set idx))))
- ;; Buffer input and output on this port.
- (setvbuf (car client) _IOFBF)
- ;; From "HOP, A Fast Server for the Diffuse Web", Serrano.
- (setsockopt (car client) SOL_SOCKET SO_SNDBUF (* 12 1024))
- (poll-set-add! poll-set (car client) *events*)
- (poll poll-set)
- (lp (1- (poll-set-nfds poll-set)))))))
- ((zero? revents)
- ;; Nothing on this port.
- (lp (1- idx)))
- ;; Otherwise, a client socket with some activity on
- ;; it. Remove it from the poll set.
- (else
- (let ((port (poll-set-remove! poll-set idx)))
- ;; Record the next index in all cases, in case the EOF check
- ;; throws an error.
- (set-http-poll-idx! server (1- idx))
- (cond
- ((eof-object? (peek-char port))
- ;; EOF.
- (close-port port)
- (lp (1- idx)))
- (else
- ;; Otherwise, try to read a request from this port.
- (with-throw-handler
- #t
- (lambda ()
- (let ((req (read-request port)))
- (values port
- req
- (read-request-body req))))
- (lambda (k . args)
- (define-syntax-rule (cleanup-catch statement)
- (catch #t
- (lambda () statement)
- (lambda (k . args)
- (format (current-error-port) "In ~a_\n" 'statement)
- (print-exception (current-error-port) #f k args))))
- (cleanup-catch (bad-request port))
- (cleanup-catch (close-port port)))))))))))))
-
-(define (keep-alive? response)
- (let ((v (response-version response)))
- (and (or (< (response-code response) 400)
- (= (response-code response) 404))
- (case (car v)
- ((1)
- (case (cdr v)
- ((1) (not (memq 'close (response-connection response))))
- ((0) (memq 'keep-alive (response-connection response)))))
- (else #f)))))
-
-;; -> 0 values
-(define (http-write server client response body)
- (let* ((response (write-response response client))
- (port (response-port response)))
- (cond
- ((not body)) ; pass
- ((bytevector? body)
- (write-response-body response body))
- (else
- (error "Expected a bytevector for body" body)))
- (cond
- ((keep-alive? response)
- (force-output port)
- (poll-set-add! (http-poll-set server) port *events*))
- (else
- (close-port port)))
- (values)))
-
-;; -> unspecified values
-(define (http-close server)
- (let ((poll-set (http-poll-set server)))
- (let lp ((n (poll-set-nfds poll-set)))
- (if (positive? n)
- (begin
- (close-port (poll-set-remove! poll-set (1- n)))
- (lp (1- n)))))))
-
-(define-server-impl http
- http-open
- http-read
- http-write
- http-close)
-;;;; (web uri) --- URI manipulation tools
-;;;;
-;;;; Copyright (C) 1997,2001,2002,2010,2011,2012,2013 Free Software Foundation, Inc.
-;;;;
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;;
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;;; Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;;
-
-;;; Commentary_
-
-;; A data type for Universal Resource Identifiers, as defined in RFC
-;; 3986.
-
-;;; Code_
-
-(define-module (web uri)
- #\use-module (srfi srfi-9)
- #\use-module (ice-9 regex)
- #\use-module (ice-9 rdelim)
- #\use-module (ice-9 control)
- #\use-module (rnrs bytevectors)
- #\use-module (ice-9 binary-ports)
- #\export (uri?
- uri-scheme uri-userinfo uri-host uri-port
- uri-path uri-query uri-fragment
-
- build-uri
- declare-default-port!
- string->uri uri->string
- uri-decode uri-encode
- split-and-decode-uri-path
- encode-and-join-uri-path))
-
-(define-record-type <uri>
- (make-uri scheme userinfo host port path query fragment)
- uri?
- (scheme uri-scheme)
- (userinfo uri-userinfo)
- (host uri-host)
- (port uri-port)
- (path uri-path)
- (query uri-query)
- (fragment uri-fragment))
-
-(define (absolute-uri? obj)
- (and (uri? obj) (uri-scheme obj) #t))
-
-(define (uri-error message . args)
- (throw 'uri-error message args))
-
-(define (positive-exact-integer? port)
- (and (number? port) (exact? port) (integer? port) (positive? port)))
-
-(define (validate-uri scheme userinfo host port path query fragment)
- (cond
- ((not (symbol? scheme))
- (uri-error "Expected a symbol for the URI scheme_ ~s" scheme))
- ((and (or userinfo port) (not host))
- (uri-error "Expected a host, given userinfo or port"))
- ((and port (not (positive-exact-integer? port)))
- (uri-error "Expected port to be an integer_ ~s" port))
- ((and host (or (not (string? host)) (not (valid-host? host))))
- (uri-error "Expected valid host_ ~s" host))
- ((and userinfo (not (string? userinfo)))
- (uri-error "Expected string for userinfo_ ~s" userinfo))
- ((not (string? path))
- (uri-error "Expected string for path_ ~s" path))
- ((and host (not (string-null? path))
- (not (eqv? (string-ref path 0) #\/)))
- (uri-error "Expected path of absolute URI to start with a /_ ~a" path))))
-
-(define* (build-uri scheme #\key userinfo host port (path "") query fragment
- (validate? #t))
- "Construct a URI object. SCHEME should be a symbol, PORT
-either a positive, exact integer or ‘#f’, and the rest of the
-fields are either strings or ‘#f’. If VALIDATE? is true,
-also run some consistency checks to make sure that the constructed URI
-is valid."
- (if validate?
- (validate-uri scheme userinfo host port path query fragment))
- (make-uri scheme userinfo host port path query fragment))
-
-;; See RFC 3986 #3.2.2 for comments on percent-encodings, IDNA (RFC
-;; 3490), and non-ASCII host names.
-;;
-(define ipv4-regexp
- (make-regexp "^([0-9.]+)$"))
-(define ipv6-regexp
- (make-regexp "^([0-9a-fA-F_.]+)$"))
-(define domain-label-regexp
- (make-regexp "^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$"))
-(define top-label-regexp
- (make-regexp "^[a-zA-Z]([a-zA-Z0-9-]*[a-zA-Z0-9])?$"))
-
-(define (valid-host? host)
- (cond
- ((regexp-exec ipv4-regexp host)
- (false-if-exception (inet-pton AF_INET host)))
- ((regexp-exec ipv6-regexp host)
- (false-if-exception (inet-pton AF_INET6 host)))
- (else
- (let lp ((start 0))
- (let ((end (string-index host #\. start)))
- (if end
- (and (regexp-exec domain-label-regexp
- (substring host start end))
- (lp (1+ end)))
- (regexp-exec top-label-regexp host start)))))))
-
-(define userinfo-pat
- "[a-zA-Z0-9_.!~*'();_&=+$,-]+")
-(define host-pat
- "[a-zA-Z0-9.-]+")
-(define ipv6-host-pat
- "[0-9a-fA-F_.]+")
-(define port-pat
- "[0-9]*")
-(define authority-regexp
- (make-regexp
- (format #f "^//((~a)@)?((~a)|(\\[(~a)\\]))(_(~a))?$"
- userinfo-pat host-pat ipv6-host-pat port-pat)))
-
-(define (parse-authority authority fail)
- (if (equal? authority "//")
- ;; Allow empty authorities_ file_///etc/hosts is a synonym of
- ;; file_/etc/hosts.
- (values #f #f #f)
- (let ((m (regexp-exec authority-regexp authority)))
- (if (and m (valid-host? (or (match_substring m 4)
- (match_substring m 6))))
- (values (match_substring m 2)
- (or (match_substring m 4)
- (match_substring m 6))
- (let ((port (match_substring m 8)))
- (and port (not (string-null? port))
- (string->number port))))
- (fail)))))
-
-
-;;; RFC 3986, #3.
-;;;
-;;; URI = scheme "_" hier-part [ "?" query ] [ "#" fragment ]
-;;;
-;;; hier-part = "//" authority path-abempty
-;;; / path-absolute
-;;; / path-rootless
-;;; / path-empty
-
-(define scheme-pat
- "[a-zA-Z][a-zA-Z0-9+.-]*")
-(define authority-pat
- "[^/?#]*")
-(define path-pat
- "[^?#]*")
-(define query-pat
- "[^#]*")
-(define fragment-pat
- ".*")
-(define uri-pat
- (format #f "^((~a)_)?(//~a)?(~a)(\\?(~a))?(#(~a))?$"
- scheme-pat authority-pat path-pat query-pat fragment-pat))
-(define uri-regexp
- (make-regexp uri-pat))
-
-(define (string->uri* string)
- "Parse STRING into a URI object. Return ‘#f’ if the string
-could not be parsed."
- (% (let ((m (regexp-exec uri-regexp string)))
- (if (not m) (abort))
- (let ((scheme (let ((str (match_substring m 2)))
- (and str (string->symbol (string-downcase str)))))
- (authority (match_substring m 3))
- (path (match_substring m 4))
- (query (match_substring m 6))
- (fragment (match_substring m 7)))
- (call-with-values
- (lambda ()
- (if authority
- (parse-authority authority abort)
- (values #f #f #f)))
- (lambda (userinfo host port)
- (make-uri scheme userinfo host port path query fragment)))))
- (lambda (k)
- #f)))
-
-(define (string->uri string)
- "Parse STRING into a URI object. Return ‘#f’ if the string
-could not be parsed."
- (let ((uri (string->uri* string)))
- (and uri (uri-scheme uri) uri)))
-
-(define *default-ports* (make-hash-table))
-
-(define (declare-default-port! scheme port)
- "Declare a default port for the given URI scheme."
- (hashq-set! *default-ports* scheme port))
-
-(define (default-port? scheme port)
- (or (not port)
- (eqv? port (hashq-ref *default-ports* scheme))))
-
-(declare-default-port! 'http 80)
-(declare-default-port! 'https 443)
-
-(define (uri->string uri)
- "Serialize URI to a string. If the URI has a port that is the
-default port for its scheme, the port is not included in the
-serialization."
- (let* ((scheme (uri-scheme uri))
- (userinfo (uri-userinfo uri))
- (host (uri-host uri))
- (port (uri-port uri))
- (path (uri-path uri))
- (query (uri-query uri))
- (fragment (uri-fragment uri)))
- (string-append
- (if scheme
- (string-append (symbol->string scheme) "_")
- "")
- (if host
- (string-append "//"
- (if userinfo (string-append userinfo "@")
- "")
- (if (string-index host #\_)
- (string-append "[" host "]")
- host)
- (if (default-port? (uri-scheme uri) port)
- ""
- (string-append "_" (number->string port))))
- "")
- path
- (if query
- (string-append "?" query)
- "")
- (if fragment
- (string-append "#" fragment)
- ""))))
-
-
-;; like call-with-output-string, but actually closes the port (doh)
-(define (call-with-output-string* proc)
- (let ((port (open-output-string)))
- (proc port)
- (let ((str (get-output-string port)))
- (close-port port)
- str)))
-
-(define (call-with-output-bytevector* proc)
- (call-with-values
- (lambda ()
- (open-bytevector-output-port))
- (lambda (port get-bytevector)
- (proc port)
- (let ((bv (get-bytevector)))
- (close-port port)
- bv))))
-
-(define (call-with-encoded-output-string encoding proc)
- (if (string-ci=? encoding "utf-8")
- (string->utf8 (call-with-output-string* proc))
- (call-with-output-bytevector*
- (lambda (port)
- (set-port-encoding! port encoding)
- (proc port)))))
-
-(define (encode-string str encoding)
- (if (string-ci=? encoding "utf-8")
- (string->utf8 str)
- (call-with-encoded-output-string encoding
- (lambda (port)
- (display str port)))))
-
-(define (decode-string bv encoding)
- (if (string-ci=? encoding "utf-8")
- (utf8->string bv)
- (let ((p (open-bytevector-input-port bv)))
- (set-port-encoding! p encoding)
- (let ((res (read-string p)))
- (close-port p)
- res))))
-
-
-;; A note on characters and bytes_ URIs are defined to be sequences of
-;; characters in a subset of ASCII. Those characters may encode a
-;; sequence of bytes (octets), which in turn may encode sequences of
-;; characters in other character sets.
-;;
-
-;; Return a new string made from uri-decoding STR. Specifically,
-;; turn ‘+’ into space, and hex-encoded ‘%XX’ strings into
-;; their eight-bit characters.
-;;
-(define hex-chars
- (string->char-set "0123456789abcdefABCDEF"))
-
-(define* (uri-decode str #\key (encoding "utf-8") (decode-plus-to-space? #t))
- "Percent-decode the given STR, according to ENCODING,
-which should be the name of a character encoding.
-
-Note that this function should not generally be applied to a full URI
-string. For paths, use ‘split-and-decode-uri-path’ instead. For query
-strings, split the query on ‘&’ and ‘=’ boundaries, and decode
-the components separately.
-
-Note also that percent-encoded strings encode _bytes_, not characters.
-There is no guarantee that a given byte sequence is a valid string
-encoding. Therefore this routine may signal an error if the decoded
-bytes are not valid for the given encoding. Pass ‘#f’ for ENCODING if
-you want decoded bytes as a bytevector directly. ‘set-port-encoding!’,
-for more information on character encodings.
-
-If DECODE-PLUS-TO-SPACE? is true, which is the default, also replace
-instances of the plus character (+) with a space character. This is
-needed when parsing application/x-www-form-urlencoded data.
-
-Returns a string of the decoded characters, or a bytevector if
-ENCODING was ‘#f’."
- (let* ((len (string-length str))
- (bv
- (call-with-output-bytevector*
- (lambda (port)
- (let lp ((i 0))
- (if (< i len)
- (let ((ch (string-ref str i)))
- (cond
- ((and (eqv? ch #\+) decode-plus-to-space?)
- (put-u8 port (char->integer #\space))
- (lp (1+ i)))
- ((and (< (+ i 2) len) (eqv? ch #\%)
- (let ((a (string-ref str (+ i 1)))
- (b (string-ref str (+ i 2))))
- (and (char-set-contains? hex-chars a)
- (char-set-contains? hex-chars b)
- (string->number (string a b) 16))))
- => (lambda (u8)
- (put-u8 port u8)
- (lp (+ i 3))))
- ((< (char->integer ch) 128)
- (put-u8 port (char->integer ch))
- (lp (1+ i)))
- (else
- (uri-error "Invalid character in encoded URI ~a_ ~s"
- str ch))))))))))
- (if encoding
- (decode-string bv encoding)
- ;; Otherwise return raw bytevector
- bv)))
-
-(define ascii-alnum-chars
- (string->char-set
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"))
-
-;; RFC 3986, #2.2.
-(define gen-delims
- (string->char-set "_/?#[]@"))
-(define sub-delims
- (string->char-set "!$&'()*+,l="))
-(define reserved-chars
- (char-set-union gen-delims sub-delims))
-
-;; RFC 3986, #2.3
-(define unreserved-chars
- (char-set-union ascii-alnum-chars
- (string->char-set "-._~")))
-
-;; Return a new string made from uri-encoding STR, unconditionally
-;; transforming any characters not in UNESCAPED-CHARS.
-;;
-(define* (uri-encode str #\key (encoding "utf-8")
- (unescaped-chars unreserved-chars))
- "Percent-encode any character not in the character set,
-UNESCAPED-CHARS.
-
-The default character set includes alphanumerics from ASCII, as well as
-the special characters ‘-’, ‘.’, ‘_’, and ‘~’. Any other character will
-be percent-encoded, by writing out the character to a bytevector within
-the given ENCODING, then encoding each byte as ‘%HH’, where HH is the
-uppercase hexadecimal representation of the byte."
- (define (needs-escaped? ch)
- (not (char-set-contains? unescaped-chars ch)))
- (if (string-index str needs-escaped?)
- (call-with-output-string*
- (lambda (port)
- (string-for-each
- (lambda (ch)
- (if (char-set-contains? unescaped-chars ch)
- (display ch port)
- (let* ((bv (encode-string (string ch) encoding))
- (len (bytevector-length bv)))
- (let lp ((i 0))
- (if (< i len)
- (let ((byte (bytevector-u8-ref bv i)))
- (display #\% port)
- (when (< byte 16)
- (display #\0 port))
- (display (string-upcase (number->string byte 16))
- port)
- (lp (1+ i))))))))
- str)))
- str))
-
-(define (split-and-decode-uri-path path)
- "Split PATH into its components, and decode each component,
-removing empty components.
-
-For example, ‘\"/foo/bar%20baz/\"’ decodes to the two-element list,
-‘(\"foo\" \"bar baz\")’."
- (filter (lambda (x) (not (string-null? x)))
- (map (lambda (s) (uri-decode s #\decode-plus-to-space? #f))
- (string-split path #\/))))
-
-(define (encode-and-join-uri-path parts)
- "URI-encode each element of PARTS, which should be a list of
-strings, and join the parts together with ‘/’ as a delimiter.
-
-For example, the list ‘(\"scrambled eggs\" \"biscuits&gravy\")’
-encodes as ‘\"scrambled%20eggs/biscuits%26gravy\"’."
- (string-join (map uri-encode parts) "/"))
-;;; common-test.scm --
-;;;
-
-;; Slightly modified for Guile by Ludovic Courtès <ludo@gnu.org>, 2010.
-
-(use-modules (system base lalr)
- (ice-9 pretty-print))
-
-(define *error* '())
-
-(define-syntax check
- (syntax-rules (=>)
- ((_ ?expr => ?expected-result)
- (check ?expr (=> equal?) ?expected-result))
-
- ((_ ?expr (=> ?equal) ?expected-result)
- (let ((result ?expr)
- (expected ?expected-result))
- (set! *error* '())
- (when (not (?equal result expected))
- (display "Failed test_ \n")
- (pretty-print (quote ?expr))(newline)
- (display "\tresult was_ ")
- (pretty-print result)(newline)
- (display "\texpected_ ")
- (pretty-print expected)(newline)
- (exit 1))))))
-
-;;; --------------------------------------------------------------------
-
-(define (display-result v)
- (if v
- (begin
- (display "==> ")
- (display v)
- (newline))))
-
-(define eoi-token
- (make-lexical-token '*eoi* #f #f))
-
-(define (make-lexer tokens)
- (lambda ()
- (if (null? tokens)
- eoi-token
- (let ((t (car tokens)))
- (set! tokens (cdr tokens))
- t))))
-
-(define (error-handler message . args)
- (set! *error* (cons `(error-handler ,message . ,(if (pair? args)
- (lexical-token-category (car args))
- '()))
- *error*))
- (cons message args))
-
-;;; end of file
-"_";exec snow -- "$0" "$@"
-;;;
-;;;; Tests for the GLR parser generator
-;;;
-;;
-;; @created "Fri Aug 19 11_23_48 EDT 2005"
-;;
-
-(package* glr-test/v1.0.0
- (require_ lalr/v2.4.0))
-
-
-(define (syntax-error msg . args)
- (display msg (current-error-port))
- (for-each (cut format (current-error-port) " ~A" <>) args)
- (newline (current-error-port))
- (throw 'misc-error))
-
-
-(define (make-lexer words)
- (let ((phrase words))
- (lambda ()
- (if (null? phrase)
- '*eoi*
- (let ((word (car phrase)))
- (set! phrase (cdr phrase))
- word)))))
-
-
-;;;
-;;;; Test 1
-;;;
-
-
-(define parser-1
- ;; Grammar taken from Tomita's "An Efficient Augmented-Context-Free Parsing Algorithm"
- (lalr-parser
- (driver_ glr)
- (expect_ 2)
- (*n *v *d *p)
- (<s> (<np> <vp>)
- (<s> <pp>))
- (<np> (*n)
- (*d *n)
- (<np> <pp>))
- (<pp> (*p <np>))
- (<vp> (*v <np>))))
-
-
-(define *phrase-1* '(*n *v *d *n *p *d *n *p *d *n *p *d *n))
-
-(define (test-1)
- (parser-1 (make-lexer *phrase-1*) syntax-error))
-
-
-;;;
-;;;; Test 2
-;;;
-
-
-(define parser-2
- ;; The dangling-else problem
- (lalr-parser
- (driver_ glr)
- (expect_ 1)
- ((nonassoc_ if then else e s))
- (<s> (s)
- (if e then <s>)
- (if e then <s> else <s>))))
-
-
-(define *phrase-2* '(if e then if e then s else s))
-
-(define (test-2)
- (parser-2 (make-lexer *phrase-2*) syntax-error))
-
-
-
-
-(define (assert-length l n test-name)
- (display "Test '")
- (display test-name)
- (display (if (not (= (length l) n)) "' failed!" "' passed!"))
- (newline))
-
-(assert-length (test-1) 14 1)
-(assert-length (test-2) 2 2)
-
-;;; test-glr-associativity.scm
-;;
-;;With the GLR parser both the terminal precedence and the non-terminal
-;;associativity are not respected; rather they generate two child
-;;processes.
-;;
-
-(load "common-test.scm")
-
-(define parser
- (lalr-parser
- (driver\_ glr)
- (expect\_ 0)
-
- (N LPAREN RPAREN
- (left\_ + -)
- (right\_ * /)
- (nonassoc\_ uminus))
-
- (output (expr) \_ $1)
- (expr (expr + expr) \_ (list $1 '+ $3)
- (expr - expr) \_ (list $1 '- $3)
- (expr * expr) \_ (list $1 '* $3)
- (expr / expr) \_ (list $1 '/ $3)
- (- expr (prec\_ uminus)) \_ (list '- $2)
- (N) \_ $1
- (LPAREN expr RPAREN) \_ $2)))
-
-(define (doit . tokens)
- (parser (make-lexer tokens) error-handler))
-
-;;; --------------------------------------------------------------------
-
-;;Remember that the result of the GLR driver is a list of parses, not a
-;;single parse.
-
-(check
- (doit (make-lexical-token 'N #f 1))
- => '(1))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token '+ #f '+)
- (make-lexical-token 'N #f 2))
- => '((1 + 2)))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token '* #f '*)
- (make-lexical-token 'N #f 2))
- => '((1 * 2)))
-
-(check
- (doit (make-lexical-token '- #f '-)
- (make-lexical-token 'N #f 1))
- => '((- 1)))
-
-(check
- (doit (make-lexical-token '- #f '-)
- (make-lexical-token '- #f '-)
- (make-lexical-token 'N #f 1))
- => '((- (- 1))))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token '+ #f '+)
- (make-lexical-token '- #f '-)
- (make-lexical-token 'N #f 2))
- => '((1 + (- 2))))
-
-;;; --------------------------------------------------------------------
-
-(check
- ;;left-associativity
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token '+ #f '+)
- (make-lexical-token 'N #f 2)
- (make-lexical-token '+ #f '+)
- (make-lexical-token 'N #f 3))
- => '(((1 + 2) + 3)))
-
-(check
- ;;right-associativity
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token '* #f '*)
- (make-lexical-token 'N #f 2)
- (make-lexical-token '* #f '*)
- (make-lexical-token 'N #f 3))
- => '(((1 * 2) * 3)
- (1 * (2 * 3))))
-
-(check
- ;;precedence
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token '+ #f '+)
- (make-lexical-token 'N #f 2)
- (make-lexical-token '* #f '*)
- (make-lexical-token 'N #f 3))
- => '(((1 + 2) * 3)
- (1 + (2 * 3))))
-
-;;; end of file
-;;; test-lr-basics-01.scm --
-;;
-;;A grammar that only accept a single terminal as input. It refuses the
-;;end-of-input as first token.
-;;
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let* ((lexer (make-lexer tokens))
- (parser (lalr-parser (expect\_ 0)
- (driver\_ glr)
- (A)
- (e (A) \_ $1))))
- (parser lexer error-handler)))
-
-(check
- (doit (make-lexical-token 'A #f 1))
- => '(1))
-
-(check
- (doit)
- => '())
-
-(check
- ;;Parse correctly the first A and reduce it. The second A triggers
- ;;an error which empties the stack and consumes all the input
- ;;tokens. Finally, an unexpected end-of-input error is returned
- ;;because EOI is invalid as first token after the start.
- (doit (make-lexical-token 'A #f 1)
- (make-lexical-token 'A #f 2)
- (make-lexical-token 'A #f 3))
- => '())
-
-;;; end of file
-;;; test-lr-basics-02.scm --
-;;
-;;A grammar that only accept a single terminal or the EOI.
-;;
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser (expect\_ 0)
- (driver\_ glr)
- (A)
- (e (A) \_ $1
- () \_ 0))))
- (parser (make-lexer tokens) error-handler)))
-
-(check
- (doit)
- => '(0))
-
-(check
- (doit (make-lexical-token 'A #f 1))
- => '(1))
-
-(check
- (doit (make-lexical-token 'A #f 1)
- (make-lexical-token 'A #f 2)
- (make-lexical-token 'A #f 3))
- => '())
-
-;;; end of file
-;;; test-lr-basics-03.scm --
-;;
-;;A grammar that accepts fixed sequences of a single terminal or the
-;;EOI.
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser (expect\_ 0)
- (driver\_ glr)
- (A)
- (e (A) \_ (list $1)
- (A A) \_ (list $1 $2)
- (A A A) \_ (list $1 $2 $3)
- () \_ 0))))
- (parser (make-lexer tokens) error-handler)))
-
-(check
- (doit (make-lexical-token 'A #f 1))
- => '((1)))
-
-(check
- (doit (make-lexical-token 'A #f 1)
- (make-lexical-token 'A #f 2))
- => '((1 2)))
-
-(check
- (doit (make-lexical-token 'A #f 1)
- (make-lexical-token 'A #f 2)
- (make-lexical-token 'A #f 3))
- => '((1 2 3)))
-
-(check
- (doit)
- => '(0))
-
-;;; end of file
-;;; test-lr-basics-04.scm --
-;;
-;;A grammar accepting a sequence of equal tokens of arbitrary length.
-;;The return value is the value of the last parsed token.
-
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser (expect\_ 0)
- (driver\_ glr)
- (A)
- (e (e A) \_ $2
- (A) \_ $1
- () \_ 0))))
- (parser (make-lexer tokens) error-handler)))
-
-(check
- (doit)
- => '(0))
-
-(check
- ;;Two results because there is a shift/reduce conflict, so two
- ;;processes are generated.
- (doit (make-lexical-token 'A #f 1))
- => '(1 1))
-
-(check
- ;;Two results because there is a shift/reduce conflict, so two
- ;;processes are generated. Notice that the rules_
- ;;
- ;; (e A) (A)
- ;;
- ;;generate only one conflict when the second "A" comes. The third
- ;;"A" comes when the state is inside the rule "(e A)", so there is
- ;;no conflict.
- ;;
- (doit (make-lexical-token 'A #f 1)
- (make-lexical-token 'A #f 2)
- (make-lexical-token 'A #f 3))
- => '(3 3))
-
-;;; end of file
-;;; test-lr-basics-05.scm --
-;;
-;;A grammar accepting a sequence of equal tokens of arbitrary length.
-;;The return value is the list of values.
-;;
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser (expect\_ 0)
- (driver\_ glr)
- (A)
- (e (e A) \_ (cons $2 $1)
- (A) \_ (list $1)
- () \_ (list 0)))))
- (parser (make-lexer tokens) error-handler)))
-
-(check
- (doit)
- => '((0)))
-
-(check
- (doit (make-lexical-token 'A #f 1))
- => '((1 0)
- (1)))
-
-(check
- (doit (make-lexical-token 'A #f 1)
- (make-lexical-token 'A #f 2))
- => '((2 1 0)
- (2 1)))
-
-(check
- (doit (make-lexical-token 'A #f 1)
- (make-lexical-token 'A #f 2)
- (make-lexical-token 'A #f 3))
- => '((3 2 1 0)
- (3 2 1)))
-
-;;; end of file
-;;; test-lr-script-expression.scm --
-;;
-;;Parse scripts, each line an expression.
-;;
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser (expect\_ 0)
- (driver\_ glr)
- (N O C T (left\_ A) (left\_ M) (nonassoc\_ U))
-
- (script (lines) \_ (reverse $1))
-
- (lines (lines line) \_ (cons $2 $1)
- (line) \_ (list $1))
-
- (line (T) \_ #\newline
- (E T) \_ $1
- (error T) \_ (list 'error-clause $2))
-
- (E (N) \_ $1
- (E A E) \_ ($2 $1 $3)
- (E M E) \_ ($2 $1 $3)
- (A E (prec\_ U)) \_ ($1 $2)
- (O E C) \_ $2))))
- (parser (make-lexer tokens) error-handler)))
-
-;;; --------------------------------------------------------------------
-;;; Correct input
-
-(check
- (doit (make-lexical-token 'T #f #\newline))
- => '((#\newline)))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'T #f #\newline))
- => '((1)))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'T #f #\newline))
- => '((3)))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'M #f *)
- (make-lexical-token 'N #f 3)
- (make-lexical-token 'T #f #\newline))
- => '((9) (7)))
-
-(check
- (doit (make-lexical-token 'N #f 10)
- (make-lexical-token 'M #f *)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 3)
- (make-lexical-token 'T #f #\newline))
- => '((23)))
-
-(check
- (doit (make-lexical-token 'O #f #\()
- (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'C #f #\))
- (make-lexical-token 'M #f *)
- (make-lexical-token 'N #f 3)
- (make-lexical-token 'T #f #\newline))
- => '((9)))
-
-(check
- (doit (make-lexical-token 'O #f #\()
- (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'C #f #\))
- (make-lexical-token 'M #f *)
- (make-lexical-token 'N #f 3)
- (make-lexical-token 'T #f #\newline)
-
- (make-lexical-token 'N #f 4)
- (make-lexical-token 'M #f /)
- (make-lexical-token 'N #f 5)
- (make-lexical-token 'T #f #\newline))
- => '((9 4/5)))
-
-;;; --------------------------------------------------------------------
-
-(check
- ;;Successful error recovery.
- (doit (make-lexical-token 'O #f #\()
- (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'M #f *)
- (make-lexical-token 'N #f 3)
- (make-lexical-token 'T #f #\newline)
-
- (make-lexical-token 'N #f 4)
- (make-lexical-token 'M #f /)
- (make-lexical-token 'N #f 5)
- (make-lexical-token 'T #f #\newline))
- => '())
-
-(check
- ;;Unexpected end of input.
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2))
- => '())
-
-(check
- ;;Unexpected end of input.
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'T #f #\newline))
- => '())
-
-;;; end of file
-;;; test-lr-single-expressions.scm --
-;;
-;;Grammar accepting single expressions.
-;;
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser (expect\_ 0)
- (driver\_ glr)
- (N O C (left\_ A) (left\_ M) (nonassoc\_ U))
-
- (E (N) \_ $1
- (E A E) \_ ($2 $1 $3)
- (E M E) \_ ($2 $1 $3)
- (A E (prec\_ U)) \_ ($1 $2)
- (O E C) \_ $2))))
- (parser (make-lexer tokens) error-handler)))
-
-;;; --------------------------------------------------------------------
-
-(check ;correct input
- (doit (make-lexical-token 'N #f 1))
- => '(1))
-
-(check ;correct input
- (doit (make-lexical-token 'A #f -)
- (make-lexical-token 'N #f 1))
- => '(-1))
-
-(check ;correct input
- (doit (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 1))
- => '(1))
-
-(check ;correct input
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2))
- => '(3))
-
-(check ;correct input
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'M #f *)
- (make-lexical-token 'N #f 3))
- => '(9 7))
-
-(check ;correct input
- (doit (make-lexical-token 'O #f #\()
- (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'C #f #\))
- (make-lexical-token 'M #f *)
- (make-lexical-token 'N #f 3))
- => '(9))
-
-;;; end of file
-;;; test-lr-associativity-01.scm --
-;;
-;;Show how to use left and right associativity. Notice that the
-;;terminal M is declared as right associative; this influences the
-;;binding of values to the $n symbols in the semantic clauses. The
-;;semantic clause in the rule_
-;;
-;; (E M E M E) _ (list $1 $2 (list $3 $4 $5))
-;;
-;;looks like it is right-associated, and it is because we have declared
-;;M as "right_".
-;;
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser
- (expect\_ 0)
- (N (left\_ A)
- (right\_ M)
- (nonassoc\_ U))
- (E (N) \_ $1
- (E A E) \_ (list $1 $2 $3)
- (E M E) \_ (list $1 $2 $3)
- (E M E M E) \_ (list $1 $2 (list $3 $4 $5))
- (A E (prec\_ U)) \_ (list '- $2)))))
- (parser (make-lexer tokens) error-handler)))
-
-;;; --------------------------------------------------------------------
-;;; Single operator.
-
-(check
- (doit (make-lexical-token 'N #f 1))
- => 1)
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 2))
- => '(1 + 2))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 2))
- => '(1 * 2))
-
-(check
- (doit (make-lexical-token 'A #f '-)
- (make-lexical-token 'N #f 1))
- => '(- 1))
-
-;;; --------------------------------------------------------------------
-;;; Precedence.
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 3))
- => '(1 + (2 * 3)))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 3))
- => '((1 * 2) + 3))
-
-;;; --------------------------------------------------------------------
-;;; Associativity.
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 3))
- => '((1 + 2) + 3))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 3))
- => '(1 * (2 * 3)))
-
-;;; end of file
-;;; test-lr-associativity-02.scm --
-;;
-;;Show how to use left and right associativity. Notice that the
-;;terminal M is declared as left associative; this influences the
-;;binding of values to the $n symbols in the semantic clauses. The
-;;semantic clause in the rule_
-;;
-;; (E M E M E) \_ (list $1 $2 (list $3 $4 $5))
-;;
-;;looks like it is right-associated, but the result is left-associated
-;;because we have declared M as "left_".
-;;
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser
- (expect\_ 0)
- (N (left\_ A)
- (left\_ M)
- (nonassoc\_ U))
- (E (N) \_ $1
- (E A E) \_ (list $1 $2 $3)
- (E M E) \_ (list $1 $2 $3)
- (E M E M E) \_ (list $1 $2 (list $3 $4 $5))
- (A E (prec\_ U)) \_ (list '- $2)))))
- (parser (make-lexer tokens) error-handler)))
-
-;;; --------------------------------------------------------------------
-;;; Single operator.
-
-(check
- (doit (make-lexical-token 'N #f 1))
- => 1)
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 2))
- => '(1 + 2))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 2))
- => '(1 * 2))
-
-(check
- (doit (make-lexical-token 'A #f '-)
- (make-lexical-token 'N #f 1))
- => '(- 1))
-
-;;; --------------------------------------------------------------------
-;;; Precedence.
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 3))
- => '(1 + (2 * 3)))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 3))
- => '((1 * 2) + 3))
-
-;;; --------------------------------------------------------------------
-;;; Associativity.
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 3))
- => '((1 + 2) + 3))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 3))
- => '((1 * 2) * 3))
-
-;;; end of file
-;;; test-lr-associativity-01.scm --
-;;
-;;Show how to use left and right associativity. Notice that the
-;;terminal M is declared as non-associative; this influences the binding
-;;of values to the $n symbols in the semantic clauses. The semantic
-;;clause in the rule_
-;;
-;; (E M E M E) \_ (list $1 $2 (list $3 $4 $5))
-;;
-;;looks like it is right-associated, and it is because we have declared
-;;M as "right_".
-;;
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser
- (expect\_ 0)
- (N (nonassoc\_ A)
- (nonassoc\_ M))
- (E (N) \_ $1
- (E A E) \_ (list $1 $2 $3)
- (E A E A E) \_ (list (list $1 $2 $3) $4 $5)
- (E M E) \_ (list $1 $2 $3)
- (E M E M E) \_ (list $1 $2 (list $3 $4 $5))))))
- (parser (make-lexer tokens) error-handler)))
-
-;;; --------------------------------------------------------------------
-;;; Single operator.
-
-(check
- (doit (make-lexical-token 'N #f 1))
- => 1)
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 2))
- => '(1 + 2))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 2))
- => '(1 * 2))
-
-;;; --------------------------------------------------------------------
-;;; Precedence.
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 3))
- => '(1 + (2 * 3)))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 3))
- => '((1 * 2) + 3))
-
-;;; --------------------------------------------------------------------
-;;; Associativity.
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 3))
- => '((1 + 2) + 3))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 3))
- => '(1 * (2 * 3)))
-
-;;; end of file
-;;; test-lr-associativity-04.scm --
-;;
-;;Show how to use associativity.
-;;
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser
- (expect\_ 0)
- (N (left\_ A)
- (left\_ M))
- (E (N) \_ $1
-
- (E A E) \_ (list $1 $2 $3)
- (E A E A E) \_ (list (list $1 $2 $3) $4 $5)
-
- (E M E) \_ (list $1 $2 $3)
- (E M E M E) \_ (list $1 $2 (list $3 $4 $5))
-
- (E A E M E) \_ (list $1 $2 $3 $4 $5)
- (E M E A E) \_ (list $1 $2 $3 $4 $5)
- ))))
- (parser (make-lexer tokens) error-handler)))
-
-;;; --------------------------------------------------------------------
-;;; Single operator.
-
-(check
- (doit (make-lexical-token 'N #f 1))
- => 1)
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 2))
- => '(1 + 2))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 2))
- => '(1 * 2))
-
-;;; --------------------------------------------------------------------
-;;; Precedence.
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 3))
- => '(1 + (2 * 3)))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 3))
- => '((1 * 2) + 3))
-
-;;; --------------------------------------------------------------------
-;;; Associativity.
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'A #f '+)
- (make-lexical-token 'N #f 3))
- => '((1 + 2) + 3))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'M #f '*)
- (make-lexical-token 'N #f 3))
- => '((1 * 2) * 3))
-
-;;; end of file
-;;; test-lr-basics-01.scm --
-;;
-;;A grammar that only accept a single terminal as input. It refuses the
-;;end-of-input as first token.
-;;
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let* ((lexer (make-lexer tokens))
- (parser (lalr-parser (expect\_ 0)
- (A)
- (e (A) \_ $1))))
- (parser lexer error-handler)))
-
-(check
- (doit (make-lexical-token 'A #f 1))
- => 1)
-
-(check
- (let ((r (doit)))
- (cons r *error*))
- => '(#f (error-handler "Syntax error: unexpected end of input")))
-
-(check
- ;;Parse correctly the first A and reduce it. The second A triggers
- ;;an error which empties the stack and consumes all the input
- ;;tokens. Finally, an unexpected end-of-input error is returned
- ;;because EOI is invalid as first token after the start.
- (let ((r (doit (make-lexical-token 'A #f 1)
- (make-lexical-token 'A #f 2)
- (make-lexical-token 'A #f 3))))
- (cons r *error*))
- => '(#f
- (error-handler "Syntax error: unexpected end of input")
- (error-handler "Syntax error: unexpected token : " . A)))
-
-;;; end of file
-;;; test-lr-basics-02.scm --
-;;
-;;A grammar that only accept a single terminal or the EOI.
-;;
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser (expect\_ 0)
- (A)
- (e (A) \_ $1
- () \_ 0))))
- (parser (make-lexer tokens) error-handler)))
-
-(check
- (doit)
- => 0)
-
-(check
- (doit (make-lexical-token 'A #f 1))
- => 1)
-
-(check
- ;;Parse correctly the first A and reduce it. The second A triggers
- ;;an error which empties the stack and consumes all the input
- ;;tokens. Finally, the end-of-input token is correctly parsed.
- (let ((r (doit (make-lexical-token 'A #f 1)
- (make-lexical-token 'A #f 2)
- (make-lexical-token 'A #f 3))))
- (cons r *error*))
- => '(0 (error-handler "Syntax error: unexpected token : " . A)))
-
-;;; end of file
-;;; test-lr-basics-03.scm --
-;;
-;;A grammar that accepts fixed sequences of a single terminal or the
-;;EOI.
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser (expect\_ 0)
- (A)
- (e (A) \_ (list $1)
- (A A) \_ (list $1 $2)
- (A A A) \_ (list $1 $2 $3)
- () \_ 0))))
- (parser (make-lexer tokens) error-handler)))
-
-(check
- (doit (make-lexical-token 'A #f 1))
- => '(1))
-
-(check
- (doit (make-lexical-token 'A #f 1)
- (make-lexical-token 'A #f 2))
- => '(1 2))
-
-(check
- (doit (make-lexical-token 'A #f 1)
- (make-lexical-token 'A #f 2)
- (make-lexical-token 'A #f 3))
- => '(1 2 3))
-
-(check
- (doit)
- => 0)
-
-;;; end of file
-;;; test-lr-basics-04.scm --
-;;
-;;A grammar accepting a sequence of equal tokens of arbitrary length.
-;;The return value is the value of the last parsed token.
-
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser (expect\_ 0)
- (A)
- (e (e A) \_ $2
- (A) \_ $1
- () \_ 0))))
- (parser (make-lexer tokens) error-handler)))
-
-(check
- (doit)
- => 0)
-
-(check
- (doit (make-lexical-token 'A #f 1))
- => 1)
-
-(check
- (doit (make-lexical-token 'A #f 1)
- (make-lexical-token 'A #f 2)
- (make-lexical-token 'A #f 3))
- => 3)
-
-;;; end of file
-;;; test-lr-basics-05.scm --
-;;
-;;A grammar accepting a sequence of equal tokens of arbitrary length.
-;;The return value is the list of values.
-;;
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser (expect\_ 0)
- (A)
- (e (e A) \_ (cons $2 $1)
- (A) \_ (list $1)
- () \_ 0))))
- (parser (make-lexer tokens) error-handler)))
-
-(check
- (doit)
- => 0)
-
-(check
- (doit (make-lexical-token 'A #f 1))
- => '(1))
-
-(check
- (doit (make-lexical-token 'A #f 1)
- (make-lexical-token 'A #f 2))
- => '(2 1))
-
-(check
- (doit (make-lexical-token 'A #f 1)
- (make-lexical-token 'A #f 2)
- (make-lexical-token 'A #f 3))
- => '(3 2 1))
-
-;;; end of file
-;;; test-lr-error-recovery-01.scm --
-;;
-;;Test error recovery with a terminator terminal.
-;;
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser
- (expect\_ 0)
- (NUMBER BAD NEWLINE)
-
- (script (lines) \_ (reverse $1)
- () \_ 0)
- (lines (lines line) \_ (cons $2 $1)
- (line) \_ (list $1))
- (line (NEWLINE) \_ (list 'line $1)
- (NUMBER NEWLINE) \_ (list 'line $1 $2)
- (NUMBER NUMBER NEWLINE) \_ (list 'line $1 $2 $3)
-
- ;;This semantic action will cause "(recover $1
- ;;$2)" to be the result of the offending line.
- (error NEWLINE) \_ (list 'recover $1 $2)))))
- (parser (make-lexer tokens) error-handler)))
-
-;;; --------------------------------------------------------------------
-;;; No errors, grammar tests.
-
-(check
- (doit)
- => 0)
-
-(check
- (doit (make-lexical-token 'NEWLINE #f #\newline))
- => '((line #\newline)))
-
-(check
- (doit (make-lexical-token 'NUMBER #f 1)
- (make-lexical-token 'NEWLINE #f #\newline))
- => '((line 1 #\newline)))
-
-(check
- (doit (make-lexical-token 'NUMBER #f 1)
- (make-lexical-token 'NUMBER #f 2)
- (make-lexical-token 'NEWLINE #f #\newline))
- => '((line 1 2 #\newline)))
-
-(check
- (doit (make-lexical-token 'NUMBER #f 1)
- (make-lexical-token 'NEWLINE #f #\newline)
- (make-lexical-token 'NUMBER #f 2)
- (make-lexical-token 'NEWLINE #f #\newline))
- => '((line 1 #\newline)
- (line 2 #\newline)))
-
-(check
- (doit (make-lexical-token 'NUMBER #f 1)
- (make-lexical-token 'NEWLINE #f #\newline)
- (make-lexical-token 'NUMBER #f 2)
- (make-lexical-token 'NEWLINE #f #\newline)
- (make-lexical-token 'NUMBER #f 3)
- (make-lexical-token 'NEWLINE #f #\newline))
- => '((line 1 #\newline)
- (line 2 #\newline)
- (line 3 #\newline)))
-
-(check
- (doit (make-lexical-token 'NUMBER #f 1)
- (make-lexical-token 'NEWLINE #f #\newline)
- (make-lexical-token 'NUMBER #f 2)
- (make-lexical-token 'NEWLINE #f #\newline)
- (make-lexical-token 'NUMBER #f 3)
- (make-lexical-token 'NEWLINE #f #\newline)
- (make-lexical-token 'NUMBER #f 41)
- (make-lexical-token 'NUMBER #f 42)
- (make-lexical-token 'NEWLINE #f #\newline))
- => '((line 1 #\newline)
- (line 2 #\newline)
- (line 3 #\newline)
- (line 41 42 #\newline)))
-
-;;; --------------------------------------------------------------------
-;;; Successful error recovery.
-
-(check
- ;;The BAD triggers an error, recovery happens, the first NEWLINE is
- ;;correctly parsed as recovery token; the second line is correct.
- (let ((r (doit (make-lexical-token 'NUMBER #f 1)
- (make-lexical-token 'BAD #f 'alpha)
- (make-lexical-token 'NEWLINE #f #\newline)
- (make-lexical-token 'NUMBER #f 2)
- (make-lexical-token 'NEWLINE #f #\newline))))
- (cons r *error*))
- => '(((recover #f #f)
- (line 2 #\newline))
- (error-handler "Syntax error: unexpected token : " . BAD)))
-
-
-(check
- ;;The first BAD triggers an error, recovery happens skipping the
- ;;second and third BADs, the first NEWLINE is detected as
- ;;synchronisation token; the second line is correct.
- (let ((r (doit (make-lexical-token 'NUMBER #f 1)
- (make-lexical-token 'BAD #f 'alpha)
- (make-lexical-token 'BAD #f 'beta)
- (make-lexical-token 'BAD #f 'delta)
- (make-lexical-token 'NEWLINE #f #\newline)
- (make-lexical-token 'NUMBER #f 2)
- (make-lexical-token 'NEWLINE #f #\newline))))
- (cons r *error*))
- => '(((recover #f #f)
- (line 2 #\newline))
- (error-handler "Syntax error: unexpected token : " . BAD)))
-
-;;; --------------------------------------------------------------------
-;;; Failed error recovery.
-
-(check
- ;;End-of-input is found after NUMBER.
- (let ((r (doit (make-lexical-token 'NUMBER #f 1))))
- (cons r *error*))
- => '(#f (error-handler "Syntax error: unexpected end of input")))
-
-(check
- ;;The BAD triggers the error, the stack is rewind up to the start,
- ;;then end-of-input happens while trying to skip tokens until the
- ;;synchronisation one is found. End-of-input is an acceptable token
- ;;after the start.
- (let ((r (doit (make-lexical-token 'NUMBER #f 1)
- (make-lexical-token 'BAD #f 'alpha)
- (make-lexical-token 'BAD #f 'beta)
- (make-lexical-token 'BAD #f 'delta))))
- (cons r *error*))
- => '(0 (error-handler "Syntax error: unexpected token : " . BAD)))
-
-(check
- ;;The BAD triggers the error, the stack is rewind up to the start,
- ;;then end-of-input happens while trying to skip tokens until the
- ;;synchronisation one is found. End-of-input is an acceptable token
- ;;after the start.
- (let ((r (doit (make-lexical-token 'BAD #f 'alpha))))
- (cons r *error*))
- => '(0 (error-handler "Syntax error: unexpected token : " . BAD)))
-
-;;; end of file
-;;; test-lr-error-recovery-02.scm --
-;;
-;;Test error recovery policy when the synchronisation terminal has the
-;;same category of the lookahead that raises the error.
-;;
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser (expect\_ 0)
- (A B C)
- (alphas (alpha) \_ $1
- (alphas alpha) \_ $2)
- (alpha (A B) \_ (list $1 $2)
- (C) \_ $1
- (error C) \_ 'error-form))))
- (parser (make-lexer tokens) error-handler)))
-
-;;; --------------------------------------------------------------------
-;;; No error, just grammar tests.
-
-(check
- (doit (make-lexical-token 'A #f 1)
- (make-lexical-token 'B #f 2))
- => '(1 2))
-
-(check
- (doit (make-lexical-token 'C #f 3))
- => '3)
-
-;;; --------------------------------------------------------------------
-;;; Successful error recovery.
-
-(check
- ;;Error, recovery, end-of-input.
- (let ((r (doit (make-lexical-token 'A #f 1)
- (make-lexical-token 'C #f 3))))
- (cons r *error*))
- => '(error-form (error-handler "Syntax error: unexpected token : " . C)))
-
-(check
- ;;Error, recovery, correct parse of "A B".
- (let ((r (doit (make-lexical-token 'A #f 1)
- (make-lexical-token 'C #f 3)
- (make-lexical-token 'A #f 1)
- (make-lexical-token 'B #f 2))))
- (cons r *error*))
- => '((1 2)
- (error-handler "Syntax error: unexpected token : " . C)))
-
-;;; end of file
-;;; test-lr-no-clause.scm --
-;;
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser (expect\_ 0)
- (NUMBER COMMA NEWLINE)
-
- (lines (lines line) \_ (list $2)
- (line) \_ (list $1))
- (line (NEWLINE) \_ #\newline
- (NUMBER NEWLINE) \_ $1
- ;;this is a rule with no semantic action
- (COMMA NUMBER NEWLINE)))))
- (parser (make-lexer tokens) error-handler)))
-
-(check
- ;;correct input
- (doit (make-lexical-token 'NUMBER #f 1)
- (make-lexical-token 'NEWLINE #f #\newline))
- => '(1))
-
-(check
- ;;correct input with comma, which is a rule with no client form
- (doit (make-lexical-token 'COMMA #f #\,)
- (make-lexical-token 'NUMBER #f 1)
- (make-lexical-token 'NEWLINE #f #\newline))
- => '(#(line-3 #\, 1 #\newline)))
-
-(check
- ;;correct input with comma, which is a rule with no client form
- (doit (make-lexical-token 'NUMBER #f 1)
- (make-lexical-token 'NEWLINE #f #\newline)
- (make-lexical-token 'COMMA #f #\,)
- (make-lexical-token 'NUMBER #f 2)
- (make-lexical-token 'NEWLINE #f #\newline))
- => '(#(line-3 #\, 2 #\newline)))
-
-;;; end of file
-;;; test-lr-script-expression.scm --
-;;
-;;Parse scripts, each line an expression.
-;;
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser (expect\_ 0)
- (N O C T (left\_ A) (left\_ M) (nonassoc\_ U))
-
- (script (lines) \_ (reverse $1))
-
- (lines (lines line) \_ (cons $2 $1)
- (line) \_ (list $1))
-
- (line (T) \_ #\newline
- (E T) \_ $1
- (error T) \_ (list 'error-clause $2))
-
- (E (N) \_ $1
- (E A E) \_ ($2 $1 $3)
- (E M E) \_ ($2 $1 $3)
- (A E (prec\_ U)) \_ ($1 $2)
- (O E C) \_ $2))))
- (parser (make-lexer tokens) error-handler)))
-
-;;; --------------------------------------------------------------------
-;;; Correct input
-
-(check
- (doit (make-lexical-token 'T #f #\newline))
- => '(#\newline))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'T #f #\newline))
- => '(1))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'T #f #\newline))
- => '(3))
-
-(check
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'M #f *)
- (make-lexical-token 'N #f 3)
- (make-lexical-token 'T #f #\newline))
- => '(7))
-
-(check
- (doit (make-lexical-token 'O #f #\()
- (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'C #f #\))
- (make-lexical-token 'M #f *)
- (make-lexical-token 'N #f 3)
- (make-lexical-token 'T #f #\newline))
- => '(9))
-
-(check
- (doit (make-lexical-token 'O #f #\()
- (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'C #f #\))
- (make-lexical-token 'M #f *)
- (make-lexical-token 'N #f 3)
- (make-lexical-token 'T #f #\newline)
-
- (make-lexical-token 'N #f 4)
- (make-lexical-token 'M #f /)
- (make-lexical-token 'N #f 5)
- (make-lexical-token 'T #f #\newline))
- => '(9 4/5))
-
-;;; --------------------------------------------------------------------
-
-(check
- ;;Successful error recovery.
- (doit (make-lexical-token 'O #f #\()
- (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'M #f *)
- (make-lexical-token 'N #f 3)
- (make-lexical-token 'T #f #\newline)
-
- (make-lexical-token 'N #f 4)
- (make-lexical-token 'M #f /)
- (make-lexical-token 'N #f 5)
- (make-lexical-token 'T #f #\newline))
- => '((error-clause #f)
- 4/5))
-
-(check
- ;;Unexpected end of input.
- (let ((r (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2))))
- (cons r *error*))
- => '(#f (error-handler "Syntax error: unexpected end of input")))
-
-(check
- ;;Unexpected end of input.
- (let ((r (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'T #f #\newline))))
- (cons r *error*))
- => '(((error-clause #f))
- (error-handler "Syntax error: unexpected token : " . T)))
-
-;;; end of file
-;;; test-lr-single-expressions.scm --
-;;
-;;Grammar accepting single expressions.
-;;
-
-(load "common-test.scm")
-
-(define (doit . tokens)
- (let ((parser (lalr-parser (expect\_ 0)
- (N O C (left\_ A) (left\_ M) (nonassoc\_ U))
-
- (E (N) \_ $1
- (E A E) \_ ($2 $1 $3)
- (E M E) \_ ($2 $1 $3)
- (A E (prec\_ U)) \_ ($1 $2)
- (O E C) \_ $2))))
- (parser (make-lexer tokens) error-handler)))
-
-;;; --------------------------------------------------------------------
-
-(check ;correct input
- (doit (make-lexical-token 'N #f 1))
- => 1)
-
-(check ;correct input
- (doit (make-lexical-token 'A #f -)
- (make-lexical-token 'N #f 1))
- => -1)
-
-(check ;correct input
- (doit (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 1))
- => 1)
-
-(check ;correct input
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2))
- => 3)
-
-(check ;correct input
- (doit (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'M #f *)
- (make-lexical-token 'N #f 3))
- => 7)
-
-(check ;correct input
- (doit (make-lexical-token 'O #f #\()
- (make-lexical-token 'N #f 1)
- (make-lexical-token 'A #f +)
- (make-lexical-token 'N #f 2)
- (make-lexical-token 'C #f #\))
- (make-lexical-token 'M #f *)
- (make-lexical-token 'N #f 3))
- => 9)
-
-;;; end of file
-(define-module (test-import-order-a)
- #\use-module (base))
-
-(push!)
-(define-module (test-import-order-b)
- #\use-module (base))
-
-(push!)
-(define-module (test-import-order-c)
- #\use-module (base))
-
-(push!)
-(define-module (test-import-order-d)
- #\use-module (base))
-
-(push!)
-;;;; test-suite/lib.scm --- generic support for testing
-;;;; Copyright (C) 1999, 2000, 2001, 2004, 2006, 2007, 2009, 2010,
-;;;; 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
-;;;;
-;;;; This program is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3, or (at your option) any later version.
-;;;;
-;;;; This program is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;;;; GNU Lesser General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this software; see the file COPYING.LESSER.
-;;;; If not, write to the Free Software Foundation, Inc., 51 Franklin
-;;;; Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-(define-module (test-suite lib)
- #\use-module (ice-9 stack-catch)
- #\use-module (ice-9 regex)
- #\autoload (srfi srfi-1) (append-map)
- #\autoload (system base compile) (compile)
- #\export (
-
- ;; Exceptions which are commonly being tested for.
- exception_syntax-pattern-unmatched
- exception_bad-variable
- exception_missing-expression
- exception_out-of-range exception_unbound-var
- exception_used-before-defined
- exception_wrong-num-args exception_wrong-type-arg
- exception_numerical-overflow
- exception_struct-set!-denied
- exception_system-error
- exception_encoding-error
- exception_miscellaneous-error
- exception_string-contains-nul
- exception_read-error
- exception_null-pointer-error
- exception_vm-error
-
- ;; Reporting passes and failures.
- run-test
- pass-if expect-fail
- pass-if-equal
- pass-if-exception expect-fail-exception
-
- ;; Naming groups of tests in a regular fashion.
- with-test-prefix
- with-test-prefix*
- with-test-prefix/c&e
- current-test-prefix
- format-test-name
-
- ;; Using the debugging evaluator.
- with-debugging-evaluator with-debugging-evaluator*
-
- ;; Clearing stale references on the C stack for GC-sensitive tests.
- clear-stale-stack-references
-
- ;; Using a given locale
- with-locale with-locale* with-latin1-locale with-latin1-locale*
-
- ;; The bit bucket.
- %null-device
-
- ;; Reporting results in various ways.
- register-reporter unregister-reporter reporter-registered?
- make-count-reporter print-counts
- make-log-reporter
- full-reporter
- user-reporter))
-
-
-;;;; If you're using Emacs's Scheme mode_
-;;;; (put 'with-test-prefix 'scheme-indent-function 1)
-
-
-;;;; CORE FUNCTIONS
-;;;;
-;;;; The function (run-test name expected-result thunk) is the heart of the
-;;;; testing environment. The first parameter NAME is a unique name for the
-;;;; test to be executed (for an explanation of this parameter see below under
-;;;; TEST NAMES). The second parameter EXPECTED-RESULT is a boolean value
-;;;; that indicates whether the corresponding test is expected to pass. If
-;;;; EXPECTED-RESULT is #t the test is expected to pass, if EXPECTED-RESULT is
-;;;; #f the test is expected to fail. Finally, THUNK is the function that
-;;;; actually performs the test. For example_
-;;;;
-;;;; (run-test "integer addition" #t (lambda () (= 2 (+ 1 1))))
-;;;;
-;;;; To report success, THUNK should either return #t or throw 'pass. To
-;;;; report failure, THUNK should either return #f or throw 'fail. If THUNK
-;;;; returns a non boolean value or throws 'unresolved, this indicates that
-;;;; the test did not perform as expected. For example the property that was
-;;;; to be tested could not be tested because something else went wrong.
-;;;; THUNK may also throw 'untested to indicate that the test was deliberately
-;;;; not performed, for example because the test case is not complete yet.
-;;;; Finally, if THUNK throws 'unsupported, this indicates that this test
-;;;; requires some feature that is not available in the configured testing
-;;;; environment. All other exceptions thrown by THUNK are considered as
-;;;; errors.
-;;;;
-;;;;
-;;;; Convenience macros for tests expected to pass or fail
-;;;;
-;;;; * (pass-if name body) is a short form for
-;;;; (run-test name #t (lambda () body))
-;;;; * (expect-fail name body) is a short form for
-;;;; (run-test name #f (lambda () body))
-;;;;
-;;;; For example_
-;;;;
-;;;; (pass-if "integer addition" (= 2 (+ 1 1)))
-;;;;
-;;;;
-;;;; Convenience macros to test for exceptions
-;;;;
-;;;; The following macros take exception parameters which are pairs
-;;;; (type . message), where type is a symbol that denotes an exception type
-;;;; like 'wrong-type-arg or 'out-of-range, and message is a string holding a
-;;;; regular expression that describes the error message for the exception
-;;;; like "Argument .* out of range".
-;;;;
-;;;; * (pass-if-exception name exception body) will pass if the execution of
-;;;; body causes the given exception to be thrown. If no exception is
-;;;; thrown, the test fails. If some other exception is thrown, it is an
-;;;; error.
-;;;; * (expect-fail-exception name exception body) will pass unexpectedly if
-;;;; the execution of body causes the given exception to be thrown. If no
-;;;; exception is thrown, the test fails expectedly. If some other
-;;;; exception is thrown, it is an error.
-
-
-;;;; TEST NAMES
-;;;;
-;;;; Every test in the test suite has a unique name, to help
-;;;; developers find tests that are failing (or unexpectedly passing),
-;;;; and to help gather statistics.
-;;;;
-;;;; A test name is a list of printable objects. For example_
-;;;; ("ports.scm" "file" "read and write back list of strings")
-;;;; ("ports.scm" "pipe" "read")
-;;;;
-;;;; Test names may contain arbitrary objects, but they always have
-;;;; the following properties_
-;;;; - Test names can be compared with EQUAL?.
-;;;; - Test names can be reliably stored and retrieved with the standard WRITE
-;;;; and READ procedures; doing so preserves their identity.
-;;;;
-;;;; For example_
-;;;;
-;;;; (pass-if "simple addition" (= 4 (+ 2 2)))
-;;;;
-;;;; In that case, the test name is the list ("simple addition").
-;;;;
-;;;; In the case of simple tests the expression that is tested would often
-;;;; suffice as a test name by itself. Therefore, the convenience macros
-;;;; pass-if and expect-fail provide a shorthand notation that allows to omit
-;;;; a test name in such cases.
-;;;;
-;;;; * (pass-if expression) is a short form for
-;;;; (run-test 'expression #t (lambda () expression))
-;;;; * (expect-fail expression) is a short form for
-;;;; (run-test 'expression #f (lambda () expression))
-;;;;
-;;;; For example_
-;;;;
-;;;; (pass-if (= 2 (+ 1 1)))
-;;;;
-;;;; The WITH-TEST-PREFIX syntax and WITH-TEST-PREFIX* procedure establish
-;;;; a prefix for the names of all tests whose results are reported
-;;;; within their dynamic scope. For example_
-;;;;
-;;;; (begin
-;;;; (with-test-prefix "basic arithmetic"
-;;;; (pass-if "addition" (= (+ 2 2) 4))
-;;;; (pass-if "subtraction" (= (- 4 2) 2)))
-;;;; (pass-if "multiplication" (= (* 2 2) 4)))
-;;;;
-;;;; In that example, the three test names are_
-;;;; ("basic arithmetic" "addition"),
-;;;; ("basic arithmetic" "subtraction"), and
-;;;; ("multiplication").
-;;;;
-;;;; WITH-TEST-PREFIX can be nested. Each WITH-TEST-PREFIX appends
-;;;; a new element to the current prefix_
-;;;;
-;;;; (with-test-prefix "arithmetic"
-;;;; (with-test-prefix "addition"
-;;;; (pass-if "integer" (= (+ 2 2) 4))
-;;;; (pass-if "complex" (= (+ 2+3i 4+5i) 6+8i)))
-;;;; (with-test-prefix "subtraction"
-;;;; (pass-if "integer" (= (- 2 2) 0))
-;;;; (pass-if "complex" (= (- 2+3i 1+2i) 1+1i))))
-;;;;
-;;;; The four test names here are_
-;;;; ("arithmetic" "addition" "integer")
-;;;; ("arithmetic" "addition" "complex")
-;;;; ("arithmetic" "subtraction" "integer")
-;;;; ("arithmetic" "subtraction" "complex")
-;;;;
-;;;; To print a name for a human reader, we DISPLAY its elements,
-;;;; separated by "_ ". So, the last set of test names would be
-;;;; reported as_
-;;;;
-;;;; arithmetic_ addition_ integer
-;;;; arithmetic_ addition_ complex
-;;;; arithmetic_ subtraction_ integer
-;;;; arithmetic_ subtraction_ complex
-;;;;
-;;;; The Guile benchmarks use with-test-prefix to include the name of
-;;;; the source file containing the test in the test name, to help
-;;;; developers to find failing tests, and to provide each file with its
-;;;; own namespace.
-
-
-;;;; REPORTERS
-;;;;
-;;;; A reporter is a function which we apply to each test outcome.
-;;;; Reporters can log results, print interesting results to the
-;;;; standard output, collect statistics, etc.
-;;;;
-;;;; A reporter function takes two mandatory arguments, RESULT and TEST, and
-;;;; possibly additional arguments depending on RESULT; its return value
-;;;; is ignored. RESULT has one of the following forms_
-;;;;
-;;;; pass - The test named TEST passed.
-;;;; Additional arguments are ignored.
-;;;; upass - The test named TEST passed unexpectedly.
-;;;; Additional arguments are ignored.
-;;;; fail - The test named TEST failed.
-;;;; Additional arguments are ignored.
-;;;; xfail - The test named TEST failed, as expected.
-;;;; Additional arguments are ignored.
-;;;; unresolved - The test named TEST did not perform as expected, for
-;;;; example the property that was to be tested could not be
-;;;; tested because something else went wrong.
-;;;; Additional arguments are ignored.
-;;;; untested - The test named TEST was not actually performed, for
-;;;; example because the test case is not complete yet.
-;;;; Additional arguments are ignored.
-;;;; unsupported - The test named TEST requires some feature that is not
-;;;; available in the configured testing environment.
-;;;; Additional arguments are ignored.
-;;;; error - An error occurred while the test named TEST was
-;;;; performed. Since this result means that the system caught
-;;;; an exception it could not handle, the exception arguments
-;;;; are passed as additional arguments.
-;;;;
-;;;; This library provides some standard reporters for logging results
-;;;; to a file, reporting interesting results to the user, and
-;;;; collecting totals.
-;;;;
-;;;; You can use the REGISTER-REPORTER function and friends to add
-;;;; whatever reporting functions you like. If you don't register any
-;;;; reporters, the library uses FULL-REPORTER, which simply writes
-;;;; all results to the standard output.
-
-
-;;;; MISCELLANEOUS
-;;;;
-
-;;; Define some exceptions which are commonly being tested for.
-(define exception_syntax-pattern-unmatched
- (cons 'syntax-error "source expression failed to match any pattern"))
-(define exception_bad-variable
- (cons 'syntax-error "Bad variable"))
-(define exception_missing-expression
- (cons 'misc-error "^missing or extra expression"))
-(define exception_out-of-range
- (cons 'out-of-range "^.*out of range"))
-(define exception_unbound-var
- (cons 'unbound-variable "^Unbound variable"))
-(define exception_used-before-defined
- (cons 'unbound-variable "^Variable used before given a value"))
-(define exception_wrong-num-args
- (cons 'wrong-number-of-args "^Wrong number of arguments"))
-(define exception_wrong-type-arg
- (cons 'wrong-type-arg "^Wrong type"))
-(define exception_numerical-overflow
- (cons 'numerical-overflow "^Numerical overflow"))
-(define exception_struct-set!-denied
- (cons 'misc-error "^set! denied for field"))
-(define exception_system-error
- (cons 'system-error ".*"))
-(define exception_encoding-error
- (cons 'encoding-error "(cannot convert.* to output locale|input (locale conversion|decoding) error)"))
-(define exception_miscellaneous-error
- (cons 'misc-error "^.*"))
-(define exception_read-error
- (cons 'read-error "^.*$"))
-(define exception_null-pointer-error
- (cons 'null-pointer-error "^.*$"))
-(define exception_vm-error
- (cons 'vm-error "^.*$"))
-
-;; as per throw in scm_to_locale_stringn()
-(define exception_string-contains-nul
- (cons 'misc-error "^string contains #\\\\nul character"))
-
-
-;;; Display all parameters to the default output port, followed by a newline.
-(define (display-line . objs)
- (for-each display objs)
- (newline))
-
-;;; Display all parameters to the given output port, followed by a newline.
-(define (display-line-port port . objs)
- (for-each (lambda (obj) (display obj port)) objs)
- (newline port))
-
-
-;;;; CORE FUNCTIONS
-;;;;
-
-;;; The central testing routine.
-;;; The idea is taken from Greg, the GNUstep regression test environment.
-(define run-test
- (let ((test-running #f))
- (lambda (name expect-pass thunk)
- (if test-running
- (error "Nested calls to run-test are not permitted."))
- (let ((test-name (full-name name)))
- (set! test-running #t)
- (catch #t
- (lambda ()
- (let ((result (thunk)))
- (if (eq? result #t) (throw 'pass))
- (if (eq? result #f) (throw 'fail))
- (throw 'unresolved)))
- (lambda (key . args)
- (case key
- ((pass)
- (report (if expect-pass 'pass 'upass) test-name))
- ((fail)
- ;; ARGS may contain extra info about the failure,
- ;; such as the expected and actual value.
- (apply report (if expect-pass 'fail 'xfail)
- test-name
- args))
- ((unresolved untested unsupported)
- (report key test-name))
- ((quit)
- (report 'unresolved test-name)
- (quit))
- (else
- (report 'error test-name (cons key args))))))
- (set! test-running #f)))))
-
-;;; A short form for tests that are expected to pass, taken from Greg.
-(define-syntax pass-if
- (syntax-rules ()
- ((_ name)
- ;; presume this is a simple test, i.e. (pass-if (even? 2))
- ;; where the body should also be the name.
- (run-test 'name #t (lambda () name)))
- ((_ name rest ...)
- (run-test name #t (lambda () rest ...)))))
-
-(define-syntax pass-if-equal
- (syntax-rules ()
- "Succeed if and only if BODY's return value is equal? to EXPECTED."
- ((_ expected body)
- (pass-if-equal 'body expected body))
- ((_ name expected body ...)
- (run-test name #t
- (lambda ()
- (let ((result (begin body ...)))
- (or (equal? expected result)
- (throw 'fail
- 'expected-value expected
- 'actual-value result))))))))
-
-;;; A short form for tests that are expected to fail, taken from Greg.
-(define-syntax expect-fail
- (syntax-rules ()
- ((_ name)
- ;; presume this is a simple test, i.e. (expect-fail (even? 2))
- ;; where the body should also be the name.
- (run-test 'name #f (lambda () name)))
- ((_ name rest ...)
- (run-test name #f (lambda () rest ...)))))
-
-;;; A helper function to implement the macros that test for exceptions.
-(define (run-test-exception name exception expect-pass thunk)
- (run-test name expect-pass
- (lambda ()
- (stack-catch (car exception)
- (lambda () (thunk) #f)
- (lambda (key proc message . rest)
- (cond
- ;; handle explicit key
- ((string-match (cdr exception) message)
- #t)
- ;; handle `(error ...)' which uses `misc-error' for key and doesn't
- ;; yet format the message and args (we have to do it here).
- ((and (eq? 'misc-error (car exception))
- (list? rest)
- (string-match (cdr exception)
- (apply simple-format #f message (car rest))))
- #t)
- ;; handle syntax errors which use `syntax-error' for key and don't
- ;; yet format the message and args (we have to do it here).
- ((and (eq? 'syntax-error (car exception))
- (list? rest)
- (string-match (cdr exception)
- (apply simple-format #f message (car rest))))
- #t)
- ;; unhandled; throw again
- (else
- (apply throw key proc message rest))))))))
-
-;;; A short form for tests that expect a certain exception to be thrown.
-(define-syntax pass-if-exception
- (syntax-rules ()
- ((_ name exception body rest ...)
- (run-test-exception name exception #t (lambda () body rest ...)))))
-
-;;; A short form for tests expected to fail to throw a certain exception.
-(define-syntax expect-fail-exception
- (syntax-rules ()
- ((_ name exception body rest ...)
- (run-test-exception name exception #f (lambda () body rest ...)))))
-
-
-;;;; TEST NAMES
-;;;;
-
-;;;; Turn a test name into a nice human-readable string.
-(define (format-test-name name)
- ;; Choose a Unicode-capable encoding so that the string port can contain any
- ;; valid Unicode character.
- (with-fluids ((%default-port-encoding "UTF-8"))
- (call-with-output-string
- (lambda (port)
- (let loop ((name name)
- (separator ""))
- (if (pair? name)
- (begin
- (display separator port)
- (display (car name) port)
- (loop (cdr name) "_ "))))))))
-
-;;;; For a given test-name, deliver the full name including all prefixes.
-(define (full-name name)
- (append (current-test-prefix) (list name)))
-
-;;; A fluid containing the current test prefix, as a list.
-(define prefix-fluid (make-fluid '()))
-(define (current-test-prefix)
- (fluid-ref prefix-fluid))
-
-;;; Postpend PREFIX to the current name prefix while evaluting THUNK.
-;;; The name prefix is only changed within the dynamic scope of the
-;;; call to with-test-prefix*. Return the value returned by THUNK.
-(define (with-test-prefix* prefix thunk)
- (with-fluids ((prefix-fluid
- (append (fluid-ref prefix-fluid) (list prefix))))
- (thunk)))
-
-;;; (with-test-prefix PREFIX BODY ...)
-;;; Postpend PREFIX to the current name prefix while evaluating BODY ...
-;;; The name prefix is only changed within the dynamic scope of the
-;;; with-test-prefix expression. Return the value returned by the last
-;;; BODY expression.
-(define-syntax with-test-prefix
- (syntax-rules ()
- ((_ prefix body ...)
- (with-test-prefix* prefix (lambda () body ...)))))
-
-(define-syntax c&e
- (syntax-rules (pass-if pass-if-equal pass-if-exception)
- "Run the given tests both with the evaluator and the compiler/VM."
- ((_ (pass-if test-name exp))
- (begin (pass-if (string-append test-name " (eval)")
- (primitive-eval 'exp))
- (pass-if (string-append test-name " (compile)")
- (compile 'exp #\to 'value #\env (current-module)))))
- ((_ (pass-if-equal test-name val exp))
- (begin (pass-if-equal (string-append test-name " (eval)") val
- (primitive-eval 'exp))
- (pass-if-equal (string-append test-name " (compile)") val
- (compile 'exp #\to 'value #\env (current-module)))))
- ((_ (pass-if-exception test-name exc exp))
- (begin (pass-if-exception (string-append test-name " (eval)")
- exc (primitive-eval 'exp))
- (pass-if-exception (string-append test-name " (compile)")
- exc (compile 'exp #\to 'value
- #\env (current-module)))))))
-
-;;; (with-test-prefix/c&e PREFIX BODY ...)
-;;; Same as `with-test-prefix', but the enclosed tests are run both with
-;;; the compiler/VM and the evaluator.
-(define-syntax with-test-prefix/c&e
- (syntax-rules ()
- ((_ section-name exp ...)
- (with-test-prefix section-name (c&e exp) ...))))
-
-;;; Call THUNK using the debugging evaluator.
-(define (with-debugging-evaluator* thunk)
- (let ((dopts #f))
- (dynamic-wind
- (lambda ()
- (set! dopts (debug-options)))
- thunk
- (lambda ()
- (debug-options dopts)))))
-
-;;; Evaluate BODY... using the debugging evaluator.
-(define-macro (with-debugging-evaluator . body)
- `(with-debugging-evaluator* (lambda () ,@body)))
-
-;; Recurse through a C function that should clear any values that might
-;; have spilled on the stack temporarily. (The salient feature of
-;; with-continuation-barrier is that currently it is implemented as a C
-;; function that recursively calls the VM.)
-;;
-(define* (clear-stale-stack-references #\optional (n 10))
- (if (positive? n)
- (with-continuation-barrier
- (lambda ()
- (clear-stale-stack-references (1- n))))))
-
-;;; Call THUNK with a given locale
-(define (with-locale* nloc thunk)
- (let ((loc #f))
- (dynamic-wind
- (lambda ()
- (if (defined? 'setlocale)
- (begin
- (set! loc (false-if-exception (setlocale LC_ALL)))
- (if (or (not loc)
- (not (false-if-exception (setlocale LC_ALL nloc))))
- (throw 'unresolved)))
- (throw 'unresolved)))
- thunk
- (lambda ()
- (if (and (defined? 'setlocale) loc)
- (setlocale LC_ALL loc))))))
-
-;;; Evaluate BODY... using the given locale.
-(define-syntax with-locale
- (syntax-rules ()
- ((_ loc body ...)
- (with-locale* loc (lambda () body ...)))))
-
-;;; Try out several ISO-8859-1 locales and run THUNK under the one that works
-;;; (if any).
-(define (with-latin1-locale* thunk)
- (define %locales
- (append-map (lambda (name)
- (list (string-append name ".ISO-8859-1")
- (string-append name ".iso88591")
- (string-append name ".ISO8859-1")))
- '("ca_ES" "da_DK" "de_DE" "es_ES" "es_MX" "en_GB" "en_US"
- "fr_FR" "pt_PT" "nl_NL" "sv_SE")))
-
- (let loop ((locales %locales))
- (if (null? locales)
- (throw 'unresolved)
- (catch 'unresolved
- (lambda ()
- (with-locale* (car locales) thunk))
- (lambda (key . args)
- (loop (cdr locales)))))))
-
-;;; Evaluate BODY... using an ISO-8859-1 locale or throw `unresolved' if none
-;;; was found.
-(define-syntax with-latin1-locale
- (syntax-rules ()
- ((_ body ...)
- (with-latin1-locale* (lambda () body ...)))))
-
-(define %null-device
- ;; On Windows (MinGW), /dev/null does not exist and we must instead
- ;; use NUL. Note that file system procedures automatically translate
- ;; /dev/null, so this variable is only useful for shell snippets.
-
- ;; Test for Windowsness by checking whether the current directory name
- ;; starts with a drive letter.
- (if (string-match "^[a-zA-Z]_[/\\]" (getcwd))
- "NUL"
- "/dev/null"))
-
-
-;;;; REPORTERS
-;;;;
-
-;;; The global list of reporters.
-(define reporters '())
-
-;;; The default reporter, to be used only if no others exist.
-(define default-reporter #f)
-
-;;; Add the procedure REPORTER to the current set of reporter functions.
-;;; Signal an error if that reporter procedure object is already registered.
-(define (register-reporter reporter)
- (if (memq reporter reporters)
- (error "register-reporter_ reporter already registered_ " reporter))
- (set! reporters (cons reporter reporters)))
-
-;;; Remove the procedure REPORTER from the current set of reporter
-;;; functions. Signal an error if REPORTER is not currently registered.
-(define (unregister-reporter reporter)
- (if (memq reporter reporters)
- (set! reporters (delq! reporter reporters))
- (error "unregister-reporter_ reporter not registered_ " reporter)))
-
-;;; Return true iff REPORTER is in the current set of reporter functions.
-(define (reporter-registered? reporter)
- (if (memq reporter reporters) #t #f))
-
-;;; Send RESULT to all currently registered reporter functions.
-(define (report . args)
- (if (pair? reporters)
- (for-each (lambda (reporter) (apply reporter args))
- reporters)
- (apply default-reporter args)))
-
-
-;;;; Some useful standard reporters_
-;;;; Count reporters count the occurrence of each test result type.
-;;;; Log reporters write all test results to a given log file.
-;;;; Full reporters write all test results to the standard output.
-;;;; User reporters write interesting test results to the standard output.
-
-;;; The complete list of possible test results.
-(define result-tags
- '((pass "PASS" "passes_ ")
- (fail "FAIL" "failures_ ")
- (upass "UPASS" "unexpected passes_ ")
- (xfail "XFAIL" "expected failures_ ")
- (unresolved "UNRESOLVED" "unresolved test cases_ ")
- (untested "UNTESTED" "untested test cases_ ")
- (unsupported "UNSUPPORTED" "unsupported test cases_ ")
- (error "ERROR" "errors_ ")))
-
-;;; The list of important test results.
-(define important-result-tags
- '(fail upass unresolved error))
-
-;;; Display a single test result in formatted form to the given port
-(define (print-result port result name . args)
- (let* ((tag (assq result result-tags))
- (label (if tag (cadr tag) #f)))
- (if label
- (begin
- (display label port)
- (display "_ " port)
- (display (format-test-name name) port)
- (if (pair? args)
- (begin
- (display " - arguments_ " port)
- (write args port)))
- (newline port))
- (error "(test-suite lib) FULL-REPORTER_ unrecognized result_ "
- result))))
-
-;;; Return a list of the form (COUNTER RESULTS), where_
-;;; - COUNTER is a reporter procedure, and
-;;; - RESULTS is a procedure taking no arguments which returns the
-;;; results seen so far by COUNTER. The return value is an alist
-;;; mapping outcome symbols (`pass', `fail', etc.) onto counts.
-(define (make-count-reporter)
- (let ((counts (map (lambda (tag) (cons (car tag) 0)) result-tags)))
- (list
- (lambda (result name . args)
- (let ((pair (assq result counts)))
- (if pair
- (set-cdr! pair (+ 1 (cdr pair)))
- (error "count-reporter_ unexpected test result_ "
- (cons result (cons name args))))))
- (lambda ()
- (append counts '())))))
-
-;;; Print a count reporter's results nicely. Pass this function the value
-;;; returned by a count reporter's RESULTS procedure.
-(define (print-counts results . port?)
- (let ((port (if (pair? port?)
- (car port?)
- (current-output-port))))
- (newline port)
- (display-line-port port "Totals for this test run_")
- (for-each
- (lambda (tag)
- (let ((result (assq (car tag) results)))
- (if result
- (display-line-port port (caddr tag) (cdr result))
- (display-line-port port
- "Test suite bug_ "
- "no total available for `" (car tag) "'"))))
- result-tags)
- (newline port)))
-
-;;; Return a reporter procedure which prints all results to the file
-;;; FILE, in human-readable form. FILE may be a filename, or a port.
-(define (make-log-reporter file)
- (let ((port (if (output-port? file) file
- (open-output-file file))))
- (lambda args
- (apply print-result port args)
- (force-output port))))
-
-;;; A reporter that reports all results to the user.
-(define (full-reporter . args)
- (apply print-result (current-output-port) args))
-
-;;; A reporter procedure which shows interesting results (failures,
-;;; unexpected passes etc.) to the user.
-(define (user-reporter result name . args)
- (if (memq result important-result-tags)
- (apply full-reporter result name args)))
-
-(set! default-reporter full-reporter)
-(close-port (current-input-port))
-(let loop ()
- (display "closed\n" (current-error-port))
- (force-output (current-error-port))
- (loop))
-;;; test of defining rnrs libraries
-
-;; Copyright (C) 2010, 2012 Free Software Foundation, Inc.
-;;
-;; This library is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU Lesser General Public
-;; License as published by the Free Software Foundation; either
-;; version 3 of the License, or (at your option) any later version.
-;;
-;; This library is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with this library; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(library (tests rnrs-test-a)
- (export double)
- (import (guile))
-
- (define (double x)
- (* x 2)))
-;;;
-;;; This is a test suite written in the notation of
-;;; SRFI-64, A Scheme API for test suites
-;;;
-
-(test-begin "SRFI 64 - Meta-Test Suite")
-
-;;;
-;;; Ironically, in order to set up the meta-test environment,
-;;; we have to invoke one of the most sophisticated features_
-;;; custom test runners
-;;;
-
-;;; The `prop-runner' invokes `thunk' in the context of a new
-;;; test runner, and returns the indicated properties of the
-;;; last-executed test result.
-
-(define (prop-runner props thunk)
- (let ((r (test-runner-null))
- (plist '()))
- ;;
- (test-runner-on-test-end!
- r
- (lambda (runner)
- (set! plist (test-result-alist runner))))
- ;;
- (test-with-runner r (thunk))
- ;; reorder the properties so they are in the order
- ;; given by `props'. Note that any property listed in `props'
- ;; that is not in the property alist will occur as #f
- (map (lambda (k)
- (assq k plist))
- props)))
-
-;;; `on-test-runner' creates a null test runner and then
-;;; arranged for `visit' to be called with the runner
-;;; whenever a test is run. The results of the calls to
-;;; `visit' are returned in a list
-
-(define (on-test-runner thunk visit)
- (let ((r (test-runner-null))
- (results '()))
- ;;
- (test-runner-on-test-end!
- r
- (lambda (runner)
- (set! results (cons (visit r) results))))
- ;;
- (test-with-runner r (thunk))
- (reverse results)))
-
-;;;
-;;; The `triv-runner' invokes `thunk'
-;;; and returns a list of 6 lists, the first 5 of which
-;;; are a list of the names of the tests that, respectively,
-;;; PASS, FAIL, XFAIL, XPASS, and SKIP.
-;;; The last item is a list of counts.
-;;;
-
-(define (triv-runner thunk)
- (let ((r (test-runner-null))
- (accum-pass '())
- (accum-fail '())
- (accum-xfail '())
- (accum-xpass '())
- (accum-skip '()))
- ;;
- (test-runner-on-bad-count!
- r
- (lambda (runner count expected-count)
- (error (string-append "bad count " (number->string count)
- " but expected "
- (number->string expected-count)))))
- (test-runner-on-bad-end-name!
- r
- (lambda (runner begin end)
- (error (string-append "bad end group name " end
- " but expected " begin))))
- (test-runner-on-test-end!
- r
- (lambda (runner)
- (let ((n (test-runner-test-name runner)))
- (case (test-result-kind runner)
- ((pass) (set! accum-pass (cons n accum-pass)))
- ((fail) (set! accum-fail (cons n accum-fail)))
- ((xpass) (set! accum-xpass (cons n accum-xpass)))
- ((xfail) (set! accum-xfail (cons n accum-xfail)))
- ((skip) (set! accum-skip (cons n accum-skip)))))))
- ;;
- (test-with-runner r (thunk))
- (list (reverse accum-pass) ; passed as expected
- (reverse accum-fail) ; failed, but was expected to pass
- (reverse accum-xfail) ; failed as expected
- (reverse accum-xpass) ; passed, but was expected to fail
- (reverse accum-skip) ; was not executed
- (list (test-runner-pass-count r)
- (test-runner-fail-count r)
- (test-runner-xfail-count r)
- (test-runner-xpass-count r)
- (test-runner-skip-count r)))))
-
-(define (path-revealing-runner thunk)
- (let ((r (test-runner-null))
- (seq '()))
- ;;
- (test-runner-on-test-end!
- r
- (lambda (runner)
- (set! seq (cons (list (test-runner-group-path runner)
- (test-runner-test-name runner))
- seq))))
- (test-with-runner r (thunk))
- (reverse seq)))
-
-;;;
-;;; Now we can start testing compliance with SRFI-64
-;;;
-
-(test-begin "1. Simple test-cases")
-
-(test-begin "1.1. test-assert")
-
-(define (t)
- (triv-runner
- (lambda ()
- (test-assert "a" #t)
- (test-assert "b" #f))))
-
-(test-equal
- "1.1.1. Very simple"
- '(("a") ("b") () () () (1 1 0 0 0))
- (t))
-
-(test-equal
- "1.1.2. A test with no name"
- '(("a") ("") () () () (1 1 0 0 0))
- (triv-runner (lambda () (test-assert "a" #t) (test-assert #f))))
-
-(test-equal
- "1.1.3. Tests can have the same name"
- '(("a" "a") () () () () (2 0 0 0 0))
- (triv-runner (lambda () (test-assert "a" #t) (test-assert "a" #t))))
-
-(define (choke)
- (vector-ref '#(1 2) 3))
-
-(test-equal
- "1.1.4. One way to FAIL is to throw an error"
- '(() ("a") () () () (0 1 0 0 0))
- (triv-runner (lambda () (test-assert "a" (choke)))))
-
-(test-end);1.1
-
-(test-begin "1.2. test-eqv")
-
-(define (mean x y)
- (/ (+ x y) 2.0))
-
-(test-equal
- "1.2.1. Simple numerical equivalence"
- '(("c") ("a" "b") () () () (1 2 0 0 0))
- (triv-runner
- (lambda ()
- (test-eqv "a" (mean 3 5) 4)
- (test-eqv "b" (mean 3 5) 4.5)
- (test-eqv "c" (mean 3 5) 4.0))))
-
-(test-end);1.2
-
-(test-end "1. Simple test-cases")
-
-;;;
-;;;
-;;;
-
-(test-begin "2. Tests for catching errors")
-
-(test-begin "2.1. test-error")
-
-(test-equal
- "2.1.1. Baseline test; PASS with no optional args"
- '(("") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- ;; PASS
- (test-error (vector-ref '#(1 2) 9)))))
-
-(test-equal
- "2.1.2. Baseline test; FAIL with no optional args"
- '(() ("") () () () (0 1 0 0 0))
- (triv-runner
- (lambda ()
- ;; FAIL_ the expr does not raise an error and `test-error' is
- ;; claiming that it will, so this test should FAIL
- (test-error (vector-ref '#(1 2) 0)))))
-
-(test-equal
- "2.1.3. PASS with a test name and error type"
- '(("a") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- ;; PASS
- (test-error "a" #t (vector-ref '#(1 2) 9)))))
-
-(test-end "2.1. test-error")
-
-(test-end "2. Tests for catching errors")
-
-;;;
-;;;
-;;;
-
-(test-begin "3. Test groups and paths")
-
-(test-equal
- "3.1. test-begin with unspecific test-end"
- '(("b") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "b" #t)
- (test-end))))
-
-(test-equal
- "3.2. test-begin with name-matching test-end"
- '(("b") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "b" #t)
- (test-end "a"))))
-
-;;; since the error raised by `test-end' on a mismatch is not a test
-;;; error, we actually expect the triv-runner itself to fail
-
-(test-error
- "3.3. test-begin with mismatched test-end"
-#t
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "b" #t)
- (test-end "x"))))
-
-(test-equal
- "3.4. test-begin with name and count"
- '(("b" "c") () () () () (2 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-begin "a" 2)
- (test-assert "b" #t)
- (test-assert "c" #t)
- (test-end "a"))))
-
-;; similarly here, a mismatched count is a lexical error
-;; and not a test failure...
-
-(test-error
- "3.5. test-begin with mismatched count"
- #t
- (triv-runner
- (lambda ()
- (test-begin "a" 99)
- (test-assert "b" #t)
- (test-end "a"))))
-
-(test-equal
- "3.6. introspecting on the group path"
- '((() "w")
- (("a" "b") "x")
- (("a" "b") "y")
- (("a") "z"))
- ;;
- ;; `path-revealing-runner' is designed to return a list
- ;; of the tests executed, in order. Each entry is a list
- ;; (GROUP-PATH TEST-NAME), and each GROUP-PATH is a list
- ;; of test groups starting from the topmost
- ;;
- (path-revealing-runner
- (lambda ()
- (test-assert "w" #t)
- (test-begin "a")
- (test-begin "b")
- (test-assert "x" #t)
- (test-assert "y" #t)
- (test-end)
- (test-assert "z" #t))))
-
-
-(test-end "3. Test groups and paths")
-
-;;;
-;;;
-;;;
-
-(test-begin "4. Handling set-up and cleanup")
-
-(test-equal "4.1. Normal exit path"
- '(in 1 2 out)
- (let ((ex '()))
- (define (do s)
- (set! ex (cons s ex)))
- ;;
- (triv-runner
- (lambda ()
- (test-group-with-cleanup
- "foo"
- (do 'in)
- (do 1)
- (do 2)
- (do 'out))))
- (reverse ex)))
-
-(test-equal "4.2. Exception exit path"
- '(in 1 out)
- (let ((ex '()))
- (define (do s)
- (set! ex (cons s ex)))
- ;;
- ;; the outer runner is to run the `test-error' in, to
- ;; catch the exception raised in the inner runner,
- ;; since we don't want to depend on any other
- ;; exception-catching support
- ;;
- (triv-runner
- (lambda ()
- (test-error
- (triv-runner
- (lambda ()
- (test-group-with-cleanup
- "foo"
- (do 'in) (test-assert #t)
- (do 1) (test-assert #t)
- (choke) (test-assert #t)
- (do 2) (test-assert #t)
- (do 'out)))))))
- (reverse ex)))
-
-(test-end "4. Handling set-up and cleanup")
-
-;;;
-;;;
-;;;
-
-(test-begin "5. Test specifiers")
-
-(test-begin "5.1. test-match-named")
-
-(test-equal "5.1.1. match test names"
- '(("y") () () () ("x") (1 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-skip (test-match-name "x"))
- (test-assert "x" #t)
- (test-assert "y" #t))))
-
-(test-equal "5.1.2. but not group names"
- '(("z") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-skip (test-match-name "x"))
- (test-begin "x")
- (test-assert "z" #t)
- (test-end))))
-
-(test-end)
-
-(test-begin "5.2. test-match-nth")
-;; See also_ [6.4. Short-circuit evaluation]
-
-(test-equal "5.2.1. skip the nth one after"
- '(("v" "w" "y" "z") () () () ("x") (4 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-nth 2))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP
- (test-assert "y" #t) ; 3
- (test-assert "z" #t)))) ; 4
-
-(test-equal "5.2.2. skip m, starting at n"
- '(("v" "w" "z") () () () ("x" "y") (3 0 0 0 2))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-nth 2 2))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP
- (test-assert "y" #t) ; 3 SKIP
- (test-assert "z" #t)))) ; 4
-
-(test-end)
-
-(test-begin "5.3. test-match-any")
-(test-equal "5.3.1. basic disjunction"
- '(("v" "w" "z") () () () ("x" "y") (3 0 0 0 2))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-any (test-match-nth 3)
- (test-match-name "x")))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP(NAME)
- (test-assert "y" #t) ; 3 SKIP(COUNT)
- (test-assert "z" #t)))) ; 4
-
-(test-equal "5.3.2. disjunction is commutative"
- '(("v" "w" "z") () () () ("x" "y") (3 0 0 0 2))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-any (test-match-name "x")
- (test-match-nth 3)))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP(NAME)
- (test-assert "y" #t) ; 3 SKIP(COUNT)
- (test-assert "z" #t)))) ; 4
-
-(test-end)
-
-(test-begin "5.4. test-match-all")
-(test-equal "5.4.1. basic conjunction"
- '(("v" "w" "y" "z") () () () ("x") (4 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-all (test-match-nth 2 2)
- (test-match-name "x")))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP(NAME) & SKIP(COUNT)
- (test-assert "y" #t) ; 3 SKIP(COUNT)
- (test-assert "z" #t)))) ; 4
-
-(test-equal "5.4.2. conjunction is commutative"
- '(("v" "w" "y" "z") () () () ("x") (4 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-assert "v" #t)
- (test-skip (test-match-all (test-match-name "x")
- (test-match-nth 2 2)))
- (test-assert "w" #t) ; 1
- (test-assert "x" #t) ; 2 SKIP(NAME) & SKIP(COUNT)
- (test-assert "y" #t) ; 3 SKIP(COUNT)
- (test-assert "z" #t)))) ; 4
-
-(test-end)
-
-(test-end "5. Test specifiers")
-
-;;;
-;;;
-;;;
-
-(test-begin "6. Skipping selected tests")
-
-(test-equal
- "6.1. Skip by specifier - match-name"
- '(("x") () () () ("y") (1 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip (test-match-name "y"))
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; SKIP
- (test-end))))
-
-(test-equal
- "6.2. Shorthand specifiers"
- '(("x") () () () ("y") (1 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "y")
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; SKIP
- (test-end))))
-
-(test-begin "6.3. Specifier Stack")
-
-(test-equal
- "6.3.1. Clearing the Specifier Stack"
- '(("x" "x") ("y") () () ("y") (2 1 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "y")
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; SKIP
- (test-end)
- (test-begin "b")
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; FAIL
- (test-end))))
-
-(test-equal
- "6.3.2. Inheriting the Specifier Stack"
- '(("x" "x") () () () ("y" "y") (2 0 0 0 2))
- (triv-runner
- (lambda ()
- (test-skip "y")
- (test-begin "a")
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; SKIP
- (test-end)
- (test-begin "b")
- (test-assert "x" #t) ; PASS
- (test-assert "y" #f) ; SKIP
- (test-end))))
-
-(test-end);6.3
-
-(test-begin "6.4. Short-circuit evaluation")
-
-(test-equal
- "6.4.1. In test-match-all"
- '(("x") ("y" "x" "z") () () ("y") (1 3 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip (test-match-all "y" (test-match-nth 2)))
- ;; let's label the substructure forms so we can
- ;; see which one `test-match-nth' is going to skip
- ;; ; # "y" 2 result
- (test-assert "x" #t) ; 1 - #f #f PASS
- (test-assert "y" #f) ; 2 - #t #t SKIP
- (test-assert "y" #f) ; 3 - #t #f FAIL
- (test-assert "x" #f) ; 4 - #f #f FAIL
- (test-assert "z" #f) ; 5 - #f #f FAIL
- (test-end))))
-
-(test-equal
- "6.4.2. In separate skip-list entries"
- '(("x") ("x" "z") () () ("y" "y") (1 2 0 0 2))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "y")
- (test-skip (test-match-nth 2))
- ;; let's label the substructure forms so we can
- ;; see which one `test-match-nth' is going to skip
- ;; ; # "y" 2 result
- (test-assert "x" #t) ; 1 - #f #f PASS
- (test-assert "y" #f) ; 2 - #t #t SKIP
- (test-assert "y" #f) ; 3 - #t #f SKIP
- (test-assert "x" #f) ; 4 - #f #f FAIL
- (test-assert "z" #f) ; 5 - #f #f FAIL
- (test-end))))
-
-(test-begin "6.4.3. Skipping test suites")
-
-(test-equal
- "6.4.3.1. Introduced using 'test-begin'"
- '(("x") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "b")
- (test-begin "b") ; not skipped
- (test-assert "x" #t)
- (test-end "b")
- (test-end "a"))))
-
-(test-expect-fail 1) ;; ???
-(test-equal
- "6.4.3.2. Introduced using 'test-group'"
- '(() () () () () (0 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "b")
- (test-group
- "b" ; skipped
- (test-assert "x" #t))
- (test-end "a"))))
-
-(test-equal
- "6.4.3.3. Non-skipped 'test-group'"
- '(("x") () () () () (1 0 0 0 0))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-skip "c")
- (test-group "b" (test-assert "x" #t))
- (test-end "a"))))
-
-(test-end) ; 6.4.3
-
-(test-end);6.4
-
-(test-end "6. Skipping selected tests")
-
-;;;
-;;;
-;;;
-
-(test-begin "7. Expected failures")
-
-(test-equal "7.1. Simple example"
- '(() ("x") ("z") () () (0 1 1 0 0))
- (triv-runner
- (lambda ()
- (test-assert "x" #f)
- (test-expect-fail "z")
- (test-assert "z" #f))))
-
-(test-equal "7.2. Expected exception"
- '(() ("x") ("z") () () (0 1 1 0 0))
- (triv-runner
- (lambda ()
- (test-assert "x" #f)
- (test-expect-fail "z")
- (test-assert "z" (choke)))))
-
-(test-equal "7.3. Unexpectedly PASS"
- '(() () ("y") ("x") () (0 0 1 1 0))
- (triv-runner
- (lambda ()
- (test-expect-fail "x")
- (test-expect-fail "y")
- (test-assert "x" #t)
- (test-assert "y" #f))))
-
-
-
-(test-end "7. Expected failures")
-
-;;;
-;;;
-;;;
-
-(test-begin "8. Test-runner")
-
-;;;
-;;; Because we want this test suite to be accurate even
-;;; when the underlying implementation chooses to use, e.g.,
-;;; a global variable to implement what could be thread variables
-;;; or SRFI-39 parameter objects, we really need to save and restore
-;;; their state ourselves
-;;;
-(define (with-factory-saved thunk)
- (let* ((saved (test-runner-factory))
- (result (thunk)))
- (test-runner-factory saved)
- result))
-
-(test-begin "8.1. test-runner-current")
-(test-assert "8.1.1. automatically restored"
- (let ((a 0)
- (b 1)
- (c 2))
- ;
- (triv-runner
- (lambda ()
- (set! a (test-runner-current))
- ;;
- (triv-runner
- (lambda ()
- (set! b (test-runner-current))))
- ;;
- (set! c (test-runner-current))))
- ;;
- (and (eq? a c)
- (not (eq? a b)))))
-
-(test-end)
-
-(test-begin "8.2. test-runner-simple")
-(test-assert "8.2.1. default on-test hook"
- (eq? (test-runner-on-test-end (test-runner-simple))
- test-on-test-end-simple))
-(test-assert "8.2.2. default on-final hook"
- (eq? (test-runner-on-final (test-runner-simple))
- test-on-final-simple))
-(test-end)
-
-(test-begin "8.3. test-runner-factory")
-
-(test-assert "8.3.1. default factory"
- (eq? (test-runner-factory) test-runner-simple))
-
-(test-assert "8.3.2. settable factory"
- (with-factory-saved
- (lambda ()
- (test-runner-factory test-runner-null)
- ;; we have no way, without bringing in other SRFIs,
- ;; to make sure the following doesn't print anything,
- ;; but it shouldn't_
- (test-with-runner
- (test-runner-create)
- (lambda ()
- (test-begin "a")
- (test-assert #t) ; pass
- (test-assert #f) ; fail
- (test-assert (vector-ref '#(3) 10)) ; fail with error
- (test-end "a")))
- (eq? (test-runner-factory) test-runner-null))))
-
-(test-end)
-
-;;; This got tested about as well as it could in 8.3.2
-
-(test-begin "8.4. test-runner-create")
-(test-end)
-
-;;; This got tested about as well as it could in 8.3.2
-
-(test-begin "8.5. test-runner-factory")
-(test-end)
-
-(test-begin "8.6. test-apply")
-(test-equal "8.6.1. Simple (form 1) test-apply"
- '(("w" "p" "v") () () () ("x") (3 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "w" #t)
- (test-apply
- (test-match-name "p")
- (lambda ()
- (test-begin "p")
- (test-assert "x" #t)
- (test-end)
- (test-begin "z")
- (test-assert "p" #t) ; only this one should execute in here
- (test-end)))
- (test-assert "v" #t))))
-
-(test-equal "8.6.2. Simple (form 2) test-apply"
- '(("w" "p" "v") () () () ("x") (3 0 0 0 1))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "w" #t)
- (test-apply
- (test-runner-current)
- (test-match-name "p")
- (lambda ()
- (test-begin "p")
- (test-assert "x" #t)
- (test-end)
- (test-begin "z")
- (test-assert "p" #t) ; only this one should execute in here
- (test-end)))
- (test-assert "v" #t))))
-
-(test-expect-fail 1) ;; depends on all test-match-nth being called.
-(test-equal "8.6.3. test-apply with skips"
- '(("w" "q" "v") () () () ("x" "p" "x") (3 0 0 0 3))
- (triv-runner
- (lambda ()
- (test-begin "a")
- (test-assert "w" #t)
- (test-skip (test-match-nth 2))
- (test-skip (test-match-nth 4))
- (test-apply
- (test-runner-current)
- (test-match-name "p")
- (test-match-name "q")
- (lambda ()
- ; only execute if SKIP=no and APPLY=yes
- (test-assert "x" #t) ; # 1 SKIP=no APPLY=no
- (test-assert "p" #t) ; # 2 SKIP=yes APPLY=yes
- (test-assert "q" #t) ; # 3 SKIP=no APPLY=yes
- (test-assert "x" #f) ; # 4 SKIP=yes APPLY=no
- 0))
- (test-assert "v" #t))))
-
-;;; Unfortunately, since there is no way to UNBIND the current test runner,
-;;; there is no way to test the behavior of `test-apply' in the absence
-;;; of a current runner within our little meta-test framework.
-;;;
-;;; To test the behavior manually, you should be able to invoke_
-;;;
-;;; (test-apply "a" (lambda () (test-assert "a" #t)))
-;;;
-;;; from the top level (with SRFI 64 available) and it should create a
-;;; new, default (simple) test runner.
-
-(test-end)
-
-;;; This entire suite depends heavily on 'test-with-runner'. If it didn't
-;;; work, this suite would probably go down in flames
-(test-begin "8.7. test-with-runner")
-(test-end)
-
-;;; Again, this suite depends heavily on many of the test-runner
-;;; components. We'll just test those that aren't being exercised
-;;; by the meta-test framework
-(test-begin "8.8. test-runner components")
-
-(define (auxtrack-runner thunk)
- (let ((r (test-runner-null)))
- (test-runner-aux-value! r '())
- (test-runner-on-test-end! r (lambda (r)
- (test-runner-aux-value!
- r
- (cons (test-runner-test-name r)
- (test-runner-aux-value r)))))
- (test-with-runner r (thunk))
- (reverse (test-runner-aux-value r))))
-
-(test-equal "8.8.1. test-runner-aux-value"
- '("x" "" "y")
- (auxtrack-runner
- (lambda ()
- (test-assert "x" #t)
- (test-begin "a")
- (test-assert #t)
- (test-end)
- (test-assert "y" #f))))
-
-(test-end) ; 8.8
-
-(test-end "8. Test-runner")
-
-(test-begin "9. Test Result Properties")
-
-(test-begin "9.1. test-result-alist")
-
-(define (symbol-alist? l)
- (if (null? l)
- #t
- (and (pair? l)
- (pair? (car l))
- (symbol? (caar l))
- (symbol-alist? (cdr l)))))
-
-;;; check the various syntactic forms
-
-(test-assert (symbol-alist?
- (car (on-test-runner
- (lambda ()
- (test-assert #t))
- (lambda (r)
- (test-result-alist r))))))
-
-(test-assert (symbol-alist?
- (car (on-test-runner
- (lambda ()
- (test-assert #t))
- (lambda (r)
- (test-result-alist r))))))
-
-;;; check to make sure the required properties are returned
-
-(test-equal '((result-kind . pass))
- (prop-runner
- '(result-kind)
- (lambda ()
- (test-assert #t)))
- )
-
-(test-equal
- '((result-kind . fail)
- (expected-value . 2)
- (actual-value . 3))
- (prop-runner
- '(result-kind expected-value actual-value)
- (lambda ()
- (test-equal 2 (+ 1 2)))))
-
-(test-end "9.1. test-result-alist")
-
-(test-begin "9.2. test-result-ref")
-
-(test-equal '(pass)
- (on-test-runner
- (lambda ()
- (test-assert #t))
- (lambda (r)
- (test-result-ref r 'result-kind))))
-
-(test-equal '(pass)
- (on-test-runner
- (lambda ()
- (test-assert #t))
- (lambda (r)
- (test-result-ref r 'result-kind))))
-
-(test-equal '(fail pass)
- (on-test-runner
- (lambda ()
- (test-assert (= 1 2))
- (test-assert (= 1 1)))
- (lambda (r)
- (test-result-ref r 'result-kind))))
-
-(test-end "9.2. test-result-ref")
-
-(test-begin "9.3. test-result-set!")
-
-(test-equal '(100 100)
- (on-test-runner
- (lambda ()
- (test-assert (= 1 2))
- (test-assert (= 1 1)))
- (lambda (r)
- (test-result-set! r 'foo 100)
- (test-result-ref r 'foo))))
-
-(test-end "9.3. test-result-set!")
-
-(test-end "9. Test Result Properties")
-
-;;;
-;;;
-;;;
-
-;#| Time to stop having fun...
-;
-;(test-begin "9. For fun, some meta-test errors")
-;
-;(test-equal
-; "9.1. Really PASSes, but test like it should FAIL"
-; '(() ("b") () () ())
-; (triv-runner
-; (lambda ()
-; (test-assert "b" #t))))
-;
-;(test-expect-fail "9.2. Expect to FAIL and do so")
-;(test-expect-fail "9.3. Expect to FAIL but PASS")
-;(test-skip "9.4. SKIP this one")
-;
-;(test-assert "9.2. Expect to FAIL and do so" #f)
-;(test-assert "9.3. Expect to FAIL but PASS" #t)
-;(test-assert "9.4. SKIP this one" #t)
-;
-;(test-end)
-; |#
-
-(test-end "SRFI 64 - Meta-Test Suite")
-
-;;;
-;;; run-vm-tests.scm -- Run Guile-VM's test suite.
-;;;
-;;; Copyright 2005, 2009, 2010 Free Software Foundation, Inc.
-;;;
-;;; This program is free software; you can redistribute it and/or
-;;; modify it under the terms of the GNU Lesser General Public License
-;;; as published by the Free Software Foundation; either version 3 of
-;;; the License, or (at your option) any later version.
-;;;
-;;; This program is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;;; GNU Lesser General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU Lesser General Public License
-;;; along with this program; if not, write to the Free Software
-;;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-(use-modules (system vm vm)
- (system vm program)
- (system base compile)
- (system base language)
- (language scheme spec)
- (language objcode spec)
- (srfi srfi-1)
- (ice-9 r5rs))
-
-
-(define (fetch-sexp-from-file file)
- (with-input-from-file file
- (lambda ()
- (let loop ((sexp (read))
- (result '()))
- (if (eof-object? sexp)
- (cons 'begin (reverse result))
- (loop (read) (cons sexp result)))))))
-
-(define (compile-to-objcode sexp)
- "Compile the expression @var{sexp} into a VM program and return it."
- (compile sexp #\from scheme #\to objcode))
-
-(define (run-vm-program objcode)
- "Run VM program contained into @var{objcode}."
- ((make-program objcode)))
-
-(define (compile/run-test-from-file file)
- "Run test from source file @var{file} and return a value indicating whether
-it succeeded."
- (run-vm-program (compile-to-objcode (fetch-sexp-from-file file))))
-
-
-(define-macro (watch-proc proc-name str)
- `(let ((orig-proc ,proc-name))
- (set! ,proc-name
- (lambda args
- (format #t (string-append ,str "... "))
- (apply orig-proc args)))))
-
-(watch-proc fetch-sexp-from-file "reading")
-(watch-proc compile-to-objcode "compiling")
-(watch-proc run-vm-program "running")
-
-
-;; The program.
-
-(define (run-vm-tests files)
- "For each file listed in @var{files}, load it and run it through both the
-interpreter and the VM (after having it compiled). Both results must be
-equal in the sense of @code{equal?}."
- (let* ((res (map (lambda (file)
- (format #t "running `~a'... " file)
- (if (catch #t
- (lambda ()
- (equal? (compile/run-test-from-file file)
- (primitive-eval (fetch-sexp-from-file file))))
- (lambda (key . args)
- (format #t "[~a/~a] " key args)
- #f))
- (format #t "ok~%")
- (begin (format #t "FAILED~%") #f)))
- files))
- (total (length files))
- (failed (length (filter not res))))
-
- (if (= 0 failed)
- (exit 0)
- (begin
- (format #t "~%~a tests failed out of ~a~%"
- failed total)
- (exit failed)))))
-
-;;; Basic RnRS constructs.
-
-(and (eq? 2 (begin (+ 2 4) 5 2))
- ((lambda (x y)
- (and (eq? x 1) (eq? y 2)
- (begin
- (set! x 11) (set! y 22)
- (and (eq? x 11) (eq? y 22)))))
- 1 2)
- (let ((x 1) (y 3))
- (and (eq? x 1) (eq? y 3)))
- (let loop ((x #t))
- (if (not x)
- #t
- (loop #f))))
-
-(let ((set-counter2 #f))
- (define (get-counter2)
- (call/cc
- (lambda (k)
- (set! set-counter2 k)
- 1)))
- (define (loop counter1)
- (let ((counter2 (get-counter2)))
- (set! counter1 (1+ counter1))
- (cond ((not (= counter1 counter2))
- (error "bad call/cc behaviour" counter1 counter2))
- ((> counter1 10)
- #t)
- (else
- (set-counter2 (1+ counter2))))))
- (loop 0))
-
-(let* ((next #f)
- (counter 0)
- (result (call/cc
- (lambda (k)
- (set! next k)
- 1))))
- (set! counter (+ 1 counter))
- (cond ((not (= counter result))
- (error "bad call/cc behaviour" counter result))
- ((> counter 10)
- #t)
- (else
- (next (+ 1 counter)))))
-;; Test that nonlocal exits of the VM work.
-
-(begin
- (define (foo thunk)
- (catch #t thunk (lambda args args)))
- (foo
- (lambda ()
- (let ((a 'one))
- (1+ a)))))
-
-(define func
- (let ((x 2))
- (lambda ()
- (let ((x++ (+ 1 x)))
- (set! x x++)
- x++))))
-
-(list (func) (func) (func))
-
-(define (uid)
- (let* ((x 2)
- (do-uid (lambda ()
- (let ((x++ (+ 1 x)))
- (set! x x++)
- x++))))
- (do-uid)))
-
-(list (uid) (uid) (uid))
-(define (stuff)
- (let* ((x 2)
- (chbouib (lambda (z)
- (+ 7 z x))))
- (chbouib 77)))
-
-(stuff)
-(define (extract-symbols exp)
- (define (process x out cont)
- (cond ((pair? x)
- (process (car x)
- out
- (lambda (car-x out)
- ;; used to have a bug here whereby `x' was
- ;; modified in the self-tail-recursion to (process
- ;; (cdr x) ...), because we didn't allocate fresh
- ;; externals when doing self-tail-recursion.
- (process (cdr x)
- out
- (lambda (cdr-x out)
- (cont (cons car-x cdr-x)
- out))))))
- ((symbol? x)
- (cont x (cons x out)))
- (else
- (cont x out))))
- (process exp '() (lambda (x out) out)))
-
-(extract-symbols '(a b . c))
-(let ((n+ 0))
- (do ((n- 5 (1- n-))
- (n+ n+ (1+ n+)))
- ((= n- 0))
- (format #f "n- = ~a~%" n-)))
-;; Are global bindings reachable at run-time? This relies on the
-;; `object-ref' and `object-set' instructions.
-
-(begin
-
- (define the-binding "hello")
-
- ((lambda () the-binding))
-
- ((lambda () (set! the-binding "world")))
-
- ((lambda () the-binding)))
-
-;; Check whether literal integers are correctly signed.
-
-(and (= 4294967295 (- (expt 2 32) 1)) ;; unsigned
- (= -2147483648 (- (expt 2 31))) ;; signed
- (= 2147483648 (expt 2 31))) ;; unsigned
-;; Are built-in macros well-expanded at compilation-time?
-
-(false-if-exception (+ 2 2))
-(read-options)
-;; Are macros well-expanded at compilation-time?
-
-(defmacro minus-binary (a b)
- `(- ,a ,b))
-
-(define-macro (plus . args)
- `(let ((res (+ ,@args)))
- ;;(format #t "plus -> ~a~%" res)
- res))
-
-
-(plus (let* ((x (minus-binary 12 7)) ;; 5
- (y (minus-binary x 1))) ;; 4
- (plus x y 5)) ;; 14
- 12 ;; 26
- (expt 2 3)) ;; => 34
-
-; Currently, map is a C function, so this is a way of testing that the
-; VM is reentrant.
-
-(begin
-
- (define (square x)
- (* x x))
-
- (map (lambda (x) (square x))
- '(1 2 3)))
-;;; Pattern matching with `(ice-9 match)'.
-;;;
-
-(use-modules (ice-9 match)
- (srfi srfi-9)) ;; record type (FIXME_ See `t-records.scm')
-
-(define-record-type <stuff>
- (%make-stuff chbouib)
- stuff?
- (chbouib stuff_chbouib stuff_set-chbouib!))
-
-(define (matches? obj)
-; (format #t "matches? ~a~%" obj)
- (match obj
- (($ <stuff>) #t)
-; (blurps #t)
- ("hello" #t)
- (else #f)))
-
-
-;(format #t "go!~%")
-(and (matches? (%make-stuff 12))
- (matches? (%make-stuff 7))
- (matches? "hello")
-; (matches? 'blurps)
- (not (matches? 66)))
-(define (even? x)
- (or (zero? x)
- (not (odd? (1- x)))))
-
-(define (odd? x)
- (not (even? (1- x))))
-
-(even? 20)
-;; all the different permutations of or
-(list
- ;; not in tail position, no args
- (or)
- ;; not in tail position, one arg
- (or 'what)
- (or #f)
- ;; not in tail position, two arg
- (or 'what 'where)
- (or #f 'where)
- (or #f #f)
- (or 'what #f)
- ;; not in tail position, value discarded
- (begin (or 'what (error "two")) 'two)
- ;; in tail position (within the lambdas)
- ((lambda ()
- (or)))
- ((lambda ()
- (or 'what)))
- ((lambda ()
- (or #f)))
- ((lambda ()
- (or 'what 'where)))
- ((lambda ()
- (or #f 'where)))
- ((lambda ()
- (or #f #f)))
- ((lambda ()
- (or 'what #f))))
-(define the-struct (vector 1 2))
-
-(define get/set
- (make-procedure-with-setter
- (lambda (struct name)
- (case name
- ((first) (vector-ref struct 0))
- ((second) (vector-ref struct 1))
- (else #f)))
- (lambda (struct name val)
- (case name
- ((first) (vector-set! struct 0 val))
- ((second) (vector-set! struct 1 val))
- (else #f)))))
-
-(and (eq? (vector-ref the-struct 0) (get/set the-struct 'first))
- (eq? (vector-ref the-struct 1) (get/set the-struct 'second))
- (begin
- (set! (get/set the-struct 'second) 77)
- (eq? (vector-ref the-struct 1) (get/set the-struct 'second))))
-(list
- `()
- `foo
- `(foo)
- `(foo bar)
- `(1 2)
- (let ((x 1)) `,x)
- (let ((x 1)) `(,x))
- (let ((x 1)) ``(,x))
- (let ((head '(a b))
- (tail 'c))
- `(,@head . ,tail)))
-;;; SRFI-9 Records.
-;;;
-
-(use-modules (srfi srfi-9))
-
-(define-record-type <stuff>
- (%make-stuff chbouib)
- stuff?
- (chbouib stuff_chbouib stuff_set-chbouib!))
-
-
-(and (stuff? (%make-stuff 12))
- (= 7 (stuff_chbouib (%make-stuff 7)))
- (not (stuff? 12)))
-(list (call-with-values
- (lambda () (values 1 2))
- (lambda (x y) (cons x y)))
-
- ;; the start-stack forces a bounce through the interpreter
- (call-with-values
- (lambda () (start-stack 'foo (values 1 2)))
- list)
-
- (call-with-values
- (lambda () (apply values '(1)))
- list))
-
diff --git a/src/test/parse.zig b/src/test/parse.zig
index 0e83258..f1a7857 100644
--- a/src/test/parse.zig
+++ b/src/test/parse.zig
@@ -1,19 +1,65 @@
const std = @import("std");
const testing = std.testing;
+const expect = testing.expect;
pub const io = @import("../zisp/io.zig");
pub const value = @import("../zisp/value.zig");
pub const Value = value.Value;
-fn parseString(str: []const u8) Value {
+fn parse(str: []const u8) Value {
var fbs = std.io.fixedBufferStream(str);
return io.parser.parse(fbs.reader().any());
}
+test "parse empty" {
+ try expect(parse("").eq(value.eof));
+ try expect(parse(";").eq(value.eof));
+ try expect(parse(";~").eq(value.eof));
+ try expect(parse(" ;").eq(value.eof));
+ try expect(parse(" ;~").eq(value.eof));
+ try expect(parse("; ").eq(value.eof));
+ try expect(parse(";~ ").eq(value.eof));
+ try expect(parse(";\n").eq(value.eof));
+ try expect(parse(";\n ").eq(value.eof));
+ try expect(parse(";~foo").eq(value.eof));
+ try expect(parse(";~foo ").eq(value.eof));
+ try expect(parse(";~foo;").eq(value.eof));
+ try expect(parse(";~foo ;").eq(value.eof));
+ try expect(parse("\t\r\n ;foo\n;~(\nbar\n)").eq(value.eof));
+}
+
+test "parse short bare string" {
+ const str = value.sstr.pack;
+ try expect(parse(".").eq(str(".")));
+ try expect(parse("..").eq(str("..")));
+ try expect(parse("...").eq(str("...")));
+ try expect(parse(".a.b").eq(str(".a.b")));
+ try expect(parse("+0.1").eq(str("+0.1")));
+ try expect(parse("-0.1").eq(str("-0.1")));
+ try expect(parse("10.1").eq(str("10.1")));
+ try expect(parse("|x()|").eq(str("x()")));
+ try expect(parse("|{\\|}|").eq(str("{|}")));
+ try expect(parse("foobar").eq(str("foobar")));
+ try expect(parse("!$%&*+").eq(str("!$%&*+")));
+ try expect(parse("-/<=>?").eq(str("-/<=>?")));
+ try expect(parse("@^_~00").eq(str("@^_~00")));
+}
+
+test "parse long bare string" {
+ const str = value.istr.intern;
+ try expect(parse("foobarbaz").eq(str("foobarbaz")));
+ try expect(parse(".foo.bar.baz").eq(str(".foo.bar.baz")));
+ try expect(parse("+foo.bar.baz").eq(str("+foo.bar.baz")));
+ try expect(parse("-foo.bar.baz").eq(str("-foo.bar.baz")));
+ try expect(parse("0foo.bar.baz").eq(str("0foo.bar.baz")));
+ try expect(parse("!$%&*+-/<=>?@^_~").eq(str("!$%&*+-/<=>?@^_~")));
+ try expect(parse("|foo\\x20bar\\x0abaz|").eq(str("foo bar\nbaz")));
+}
+
test "parse" {
- const val = parseString("\"foo\"");
+ const val = parse("foo");
try testing.expect(value.sstr.check(val));
@@ -22,7 +68,7 @@ test "parse" {
}
test "parse2" {
- const val = parseString(
+ const val = parse(
\\ ;; Testing some crazy datum comments
\\ ;~"bar"([x #"y"]{##`,'z}) #"foo"
\\ ;; end
@@ -31,7 +77,7 @@ test "parse2" {
const r = value.rune.unpack(value.pair.car(val));
try testing.expectEqualStrings("HASH", r.slice());
- const s = value.pair.cdr(val);
+ const s = value.pair.cdr(value.pair.cdr(val));
try testing.expect(value.sstr.check(s));
const f = value.sstr.unpack(s);
@@ -39,7 +85,7 @@ test "parse2" {
}
test "parse3" {
- const val = parseString(
+ const val = parse(
\\(foo ;~x ;~(x y) ;~x #bar [#x #"baz"] 'bat)
);
@@ -53,12 +99,12 @@ test "parse3" {
try testing.expect(value.sstr.check(e1));
try testing.expect(value.rune.check(e2));
- try testing.expect(value.pair.check(e3));
- try testing.expect(value.pair.check(e4));
+ try testing.expect(value.pair.check(e3) != null);
+ try testing.expect(value.pair.check(e4) != null);
}
test "parse4" {
- const val = parseString("(foo . ;~x bar ;~y)");
+ const val = parse("(foo . ;~x bar ;~y)");
const s = value.sstr.unpack(value.pair.car(val));
try testing.expectEqualStrings("foo", s.slice());
@@ -72,7 +118,7 @@ test "unparse" {
var out: std.ArrayList(u8) = .init(gpa.allocator());
const w = out.writer();
- const v = parseString("#foo");
+ const v = parse("#foo");
try io.unparser.unparse(w, v);
try testing.expectEqualStrings("#foo", try out.toOwnedSlice());
}
@@ -82,7 +128,7 @@ test "unparse2" {
var out: std.ArrayList(u8) = .init(gpa.allocator());
const w = out.writer();
- const v = parseString("#{foo bar['x]}");
+ const v = parse("#{foo bar['x]}");
try io.unparser.unparse(w, v);
try testing.expectEqualStrings(
"(#HASH #BRACE foo (#JOIN bar #SQUARE (#QUOTE . x)))",
@@ -92,7 +138,7 @@ test "unparse2" {
fn writeParseResult(str: []const u8) !void {
const w = std.io.getStdErr().writer();
- const v = parseString(str);
+ const v = parse(str);
try io.unparser.unparse(w, v);
try w.writeByte('\n');
}
@@ -158,5 +204,5 @@ fn parseBench(path: []const u8, iters: usize) !void {
test "parse bench" {
try parseBench("src/test/data/parser-test-1.scm", 200);
try parseBench("src/test/data/parser-test-2.scm", 800);
- try parseBench("src/test/data/parser-torture.scm", 1);
+ //try parseBench("src/test/data/parser-torture.scm", 1);
}
diff --git a/src/test/strings.zig b/src/test/strings.zig
index 629bc46..8f640f4 100644
--- a/src/test/strings.zig
+++ b/src/test/strings.zig
@@ -4,14 +4,14 @@ const testing = std.testing;
pub const value = @import("../zisp/value.zig");
-test "istr" {
- const istr = value.istr;
- const fx = value.fixnum;
+const istr = value.istr;
+const fx = value.fixnum;
+test "istr" {
const s1 = "foo bar baz";
- const v1 = istr.intern(s1, false);
+ const v1 = istr.intern(s1);
const v1_len: usize = @intCast(fx.unpack(istr.len(v1)));
- try testing.expectEqualStrings(s1, istr.getHeader(v1).bytes());
+ try testing.expectEqualStrings(s1, istr.assert(v1).bytes());
try testing.expectEqual(s1.len, v1_len);
const file = try std.fs.cwd().openFile("src/test/data/string.txt", .{});
@@ -19,12 +19,12 @@ test "istr" {
var s2_buf: [4096]u8 = undefined;
const s2_len = try file.readAll(&s2_buf);
var s2: []u8 = s2_buf[0..s2_len];
- const v2 = istr.intern(s2, false);
+ const v2 = istr.intern(s2);
const v2_len: usize = @intCast(fx.unpack(istr.len(v2)));
var s2_orig_buf: [4096]u8 = undefined;
@memcpy(&s2_orig_buf, &s2_buf);
const s2_orig = s2_orig_buf[0..s2_len];
s2[0] = s2[0] +% 1;
- try testing.expectEqualStrings(s2_orig, istr.getHeader(v2).bytes());
+ try testing.expectEqualStrings(s2_orig, istr.assert(v2).bytes());
try testing.expectEqual(s2_len, v2_len);
}
diff --git a/src/test/values.zig b/src/test/values.zig
index 7339f22..4a62afa 100644
--- a/src/test/values.zig
+++ b/src/test/values.zig
@@ -8,9 +8,8 @@ pub const io = @import("../zisp/io.zig");
pub const lib = @import("../zisp/lib.zig");
pub const value = @import("../zisp/value.zig");
-pub const Hval = gc.Hval;
-
-pub const ShortString = value.ShortString;
+pub const Zptr = value.Zptr;
+pub const PtrTag = value.PtrTag;
pub const Value = value.Value;
test "double" {
@@ -46,13 +45,11 @@ test "fixnum" {
test "ptr" {
const ptr = value.ptr;
- const val: *Hval = @ptrFromInt(256);
- const tag = ptr.Tag.pair;
+ const val: Zptr = @ptrFromInt(256);
+ const tag = PtrTag.pair;
const p = ptr.pack(val, tag);
try testing.expect(ptr.check(p));
- try testing.expect(ptr.checkZispTag(p, tag));
- try testing.expect(ptr.checkStrong(p));
const pv, const pt = ptr.unpack(p);
try testing.expectEqual(val, pv);
@@ -60,10 +57,9 @@ test "ptr" {
var w = ptr.makeWeak(p);
try testing.expect(ptr.check(w));
- try testing.expect(ptr.checkZispTag(w, tag));
try testing.expect(ptr.checkWeak(w));
- try testing.expectEqual(true, value.boole.unpack(ptr.predWeak(w)));
- try testing.expectEqual(false, value.boole.unpack(ptr.predWeakNull(w)));
+ try testing.expect(value.t.eq(ptr.predWeak(w)));
+ try testing.expect(value.f.eq(ptr.predWeakNull(w)));
const wv, const wt = ptr.unpack(w);
try testing.expectEqual(val, wv);
@@ -77,24 +73,9 @@ test "ptr" {
try testing.expect(ptr.check(w));
try testing.expect(ptr.checkWeak(w));
try testing.expect(ptr.isWeakNull(w));
- try testing.expectEqual(true, value.boole.unpack(ptr.predWeak(w)));
- try testing.expectEqual(true, value.boole.unpack(ptr.predWeakNull(w)));
- try testing.expectEqual(false, value.boole.unpack(ptr.getWeak(w)));
-}
-
-test "fptr" {
- const ptr = value.ptr;
-
- const int1: u50 = 0;
- const int2: u50 = std.math.maxInt(u50);
-
- const f1 = ptr.packForeign(int1);
- try testing.expect(ptr.checkForeign(f1));
- try testing.expectEqual(int1, ptr.unpackForeign(f1));
-
- const f2 = ptr.packForeign(int2);
- try testing.expect(ptr.checkForeign(f2));
- try testing.expectEqual(int2, ptr.unpackForeign(f2));
+ try testing.expect(value.t.eq(ptr.predWeak(w)));
+ try testing.expect(value.t.eq(ptr.predWeakNull(w)));
+ try testing.expect(value.f.eq(ptr.getWeak(w)));
}
test "rune" {
@@ -107,7 +88,7 @@ test "rune" {
const SstrImpl = struct { SstrPack, SstrUnpack };
const SstrPack = *const fn ([]const u8) Value;
-const SstrUnpack = *const fn (Value) ShortString;
+const SstrUnpack = *const fn (Value) value.ShortString;
test "sstr" {
const impls = [_]SstrImpl{
@@ -205,13 +186,13 @@ test "misc" {
const f = value.boole.pack(false);
try testing.expect(value.f.eq(f));
try testing.expect(value.boole.check(f));
- try testing.expectEqual(false, value.boole.unpack(f));
+ try testing.expect(!value.boole.unpack(f));
try testing.expect(value.boole.unpack(value.boole.pred(f)));
const t = value.boole.pack(true);
try testing.expect(value.t.eq(t));
try testing.expect(value.boole.check(t));
- try testing.expectEqual(true, value.boole.unpack(t));
+ try testing.expect(value.boole.unpack(t));
try testing.expect(value.boole.unpack(value.boole.pred(t)));
const nil = value.nil;
@@ -229,7 +210,7 @@ test "pair" {
const v4 = value.fixnum.pack(4);
const p = value.pair.cons(v1, v2);
- try testing.expect(value.pair.check(p));
+ try testing.expect(value.pair.check(p) != null);
try testing.expect(value.boole.unpack(value.pair.pred(p)));
const car = value.pair.car(p);
@@ -237,8 +218,8 @@ test "pair" {
try testing.expectEqual(1, value.fixnum.unpack(car));
try testing.expectEqual(2, value.fixnum.unpack(cdr));
- value.pair.setcar(p, v3);
- value.pair.setcdr(p, v4);
+ value.pair.setCar(p, v3);
+ value.pair.setCdr(p, v4);
const car2 = value.pair.car(p);
const cdr2 = value.pair.cdr(p);
diff --git a/src/zisp.zig b/src/zisp.zig
index d349a5f..c31210a 100644
--- a/src/zisp.zig
+++ b/src/zisp.zig
@@ -6,10 +6,9 @@ pub const io = @import("zisp/io.zig");
pub const lib = @import("zisp/lib.zig");
pub const value = @import("zisp/value.zig");
-pub const Hval = gc.Hval;
-
pub const ShortString = value.ShortString;
pub const Value = value.Value;
+pub const Zptr = value.Zptr;
pub const _test = @import("test/all.zig");
diff --git a/src/zisp/gc.zig b/src/zisp/gc.zig
index d778b77..cf2d32f 100644
--- a/src/zisp/gc.zig
+++ b/src/zisp/gc.zig
@@ -3,15 +3,11 @@ const std = @import("std");
const value = @import("value.zig");
+const ptr = value.ptr;
const seq = value.seq;
const Value = value.Value;
-
-/// A "heap value" that could be a Value or object header.
-pub const Hval = union {
- value: Value,
- seq_header: seq.Header,
-};
+const Zptr = value.Zptr;
pub const alloc = std.heap.smp_allocator;
@@ -28,20 +24,42 @@ pub fn cons(v1: Value, v2: Value) *[2]Value {
// Interned strings
-var istr_pool = std.hash_map.StringHashMap(void).init(alloc);
-
-pub fn intern(header: seq.Header, str: []const u8) *seq.Header {
- const hs = @sizeOf(seq.Header);
- const size = str.len + hs;
- const copy = alloc.alloc(u8, size) catch @panic("OOM");
- const header_bytes: [hs]u8 = @bitCast(header);
- @memcpy(copy[0..hs], &header_bytes);
- @memcpy(copy[hs..size], str);
- const entry = istr_pool.getOrPutValue(copy, {}) catch @panic("OOM");
- return @ptrCast(entry.key_ptr);
-}
+const istr_ctx = std.hash_map.StringContext{};
+var istr_pool = std.hash_map.StringHashMap(*seq.Header).init(alloc);
+
+pub fn internString(str: []const u8) Value {
+ const gop = istr_pool.getOrPutAdapted(
+ str,
+ istr_ctx,
+ ) catch @panic("OOM");
+ if (gop.found_existing) {
+ const p: *seq.Header = gop.value_ptr.*;
+ return ptr.pack(p, .seq);
+ }
+
+ std.debug.assert(str.len <= std.math.maxInt(u48));
+
+ const header: seq.Header = .{
+ .type = .string,
+ .info = .{ .string = .{
+ .enc = .utf8,
+ .interned = true,
+ } },
+ .size = @intCast(str.len),
+ };
+
+ const h_align = @alignOf(seq.Header);
+ const h_size = @sizeOf(seq.Header);
+ const size = str.len + h_size;
+
+ const mem = alloc.alignedAlloc(u8, h_align, size) catch @panic("OOM");
+
+ const h_bytes: [h_size]u8 = @bitCast(header);
+ @memcpy(mem[0..h_size], &h_bytes);
+ @memcpy(mem[h_size..size], str);
+
+ gop.key_ptr.* = alloc.dupe(u8, str) catch @panic("OOM");
+ gop.value_ptr.* = @ptrCast(mem.ptr);
-pub fn istrHeader(ptr: *Hval) *seq.Header {
- const entry_key: *[]u8 = @ptrCast(ptr);
- return @alignCast(@ptrCast(entry_key.ptr));
+ return ptr.pack(mem.ptr, .seq);
}
diff --git a/src/zisp/io/Parser.zig b/src/zisp/io/Parser.zig
index 7d14808..14db959 100644
--- a/src/zisp/io/Parser.zig
+++ b/src/zisp/io/Parser.zig
@@ -188,20 +188,12 @@ fn addChar(p: *Parser, c: u8) !void {
try p.chars.append(p.alloc.chars, c);
}
-fn getBareString(p: *Parser) Value {
+fn getString(p: *Parser) Value {
defer p.chars.clearRetainingCapacity();
- return if (p.chars.items.len <= 6)
+ return if (value.sstr.isValidSstr(p.chars.items))
value.sstr.pack(p.chars.items)
else
- value.istr.intern(p.chars.items, false);
-}
-
-fn getQuotedString(p: *Parser) Value {
- defer p.chars.clearRetainingCapacity();
- return if (p.chars.items.len <= 6)
- value.sstr.packQuoted(p.chars.items)
- else
- value.istr.intern(p.chars.items, true);
+ value.istr.intern(p.chars.items);
}
fn getRune(p: *Parser) Value {
@@ -223,7 +215,6 @@ const Fn = enum {
parseJoin,
parseHashDatum,
endHashDatum,
- parseRuneDatum,
endRuneDatum,
endLabelDatum,
continueList,
@@ -234,22 +225,21 @@ const Fn = enum {
fn call(p: *Parser, f: Fn) !void {
try switch (f) {
- .parseUnit => parseUnit(p),
- .parseDatum => parseDatum(p),
- .endUnit => endUnit(p),
- .returnContext => returnContext(p),
- .endFirstDatum => endFirstDatum(p),
- .endJoinDatum => endJoinDatum(p),
- .parseJoin => parseJoin(p),
- .parseHashDatum => parseHashDatum(p),
- .endHashDatum => endHashDatum(p),
- .parseRuneDatum => parseRuneDatum(p),
- .endRuneDatum => endRuneDatum(p),
- .endLabelDatum => endLabelDatum(p),
- .continueList => continueList(p),
- .endImproperList => endImproperList(p),
- .closeImproperList => closeImproperList(p),
- .endQuoteExpr => endQuoteExpr(p),
+ .parseUnit => p.parseUnit(),
+ .parseDatum => p.parseDatum(),
+ .endUnit => p.endUnit(),
+ .returnContext => p.returnContext(),
+ .endFirstDatum => p.endFirstDatum(),
+ .endJoinDatum => p.endJoinDatum(),
+ .parseJoin => p.parseJoin(),
+ .parseHashDatum => p.parseHashDatum(),
+ .endHashDatum => p.endHashDatum(),
+ .endRuneDatum => p.endRuneDatum(),
+ .endLabelDatum => p.endLabelDatum(),
+ .continueList => p.continueList(),
+ .endImproperList => p.endImproperList(),
+ .closeImproperList => p.closeImproperList(),
+ .endQuoteExpr => p.endQuoteExpr(),
};
}
@@ -257,7 +247,7 @@ pub fn run(p: *Parser, input: std.io.AnyReader) !Value {
p.input = input;
p.context.next = .parseUnit;
while (p.context.next) |next| {
- if (detailed_debug) printStack(p);
+ if (detailed_debug) p.printStack();
try p.call(next);
}
if (p.unread_char) |_| {
@@ -360,7 +350,7 @@ fn endUnit(p: *Parser) !void {
const c = p.getUnread() orelse return p.ret();
switch (try p.checkBlanks(c)) {
.yes => {},
- .skip_unit => return skipUnitAndReturn(p),
+ .skip_unit => return p.skipUnitAndReturn(),
.no => p.unread(c),
}
return p.ret();
@@ -383,7 +373,7 @@ fn endFirstDatum(p: *Parser) !void {
if (p.result.eq(value.none)) {
return p.ret();
}
- return parseJoin(p);
+ return p.parseJoin();
}
fn parseJoin(p: *Parser) !void {
@@ -429,54 +419,27 @@ fn parseOneDatum(p: *Parser, c: u8, next: Fn) !void {
return p.parseCladDatum(c, next);
}
-fn parseBareString(p: *Parser, c: u8) !Value {
- const allow_dots = std.ascii.isDigit(c) or switch (c) {
+fn parseBareString(p: *Parser, c1: u8) !Value {
+ const allow_dots = std.ascii.isDigit(c1) or switch (c1) {
'.', '+', '-' => true,
else => false,
};
- try p.addChar(c);
- return p.parseBareStringRest(allow_dots);
-}
-
-fn parseBareEscString(p: *Parser) !Value {
- try p.addChar(try parseBareEsc(p));
- return p.parseBareStringRest(false);
-}
-
-fn parseBareStringRest(p: *Parser, allow_dots: bool) !Value {
+ try p.addChar(c1);
while (try p.read()) |c| {
if (isBareChar(c) or (allow_dots and c == '.')) {
try p.addChar(c);
- } else if (c == '\\') {
- try p.addChar(try parseBareEsc(p));
} else {
p.unread(c);
break;
}
}
- return p.getBareString();
-}
-
-fn parseBareEsc(p: *Parser) !u8 {
- const c = try p.readNoEof("bare escape");
- if (isBareEsc(c)) {
- return c;
- } else {
- return p.err(.InvalidCharacter, "bare escape");
- }
+ return p.getString();
}
fn parseCladDatum(p: *Parser, c: u8, next: Fn) !void {
- if (c == '\\') {
- return p.jump(next, try parseBareEscString(p));
- }
- if (c == '"') {
- return p.jump(next, try p.parseQuotedString('"'));
- }
- if (c == '|') {
- return p.jump(next, try p.parseQuotedString('|'));
- }
return switch (c) {
+ '|' => p.jump(next, try p.parseEscapedString('|')),
+ '"' => p.jump(next, try p.parseEscapedString('"')),
'#' => p.parseHashExpression(next),
'(', '[', '{' => p.parseList(c, next),
'\'', '`', ',' => p.parseQuoteExpr(c, next),
@@ -484,10 +447,11 @@ fn parseCladDatum(p: *Parser, c: u8, next: Fn) !void {
};
}
-fn parseQuotedString(p: *Parser, close: u8) !Value {
+fn parseEscapedString(p: *Parser, close: u8) !Value {
while (try p.read()) |c| {
if (c == close) {
- return p.getQuotedString();
+ const s = p.getString();
+ return if (close == '"') p.cons(QUOTE, s) else s;
}
if (c != '\\') {
try p.addChar(c);
@@ -500,12 +464,8 @@ fn parseQuotedString(p: *Parser, close: u8) !Value {
fn parseQuotedEsc(p: *Parser, close: u8) !void {
const c = try p.readNoEof("quoted escape");
- if (c == close) {
- return p.addChar(close);
- }
- if (c == 'u') {
- return parseUniHexHandleErrors(p);
- }
+ if (c == close) return p.addChar(close);
+ if (c == 'u') return p.parseUniHexHandleErrors();
try p.addChar(switch (c) {
'\\' => c,
'0' => 0,
@@ -523,7 +483,7 @@ fn parseQuotedEsc(p: *Parser, close: u8) !void {
}
fn parseUniHexHandleErrors(p: *Parser) !void {
- return parseUniHex(p) catch |e| switch (e) {
+ return p.parseUniHex() catch |e| switch (e) {
error.Utf8CannotEncodeSurrogateHalf => p.err(
.UnicodeError,
"unicode escape",
@@ -552,16 +512,16 @@ fn parseUniHex(p: *Parser) !void {
fn parseHashExpression(p: *Parser, next: Fn) !void {
const c = try p.readNoEof("hash expression");
- if (try p.checkBlanks(c) != .no) {
- return p.err(.InvalidCharacter, "hash expression");
- }
if (std.ascii.isAlphabetic(c)) {
const r = try p.parseRune(c);
return p.parseRuneEnd(r, next);
}
+ if (c == '\\') {
+ const c1 = try p.readNoEof("bare string after hash");
+ return p.jump(next, p.cons(HASH, try p.parseBareString(c1)));
+ }
if (c == '%') {
- const l = try parseLabel(p);
- return p.parseLabelEnd(l, next);
+ return p.parseLabel(next);
}
p.unread(c);
return p.subr(.parseHashDatum, next);
@@ -594,53 +554,42 @@ fn parseRune(p: *Parser, c1: u8) !Value {
fn parseRuneEnd(p: *Parser, r: Value, next: Fn) !void {
const c = p.getUnread() orelse return p.jump(next, r);
if (c == '\\') {
- return p.jump(next, p.cons(r, try p.parseBareString(c)));
+ const c1 = try p.readNoEof("bare string at rune end");
+ return p.jump(next, p.cons(r, try p.parseBareString(c1)));
}
if (c == '"') {
- return p.jump(next, p.cons(r, try p.parseQuotedString('"')));
+ return p.jump(next, p.cons(r, try p.parseEscapedString('"')));
}
if (c == '|') {
- return p.jump(next, p.cons(r, try p.parseQuotedString('|')));
+ return p.jump(next, p.cons(r, try p.parseEscapedString('|')));
}
p.unread(c);
switch (c) {
'#', '(', '[', '{', '\'', '`', ',' => {
try p.push(next);
p.context.val = r;
- // Use jump to prevent recursion.
- return p.jump(.parseRuneDatum, null);
+ return p.subr(.parseDatum, .endRuneDatum);
},
else => return p.jump(next, r),
}
}
-fn parseRuneDatum(p: *Parser) !void {
- return p.parseCladDatum(p.getUnread().?, .endRuneDatum);
-}
-
fn endRuneDatum(p: *Parser) !void {
- if (p.result.eq(value.none)) {
- p.retval(p.context.val);
- }
return p.retval(p.cons(p.context.val, p.result));
}
-fn parseLabel(p: *Parser) !Value {
- const label = try p.parseHex(u48, "datum label");
- return value.fixnum.pack(label);
-}
-
-fn parseLabelEnd(p: *Parser, l: Value, next: Fn) !void {
- const c = p.getUnread() orelse return p.err(.UnexpectedEof, "datum label");
- if (c == '%') {
- return p.jump(next, p.cons(LABEL, l));
- }
- if (c == '=') {
- try p.push(next);
- p.context.val = l;
- return p.subr(.parseDatum, .endLabelDatum);
+fn parseLabel(p: *Parser, next: Fn) !void {
+ const n = try p.parseHex(u48, "datum label");
+ const l = value.fixnum.pack(n);
+ switch (p.getUnread() orelse try p.readNoEof("datum label")) {
+ '%' => return p.jump(next, p.cons(LABEL, l)),
+ '=' => {
+ try p.push(next);
+ p.context.val = l;
+ return p.subr(.parseDatum, .endLabelDatum);
+ },
+ else => return p.err(.InvalidCharacter, "datum label"),
}
- return p.err(.InvalidCharacter, "datum label");
}
fn endLabelDatum(p: *Parser) !void {
@@ -696,7 +645,7 @@ fn continueList(p: *Parser) !void {
if (p.result.eq(value.none)) {
const c = p.getUnread().?;
if (c == close) {
- return endList(p);
+ return p.endList();
}
return p.err(.InvalidCharacter, "list");
}
@@ -710,7 +659,7 @@ fn continueList(p: *Parser) !void {
var c1 = p.getUnread() orelse try p.read();
while (c1) |c| : (c1 = try p.read()) {
if (c == close) {
- return endList(p);
+ return p.endList();
}
switch (try p.checkBlanks(c)) {
.yes => {},
@@ -736,7 +685,7 @@ fn endImproperList(p: *Parser) !void {
return p.err(.InvalidCharacter, "list tail");
}
p.context.val = lib.list.reverseWithTail(p.context.val, p.result);
- return closeImproperList(p);
+ return p.closeImproperList();
}
fn closeImproperList(p: *Parser) !void {
diff --git a/src/zisp/io/unparser.zig b/src/zisp/io/unparser.zig
index e72f130..64e18d0 100644
--- a/src/zisp/io/unparser.zig
+++ b/src/zisp/io/unparser.zig
@@ -1,23 +1,27 @@
const std = @import("std");
+const gc = @import("../gc.zig");
const value = @import("../value.zig");
const istr = value.istr;
const seq = value.seq;
const ShortString = value.ShortString;
-const OtherTag = value.OtherTag;
const Value = value.Value;
pub fn unparse(w: anytype, v: Value) anyerror!void {
- try if (value.double.check(v))
- unparseDouble(w, v)
- else if (value.fixnum.check(v))
- unparseFixnum(w, v)
- else if (value.ptr.checkZisp(v))
- unparseHeap(w, v)
- else
- unparseOther(w, v);
+ // zig fmt: off
+ try if (v.isDouble()) unparseDouble(w, v)
+ else if (v.isFixnum()) unparseFixnum(w, v)
+ else if (v.getPtr(.pair)) |p| unparsePair(w, @ptrCast(p))
+ else if (v.getPtr(.seq)) |p| unparseSeq(w, @ptrCast(p))
+ else if (v.isRune()) unparseRune(w, v)
+ else if (v.isChar()) unparseChar(w, v)
+ else if (v.isMisc()) unparseMisc(w, v)
+ else if (v.isSrat()) unparseSrat(w, v)
+ else if (v.isSstr()) unparseSstr(w, v)
+ ;
+ // zig fmt: on
}
fn unparseDouble(w: anytype, v: Value) !void {
@@ -32,25 +36,6 @@ fn unparseFixnum(w: anytype, v: Value) !void {
@panic("not implemented");
}
-fn unparseHeap(w: anytype, v: Value) !void {
- const p, const t = value.ptr.unpack(v);
- try switch (t) {
- .pair => unparsePair(w, @ptrCast(p)),
- .seq => unparseSeq(w, @ptrCast(p)),
- else => @panic("not implemented"),
- };
-}
-
-fn unparseOther(w: anytype, v: Value) !void {
- try switch (v.other.tag) {
- .rune => unparseRune(w, v),
- .sstr => unparseSstr(w, v),
- .qstr => unparseQstr(w, v),
- .char => unparseChar(w, v),
- .misc => unparseMisc(w, v),
- };
-}
-
fn unparseRune(w: anytype, v: Value) !void {
const name = value.rune.unpack(v);
try w.writeByte('#');
@@ -58,41 +43,43 @@ fn unparseRune(w: anytype, v: Value) !void {
}
fn unparseSstr(w: anytype, v: Value) !void {
+ // TODO: Check if pipes/escapes necessary.
const str = value.sstr.unpack(v);
try w.writeAll(str.constSlice());
}
-fn unparseQstr(w: anytype, v: Value) !void {
- const str = value.sstr.unpack(v);
- try w.writeByte('"');
- try w.writeAll(str.constSlice());
- try w.writeByte('"');
-}
-
fn unparseChar(w: anytype, v: Value) !void {
+ const uc: u21 = value.char.unpack(v);
var buf: [4]u8 = undefined;
- const len = try std.unicode.utf8Encode(v.char.value, &buf);
+ const len = try std.unicode.utf8Encode(uc, &buf);
try w.writeAll(buf[0..len]);
}
fn unparseMisc(w: anytype, v: Value) !void {
- try switch (v.misc.value) {
- .f => w.writeAll("#f"),
- .t => w.writeAll("#t"),
- .nil => w.writeAll("()"),
- .eof => w.writeAll("#EOF"),
- .none => w.writeAll("#NONE"),
- .undef => w.writeAll("#UNDEF"),
+ try switch (v.bits) {
+ value.f.bits => w.writeAll("#f"),
+ value.t.bits => w.writeAll("#t"),
+ value.nil.bits => w.writeAll("()"),
+ value.eof.bits => w.writeAll("#EOF"),
+ value.none.bits => w.writeAll("#NONE"),
+ value.undef.bits => w.writeAll("#UNDEF"),
+ else => @panic("not implemented"),
};
}
+fn unparseSrat(w: anytype, v: Value) !void {
+ _ = w;
+ _ = v;
+ @panic("not implemented");
+}
+
fn unparsePair(w: anytype, p: *[2]Value) !void {
try w.writeByte('(');
try unparse(w, p[0]);
var cdr = p[1];
- while (value.pair.check(cdr)) : (cdr = value.pair.cdr(cdr)) {
+ while (value.pair.check(cdr)) |p2| : (cdr = value.pair.cdr(cdr)) {
try w.writeByte(' ');
- try unparse(w, value.pair.car(cdr));
+ try unparse(w, p2[0]);
}
if (!value.nil.eq(cdr)) {
try w.writeByte(' ');
@@ -103,21 +90,16 @@ fn unparsePair(w: anytype, p: *[2]Value) !void {
try w.writeByte(')');
}
-fn unparseSeq(w: anytype, p: *seq.Header) !void {
- const h = istr.getHeaderFromPtr(@ptrCast(p));
- switch (h.type) {
- .string => try unparseString(w, h),
+fn unparseSeq(w: anytype, s: *seq.Header) !void {
+ switch (s.type) {
+ .string => try unparseString(w, s),
else => @panic("not implemented"),
}
}
-fn unparseString(w: anytype, h: *seq.Header) !void {
- const info = h.info.string;
- if (info.quoted) {
- try w.writeByte('"');
- }
- try w.writeAll(h.bytes());
- if (info.quoted) {
- try w.writeByte('"');
- }
+fn unparseString(w: anytype, s: *seq.Header) !void {
+ // TODO: Check if pipes/escapes necessary.
+ try w.writeByte('|');
+ try w.writeAll(s.bytes());
+ try w.writeByte('|');
}
diff --git a/src/zisp/lib/list.zig b/src/zisp/lib/list.zig
index be40af7..cd6b553 100644
--- a/src/zisp/lib/list.zig
+++ b/src/zisp/lib/list.zig
@@ -10,11 +10,9 @@ pub fn reverseWithTail(list: Value, tail: Value) Value {
var head = list;
var result = tail;
while (!value.nil.eq(head)) {
- value.pair.assert(head);
- const car = value.pair.car(head);
- const cdr = value.pair.cdr(head);
- result = value.pair.cons(car, result);
- head = cdr;
+ const p = value.pair.unpack(head);
+ result = value.pair.cons(p[0], result);
+ head = p[1];
}
return result;
}
diff --git a/src/zisp/value.zig b/src/zisp/value.zig
index 47ac144..465cbbb 100644
--- a/src/zisp/value.zig
+++ b/src/zisp/value.zig
@@ -1,9 +1,9 @@
//
// === NaN Packing Strategy ===
//
-// Format of a double, in Zig least-to-most significant field order:
+// Format of a double, in most to least significant field order:
//
-// { fraction: u52, exponent: u11, sign: u1 }
+// { sign: u1, exponent: u11, fraction: u52 }
//
// When the exponent bits are all set, it's either a NaN or an Infinity.
//
@@ -27,14 +27,17 @@
// demarcates quiet NaNs. The rest being zero makes it the canonical qNaN.
//
// The positive and negative cqNaN are the *only* NaN values that can actually
-// be returned by any FP operations, which is why we don't use them to pack
-// values; we want to be able to represent NaN in Zisp as a double.
+// be returned by FP operations, which is convenient because it means we can
+// simply use them to represent themselves in Zisp.
+//
+// Infinity values may also be returned by FP operations, and we want them to
+// exist in Zisp as doubles as well, so they also represent themselves.
//
// Beyond those four bit patterns, all values with a maximum exponent (all bits
// set) are fair game for representing other values, so 2^53 - 4 possibilities.
//
// We split those 2^53 - 4 available values into four groups, each allowing for
-// 2^51 - 1 different values to be encoded in them:
+// 2^51 - 1 different values to be encoded. (51-bit values excluding zero.)
//
// sign = 1, quiet = 1 :: Negative Fixnum from -1 to -2^51+1
//
@@ -49,11 +52,11 @@
//
// Negative fixnums actually represent themselves without needing to go through
// any transformation. Only the smallest 52-bit signed negative, -2^51, cannot
-// be represented, as it would step on forbidden value 1, Negative cqNaN.
+// be represented, as it would step on Forbidden Value #1, Negative cqNaN.
//
// Positive fixnums go through bitsiwe NOT (implemented via an XOR mask here to
// make it one operation together with the NaN masking) to avoid the all-zero
-// payload value, which would step on forbidden value 2, Negative Infinity.
+// payload value, which would step on Forbidden Value #2, Negative Infinity.
//
//
// === Pointers ===
@@ -61,90 +64,93 @@
// Pointers are further subdivided as follows based on the remaining 51 bits,
// with the first three bits used as a sort of tag:
//
-// 000 :: Pointer to Zisp heap object (string, vector, etc.)
+// 000 :: Regular pointer to Zisp heap object (string, vector, etc.)
//
// 001 :: Weak pointer to Zisp heap object
//
-// 01. :: Undefined (may be used by GC to flag pointers for some reason?)
+// 01. :: Undefined
//
-// 1.. :: Foreign pointer (basically, a 50-bit fixnum of another type)
+// 1.. :: Undefined
//
// This means pointers to the Zisp heap are 48 bits. Of those, we only really
// need 45, since 64-bit platforms are in practice limited to 48-bit addresses,
-// and allocations happen at 8-byte boundaries, meaning the least significant 3
+// and Zisp heap allocations happen at 8-byte boundaries, meaning the lowest 3
// bits are always unset. Thus, we are able to store yet another 3-bit tag in
// those 48-bit pointers alongside the actual, multiple-of-8, 48-bit address.
//
-// The forbidden value 3, Positive cqNaN, is avoided thanks to the fact that a
+// Forbidden Value #3, Positive cqNaN, is avoided thanks to the fact that a
// regular Zisp heap pointer can never be null. Weak pointers, which can be
// null, avoid stepping on that forbidden value thanks to bit 49 being set.
//
-// Foreign pointers allow storing arbitrary pointers, or integers basically, of
-// up to 50 bits, without any further definition in Zisp of what they mean.
-//
//
// === Other values ===
//
-// This 51-bit range is divided as follows, based on the high bits:
+// This 51-bit range is divided as follows:
//
-// 000 :: Rune
+// 000 :: Subdivided as follows:
//
-// 001 :: Short string
+// 0....... 0....... 0....... (etc.) :: Rune
//
-// 010 :: Short string literal
+// 1....... :: 128 40-bit types
//
-// 011 :: Unicode code point
+// 0....... 1....... :: 16384 32-bit types
//
-// 100 :: Singleton values
+// 0....... 0....... 1....... :: 2097152 24-bit types
//
-// 101, 110, 111 :: Undefined
+// (etc.)
//
-// Runes are symbols of 1 to 6 ASCII characters used to implement reader syntax.
+// 001 :: Short string
//
-// Zisp strings are immutable. Any string fitting into 6 bytes or less will be
-// stored as an immediate value, not requiring any heap allocation or interning.
-// It's implicitly interned, so to speak. This includes the empty string.
+// 01. :: Small rational
//
-// The null byte serves as a terminator for strings shorter than 6 bytes, and
-// therefore cannot appear in these strings; a string that short but actually
-// containing a null byte will need to be heap allocated like other strings.
+// 1.. :: Undefined
//
-// There may also be strings that are this short, but ended up on the heap due
-// to being uninterned. Interning them will return the equivalent short string
-// as an immediate.
+// ==== Runes and Small Values ====
//
-// The separate type for a short string *literal* is for an efficiency hack in
-// the parser; see commentary there.
+// Runes are symbols of 1 to 6 ASCII characters used to implement reader syntax.
+// They are NUL-terminated if shorter than six characters, meaning they cannot
+// contain the NUL byte in their value.
//
-// Unicode code points need a maximum of 21 bits, yet we have 48 available.
-// This may be exploited for a future extension.
+// NOTE: The order in which the characters of the rune are encoded depends on
+// endianness. On little-endian systems (i.e. most modern architectures) the
+// characters will be in "reverse" order, with the first character in lowest
+// position, so the terminating NUL has to be searched from low to high.
//
-// Similarly, it's very unlikely that we will ever need more than a handful of
-// singleton values (false, true, nil, and so on). As such, this range of bit
-// patterns may be subdivided in the future. Right now, only the lowest 8 bits
-// are allowed to be set, with the other 40 being reserved, so there's a limit
-// of 256 singleton values that can be defined.
+// Forbidden Value #4, Positive Infinity, would denote a rune of length zero
+// (all NUL bytes) which isn't allowed, so we avoid stepping on it.
//
-// And top of that, we have three more 48-bit value ranges that are unused!
+// The fact that runes are limited to ASCII opens up a lot of space for other
+// small values to co-inhabit the same 48-bit range. We subdivide this space
+// into increasingly many potential types, with smaller and smaller payloads,
+// where the highest byte with a non-zero MSb determines which size category
+// we're in: If the highest byte has its MSb set, then its seven non-MSb bits
+// define a type and each type has a 40-bit value range; if the second highest
+// byte has its MSb set, then the 14 non-MSb bits of the two high bytes define
+// the type and each has a 32-bit value range; and so on.
//
-// The forbidden value 4, Positive Infinity, would be the "empty string rune"
-// but that isn't allowed anyway, so all is fine.
+// Unicode code points need 21 bits, so we use a 24-bit type for Characters.
+// Miscellaneous values like true, false, nil, eof, etc. are placed into an
+// 8-bit type, since there will never be that many of them.
//
-
-// Here's the original article explaining the strategy:
+// ==== Strings ====
//
-// https://tkammer.de/zisp/notes/nan.html
+// Another 48-bit space is used for strings of zero to six bytes. These are
+// NUL-terminated if shorter than six bytes, meaning that NUL cannot appear in
+// them, and they must be valid UTF-8, meaning that some other values could be
+// hidden here in the future. (UTF-8 sequences cannot contain 0xFE or 0xFF.)
//
-// More about runes:
+// ==== Small rationals ====
//
-// https://tkammer.de/zisp/notes/symbols.html
+// We use a 49-bit space for small exact rational numbers, with the numerator
+// being a two's complement 25-bit signed integer, and denominator a 24-bit
+// unsigned integer.
//
-// Note: Packed structs are least-to-most significant, so the order of fields
-// must be reversed relative to a typical big-endian illustration of the bit
-// patterns of IEEE 754 double-precision floating point numbers.
+const builtin = @import("builtin");
const std = @import("std");
+const gc = @import("gc.zig");
+
pub const double = @import("value/double.zig");
pub const fixnum = @import("value/fixnum.zig");
@@ -152,22 +158,38 @@ pub const ptr = @import("value/ptr.zig");
pub const seq = @import("value/seq.zig");
pub const rune = @import("value/rune.zig");
-pub const sstr = @import("value/sstr.zig");
pub const char = @import("value/char.zig");
pub const misc = @import("value/misc.zig");
+pub const sstr = @import("value/sstr.zig");
pub const boole = @import("value/boole.zig");
pub const pair = @import("value/pair.zig");
pub const istr = @import("value/istr.zig");
-// To fill up the u11 exponent part of a NaN.
-const FILL = 0x7ff;
+const endian = builtin.target.cpu.arch.endian();
-// Used when dealing with runes and short strings.
-pub const ShortString = std.BoundedArray(u8, 6);
+const max = std.math.maxInt;
-pub const OtherTag = enum(u3) { rune, sstr, qstr, char, misc };
+/// Used when dealing with runes and short strings.
+pub const ShortString = std.BoundedArray(u8, 6);
+/// Used to find the length of a rune or short string.
+pub fn sstrLen(x: u64) u8 {
+ const bytes: @Vector(8, u8) = @bitCast(x);
+ const nulls: @Vector(8, u8) = [_]u8{0} ** 8;
+ const comps: u8 = @bitCast(bytes == nulls);
+ // Two bits will always be 0, since the actual short string starts at the
+ // third byte; third lowest or third highest depending on endianness. So,
+ // depending on endianness, either cut off the two leading bits and ensure
+ // that the second-last is set, or ensure that the second highest set, to
+ // limit the length to 6.
+ return switch (endian) {
+ .big => @clz(comps << 2 | 2),
+ .little => @ctz(comps | 64),
+ };
+}
+
+// Make sure false/true only differ in LSb.
pub const MiscValue = enum(u8) { f, t, nil, eof, none, undef };
// zig fmt: off
@@ -179,18 +201,39 @@ pub const none = Value{ .misc = .{ .value = .none } };
pub const undef = Value{ .misc = .{ .value = .undef } };
// zig fmt: on
+/// A pointer into the Zisp heap.
+pub const Zptr = *align(8) anyopaque;
+
+/// Values for the lowest 3 bits of a heap pointer, indicating the heap type.
+pub const PtrTag = enum(u3) {
+ /// Pair aka cons cell aka *[2]Value
+ pair,
+ /// Sequence of various kinds (16-bit meta, 48-bit length, then data)
+ seq,
+ /// Procedure
+ proc,
+};
+
+// Non-pointer high bits (sign=0,exp=MAX,quiet=0) but as a u13 field.
+const non_ptr: u13 = max(u11) << 1;
+
/// Represents a Zisp value/object.
pub const Value = packed union {
/// To get the value as a regular double.
double: f64,
- /// To get an agnostic value for direct comparison with == i.e. eq?.
+ /// To get an agnostic value for direct comparison with == i.e. eq? as well
+ /// as manual bit-fiddling to test for and extract packed values.
bits: u64,
// Some of the structs below are just for inspection, whereas others are to
// initialize a new value of that category as well as read it that way.
+ // Note: Zig packed struct fields are ordered from LSb to MSb, contrary to
+ // most diagrams used to represent IEEE 754, including our own at the top.
+
/// Inspection through the lens of the general IEEE 754 double layout.
+ /// (Unused because we do manual bit-fiddling for optimum results.)
ieee: packed struct {
rest: u51,
quiet: bool,
@@ -202,93 +245,83 @@ pub const Value = packed union {
fixnum: packed struct {
code: u51,
negative: bool,
- _: u11 = FILL,
+ _: u11 = max(u11),
_is_fixnum: bool = true,
},
- /// Inspection through the lens of the ptr category.
+ /// For initializing and reading pointers.
ptr: packed struct {
- _value: u48,
- is_weak: bool,
- _unused: bool,
- is_foreign: bool,
- _is_ptr: bool,
- _: u11,
- _is_fixnum: bool,
- },
-
- /// For initializing and reading foreign pointers.
- fptr: packed struct {
- value: u50,
- _is_foreign: bool = true,
- _is_ptr: bool = true,
- _: u11 = FILL,
- _is_fixnum: bool = false,
- },
-
- /// For initializing and reading Zisp heap pointers.
- zptr: packed struct {
tagged_value: u48,
is_weak: bool = false,
- _unused: bool = false,
- _is_foreign: bool = false,
+ _unused1: bool = false,
+ _unused2: bool = false,
_is_ptr: bool = true,
- _: u11 = FILL,
+ _: u11 = max(u11),
_is_fixnum: bool = false,
},
- /// Inspection as an other (non-fixnum, non-pointer) packed value.
- other: packed struct {
- _value: u48,
- tag: OtherTag,
- _is_ptr: bool,
- _: u11,
- _is_ifxnum: bool,
- },
-
/// For initializing and reading runes.
rune: packed struct {
// actually [6]u8 but packed struct cannot contain arrays
name: u48,
- _tag: OtherTag = .rune,
- _is_ptr: bool = false,
- _: u11 = FILL,
- _is_fixnum: bool = false,
+ _tag: u3 = 0b000,
+ _: u13 = non_ptr,
+ },
+
+ // TODO: Use a general Small Value type registration mechanism.
+ /// For initializing and reading characters.
+ char: packed struct {
+ value: u24,
+ _sv_tag: u24 = 0x000080,
+ _tag: u3 = 0b000,
+ _: u13 = non_ptr,
+ },
+
+ // TODO: Use a general Small Value type registration mechanism.
+ /// For initializing and reading miscellaneous values.
+ misc: packed struct {
+ value: MiscValue,
+ _sv_tag: u40 = 0x0000000080,
+ _tag: u3 = 0b000,
+ _: u13 = non_ptr,
},
/// For initializing and reading short strings.
sstr: packed struct {
// actually [6]u8 but packed struct cannot contain arrays
string: u48,
- tag: OtherTag,
- _is_ptr: bool = false,
- _: u11 = FILL,
- _is_fixnum: bool = false,
+ _tag: u3 = 0b001,
+ _: u13 = non_ptr,
},
- /// For initializing and reading characters.
- char: packed struct {
- value: u21,
- _reserved: u27 = 0,
- _tag: OtherTag = .char,
- _is_ptr: bool = false,
- _: u11 = FILL,
- _is_fixnum: bool = false,
+ /// For initializing and reading small rats (rational numbers).
+ srat: packed struct {
+ q: u24,
+ p: u25,
+ _tag: u2 = 0b01,
+ _: u13 = non_ptr,
},
- /// For initializing and reading misc values aka singletons.
- misc: packed struct {
- value: MiscValue,
- _reserved: u40 = 0,
- _tag: OtherTag = .misc,
- _is_ptr: bool = false,
- _: u11 = FILL,
- _is_fixnum: bool = false,
- },
+ // Disjoint masks where a specific bit or bit-group are set.
+ // zig fmt: off
+ const mask_sign: u64 = 1 << 63 ; // 1 sign bit
+ const mask_exp: u64 = max(u11) << 52 ; // 11 exponent bits
+ const mask_quiet: u64 = 1 << 51 ; // 1 quiet bit of fraction
+ const mask_rest: u64 = max(u51) ; // 51 rest bits of fraction
+ // zig fmt: on
- /// Hexdumps the value.
+ /// Dumps the value for inspection.
pub fn dump(v: Value) void {
- std.debug.dumpHex(std.mem.asBytes(&v));
+ const sign: u1 = @intCast((v.bits & mask_sign) >> 63);
+ const exp: u11 = @intCast((v.bits & mask_exp) >> 52);
+ const quiet: u1 = @intCast((v.bits & mask_quiet) >> 51);
+ const rest: u51 = @intCast(v.bits & mask_rest);
+ std.debug.print(
+ \\value: 0x{x}
+ \\sign: {}, exp: {b}, quiet: {}
+ \\rest: 0x{x}
+ \\
+ , .{ v.bits, sign, exp, quiet, rest });
}
/// Checks for bit-equality i.e. == comprison.
@@ -296,37 +329,160 @@ pub const Value = packed union {
return v1.bits == v2.bits;
}
- // The following aren't type predicates per se, but rather determine which
- // general category the value is in. The exceptions are fixnum and double,
- // since those aren't sub-categorized into further types.
+ // It would be great if we could just write the most readable code here,
+ // using the packed struct definitions above, but the optimizer isn't
+ // sufficiently intelligent to turn that into optimal instructions, so
+ // manual bit fiddling it is.
+
+ // The isDouble and isFixnum checks are not as efficient as we would like
+ // them to be, each requiring two 10-byte MOV instructions to load 64-bit
+ // values for masking and comparison. This seems difficult to improve on
+ // without sacrificing large value ranges in our NaN-packing scheme; it's
+ // probably best to leave it like this and hope that type checks will be
+ // hoisted outside hot-spots by a smart compiler.
/// Checks for a Zisp double, including: +nan.0, -nan.0, +inf.0, -inf.0
pub fn isDouble(v: Value) bool {
- return v.ieee.exp != FILL or v.ieee.rest == 0;
+ // Readable version:
+ //
+ // return v.ieee.exp != max(u11) or v.ieee.rest == 0;
+ //
+ // Optimized:
+ //
+ // 1. AND away the sign and quiet bits, since they may vary.
+ //
+ // 2. If exp is all 1, and rest non-zero, it's a packed value, so check
+ // if the value is less than that.
+ //
+ return v.bits & ~(mask_sign | mask_quiet) <= mask_exp;
}
- /// Checks for a non-double Zisp value packed into a NaN.
- pub fn isPacked(v: Value) bool {
- return !v.isDouble();
- }
+ // Imagine there's an isPacked() implemented as !isDouble() here, used to
+ // make the following functions more readable.
/// Checks for a fixnum.
pub fn isFixnum(v: Value) bool {
- return v.isPacked() and v.ieee.sign;
+ // Readable version:
+ //
+ // return v.isPacked() and v.ieee.sign;
+ //
+ // Optimized:
+ //
+ // 1. AND away the quiet bit, since it may vary.
+ //
+ // 2. Check if highest 12 all 1, rest not all 0, with a greater-than.
+ //
+ return v.bits & ~mask_quiet > mask_sign | mask_exp;
+ }
+
+ /// Checks for a pointer with a given type tag, returning null on failure
+ /// and the pointer value otherwise. This function is for when a value is
+ /// assumed to be of a heap type, such as when `car` is called on it, and
+ /// the "high" pointer tags (49-51) that encode properties like weakness
+ /// don't matter; we only care if it's some kind of valid heap pointer of
+ /// the given type, so we only check the type tag in the low bits (0-2).
+ pub fn getPtr(v: Value, tag: PtrTag) ?Zptr {
+ // Readable version:
+ //
+ // if (v.isPacked() and !v.ieee.sign and v.ieee.quiet ...)
+ //
+ // Optimized:
+ //
+ // I'm not explaining that shit. Figure it out yourself.
+ //
+ // Jokes aside, the code should be self-explanatory, except for:
+ //
+ // Intermediate values should be given explicit types here, for optimal
+ // code-gen. And it should be noted that the null pointer, with no
+ // other high bits set (49-51), which would represent +cqNaN, also
+ // results in a null return, so it can't be mistaken for a pointer.
+ //
+ // This function currently assumes that bits 50 and 51 being set, which
+ // has no defined semantics right now, doesn't change the fact that the
+ // pointer is a Zisp heap pointer. If we decide to use those bits for
+ // something else, simply change the hi_bits check to include them, so
+ // that for example bits 50 and 51 must be zero, or 51 must be zero
+ // while 50 is still ignored, and so on, as appropriate.
+ //
+ const ptr_val: u48 = @intCast(v.bits & (max(u45) << 3));
+ const hi_bits: u13 = @intCast(v.bits >> 51);
+ const tag_bits: u3 = @intCast(v.bits & 7);
+ const is_ptr = hi_bits == 0b0111111111111;
+ const is_tag = tag_bits == @intFromEnum(tag);
+ return if (is_ptr and is_tag) @ptrFromInt(ptr_val) else null;
+ }
+
+ /// Checks whether the value is a pointer with certain property bits, not
+ /// caring about the type tag bits. NOTE: This function doesn't check for
+ /// +cqNaN, which will be mis-identified as a pointer with zero props and
+ /// address. Therefore, don't expose this function to Zisp! Parameter
+ /// `props_mask` specifies props bits to mask away with a NAND, so it's
+ /// possible to only check for bits you care for. For example, to check
+ /// weakness, pass `0b110` for `props_mask` and `0b001` for `props`.
+ pub fn isPtrProps(v: Value, props_mask: u3, props: u3) bool {
+ const hi_bits: u16 = @intCast(v.bits >> 48 & ~@as(u16, props_mask));
+ return hi_bits == 0b0111111111111000 | @as(u16, props);
+ }
+
+ /// Checks whether the value is any kind of pointer, making sure not to
+ /// mis-identify +cqNaN as a null pointer with zero property bits, which
+ /// makes this a bit slower than `isPtrProps()`.
+ pub fn isPtrAny(v: Value) bool {
+ const hi: u13 = @intCast(v.bits >> 51);
+ const lo: u51 = @truncate(v.bits);
+ return hi == 0b0111111111111 and lo != 0;
+ }
+
+ /// Checks for a rune.
+ pub fn isRune(v: Value) bool {
+ //
+ // 1. AND away non-MSb bits of the low 6 bytes, since they may vary.
+ //
+ // 2. Check if the rest is the exact pattern we expect, with sign = 0,
+ // exp all 1, quiet = 0, 3-bit tag = 0, and MSb of remaining bytes 0,
+ // i.e., only the exp bits are set.
+ //
+ // 3. Ensnure that the "first" (based on endianness) of the 6 lowest
+ // bytes is not NUL, which would mean it's the illegal "empty string"
+ // rune corresponding to +Infinity.
+ //
+ // Note that, while this is a rather complicated check for a type that
+ // appears ubiquitously in parser output, we rarely use this type check,
+ // instead using direct comparison to specific rune values, so it's not
+ // important if this is a little inefficient.
+ //
+ const msb_mask = 0xffff808080808080;
+ const nul_mask = switch (endian) {
+ .big => 127 << 40,
+ .little => 127,
+ };
+ return v.bits & msb_mask == mask_exp and v.bits & nul_mask != 0;
+ }
+
+ // The rest are straightforward compared to the above, since the cqNaN and
+ // Infinity special-cases are out of the way and we just need to compare N
+ // high bits to a specific pattern.
+
+ /// Checks for a 24-bit character value. This makes no guarantees related
+ /// to Unicode, such as being a valid Code Point or Unicode Scalar Value.
+ pub fn isChar(v: Value) bool {
+ // TODO: Use a general Small Value type registration mechanism.
+ return v.bits >> 24 == 0x7ff0000080;
}
- /// Checks for any kind of pointer.
- pub fn isPtr(v: Value) bool {
- return v.isPacked() and !v.ieee.sign and v.ieee.quiet;
+ /// Checks for an 8-bit miscellaneous value.
+ pub fn isMisc(v: Value) bool {
+ // TODO: Use a general Small Value type registration mechanism.
+ return v.bits >> 8 == 0x7ff00000000080;
}
- /// Checks for a non-double, non-fixnum, non-pointer Zisp value.
- pub fn isOther(v: Value) bool {
- return v.isPacked() and !v.ieee.sign and !v.ieee.quiet;
+ /// Checks if the value is a short string of either kind.
+ pub fn isSstr(v: Value) bool {
+ return v.bits >> 48 == 0x7ff1;
}
- /// Checks for an other type of value based on tag.
- pub fn isOtherTag(v: Value, tag: OtherTag) bool {
- return v.isOther() and v.other.tag == tag;
+ /// Checks if the value is a small rat (rational number).
+ pub fn isSrat(v: Value) bool {
+ return v.bits >> 49 == 0x7ff2 >> 1;
}
};
diff --git a/src/zisp/value/boole.zig b/src/zisp/value/boole.zig
index 26a1a0a..2e4933c 100644
--- a/src/zisp/value/boole.zig
+++ b/src/zisp/value/boole.zig
@@ -6,7 +6,7 @@ const Value = value.Value;
/// Checks if the value is a boole.
pub fn check(v: Value) bool {
- return v.eq(value.f) or v.eq(value.t);
+ return v.bits >> 1 == value.f.bits >> 1;
}
pub fn assert(v: Value) void {
@@ -17,7 +17,7 @@ pub fn assert(v: Value) void {
}
pub fn pack(b: bool) Value {
- return if (b) value.t else value.f;
+ return @bitCast(value.f.bits | @intFromBool(b));
}
pub fn unpack(v: Value) bool {
diff --git a/src/zisp/value/char.zig b/src/zisp/value/char.zig
index 09a3034..7091303 100644
--- a/src/zisp/value/char.zig
+++ b/src/zisp/value/char.zig
@@ -5,7 +5,7 @@ const Value = value.Value;
// Zig API
pub fn check(v: Value) bool {
- return v.isOtherTag(.char);
+ return v.isChar();
}
pub fn assert(v: Value) void {
@@ -21,7 +21,7 @@ pub fn pack(c: u21) Value {
pub fn unpack(v: Value) u21 {
assert(v);
- return @truncate(v.char.value);
+ return @intCast(v.char.value);
}
// Zisp API
diff --git a/src/zisp/value/fixnum.zig b/src/zisp/value/fixnum.zig
index 80fb4ae..d6e9184 100644
--- a/src/zisp/value/fixnum.zig
+++ b/src/zisp/value/fixnum.zig
@@ -33,6 +33,8 @@ fn assertValidRange(int: i64) void {
}
}
+const positive_mask: u64 = 0xfff7ffffffffffff;
+
fn packNegative(int: i64) Value {
return @bitCast(int);
}
@@ -41,8 +43,6 @@ fn unpackNegative(v: Value) i64 {
return @bitCast(v);
}
-const positive_mask: u64 = 0xfff7ffffffffffff;
-
fn packPositive(int: i64) Value {
const uint: u64 = @bitCast(int);
return @bitCast(uint ^ positive_mask);
@@ -53,6 +53,8 @@ fn unpackPositive(v: Value) i64 {
return @bitCast(uint ^ positive_mask);
}
+// Although we use if, these should compile to branchless code using cmov.
+
pub fn pack(int: i64) Value {
assertValidRange(int);
if (int < 0) {
diff --git a/src/zisp/value/istr.zig b/src/zisp/value/istr.zig
index abd0447..c1c7093 100644
--- a/src/zisp/value/istr.zig
+++ b/src/zisp/value/istr.zig
@@ -6,60 +6,39 @@ const gc = @import("../gc.zig");
const ptr = @import("ptr.zig");
const seq = @import("seq.zig");
-const Hval = gc.Hval;
-
const Value = value.Value;
// Zig API
-pub fn check(v: Value) bool {
- return ptr.checkZispTag(v, .seq);
+pub fn check(v: Value) ?*seq.Header {
+ if (v.getPtr(.seq)) |p| {
+ const h: *seq.Header = @ptrCast(p);
+ if (h.type == .string and h.info.string.interned) {
+ return h;
+ }
+ }
+ return null;
}
-pub fn assert(v: Value) void {
- if (!check(v)) {
+pub fn assert(v: Value) *seq.Header {
+ return check(v) orelse {
v.dump();
@panic("not istr");
- }
-}
-
-pub fn intern(str: []const u8, quoted: bool) Value {
- if (str.len > value.fixnum.max) {
- @panic("String length out of fixnum range.");
- }
- const header: seq.Header = .{
- .type = .string,
- .info = .{ .string = .{
- .enc = .utf8,
- .quoted = quoted,
- .interned = true,
- } },
- .size = @intCast(str.len),
};
- const header_ptr = gc.intern(header, str);
- return ptr.pack(@ptrCast(header_ptr), .seq);
-}
-
-pub fn getHeader(v: Value) *seq.Header {
- assert(v);
- const header_ptr, _ = ptr.unpack(v);
- return gc.istrHeader(header_ptr);
}
-pub fn getHeaderFromPtr(p: *Hval) *seq.Header {
- return gc.istrHeader(p);
+pub fn intern(str: []const u8) Value {
+ return gc.internString(str);
}
// Zisp API
pub fn pred(v: Value) Value {
- return value.boole.pack(check(v));
+ return value.boole.pack(check(v) != null);
}
pub fn len(v: Value) Value {
- const l = getHeader(v).size;
- if (l > value.fixnum.max) {
- @panic("string length out of range");
- }
+ const l = assert(v).size;
+ std.debug.assert(l <= value.fixnum.max);
return value.fixnum.pack(@intCast(l));
}
diff --git a/src/zisp/value/pair.zig b/src/zisp/value/pair.zig
index 6ea1edf..9afaf12 100644
--- a/src/zisp/value/pair.zig
+++ b/src/zisp/value/pair.zig
@@ -9,47 +9,43 @@ const Value = value.Value;
// Zig API
-pub fn check(v: Value) bool {
- return ptr.checkZispTag(v, .pair);
+pub fn check(v: Value) ?*[2]Value {
+ return @ptrCast(v.getPtr(.pair));
}
-pub fn assert(v: Value) void {
- if (!check(v)) {
+pub fn assert(v: Value) *[2]Value {
+ return check(v) orelse {
v.dump();
@panic("not pair");
- }
+ };
+}
+
+pub fn unpack(v: Value) *[2]Value {
+ return assert(v);
}
// Zisp API
pub fn pred(v: Value) Value {
- return value.boole.pack(check(v));
+ return value.boole.pack(check(v) != null);
}
pub fn cons(v1: Value, v2: Value) Value {
return ptr.pack(@ptrCast(gc.cons(v1, v2)), .pair);
}
-fn getMem(v: Value) *[2]Value {
- return @ptrCast(ptr.unpack(v).@"0");
-}
-
pub fn car(v: Value) Value {
- assert(v);
- return getMem(v)[0];
+ return unpack(v)[0];
}
pub fn cdr(v: Value) Value {
- assert(v);
- return getMem(v)[1];
+ return unpack(v)[1];
}
-pub fn setcar(v: Value, new: Value) void {
- assert(v);
- getMem(v)[0] = new;
+pub fn setCar(v: Value, new: Value) void {
+ unpack(v)[0] = new;
}
-pub fn setcdr(v: Value, new: Value) void {
- assert(v);
- getMem(v)[1] = new;
+pub fn setCdr(v: Value, new: Value) void {
+ unpack(v)[1] = new;
}
diff --git a/src/zisp/value/ptr.zig b/src/zisp/value/ptr.zig
index aa2e9a6..8696056 100644
--- a/src/zisp/value/ptr.zig
+++ b/src/zisp/value/ptr.zig
@@ -3,155 +3,75 @@ const std = @import("std");
const gc = @import("../gc.zig");
const value = @import("../value.zig");
-const Hval = gc.Hval;
-
+const PtrTag = value.PtrTag;
const Value = value.Value;
+const Zptr = value.Zptr;
// Zig API
pub fn check(v: Value) bool {
- return v.isPtr();
+ return v.isPtrAny();
}
pub fn assert(v: Value) void {
if (!check(v)) {
v.dump();
- @panic("not a pointer");
- }
-}
-
-// Foreign Pointers
-
-pub fn checkForeign(v: Value) bool {
- return check(v) and v.ptr.is_foreign;
-}
-
-pub fn assertForeign(v: Value) void {
- if (!checkForeign(v)) {
- v.dump();
- @panic("not foreign pointer");
- }
-}
-
-pub fn packForeign(int: u50) Value {
- return .{ .fptr = .{ .value = int } };
-}
-
-pub fn unpackForeign(v: Value) u50 {
- assertForeign(v);
- return v.fptr.value;
-}
-
-// Zisp Pointers
-
-pub fn checkZisp(v: Value) bool {
- return check(v) and !v.ptr.is_foreign;
-}
-
-pub fn assertZisp(v: Value) void {
- if (!checkZisp(v)) {
- v.dump();
- @panic("not zisp pointer");
+ @panic("not pointer");
}
}
pub fn checkWeak(v: Value) bool {
- return checkZisp(v) and v.zptr.is_weak;
+ return v.isPtrProps(0b110, 0b001);
}
pub fn assertWeak(v: Value) void {
if (!checkWeak(v)) {
v.dump();
- @panic("not zisp weak pointer");
+ @panic("not weak pointer");
}
}
-pub fn checkZispTag(v: Value, tag: Tag) bool {
- return checkZisp(v) and unpack(v).@"1" == tag;
+pub fn _pack(ptr: Zptr, tag: PtrTag, is_weak: bool) Value {
+ const ptr_val: usize = @intFromPtr(ptr);
+ std.debug.assert(ptr_val < std.math.maxInt(u48));
+ const tagged: u48 = @intCast(ptr_val | @intFromEnum(tag));
+ return .{ .ptr = .{ .tagged_value = tagged, .is_weak = is_weak } };
}
-pub fn assertZispTag(v: Value, tag: Tag) void {
- if (!checkZispTag(v, tag)) {
- v.dump();
- @panic("not zisp pointer or wrong tag");
- }
-}
-
-pub fn checkStrong(v: Value) bool {
- return checkZisp(v) and !v.zptr.is_weak;
-}
-
-pub fn assertStrong(v: Value) void {
- if (!checkStrong(v)) {
- v.dump();
- @panic("not zisp strong pointer");
- }
-}
-
-pub fn packZisp(ptr: *Hval, tag: Tag, is_weak: bool) Value {
- return .{ .zptr = .{
- .tagged_value = tagPtr(ptr, tag),
- .is_weak = is_weak,
- } };
+pub fn pack(ptr: Zptr, tag: PtrTag) Value {
+ return _pack(ptr, tag, false);
}
-pub fn pack(ptr: *Hval, tag: Tag) Value {
- return packZisp(ptr, tag, false);
-}
-
-pub fn packWeak(ptr: *Hval, tag: Tag) Value {
- return packZisp(ptr, tag, true);
-}
-
-// Unpacks weak as well; no need for a separate fn.
-pub fn unpack(v: Value) struct { *Hval, Tag } {
- assertZisp(v);
- return untagPtr(v.zptr.tagged_value);
+pub fn packWeak(ptr: Zptr, tag: PtrTag) Value {
+ return _pack(ptr, tag, true);
}
pub fn setWeakNull(v: *Value) void {
assertWeak(v.*);
- v.zptr.tagged_value = 0;
+ v.ptr.tagged_value = 0;
}
pub fn isWeakNull(v: Value) bool {
assertWeak(v);
- return v.zptr.tagged_value == 0;
+ return v.ptr.tagged_value == 0;
}
-fn tagPtr(ptr: *Hval, tag: Tag) u48 {
- const int: usize = @intFromPtr(ptr);
- const untagged: u48 = @intCast(int);
- return untagged | @intFromEnum(tag);
-}
-
-fn untagPtr(tagged: u48) struct { *Hval, Tag } {
- const untagged: u48 = tagged & 0xfffffffffff8;
- const ptr: *Hval = @ptrFromInt(untagged);
- const int: u3 = @truncate(tagged);
- const tag: Tag = @enumFromInt(int);
+pub fn unpack(v: Value) struct { Zptr, PtrTag } {
+ assert(v);
+ const tagged = v.ptr.tagged_value;
+ const ptr_val: u48 = tagged & ~@as(u48, 7);
+ const tag_val: u3 = @intCast(tagged & 7);
+ const ptr: Zptr = @ptrFromInt(ptr_val);
+ const tag: PtrTag = @enumFromInt(tag_val);
return .{ ptr, tag };
}
-pub const Tag = enum(u3) {
- /// Pair aka cons cell aka *[2]Value
- pair,
- /// Sequence of various kinds (16-bit meta, 48-bit length, then data)
- seq,
- /// Procedure
- proc,
-};
-
// Zisp API
-pub fn predForeign(v: Value) Value {
- return value.boole.pack(checkForeign(v));
-}
-
pub fn makeWeak(v: Value) Value {
- assertStrong(v);
+ assert(v);
var copy = v;
- copy.zptr.is_weak = true;
+ copy.ptr.is_weak = true;
return copy;
}
@@ -168,7 +88,7 @@ pub fn getWeak(v: Value) Value {
return value.f;
} else {
var copy = v;
- copy.zptr.is_weak = false;
+ copy.ptr.is_weak = false;
return copy;
}
}
diff --git a/src/zisp/value/rune.zig b/src/zisp/value/rune.zig
index 195210e..e75c276 100644
--- a/src/zisp/value/rune.zig
+++ b/src/zisp/value/rune.zig
@@ -8,7 +8,7 @@ const Value = value.Value;
// Zig API
pub fn check(v: Value) bool {
- return v.isOtherTag(.rune);
+ return v.isRune();
}
pub fn assert(v: Value) void {
@@ -19,17 +19,9 @@ pub fn assert(v: Value) void {
}
pub fn isValidRune(s: []const u8) bool {
- if (s.len == 0 or s.len > 6) {
- return false;
- }
- if (!std.ascii.isAlphabetic(s[0])) {
- return false;
- }
- for (s[1..]) |c| {
- if (!std.ascii.isAlphanumeric(c)) {
- return false;
- }
- }
+ if (s.len == 0 or s.len > 6) return false;
+ if (!std.ascii.isAlphabetic(s[0])) return false;
+ for (s[1..]) |c| if (!std.ascii.isAlphanumeric(c)) return false;
return true;
}
@@ -48,6 +40,7 @@ pub fn pack(s: []const u8) Value {
}
pub fn packForced(s: []const u8) Value {
+ std.debug.assert(0 < s.len and s.len < 7);
var v = Value{ .rune = .{ .name = 0 } };
const dest: [*]u8 = @ptrCast(&v.rune.name);
@memcpy(dest, s);
@@ -57,10 +50,8 @@ pub fn packForced(s: []const u8) Value {
pub fn unpack(v: Value) ShortString {
assert(v);
const s: [6]u8 = @bitCast(v.rune.name);
- inline for (0..6) |i| {
- if (s[i] == 0) return .{ .buffer = s, .len = i };
- }
- return .{ .buffer = s, .len = 6 };
+ const l = value.sstrLen(v.bits);
+ return .{ .buffer = s, .len = l };
}
// Zisp API
@@ -68,15 +59,3 @@ pub fn unpack(v: Value) ShortString {
pub fn pred(v: Value) Value {
return value.boole.pack(check(v));
}
-
-pub fn make(v: Value) Value {
- const s, const l = value.sstr.unpack(v);
- return pack(s[0..l]);
-}
-
-pub fn getName(v: Value) Value {
- const s, const l = unpack(v);
- return value.sstr.pack(s[0..l]);
-}
-
-// TODO: Registering decoders
diff --git a/src/zisp/value/seq.zig b/src/zisp/value/seq.zig
index 3418a5a..cba46ab 100644
--- a/src/zisp/value/seq.zig
+++ b/src/zisp/value/seq.zig
@@ -31,9 +31,8 @@ pub const Header = packed struct(u64) {
string: packed struct(u14) {
enc: enum(u4) { utf8, utf16, utf24, utf32 },
endian: Endian = .native,
- quoted: bool,
interned: bool,
- _: u7 = 0,
+ _: u8 = 0,
},
ints: packed struct(u14) {
signed: bool,
diff --git a/src/zisp/value/sstr.zig b/src/zisp/value/sstr.zig
index b02fd3d..4f0336e 100644
--- a/src/zisp/value/sstr.zig
+++ b/src/zisp/value/sstr.zig
@@ -3,13 +3,12 @@ const std = @import("std");
const value = @import("../value.zig");
const ShortString = value.ShortString;
-const OtherTag = value.OtherTag;
const Value = value.Value;
// Zig API
pub fn check(v: Value) bool {
- return v.isOtherTag(.sstr) or v.isOtherTag(.qstr);
+ return v.isSstr();
}
pub fn assert(v: Value) void {
@@ -19,21 +18,11 @@ pub fn assert(v: Value) void {
}
}
-pub fn checkQuoted(v: Value) bool {
- return v.isOtherTag(.qstr);
-}
-
// For now, ignore encoding, just treat it as []u8.
pub fn isValidSstr(s: []const u8) bool {
- if (s.len > 6) {
- return false;
- }
- for (s) |c| {
- if (c == 0) {
- return false;
- }
- }
+ if (s.len > 6) return false;
+ for (s) |c| if (c == 0) return false;
return true;
}
@@ -44,23 +33,9 @@ fn assertValidSstr(s: []const u8) void {
}
}
-// Different ways of doing the following have been tested, including manual
-// shifting and bit masking, but memcpy always wins easily according to our
-// micro-benchmarks, under both ReleaseSafe and ReleaseFast.
-
-// Note: rune.zig uses equivalent code; probably good to keep in sync.
-
pub fn pack(s: []const u8) Value {
- return _pack(s, .sstr);
-}
-
-pub fn packQuoted(s: []const u8) Value {
- return _pack(s, .qstr);
-}
-
-fn _pack(s: []const u8, tag: OtherTag) Value {
assertValidSstr(s);
- var v = Value{ .sstr = .{ .string = 0, .tag = tag } };
+ var v = Value{ .sstr = .{ .string = 0 } };
const dest: [*]u8 = @ptrCast(&v.sstr.string);
@memcpy(dest, s);
return v;
@@ -69,10 +44,8 @@ fn _pack(s: []const u8, tag: OtherTag) Value {
pub fn unpack(v: Value) ShortString {
assert(v);
const s: [6]u8 = @bitCast(v.sstr.string);
- inline for (0..6) |i| {
- if (s[i] == 0) return .{ .buffer = s, .len = i };
- }
- return .{ .buffer = s, .len = 6 };
+ const l = value.sstrLen(v.bits);
+ return .{ .buffer = s, .len = l };
}
// No Zisp API for sstr specifically, since it's a string. See string.zig.