📄 save.scm
字号:
;;; installed-scm-file;;;; Copyright (C) 2000, 2001 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 2, 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., 59 Temple Place, Suite 330,;;;; Boston, MA 02111-1307 USA;;;;;;;; As a special exception, the Free Software Foundation gives permission;;;; for additional uses of the text contained in its release of GUILE.;;;;;;;; The exception is that, if you link the GUILE library with other files;;;; to produce an executable, this does not by itself cause the;;;; resulting executable to be covered by the GNU General Public License.;;;; Your use of that executable is in no way restricted on account of;;;; linking the GUILE library code into it.;;;;;;;; This exception does not however invalidate any other reasons why;;;; the executable file might be covered by the GNU General Public License.;;;;;;;; This exception applies only to the code released by the;;;; Free Software Foundation under the name GUILE. If you copy;;;; code from other Free Software Foundation releases into a copy of;;;; GUILE, as the General Public License permits, the exception does;;;; not apply to the code that you add in this way. To avoid misleading;;;; anyone as to the status of such modified files, you must delete;;;; this exception notice from them.;;;;;;;; If you write modifications of your own for GUILE, it is your choice;;;; whether to permit this exception to apply to your modifications.;;;; If you do not wish that, delete this exception notice.;;;; (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;;;(if (or (not (defined? 'readables)) (not readables)) (define readables (make-weak-key-hash-table 61)))(define readable (procedure->memoizing-macro (lambda (exp env) `(make-readable ,(cadr exp) ',(copy-tree (cadr exp))))))(define (make-readable obj expr) (hashq-set! readables obj expr) obj)(define (readable-expression obj) `(readable ,(hashq-ref readables obj)))(define (readable? obj) (hashq-get-handle readables obj));;;;;; 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* ((dims (array-dimensions array)) (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 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 restore (procedure->macro (lambda (exp env) "(restore CLASS (SLOT-NAME1 ...) EXP1 ...)" `(let ((o (,%allocate-instance ,(cadr exp) '()))) (for-each (lambda (name val) (,slot-set! o name val)) ',(caddr exp) (list ,@(cdddr exp))) 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))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -