1995-10-13 23:34:21 -04:00
|
|
|
; Copyright (c) 1993, 1994 Richard Kelsey and Jonathan Rees. See file COPYING.
|
|
|
|
|
|
|
|
|
|
|
|
; File processing, etc. for compiler
|
|
|
|
|
|
|
|
|
|
|
|
; Compile-form and compile-file return templates, suitable for
|
|
|
|
; execution via (invoke-closure (make-closure <template> <arbitrary>)).
|
|
|
|
|
|
|
|
(define (compile-form form p)
|
|
|
|
(compile-scanned-forms (scan-forms (list form) p #f)
|
|
|
|
p #f #f))
|
|
|
|
|
|
|
|
|
|
|
|
; Compile a file. The entire file is first scanned to process all
|
|
|
|
; definitions. Then it's compiled. The result is a template.
|
|
|
|
|
|
|
|
(define (compile-file filename p) ; => thunk template
|
|
|
|
(let* ((scanned-forms (scan-file filename p))
|
|
|
|
(o-port (current-output-port))
|
|
|
|
(template (compile-scanned-forms scanned-forms p filename o-port)))
|
|
|
|
(newline o-port)
|
|
|
|
(force-output o-port)
|
|
|
|
template))
|
|
|
|
|
|
|
|
|
|
|
|
; compile-and-run-{form,file} are the same as compile-{form, file},
|
|
|
|
; but instead of returning templates, they produce a template for each
|
|
|
|
; form as the form is compiled. Each template is passed to a
|
|
|
|
; specified action procedure.
|
|
|
|
|
|
|
|
(define (compile-and-run-forms forms p filename run noisy?)
|
|
|
|
(compile-and-run-scanned-forms (scan-forms forms p filename)
|
|
|
|
p filename run noisy?))
|
|
|
|
|
|
|
|
(define (compile-and-run-file filename p run noisy?)
|
|
|
|
(compile-and-run-scanned-forms (really-scan-file filename p noisy?)
|
|
|
|
p filename run
|
|
|
|
noisy?))
|
|
|
|
|
2001-03-10 22:47:00 -05:00
|
|
|
(define (compile-and-run-port port p run noisy?)
|
|
|
|
(compile-and-run-scanned-forms (really-scan-port port p)
|
|
|
|
p #f run noisy?))
|
|
|
|
|
1995-10-13 23:34:21 -04:00
|
|
|
; --------------------
|
|
|
|
|
|
|
|
; If non-noisy, this tail-recurs to last form.
|
|
|
|
|
|
|
|
(define (compile-and-run-scanned-forms scanned-forms p filename run noisy?)
|
|
|
|
(let* ((do-it (lambda (scanned-form)
|
|
|
|
(compile-scanned-forms (list scanned-form)
|
|
|
|
p filename noisy?)))
|
|
|
|
(do-them (lambda ()
|
|
|
|
(if (null? scanned-forms)
|
|
|
|
(compile-scanned-forms '() p #f #f)
|
|
|
|
(do-it (do ((scanned-forms scanned-forms
|
|
|
|
(cdr scanned-forms)))
|
|
|
|
((null? (cdr scanned-forms))
|
|
|
|
(car scanned-forms))
|
|
|
|
(run (do-it (car scanned-forms)))))))))
|
|
|
|
(if noisy?
|
|
|
|
(call-with-values (lambda ()
|
|
|
|
(run (do-them)))
|
|
|
|
(lambda results
|
|
|
|
(newline noisy?)
|
|
|
|
(force-output noisy?)
|
|
|
|
(apply values results)))
|
|
|
|
(run (do-them)))))
|
|
|
|
|
|
|
|
|
|
|
|
; compile-scanned-forms: returns a template.
|
|
|
|
|
|
|
|
(define (compile-scanned-forms scanned-forms p filename noisy? . env-option)
|
|
|
|
(let ((cenv (bind-source-file-name
|
|
|
|
filename
|
|
|
|
(if (null? env-option)
|
|
|
|
(package->environment p)
|
|
|
|
(car env-option)))))
|
|
|
|
(segment->template
|
|
|
|
(if (null? scanned-forms)
|
|
|
|
(deliver-value (instruction (enum op unspecific))
|
|
|
|
(return-cont #f))
|
|
|
|
(let recur ((scanned-forms scanned-forms))
|
|
|
|
(if (null? (cdr scanned-forms))
|
|
|
|
(compile-scanned-form (car scanned-forms) cenv
|
|
|
|
(return-cont #f) noisy?)
|
|
|
|
(careful-sequentially
|
|
|
|
(compile-scanned-form (car scanned-forms) cenv
|
|
|
|
an-ignore-values-cont noisy?)
|
|
|
|
;; Cf. compile-begin
|
|
|
|
(recur (cdr scanned-forms))
|
|
|
|
0
|
|
|
|
(return-cont #f)))))
|
|
|
|
filename
|
|
|
|
#f))) ;pc-in-segment = #f
|
|
|
|
|
|
|
|
; Compile a single top-level form, returning a segment.
|
|
|
|
|
|
|
|
(define (compile-scanned-form node cenv cont noisy?)
|
|
|
|
(cond ((define-node? node)
|
|
|
|
(let ((segment (compile-definition node cenv cont noisy?)))
|
|
|
|
(if noisy?
|
|
|
|
(begin (write-char #\. noisy?)
|
|
|
|
(force-output noisy?)))
|
|
|
|
segment))
|
|
|
|
((define-syntax-node? node)
|
|
|
|
(deliver-value (instruction (enum op unspecific)) cont))
|
|
|
|
(else
|
|
|
|
(compile-top node cenv 0 cont))))
|
|
|
|
|
|
|
|
|
|
|
|
; Definitions must be treated differently from assignments: we must
|
|
|
|
; use SET-CONTENTS! instead of SET-GLOBAL! because the SET-GLOBAL!
|
|
|
|
; instruction traps if an attempt is made to store into an undefined
|
|
|
|
; location.
|
|
|
|
|
|
|
|
(define compile-definition
|
|
|
|
(let ((location-contents-offset 0)) ;should be found in stob-data
|
|
|
|
(lambda (node cenv cont noisy?)
|
|
|
|
(let* ((form (node-form node))
|
|
|
|
(name (cadr form))
|
|
|
|
(loc (binding-place (lookup cenv name))))
|
|
|
|
(sequentially (instruction-with-location (enum op literal)
|
|
|
|
(lambda () loc))
|
|
|
|
(instruction (enum op push))
|
|
|
|
(compile-top (caddr form)
|
|
|
|
cenv
|
|
|
|
1
|
|
|
|
(named-cont name))
|
|
|
|
(deliver-value
|
|
|
|
(instruction (enum op stored-object-set!)
|
|
|
|
(enum stob location)
|
|
|
|
location-contents-offset)
|
|
|
|
cont))))))
|
|
|
|
|
|
|
|
; --------------------
|
|
|
|
; Hairy stuff for dealing with undefined variables.
|
|
|
|
|
|
|
|
; GET-LOCATION returns a thunk that will obtain a location to be
|
|
|
|
; stored away in a template. The hair here results from a desire to
|
|
|
|
; be able to recover from errors.
|
|
|
|
|
|
|
|
(define (get-location binding cenv name want-type)
|
|
|
|
(if (binding? binding)
|
|
|
|
(let ((win (lambda ()
|
|
|
|
(note-caching cenv name (binding-place binding)))))
|
|
|
|
(if (compatible-types? (binding-type binding) want-type)
|
|
|
|
win
|
|
|
|
(if (variable-type? want-type)
|
|
|
|
(get-location-for-unassignable cenv name)
|
|
|
|
(begin (warn "invalid variable reference" name)
|
|
|
|
win))))
|
|
|
|
(get-location-for-undefined cenv name)))
|
|
|
|
|
|
|
|
(define (get-location-for-undefined cenv name)
|
|
|
|
(if (generated? name)
|
|
|
|
(get-location-for-undefined (generated-env name)
|
|
|
|
(generated-symbol name))
|
|
|
|
(let ((p (grumble cenv)))
|
|
|
|
(note-undefined! p name)
|
|
|
|
(if (package? p)
|
|
|
|
(lambda ()
|
|
|
|
(package-note-caching p name
|
|
|
|
(location-for-reference p name)))
|
|
|
|
(lambda ()
|
|
|
|
(make-undefined-location name))))))
|
|
|
|
|
|
|
|
(define (get-location-for-unassignable cenv name)
|
|
|
|
(if (generated? name)
|
|
|
|
(get-location-for-unassignable (generated-env name)
|
|
|
|
(generated-symbol name))
|
|
|
|
(let ((p (grumble cenv)))
|
|
|
|
(warn "invalid assignment" name)
|
|
|
|
(if (package? p)
|
|
|
|
(lambda () (location-for-assignment p name))
|
|
|
|
(lambda () (make-undefined-location name))))))
|
|
|
|
|
|
|
|
(define (package-accessor-on-demand id init)
|
|
|
|
(lambda (p)
|
|
|
|
(or (package-get p id)
|
|
|
|
(let ((foo (init)))
|
|
|
|
(package-put! p id foo)
|
|
|
|
foo))))
|
|
|
|
|
|
|
|
(define (location-on-demand accessor)
|
|
|
|
(lambda (p name)
|
|
|
|
(let ((table (accessor p)))
|
|
|
|
(or (table-ref table name)
|
|
|
|
(let ((new (make-new-location p name)))
|
|
|
|
(table-set! table name new)
|
|
|
|
new)))))
|
|
|
|
|
|
|
|
; Create undefined variable -> location tables on demand.
|
|
|
|
|
|
|
|
(define package-undefineds
|
|
|
|
(package-accessor-on-demand 'undefineds
|
|
|
|
(lambda () (make-table name-hash))))
|
|
|
|
|
|
|
|
(define location-for-reference
|
|
|
|
(let ((get-undefined (location-on-demand package-undefineds)))
|
|
|
|
(lambda (p name)
|
|
|
|
(let loop ((opens (package-opens p)))
|
|
|
|
(if (null? opens)
|
|
|
|
(get-undefined p name)
|
|
|
|
(if (interface-ref (structure-interface (car opens))
|
|
|
|
name)
|
|
|
|
(location-for-reference (structure-package (car opens)) name)
|
|
|
|
(loop (cdr opens))))))))
|
|
|
|
|
|
|
|
(define package-undefined-but-assigneds
|
|
|
|
(package-accessor-on-demand 'undefined-but-assigneds
|
|
|
|
(lambda () (make-table name-hash))))
|
|
|
|
|
|
|
|
(define location-for-assignment
|
|
|
|
(location-on-demand package-undefined-but-assigneds))
|
|
|
|
|
|
|
|
|
|
|
|
(define (note-caching cenv name place)
|
|
|
|
(if (generated? name)
|
|
|
|
(note-caching (generated-env name)
|
|
|
|
(generated-symbol name)
|
|
|
|
place)
|
|
|
|
(let ((p (grumble cenv)))
|
|
|
|
(if (package? p)
|
|
|
|
(package-note-caching p name place)
|
|
|
|
place))))
|
|
|
|
|
|
|
|
(define (grumble cenv)
|
|
|
|
(cond ((procedure? cenv)
|
|
|
|
;; This returns #f if package is stable (static linking).
|
|
|
|
(extract-package-from-environment cenv))
|
|
|
|
((package? cenv) cenv)
|
|
|
|
((structure? cenv) (structure-package cenv))))
|
|
|
|
|
|
|
|
; --------------------
|
|
|
|
; Make a startup procedure from a list of initialization thunks. This
|
|
|
|
; is only used by the static linker.
|
|
|
|
|
|
|
|
(define (make-startup-procedure inits resumer)
|
|
|
|
(let ((nargs 3))
|
|
|
|
(segment->template
|
|
|
|
(reduce (lambda (init seg)
|
|
|
|
(sequentially
|
|
|
|
(maybe-push-continuation
|
|
|
|
(sequentially
|
|
|
|
(instruction-with-literal (enum op closure) init)
|
|
|
|
(instruction (enum op call) 0))
|
|
|
|
nargs
|
|
|
|
an-ignore-values-cont)
|
|
|
|
seg))
|
|
|
|
(sequentially
|
|
|
|
(maybe-push-continuation
|
|
|
|
(sequentially
|
|
|
|
(instruction-with-literal (enum op closure) resumer)
|
|
|
|
(instruction (enum op call) 0))
|
|
|
|
nargs
|
|
|
|
(fall-through-cont #f #f))
|
|
|
|
;; was (compile resumer p nargs (fall-through-cont))
|
|
|
|
(instruction (enum op call) nargs))
|
|
|
|
inits)
|
|
|
|
#f #f)))
|
|
|
|
|
|
|
|
(define an-ignore-values-cont (ignore-values-cont #f #f))
|