mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 03:40:34 +02:00
* doc/ref/api-data.texi (List Constructors): * doc/ref/api-utility.texi (Copying): Update docs to mention module. * libguile.h: Remove trees.h inclusion. * libguile/Makefile.am (libguile_@GUILE_EFFECTIVE_VERSION@_la_SOURCES): (DOT_X_FILES, DOT_DOC_FILES, modinclude_HEADERS): Remove trees.c, trees.h, and related files. * libguile/init.c (scm_i_init_guile): Remove trees.h and the scm_init_trees call. * libguile/trees.c: * libguile/trees.h: Remove. * module/Makefile.am (SOURCES): Add ice-9/copy-tree.scm. * module/ice-9/copy-tree.scm: New file. * module/ice-9/deprecated.scm (copy-tree*): Export as copy-tree, proxying to (ice-9 copy-tree). * module/system/repl/common.scm: * module/web/client.scm: * test-suite/tests/elisp.test: * test-suite/tests/srfi-1.test: * module/oop/goops/save.scm: Use (ice-9 copy-tree). * test-suite/Makefile.am (SCM_TESTS): Add copy-tree.test. * test-suite/tests/copy-tree.test: New file; test pulled from eval.test. * libguile/deprecated.h: * libguile/deprecated.c (scm_copy_tree): Deprecate.
875 lines
24 KiB
Scheme
875 lines
24 KiB
Scheme
;;; installed-scm-file
|
||
|
||
;;;; Copyright (C) 2000,2001,2002, 2006, 2009, 2010, 2013, 2015, 2020 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 (ice-9 copy-tree)
|
||
#:use-module (oop goops internal)
|
||
#:export (make-unbound save-objects load-objects restore
|
||
enumerate! enumerate-component!
|
||
write-readably write-component write-component-procedure
|
||
literal? readable make-readable))
|
||
|
||
(define (make-unbound)
|
||
*unbound*)
|
||
|
||
;;;
|
||
;;; 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 (slot)
|
||
(unless (memq (slot-definition-allocation slot)
|
||
'(#:class #:each-subclass))
|
||
(let ((ref (slot-definition-slot-ref slot))
|
||
(set (slot-definition-slot-set! slot))
|
||
(index (slot-definition-index slot)))
|
||
(if ref
|
||
(proc ref set)
|
||
(proc (standard-get index) (standard-set index))))))
|
||
(class-slots class)))
|
||
|
||
(define (access-for-each proc class)
|
||
(for-each (lambda (slot)
|
||
(unless (memq (slot-definition-allocation slot)
|
||
'(#:class #:each-subclass))
|
||
(let ((name (slot-definition-name slot))
|
||
(accessor (and=> (slot-definition-accessor slot)
|
||
generic-function-name))
|
||
(ref (slot-definition-slot-ref slot))
|
||
(set (slot-definition-slot-set! slot))
|
||
(index (slot-definition-index slot)))
|
||
(if ref
|
||
(proc name accessor ref set)
|
||
(proc name accessor
|
||
(standard-get index) (standard-set index))))))
|
||
(class-slots class)))
|
||
|
||
(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)))
|