;;; scope.scm -- variable scoping and precedence parsing phase ;;; ;;; author : John & Sandra ;;; date : 11 Feb 1992 ;;; ;;; ;;;=================================================================== ;;; Basic support ;;;=================================================================== (define (scope-modules modules) (walk-modules modules (lambda () (setf (module-decls *module*) (scope-ast-decls (module-decls *module*))) (dolist (a (module-annotations *module*)) ;;; This is currently bogus since it assumes only vars are annotated. (when (annotation-decl? a) (dolist (n (annotation-decl-names a)) (let ((v (table-entry *symbol-table* n))) (when (or (eq? v '#f) (not (var? v))) (fatal-error 'unknown-annotation-name "~A: not a var in annotation decl~%" n)) (when (not (eq? (def-module v) *module-name*)) (fatal-error 'non-local-name-in-annotation "~A: not a local var in annotation decl~%" n)) (setf (var-annotations v) (append (var-annotations v) (annotation-decl-annotations a)))))))))) ;;; Define the basic walker and some helper functions. (define-walker scope ast-td-scope-walker) (define (scope-ast-1 x env) ; (call-walker scope x env)) (remember-context x (call-walker scope x env))) (define (scope-ast/list l env) (scope-ast/list-aux l env) l) (define (scope-ast/list-aux l env) (when (not (null? l)) (setf (car l) (scope-ast-1 (car l) env)) (scope-ast/list-aux (cdr l) env))) ;;; This filters out signdecls from decl lists. These declarations are moved ;;; into the var definitions. (define (scope-ast/decl-list l env) (if (null? l) '() (let ((x (scope-ast-1 (car l) env)) (rest (scope-ast/decl-list (cdr l) env))) (if (or (annotation-decls? x) (and (signdecl? x) (not (eq? (module-type *module*) 'interface)))) rest (begin (setf (car l) x) (setf (cdr l) rest) l))))) ;;; This is the main entry point. It is called by the driver ;;; on each top-level decl in the module. (define (scope-ast-decls x) (let ((result (scope-ast/decl-list x '()))) ; (pprint result) ;*** debug result)) ;;; All top-level names are entered in the *symbol-table* hash table. ;;; This is done by the import/export phase of the compiler before ;;; we get here. ;;; The env is a list of a-lists that associates locally-defined names with ;;; their definitions. Each nested a-list corresponds to a "level" or ;;; scope. ;;; *** If many variables are being added in each scope, it might be ;;; *** better to use a table instead of an alist to represent each contour. (define (lookup-name name env) (if (null? env) (lookup-toplevel-name name) (let ((info (assq name (car env)))) (if info (cdr info) (lookup-name name (cdr env)))))) ;;; Some kinds of names (e.g. type definitions) appear only at top-level, ;;; so use this to look for them directly. (define (lookup-toplevel-name name) (or (resolve-toplevel-name name) (begin (signal-undefined-symbol name) *undefined-def*))) ;;; Some kinds of lookups (e.g., matching a signature declaration) ;;; require that the name be defined in the current scope and not ;;; an outer one. Use this function. (define (lookup-local-name name env) (if (null? env) (lookup-toplevel-name name) (let ((info (assq name (car env)))) (if info (cdr info) (begin (signal-undefined-local-symbol name) *undefined-def*))))) ;;; Add local declarations to the environment, returning a new env. ;;; Do not actually walk the local declarations here. (define *scope-info* '()) (define (add-local-declarations decls env) (if (null? decls) env (let ((contour '())) (dolist (d decls) (if (is-type? 'valdef d) (setf contour (add-bindings (collect-pattern-vars (valdef-lhs d)) contour)))) (cons contour env)))) ;;; Similar, but for adding lambda and function argument bindings to the ;;; environment. (define (add-pattern-variables patterns env) (if (null? patterns) env (let ((contour '())) (dolist (p patterns) (setf contour (add-bindings (collect-pattern-vars p) contour))) (cons contour env)))) ;;; Given a list of var-refs, create defs for them and add them to ;;; the local environment. ;;; Also check to see that there are no duplicates. (define (add-bindings var-refs contour) (dolist (v var-refs) (when (eq? (var-ref-var v) *undefined-def*) (let* ((name (var-ref-name v)) (def (create-local-definition name))) (setf (var-ref-var v) def) (if (assq name contour) (signal-multiple-bindings name) (push (cons name def) contour))))) contour) ;;; Error signalling utilities. (define (signal-undefined-local-symbol name) (phase-error 'undefined-local-symbol "The name ~a has no definition in the current scope." name)) (define (signal-multiple-signatures name) (phase-error 'multiple-signatures "There are multiple signatures for the name ~a." name)) (define (signal-multiple-bindings name) (phase-error 'multiple-bindings "The name ~a appears more than once in a function or pattern binding." name)) ;;;=================================================================== ;;; Default traversal methods ;;;=================================================================== (define-local-syntax (make-scope-code slot type) (let ((stype (sd-type slot)) (sname (sd-name slot))) (cond ((and (symbol? stype) (or (eq? stype 'exp) (subtype? stype 'exp))) `(setf (struct-slot ',type ',sname object) (scope-ast-1 (struct-slot ',type ',sname object) env))) ((and (pair? stype) (eq? (car stype) 'list) (symbol? (cadr stype)) (or (eq? (cadr stype) 'exp) (subtype? (cadr stype) 'exp))) `(setf (struct-slot ',type ',sname object) (scope-ast/list (struct-slot ',type ',sname object) env))) (else ; (format '#t "Scope: skipping slot ~A in ~A~%" ; (sd-name slot) ; type) '#f)))) (define-modify-walker-methods scope (guarded-rhs ; exp slots if ; exp slots app ; exp slots integer-const float-const char-const string-const ; no slots list-exp ; (list exp) slot sequence sequence-to sequence-then sequence-then-to ; exp slots section-l section-r ; exp slots omitted-guard overloaded-var-ref ; no slots negate ; no slots sel prim-definition con-number cast ) (object env) make-scope-code) ;;;=================================================================== ;;; valdef-structs ;;;=================================================================== ;;; Signature declarations must appear at the same level as the names ;;; they apply to. There must not be more than one signature declaration ;;; applying to a given name. (define-walker-method scope signdecl (object env) (let ((signature (signdecl-signature object))) (resolve-signature signature) (let ((gtype (ast->gtype (signature-context signature) (signature-type signature)))) (dolist (v (signdecl-vars object)) (when (eq? (var-ref-var v) *undefined-def*) (setf (var-ref-var v) (lookup-local-name (var-ref-name v) env))) (let ((def (var-ref-var v))) (when (not (eq? def *undefined-def*)) ;; The lookup-local-name may fail if there is a program error. ;; In that case, skip this. (if (var-signature def) (signal-multiple-signatures (var-ref-name v)) (setf (var-signature def) gtype)))))) object)) ;;; This attaches annotations to locally defined vars in the same ;;; manner as signdecl annotations. (define-walker-method scope annotation-decls (object env) (let ((anns (annotation-decls-annotations object))) (dolist (a anns) (cond ((annotation-value? a) (recoverable-error 'unknown-annotation "Unknown annotation: ~A" a)) ((annotation-decl? a) (dolist (v (annotation-decl-names a)) (let ((name (lookup-local-name v env))) (when (not (eq? name *undefined-def*)) (setf (var-annotations name) (append (var-annotations name) (annotation-decl-annotations a)))))))))) object) (define-walker-method scope exp-sign (object env) (resolve-signature (exp-sign-signature object)) (setf (exp-sign-exp object) (scope-ast-1 (exp-sign-exp object) env)) object) ;;; By the time we get to walking a valdef, all the variables it ;;; declares have been entered into the environment. All we need to ;;; do is massage the pattern and recursively walk the definitions. (define-walker-method scope valdef (object env) (setf (valdef-module object) *module-name*) (setf (valdef-lhs object) (massage-pattern (valdef-lhs object))) (setf (valdef-definitions object) (scope-ast/list (valdef-definitions object) env)) object) ;;; For a single-fun-def, do the where-decls first, and then walk the ;;; rhs in an env that includes both the where-decls and the args. (define-walker-method scope single-fun-def (object env) (setf env (add-pattern-variables (single-fun-def-args object) env)) (setf env (add-local-declarations (single-fun-def-where-decls object) env)) (setf (single-fun-def-where-decls object) (scope-ast/decl-list (single-fun-def-where-decls object) env)) (setf (single-fun-def-args object) (massage-pattern-list (single-fun-def-args object))) (setf (single-fun-def-rhs-list object) (scope-ast/list (single-fun-def-rhs-list object) env)) object) ;;;=================================================================== ;;; exp-structs ;;;=================================================================== (define-walker-method scope lambda (object env) (setf env (add-pattern-variables (lambda-pats object) env)) (setf (lambda-pats object) (massage-pattern-list (lambda-pats object))) (setf (lambda-body object) (scope-ast-1 (lambda-body object) env)) object) (define-walker-method scope let (object env) (setf env (add-local-declarations (let-decls object) env)) (setf (let-decls object) (scope-ast/decl-list (let-decls object) env)) (setf (let-body object) (scope-ast-1 (let-body object) env)) object) ;;; Case alts are treated very much like single-fun-defs. (define-walker-method scope case (object env) (setf (case-exp object) (scope-ast-1 (case-exp object) env)) (dolist (a (case-alts object)) (let ((env (add-pattern-variables (list (alt-pat a)) env))) (setf env (add-local-declarations (alt-where-decls a) env)) (setf (alt-where-decls a) (scope-ast/decl-list (alt-where-decls a) env)) (setf (alt-pat a) (massage-pattern (alt-pat a))) (setf (alt-rhs-list a) (scope-ast/list (alt-rhs-list a) env)))) object) (define-walker-method scope var-ref (object env) (when (eq? (var-ref-var object) *undefined-def*) (setf (var-ref-var object) (lookup-name (var-ref-name object) env))) object) (define-walker-method scope con-ref (object env) (declare (ignore env)) (when (eq? (con-ref-con object) *undefined-def*) (setf (con-ref-con object) (lookup-toplevel-name (con-ref-name object)))) object) (define-walker-method scope list-comp (object env) (dolist (q (list-comp-quals object)) (cond ((is-type? 'qual-generator q) (setf (qual-generator-exp q) (scope-ast-1 (qual-generator-exp q) env)) (setf env (add-pattern-variables (list (qual-generator-pat q)) env)) (setf (qual-generator-pat q) (massage-pattern (qual-generator-pat q)))) ((is-type? 'qual-filter q) (setf (qual-filter-exp q) (scope-ast-1 (qual-filter-exp q) env))))) (setf (list-comp-exp object) (scope-ast-1 (list-comp-exp object) env)) object) (define-walker-method scope pp-exp-list (object env) (massage-pp-exp-list (scope-ast/list (pp-exp-list-exps object) env)))