scsh-0.6/scheme/bcomp/package.scm

432 lines
13 KiB
Scheme

; Copyright (c) 1993-1999 by Richard Kelsey and Jonathan Rees. See file COPYING.
; Structures 'n' packages.
; --------------------
; Structures
;
; A structure is a map from names to binding records, determined by an
; interface (a set of names) and a package (a map from names to binding
; records).
;
; The interface is specified as a thunk. This removes dependencies on the
; order in which structures are defined. Also, if the interface is redefined,
; re-evaluating the thunk produces the new, correct interface (see
; env/pedit.scm).
;
; Clients are packages that import the structure's bindings.
(define-record-type structure :structure
(really-make-structure package interface-thunk interface clients name)
structure?
(interface-thunk structure-interface-thunk)
(interface structure-interface-really set-structure-interface!)
(package structure-package)
(clients structure-clients)
(name structure-name set-structure-name!))
(define-record-discloser :structure
(lambda (structure)
(list 'structure
(package-uid (structure-package structure))
(structure-name structure))))
; Get the actual interface, calling the thunk if necessary.
(define (structure-interface structure)
(or (structure-interface-really structure)
(begin (initialize-structure! structure)
(structure-interface-really structure))))
(define (initialize-structure! structure)
(let ((int ((structure-interface-thunk structure))))
(if (interface? int)
(begin (set-structure-interface! structure int)
(note-reference-to-interface! int structure))
(call-error "invalid interface" initialize-structure! structure))))
; Make a structure over PACKAGE and the interface returned by INT-THUNK.
(define (make-structure package int-thunk . name-option)
(if (not (package? package))
(call-error "invalid package" make-structure package int-thunk))
(let ((struct (really-make-structure package
(if (procedure? int-thunk)
int-thunk
(lambda () int-thunk))
#f
(make-population)
#f)))
(if (not (null? name-option))
(note-structure-name! struct (car name-option)))
(add-to-population! struct (package-clients package))
struct))
; Make a structure by using COMMANDS to modify the STRUCTURE's interface.
(define (make-modified-structure structure commands)
(let ((new-struct (make-structure (structure-package structure)
(lambda ()
(make-modified-interface
(structure-interface structure)
commands)))))
(if (structure-unstable? structure)
(add-to-population! new-struct (structure-clients structure)))
new-struct))
; STRUCT has name NAME. NAME can then also be used to refer to STRUCT's
; package.
(define (note-structure-name! struct name)
(if (and name (not (structure-name struct)))
(begin (set-structure-name! struct name)
(note-package-name! (structure-package struct) name))))
; A structure is unstable if its package is. An unstable package is one
; where new code may be added, possibly modifying the exported bindings.
(define (structure-unstable? struct)
(package-unstable? (structure-package struct)))
; Map PROC down the the [name type binding] triples provided by STRUCT.
(define (for-each-export proc struct)
(let ((int (structure-interface struct)))
(for-each-declaration
(lambda (name base-name want-type)
(let ((binding (real-structure-lookup struct base-name want-type #t)))
(proc name
(if (and (binding? binding)
(eq? want-type undeclared-type))
(let ((type (binding-type binding)))
(if (variable-type? type)
(variable-value-type type)
type))
want-type)
binding)))
int)))
; --------------------
; Packages
(define-record-type package :package
(really-make-package uid
opens-thunk opens accesses-thunk
definitions
undefineds
undefined-but-assigneds
get-location
cached
clients
unstable?
integrate?
file-name clauses loaded?)
package?
(uid package-uid)
(opens package-opens-really set-package-opens!)
(definitions package-definitions)
(unstable? package-unstable?)
(integrate? package-integrate? set-package-integrate?!)
;; For EVAL and LOAD (which can only be done in unstable packages)
(get-location package-get-location set-package-get-location!)
(file-name package-file-name)
(clauses package-clauses)
(loaded? package-loaded? set-package-loaded?!)
(env package->environment set-package->environment!)
;; For package mutation
(opens-thunk package-opens-thunk set-package-opens-thunk!)
(accesses-thunk package-accesses-thunk)
(undefineds package-real-undefineds set-package-undefineds!)
(undefined-but-assigneds
package-real-undefined-but-assigneds
set-package-undefined-but-assigneds!)
(clients package-clients)
(cached package-cached))
(define-record-discloser :package
(lambda (package)
(let ((name (package-name package)))
(if name
(list 'package (package-uid package) name)
(list 'package (package-uid package))))))
(define (make-package opens-thunk accesses-thunk unstable? tower file clauses
uid name)
(let ((new (really-make-package
(if uid
(begin (if (>= uid *package-uid*)
(set! *package-uid* (+ uid 1)))
uid)
(new-package-uid))
opens-thunk
#f ;opens
accesses-thunk ;thunk returning alist
(make-name-table) ;definitions
#f ;undefineds
#f ;undefined-but-assigned
(fluid $get-location) ;procedure for making new locations
(make-name-table) ;bindings cached in templates
(make-population) ;structures
unstable? ;unstable (suitable for EVAL)?
#t ;integrate?
file ;file containing DEFINE-STRUCTURE form
clauses ;misc. DEFINE-STRUCTURE clauses
#f))) ;loaded?
(note-package-name! new name)
(set-package->environment! new (really-package->environment new tower))
new))
; TOWER is a promise that is expected to deliver, when forced, a
; pair (eval . env).
(define (really-package->environment package tower)
(make-compiler-env (lambda (name)
(package-lookup package name))
(lambda (name type . maybe-static)
(package-define! package
name
type
#f
(if (null? maybe-static)
#f
(car maybe-static))))
tower
package)) ; interim hack
; Two tables that we add lazily.
(define (lazy-table-accessor slot-ref slot-set!)
(lambda (package)
(or (slot-ref package)
(let ((table (make-name-table)))
(slot-set! package table)
table))))
(define package-undefineds
(lazy-table-accessor package-real-undefineds
set-package-undefineds!))
(define package-undefined-but-assigneds
(lazy-table-accessor package-real-undefined-but-assigneds
set-package-undefined-but-assigneds!))
; Unique id's
(define (new-package-uid)
(let ((uid *package-uid*)) ;unique identifier
(set! *package-uid* (+ *package-uid* 1))
uid))
(define *package-uid* 0)
; Package names
(define package-name-table (make-table))
(define (package-name package)
(table-ref package-name-table (package-uid package)))
(define (note-package-name! package name)
(if name
(let ((uid (package-uid package)))
(if (not (table-ref package-name-table uid))
(table-set! package-name-table uid name)))))
(define (package-opens package)
(initialize-package-if-necessary! package)
(package-opens-really package))
(define (initialize-package-if-necessary! package)
(if (not (package-opens-really package))
(initialize-package! package)))
(define (package-accesses package) ;=> alist
((package-accesses-thunk package)))
; --------------------
; A simple package has no ACCESSes or other far-out clauses.
(define (make-simple-package opens unstable? tower . name-option)
(if (not (list? opens))
(error "invalid package opens list" opens))
(let ((package (make-package (lambda () opens)
(lambda () '()) ;accesses-thunk
unstable?
tower
"" ;file containing DEFINE-STRUCTURE form
'() ;clauses
#f ;uid
(if (null? name-option)
#f
(car name-option)))))
(set-package-loaded?! package #t)
package))
; --------------------
; The definitions table
; Each entry in the package-definitions table is a binding.
(define (package-definition package name)
(initialize-package-if-necessary! package)
(let ((probe (table-ref (package-definitions package) name)))
(if probe
(maybe-fix-place! probe)
#f)))
(define (package-define! package name type place static)
(let ((probe (table-ref (package-definitions package) name)))
(if probe
(begin
(clobber-binding! probe type place static)
(binding-place (maybe-fix-place! probe)))
(let ((place (or place (get-new-location package name))))
(table-set! (package-definitions package)
name
(make-binding type place static))
place))))
(define (package-add-static! package name static)
(let ((probe (table-ref (package-definitions package) name)))
(if probe
(clobber-binding! probe
(binding-type probe)
(binding-place probe)
static)
(error "internal error: name not bound" package name))))
(define (package-refine-type! package name type)
(let ((probe (table-ref (package-definitions package) name)))
(if probe
(clobber-binding! probe
type
(binding-place probe)
(binding-static probe))
(error "internal error: name not bound" package name))))
; --------------------
; Lookup
; Look up a name in a package. Returns a binding if bound or #F if not.
(define (package-lookup package name)
(really-package-lookup package name (package-integrate? package)))
(define (really-package-lookup package name integrate?)
(let ((probe (package-definition package name)))
(cond (probe
(if integrate?
probe
(forget-integration probe)))
((generated? name)
; Access path is (generated-parent-name name)
(generic-lookup (generated-env name)
(generated-name name)))
(else
(search-opens (package-opens-really package) name integrate?)))))
; Look for NAME in structures OPENS.
(define (search-opens opens name integrate?)
(let loop ((opens opens))
(if (null? opens)
#f
(or (structure-lookup (car opens) name integrate?)
(loop (cdr opens))))))
(define (structure-lookup struct name integrate?)
(call-with-values
(lambda ()
(interface-ref (structure-interface struct) name))
(lambda (base-name type)
(if type
(real-structure-lookup struct base-name type integrate?)
#f))))
(define (real-structure-lookup struct name type integrate?)
(impose-type type
(really-package-lookup (structure-package struct)
name
integrate?)
integrate?))
(define (generic-lookup env name)
(cond ((package? env)
(package-lookup env name))
((structure? env)
(or (structure-lookup env
name
(package-integrate? (structure-package env)))
(call-error "not exported" generic-lookup env name)))
((procedure? env)
(lookup env name))
(else
(error "invalid environment" env name))))
; --------------------
; Package initialization
(define (initialize-package! package)
(let ((opens ((package-opens-thunk package))))
(set-package-opens! package opens)
(for-each (lambda (struct)
(if (structure-unstable? struct)
(add-to-population! package (structure-clients struct))))
opens))
(for-each (lambda (name+struct)
;; Cf. CLASSIFY method for STRUCTURE-REF
(package-define! package
(car name+struct)
structure-type
#f
(cdr name+struct)))
(package-accesses package)))
; (define (package->environment? env)
; (eq? env (package->environment
; (extract-package-from-environment env))))
; --------------------
; For implementation of INTEGRATE-ALL-PRIMITIVES! in scanner, etc.
(define (for-each-definition proc package)
(table-walk (lambda (name binding)
(proc name (maybe-fix-place! binding)))
(package-definitions package)))
; --------------------
; Locations
(define (get-new-location package name)
((package-get-location package) package name))
; Default new-location method for new packages
(define (make-new-location package name)
(let ((uid *location-uid*))
(set! *location-uid* (+ *location-uid* 1))
(table-set! location-info-table uid
(make-immutable!
(cons (name->symbol name) (package-uid package))))
(make-undefined-location uid)))
(define $get-location (make-fluid make-new-location))
(define *location-uid* 5000) ; 1510 in initial system as of 1/22/94
(define location-info-table (make-table))
(define (flush-location-names)
(set! location-info-table (make-table))
;; (set! package-name-table (make-table)) ;hmm, not much of a space saver
)
; (put 'package-define! 'scheme-indent-hook 2)