diff options
Diffstat (limited to 'synclo.scm')
-rw-r--r-- | synclo.scm | 748 |
1 files changed, 748 insertions, 0 deletions
diff --git a/synclo.scm b/synclo.scm new file mode 100644 index 0000000..3c61de3 --- /dev/null +++ b/synclo.scm @@ -0,0 +1,748 @@ +;;; "synclo.scm" Syntactic Closures -*-Scheme-*- +;;; Copyright (c) 1989-91 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 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. 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. + +;;;; Syntactic Closures +;;; written by Alan Bawden +;;; extensively modified by Chris Hanson + +;;; See "Syntactic Closures", by Alan Bawden and Jonathan Rees, in +;;; Proceedings of the 1988 ACM Conference on Lisp and Functional +;;; Programming, page 86. + +;;;; Classifier +;;; The classifier maps forms into items. In addition to locating +;;; definitions so that they can be properly processed, it also +;;; identifies keywords and variables, which allows a powerful form +;;; of syntactic binding to be implemented. + +(define (classify/form form environment definition-environment) + (cond ((identifier? form) + (syntactic-environment/lookup environment form)) + ((syntactic-closure? form) + (let ((form (syntactic-closure/form form)) + (environment + (filter-syntactic-environment + (syntactic-closure/free-names form) + environment + (syntactic-closure/environment form)))) + (classify/form form + environment + definition-environment))) + ((pair? form) + (let ((item + (classify/subexpression (car form) environment))) + (cond ((keyword-item? item) + ((keyword-item/classifier item) form + environment + definition-environment)) + ((list? (cdr form)) + (let ((items + (classify/subexpressions (cdr form) + environment))) + (make-expression-item + (lambda () + (output/combination + (compile-item/expression item) + (map compile-item/expression items))) + form))) + (else + (syntax-error "combination must be a proper list" + form))))) + (else + (make-expression-item ;don't quote literals evaluating to themselves + (if (or (boolean? form) (char? form) (number? form) (string? form)) + (lambda () (output/literal-unquoted form)) + (lambda () (output/literal-quoted form))) form)))) + +(define (classify/subform form environment definition-environment) + (classify/form form + environment + definition-environment)) + +(define (classify/subforms forms environment definition-environment) + (map (lambda (form) + (classify/subform form environment definition-environment)) + forms)) + +(define (classify/subexpression expression environment) + (classify/subform expression environment environment)) + +(define (classify/subexpressions expressions environment) + (classify/subforms expressions environment environment)) + +;;;; Compiler +;;; The compiler maps items into the output language. + +(define (compile-item/expression item) + (let ((illegal + (lambda (item name) + (let ((decompiled (decompile-item item))) (newline) + (slib:error (string-append name + " may not be used as an expression") + decompiled))))) + (cond ((variable-item? item) + (output/variable (variable-item/name item))) + ((expression-item? item) + ((expression-item/compiler item))) + ((body-item? item) + (let ((items (flatten-body-items (body-item/components item)))) + (if (null? items) + (illegal item "empty sequence") + (output/sequence (map compile-item/expression items))))) + ((definition-item? item) + (let ((binding ;allows later scheme errors, but allows top-level + (bind-definition-item! ;(if (not (defined? x)) define it) + scheme-syntactic-environment item))) ;as in Init.scm + (output/top-level-definition + (car binding) + (compile-item/expression (cdr binding))))) + ((keyword-item? item) + (illegal item "keyword")) + (else + (impl-error "unknown item" item))))) + +(define (compile/subexpression expression environment) + (compile-item/expression + (classify/subexpression expression environment))) + +(define (compile/top-level forms environment) + ;; Top-level syntactic definitions affect all forms that appear + ;; after them. + (output/top-level-sequence + (let forms-loop ((forms forms)) + (if (null? forms) + '() + (let items-loop + ((items + (item->list + (classify/subform (car forms) + environment + environment)))) + (cond ((null? items) + (forms-loop (cdr forms))) + ((definition-item? (car items)) + (let ((binding + (bind-definition-item! environment (car items)))) + (if binding + (cons (output/top-level-definition + (car binding) + (compile-item/expression (cdr binding))) + (items-loop (cdr items))) + (items-loop (cdr items))))) + (else + (cons (compile-item/expression (car items)) + (items-loop (cdr items)))))))))) + +;;;; De-Compiler +;;; The de-compiler maps partly-compiled things back to the input language, +;;; as far as possible. Used to display more meaningful macro error messages. + +(define (decompile-item item) + (display " ") + (cond ((variable-item? item) (variable-item/name item)) + ((expression-item? item) + (decompile-item (expression-item/annotation item))) + ((body-item? item) + (let ((items (flatten-body-items (body-item/components item)))) + (display "sequence") + (if (null? items) + "empty sequence" + "non-empty sequence"))) + ((definition-item? item) "definition") + ((keyword-item? item) + (decompile-item (keyword-item/name item)));in case expression + ((syntactic-closure? item); (display "syntactic-closure;") + (decompile-item (syntactic-closure/form item))) + ((list? item) (display "(") + (map decompile-item item) (display ")") "see list above") + ((string? item) item);explicit name-string for keyword-item + ((symbol? item) (display item) item) ;symbol for syntactic-closures + ((boolean? item) (display item) item) ;symbol for syntactic-closures + (else (write item) (impl-error "unknown item" item)))) + +;;;; Syntactic Closures + +(define syntactic-closure-type + (make-record-type "syntactic-closure" '(ENVIRONMENT FREE-NAMES FORM))) + +(define make-syntactic-closure + (record-constructor syntactic-closure-type '(ENVIRONMENT FREE-NAMES FORM))) + +(define syntactic-closure? + (record-predicate syntactic-closure-type)) + +(define syntactic-closure/environment + (record-accessor syntactic-closure-type 'ENVIRONMENT)) + +(define syntactic-closure/free-names + (record-accessor syntactic-closure-type 'FREE-NAMES)) + +(define syntactic-closure/form + (record-accessor syntactic-closure-type 'FORM)) + +(define (make-syntactic-closure-list environment free-names forms) + (map (lambda (form) (make-syntactic-closure environment free-names form)) + forms)) + +(define (strip-syntactic-closures object) + (cond ((syntactic-closure? object) + (strip-syntactic-closures (syntactic-closure/form object))) + ((pair? object) + (cons (strip-syntactic-closures (car object)) + (strip-syntactic-closures (cdr object)))) + ((vector? object) + (let ((length (vector-length object))) + (let ((result (make-vector length))) + (do ((i 0 (+ i 1))) + ((= i length)) + (vector-set! result i + (strip-syntactic-closures (vector-ref object i)))) + result))) + (else + object))) + +(define (identifier? object) + (or (symbol? object) + (synthetic-identifier? object))) + +(define (synthetic-identifier? object) + (and (syntactic-closure? object) + (identifier? (syntactic-closure/form object)))) + +(define (identifier->symbol identifier) + (cond ((symbol? identifier) + identifier) + ((synthetic-identifier? identifier) + (identifier->symbol (syntactic-closure/form identifier))) + (else + (impl-error "not an identifier" identifier)))) + +(define (identifier=? environment-1 identifier-1 environment-2 identifier-2) + (let ((item-1 (syntactic-environment/lookup environment-1 identifier-1)) + (item-2 (syntactic-environment/lookup environment-2 identifier-2))) + (or (eq? item-1 item-2) + ;; This is necessary because an identifier that is not + ;; explicitly bound by an environment is mapped to a variable + ;; item, and the variable items are not cached. Therefore + ;; two references to the same variable result in two + ;; different variable items. + (and (variable-item? item-1) + (variable-item? item-2) + (eq? (variable-item/name item-1) + (variable-item/name item-2)))))) + +;;;; Syntactic Environments + +(define syntactic-environment-type + (make-record-type + "syntactic-environment" + '(PARENT + LOOKUP-OPERATION + RENAME-OPERATION + DEFINE-OPERATION + BINDINGS-OPERATION))) + +(define make-syntactic-environment + (record-constructor syntactic-environment-type + '(PARENT + LOOKUP-OPERATION + RENAME-OPERATION + DEFINE-OPERATION + BINDINGS-OPERATION))) + +(define syntactic-environment? + (record-predicate syntactic-environment-type)) + +(define syntactic-environment/parent + (record-accessor syntactic-environment-type 'PARENT)) + +(define syntactic-environment/lookup-operation + (record-accessor syntactic-environment-type 'LOOKUP-OPERATION)) + +(define (syntactic-environment/assign! environment name item) + (let ((binding + ((syntactic-environment/lookup-operation environment) name))) + (if binding + (set-cdr! binding item) + (impl-error "can't assign unbound identifier" name)))) + +(define syntactic-environment/rename-operation + (record-accessor syntactic-environment-type 'RENAME-OPERATION)) + +(define (syntactic-environment/rename environment name) + ((syntactic-environment/rename-operation environment) name)) + +(define syntactic-environment/define! + (let ((accessor + (record-accessor syntactic-environment-type 'DEFINE-OPERATION))) + (lambda (environment name item) + ((accessor environment) name item)))) + +(define syntactic-environment/bindings + (let ((accessor + (record-accessor syntactic-environment-type 'BINDINGS-OPERATION))) + (lambda (environment) + ((accessor environment))))) + +(define (syntactic-environment/lookup environment name) + (let ((binding + ((syntactic-environment/lookup-operation environment) name))) + (cond (binding + (let ((item (cdr binding))) + (if (reserved-name-item? item) + (syntax-error "premature reference to reserved name" + name) + item))) + ((symbol? name) + (make-variable-item name)) + ((synthetic-identifier? name) + (syntactic-environment/lookup (syntactic-closure/environment name) + (syntactic-closure/form name))) + (else + (impl-error "not an identifier" name))))) + +(define root-syntactic-environment + (make-syntactic-environment + #f + (lambda (name) + name + #f) + (lambda (name) + name) + (lambda (name item) + (impl-error "can't bind name in root syntactic environment" name item)) + (lambda () + '()))) + +(define null-syntactic-environment + (make-syntactic-environment + #f + (lambda (name) + (impl-error "can't lookup name in null syntactic environment" name)) + (lambda (name) + (impl-error "can't rename name in null syntactic environment" name)) + (lambda (name item) + (impl-error "can't bind name in null syntactic environment" name item)) + (lambda () + '()))) + +(define (top-level-syntactic-environment parent) + (let ((bound '())) + (make-syntactic-environment + parent + (let ((parent-lookup (syntactic-environment/lookup-operation parent))) + (lambda (name) + (or (assq name bound) + (parent-lookup name)))) + (lambda (name) + name) + (lambda (name item) + (let ((binding (assq name bound))) + (if binding + (set-cdr! binding item) + (set! bound (cons (cons name item) bound))))) + (lambda () + (map (lambda (pair) (cons (car pair) (cdr pair))) bound))))) + +(define (internal-syntactic-environment parent) + (let ((bound '()) + (free '())) + (make-syntactic-environment + parent + (let ((parent-lookup (syntactic-environment/lookup-operation parent))) + (lambda (name) + (or (assq name bound) + (assq name free) + (let ((binding (parent-lookup name))) + (if binding (set! free (cons binding free))) + binding)))) + (make-name-generator) + (lambda (name item) + (cond ((assq name bound) + => + (lambda (association) + (if (and (reserved-name-item? (cdr association)) + (not (reserved-name-item? item))) + (set-cdr! association item) + (impl-error "can't redefine name; already bound" name)))) + ((assq name free) + (if (reserved-name-item? item) + (syntax-error "premature reference to reserved name" + name) + (impl-error "can't define name; already free" name))) + (else + (set! bound (cons (cons name item) bound))))) + (lambda () + (map (lambda (pair) (cons (car pair) (cdr pair))) bound))))) + +(define (filter-syntactic-environment names names-env else-env) + (if (or (null? names) + (eq? names-env else-env)) + else-env + (let ((make-operation + (lambda (get-operation) + (let ((names-operation (get-operation names-env)) + (else-operation (get-operation else-env))) + (lambda (name) + ((if (memq name names) names-operation else-operation) + name)))))) + (make-syntactic-environment + else-env + (make-operation syntactic-environment/lookup-operation) + (make-operation syntactic-environment/rename-operation) + (lambda (name item) + (impl-error "can't bind name in filtered syntactic environment" + name item)) + (lambda () + (map (lambda (name) + (cons name + (syntactic-environment/lookup names-env name))) + names)))))) + +;;;; Items + +;;; Reserved name items do not represent any form, but instead are +;;; used to reserve a particular name in a syntactic environment. If +;;; the classifier refers to a reserved name, a syntax error is +;;; signalled. This is used in the implementation of LETREC-SYNTAX +;;; to signal a meaningful error when one of the <init>s refers to +;;; one of the names being bound. + +(define reserved-name-item-type + (make-record-type "reserved-name-item" '())) + +(define make-reserved-name-item + (record-constructor reserved-name-item-type)) ; '() + +(define reserved-name-item? + (record-predicate reserved-name-item-type)) + +;;; Keyword items represent macro keywords. + +(define keyword-item-type + (make-record-type "keyword-item" '(CLASSIFIER NAME))) +; (make-record-type "keyword-item" '(CLASSIFIER))) + +(define make-keyword-item +; (lambda (cl) (display "make-keyword-item:") (write cl) (newline) +; ((record-constructor keyword-item-type '(CLASSIFIER)) cl))) + (record-constructor keyword-item-type '(CLASSIFIER NAME))) +; (record-constructor keyword-item-type '(CLASSIFIER))) + +(define keyword-item? + (record-predicate keyword-item-type)) + +(define keyword-item/classifier + (record-accessor keyword-item-type 'CLASSIFIER)) + +(define keyword-item/name + (record-accessor keyword-item-type 'NAME)) + +;;; Variable items represent run-time variables. + +(define variable-item-type + (make-record-type "variable-item" '(NAME))) + +(define make-variable-item + (record-constructor variable-item-type '(NAME))) + +(define variable-item? + (record-predicate variable-item-type)) + +(define variable-item/name + (record-accessor variable-item-type 'NAME)) + +;;; Expression items represent any kind of expression other than a +;;; run-time variable or a sequence. The ANNOTATION field is used to +;;; make expression items that can appear in non-expression contexts +;;; (for example, this could be used in the implementation of SETF). + +(define expression-item-type + (make-record-type "expression-item" '(COMPILER ANNOTATION))) + +(define make-expression-item + (record-constructor expression-item-type '(COMPILER ANNOTATION))) + +(define expression-item? + (record-predicate expression-item-type)) + +(define expression-item/compiler + (record-accessor expression-item-type 'COMPILER)) + +(define expression-item/annotation + (record-accessor expression-item-type 'ANNOTATION)) + +;;; Body items represent sequences (e.g. BEGIN). + +(define body-item-type + (make-record-type "body-item" '(COMPONENTS))) + +(define make-body-item + (record-constructor body-item-type '(COMPONENTS))) + +(define body-item? + (record-predicate body-item-type)) + +(define body-item/components + (record-accessor body-item-type 'COMPONENTS)) + +;;; Definition items represent definitions, whether top-level or +;;; internal, keyword or variable. + +(define definition-item-type + (make-record-type "definition-item" '(BINDING-THEORY NAME VALUE))) + +(define make-definition-item + (record-constructor definition-item-type '(BINDING-THEORY NAME VALUE))) + +(define definition-item? + (record-predicate definition-item-type)) + +(define definition-item/binding-theory + (record-accessor definition-item-type 'BINDING-THEORY)) + +(define definition-item/name + (record-accessor definition-item-type 'NAME)) + +(define definition-item/value + (record-accessor definition-item-type 'VALUE)) + +(define (bind-definition-item! environment item) + ((definition-item/binding-theory item) + environment + (definition-item/name item) + (promise:force (definition-item/value item)))) + +(define (syntactic-binding-theory environment name item) + (if (or (keyword-item? item) + (variable-item? item)) + (begin + (syntactic-environment/define! environment name item) + #f) + (syntax-error "syntactic binding value must be a keyword or a variable" + item))) + +(define (variable-binding-theory environment name item) + ;; If ITEM isn't a valid expression, an error will be signalled by + ;; COMPILE-ITEM/EXPRESSION later. + (cons (bind-variable! environment name) item)) + +(define (overloaded-binding-theory environment name item) + (if (keyword-item? item) + (begin + (syntactic-environment/define! environment name item) + #f) + (cons (bind-variable! environment name) item))) + +;;;; Classifiers, Compilers, Expanders + +(define (sc-expander->classifier expander keyword-environment) + (lambda (form environment definition-environment) + (classify/form (expander form environment) + keyword-environment + definition-environment))) + +(define (er-expander->classifier expander keyword-environment) + (sc-expander->classifier (er->sc-expander expander) keyword-environment)) + +(define (er->sc-expander expander) + (lambda (form environment) + (capture-syntactic-environment + (lambda (keyword-environment) + (make-syntactic-closure + environment '() + (expander form + (let ((renames '())) + (lambda (identifier) + (let ((association (assq identifier renames))) + (if association + (cdr association) + (let ((rename + (make-syntactic-closure + keyword-environment + '() + identifier))) + (set! renames + (cons (cons identifier rename) + renames)) + rename))))) + (lambda (x y) + (identifier=? environment x + environment y)))))))) + +(define (classifier->keyword classifier) + (make-syntactic-closure + (let ((environment + (internal-syntactic-environment null-syntactic-environment))) + (syntactic-environment/define! environment + 'KEYWORD + (make-keyword-item classifier "c->k")) + environment) + '() + 'KEYWORD)) + +(define (compiler->keyword compiler) + (classifier->keyword (compiler->classifier compiler))) + +(define (classifier->form classifier) + `(,(classifier->keyword classifier))) + +(define (compiler->form compiler) + (classifier->form (compiler->classifier compiler))) + +(define (compiler->classifier compiler) + (lambda (form environment definition-environment) + definition-environment ;ignore + (make-expression-item + (lambda () (compiler form environment)) form))) + +;;;; Macrologies +;;; A macrology is a procedure that accepts a syntactic environment +;;; as an argument, producing a new syntactic environment that is an +;;; extension of the argument. + +(define (make-primitive-macrology generate-definitions) + (lambda (base-environment) + (let ((environment (top-level-syntactic-environment base-environment))) + (let ((define-classifier + (lambda (keyword classifier) + (syntactic-environment/define! + environment + keyword + (make-keyword-item classifier keyword))))) + (generate-definitions + define-classifier + (lambda (keyword compiler) + (define-classifier keyword (compiler->classifier compiler))))) + environment))) + +(define (make-expander-macrology object->classifier generate-definitions) + (lambda (base-environment) + (let ((environment (top-level-syntactic-environment base-environment))) + (generate-definitions + (lambda (keyword object) + (syntactic-environment/define! + environment + keyword + (make-keyword-item (object->classifier object environment) keyword))) + base-environment) + environment))) + +(define (make-sc-expander-macrology generate-definitions) + (make-expander-macrology sc-expander->classifier generate-definitions)) + +(define (make-er-expander-macrology generate-definitions) + (make-expander-macrology er-expander->classifier generate-definitions)) + +(define (compose-macrologies . macrologies) + (lambda (environment) + (do ((macrologies macrologies (cdr macrologies)) + (environment environment ((car macrologies) environment))) + ((null? macrologies) environment)))) + +;;;; Utilities + +(define (bind-variable! environment name) + (let ((rename (syntactic-environment/rename environment name))) + (syntactic-environment/define! environment + name + (make-variable-item rename)) + rename)) + +(define (reserve-names! names environment) + (let ((item (make-reserved-name-item))) + (for-each (lambda (name) + (syntactic-environment/define! environment name item)) + names))) + +(define (capture-syntactic-environment expander) + (classifier->form + (lambda (form environment definition-environment) + form ;ignore + (classify/form (expander environment) + environment + definition-environment)))) + +(define (unspecific-expression) + (compiler->form + (lambda (form environment) + form environment ;ignore + (output/unspecific)))) + +(define (unassigned-expression) + (compiler->form + (lambda (form environment) + form environment ;ignore + (output/unassigned)))) + +(define (syntax-quote expression) + `(,(compiler->keyword + (lambda (form environment) + environment ;ignore + (syntax-check '(KEYWORD DATUM) form) + (output/literal-quoted (cadr form)))) + ,expression)) + +(define (flatten-body-items items) + (append-map item->list items)) + +(define (item->list item) + (if (body-item? item) + (flatten-body-items (body-item/components item)) + (list item))) + +(define (output/let names values body) + (if (null? names) + body + (output/combination (output/lambda names body) values))) + +(define (output/letrec names values body) + (if (null? names) + body + (output/let + names + (map (lambda (name) name (output/unassigned)) names) + (output/sequence + (list (if (null? (cdr names)) + (output/assignment (car names) (car values)) + (let ((temps (map (make-name-generator) names))) + (output/let + temps + values + (output/sequence + (map output/assignment names temps))))) + body))))) + +(define (output/top-level-sequence expressions) + (if (null? expressions) + (output/unspecific) + (output/sequence expressions))) |