git.fiddlerwoaroof.com
prec/scope.scm
4e987026
 ;;; 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)))