[comp.sources.unix] v10i083: Common Objects, Common Loops, Common Lisp, Part09/13

rs@uunet.UU.NET (Rich Salz) (08/03/87)

Submitted-by: Roy D'Souza <dsouza%hplabsc@hplabs.HP.COM>
Posting-number: Volume 10, Issue 83
Archive-name: comobj.lisp/Part09

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 9 (of 13)."
# Contents:  walk.l
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'walk.l' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'walk.l'\"
else
echo shar: Extracting \"'walk.l'\" \(33372 characters\)
sed "s/^X//" >'walk.l' <<'END_OF_FILE'
X;;;-*- Mode:LISP; Package:(WALKER LISP 1000); Base:10; Syntax:Common-lisp -*-
X;;;
X;;; *************************************************************************
X;;; Copyright (c) 1985 Xerox Corporation.  All rights reserved.
X;;;
X;;; Use and copying of this software and preparation of derivative works
X;;; based upon this software are permitted.  Any distribution of this
X;;; software or derivative works must comply with all applicable United
X;;; States export control laws.
X;;; 
X;;; This software is made available AS IS, and Xerox Corporation makes no
X;;; warranty about the software, its performance or its conformity to any
X;;; specification.
X;;; 
X;;; Any person obtaining a copy of this software is requested to send their
X;;; name and post office or electronic mail address to:
X;;;   CommonLoops Coordinator
X;;;   Xerox Artifical Intelligence Systems
X;;;   2400 Hanover St.
X;;;   Palo Alto, CA 94303
X;;; (or send Arpanet mail to CommonLoops-Coordinator.pa@Xerox.arpa)
X;;;
X;;; Suggestions, comments and requests for improvements are also welcome.
X;;; *************************************************************************
X;;; 
X;;; A simple code walker, based IN PART on: (roll the credits)
X;;;   Larry Masinter's Masterscope
X;;;   Moon's Common Lisp code walker
X;;;   Gary Drescher's code walker
X;;;   Larry Masinter's simple code walker
X;;;   .
X;;;   .
X;;;   boy, thats fair (I hope).
X;;;
X;;; For now at least, this code walker really only does what PCL needs it to
X;;; do.  Maybe it will grow up someday.
X;;;
X
X(in-package 'walker)
X
X(export '(define-walker-template
X	  walk-form
X	  variable-lexical-p
X	  variable-special-p
X	  ))
X
X;;; *walk-function* is the function being called on each sub-form as we walk.
X;;; Normally it is supplied using the :walk-function keyword argument to
X;;; walk-form, but it is OK to bind it around a call to walk-form-internal.
X(defvar *walk-function*)
X
X;;; *walk-form* is used by the IF template.  When the first argument to the
X;;; if template is a list it will be evaluated with *walk-form* bound to the 
X;;; form currently being walked.
X(defvar *walk-form*)
X
X;;; *declarations* is a list of the declarations currently in effect.
X(defvar *declarations*)
X	
X;;; *lexical-variables* is a list of the variables bound in the current
X;;; contour. In *lexical-variables* the cons whose car is the variable is
X;;; meaningful in the sense that the cons whose car is the variable can be
X;;; used to keep track of which contour the variable is bound in.
X;;;
X;;; Now isn't that just the cats pajamas.
X;;;
X(defvar *lexical-variables*)
X
X;;; An environment of the kind that macroexpand-1 gets as its second
X;;; argument.  In fact, that is exactly where it comes from.  This is kind of
X;;; kludgy since Common Lisp is somewhat screwed up in this respect.
X;;; Hopefully Common Lisp will fix this soon.  For more info see:
X;;; MAKE-LEXICAL-ENVIRONMENT
X(defvar *environment*)
X
X;;;
X;;; With new contour is used to enter a new lexical binding contour which
X;;; inherits from the exisiting one.  I admit that using with-new-contour is
X;;; often overkill.  It would suffice for the the walker to rebind
X;;; *lexical-variables* and *declarations* when walking LET and rebind
X;;; *environment* and *declarations* when walking MACROLET etc.
X;;; WITH-NEW-CONTOUR is much more convenient and just as correct.
X;;; 
X(defmacro with-new-contour (&body body)
X  `(let ((*declarations* ())			;If Common Lisp got an
X						;unspecial declaration
X						;this would need to be
X						;re-worked.
X         (*lexical-variables* *lexical-variables*)
X         (*environment* *environment*))
X     . ,body))
X
X(defmacro note-lexical-binding (thing)
X  `(push ,thing *lexical-variables*))
X
X(defmacro note-declaration (declaration)
X  `(push ,declaration *declarations*))
X
X
X(defun variable-lexically-boundp (var)
X  (if (not (boundp '*walk-function*))
X      :unsure
X      (values (member var *lexical-variables* :test (function eq))
X	      (variable-special-p var) 't)))
X
X(defun variable-lexical-p (var)
X  (if (not (boundp '*walk-function*))
X      :unsure
X      (and (not (eq (variable-special-p var) 't))
X	   (member var *lexical-variables* :test (function eq)))))
X
X(defun variable-special-p (var)
X  (if (not (boundp '*walk-function*))
X      (or (variable-globally-special-p var) :unsure)
X      (or (dolist (decl *declarations*)
X	    (and (eq (car decl) 'special)
X		 (member var (cdr decl) :test #'eq)
X		 (return t)))
X	  (variable-globally-special-p var))))
X
X;;;
X;;; VARIABLE-GLOBALLY-SPECIAL-P is used to ask if a variable has been
X;;; declared globally special.  Any particular CommonLisp implementation
X;;; should customize this function accordingly and send their customization
X;;; back.
X;;;
X;;; The default version of variable-globally-special-p is probably pretty
X;;; slow, so it uses *globally-special-variables* as a cache to remember
X;;; variables that it has already figured out are globally special.
X;;;
X;;; This would need to be reworked if an unspecial declaration got added to
X;;; Common Lisp.
X;;;
X;;; Common Lisp nit:
X;;;   variable-globally-special-p should be defined in Common Lisp.
X;;;
X#-(or Symbolics Xerox TI VaxLisp KCL LMI excl)
X(defvar *globally-special-variables* ())
X
X(defun variable-globally-special-p (symbol)
X  #+Symbolics                   (si:special-variable-p symbol)
X  #+(or Lucid TI LMI)           (get symbol 'special)
X  #+Xerox                       (il:variable-globally-special-p symbol)
X  #+VaxLisp                     (get symbol 'system::globally-special)
X  #+KCL			        (si:specialp symbol)
X  #+excl                        (get symbol 'excl::.globally-special.)
X  #+HP                          (member (get symbol 'impl:vartype)
X					'(impl:fluid impl:global)
X					:test #'eq)
X  #-(or Symbolics Lucid TI LMI Xerox VaxLisp KCL excl HP)
X  (or (not (null (member symbol *globally-special-variables* :test #'eq)))
X      (when (eval `(flet ((ref () ,symbol))
X		     (let ((,symbol '#,(list nil)))
X		       (and (boundp ',symbol) (eq ,symbol (ref))))))
X	(push symbol *globally-special-variables*)
X	t)))
X
X
X  ;;   
X;;;;;; Handling of special forms (the infamous 24).
X  ;;
X;;;
X;;; and I quote...
X;;; 
X;;;     The set of special forms is purposely kept very small because
X;;;     any program analyzing program (read code walker) must have
X;;;     special knowledge about every type of special form. Such a
X;;;     program needs no special knowledge about macros...
X;;;
X;;; So all we have to do here is a define a way to store and retrieve
X;;; templates which describe how to walk the 24 special forms and we are all
X;;; set...
X;;;
X;;; Well, its a nice concept, and I have to admit to being naive enough that
X;;; I believed it for a while, but not everyone takes having only 24 special
X;;; forms as seriously as might be nice.  There are (at least) 3 ways to
X;;; lose:
X;;
X;;;   1 - Implementation x implements a Common Lisp special form as a macro
X;;;       which expands into a special form which:
X;;;         - Is a common lisp special form (not likely)
X;;;         - Is not a common lisp special form (on the 3600 IF --> COND).
X;;;
X;;;     * We can safe ourselves from this case (second subcase really) by
X;;;       checking to see if there is a template defined for something
X;;;       before we check to see if we we can macroexpand it.
X;;;
X;;;   2 - Implementation x implements a Common Lisp macro as a special form.
X;;;
X;;;     * This is a screw, but not so bad, we save ourselves from it by
X;;;       defining extra templates for the macros which are *likely* to
X;;;       be implemented as special forms.  (DO, DO* ...)
X;;;
X;;;   3 - Implementation x has a special form which is not on the list of
X;;;       Common Lisp special forms.
X;;;
X;;;     * This is a bad sort of a screw and happens more than I would like
X;;;       to think, especially in the implementations which provide more
X;;;       than just Common Lisp (3600, Xerox etc.).
X;;;       The fix is not terribly staisfactory, but will have to do for
X;;;       now.  There is a hook in get walker-template which can get a
X;;;       template from the implementation's own walker.  That template
X;;;       has to be converted, and so it may be that the right way to do
X;;;       this would actually be for that implementation to provide an
X;;;       interface to its walker which looks like the interface to this
X;;;       walker.
X;;;
X(defmacro get-walker-template-internal (x)
X  `(get ,x 'walker-template))
X
X(defun get-walker-template (x)
X  (cond ((symbolp x)
X	 (or (get-walker-template-internal x)
X	     (get-implementation-dependent-walker-template x)))
X	((and (listp x) (eq (car x) 'lambda))
X	 '(lambda repeat (eval)))
X	((and (listp x) (eq (car x) 'lambda))
X	 '(call repeat (eval)))))
X
X(defun get-implementation-dependent-walker-template (x)
X  (declare (ignore x))
X  ())
X
X(eval-when (compile load eval)
X(defmacro define-walker-template (name template)
X  `(eval-when (load eval)
X     (setf (get-walker-template-internal ',name) ',template)))
X)
X
X
X  ;;   
X;;;;;; The actual templates
X  ;;   
X
X(define-walker-template BLOCK                (NIL NIL REPEAT (EVAL)))
X(define-walker-template CATCH                (NIL EVAL REPEAT (EVAL)))
X(define-walker-template COMPILER-LET         walk-compiler-let)
X(define-walker-template DECLARE              walk-unexpected-declare)
X(define-walker-template EVAL-WHEN            (NIL QUOTE REPEAT (EVAL)))
X(define-walker-template FLET                 walk-flet/labels)
X(define-walker-template FUNCTION             (NIL CALL))
X(define-walker-template GO                   (NIL QUOTE))
X(define-walker-template IF                   (NIL TEST RETURN RETURN))
X(define-walker-template LABELS               walk-flet/labels)
X(define-walker-template LAMBDA               walk-lambda)
X(define-walker-template LET                  walk-let)
X(define-walker-template LET*                 walk-let*)
X(define-walker-template MACROLET             walk-macrolet)
X(define-walker-template MULTIPLE-VALUE-CALL  (NIL EVAL REPEAT (EVAL)))
X(define-walker-template MULTIPLE-VALUE-PROG1 (NIL RETURN REPEAT (EVAL)))
X(define-walker-template MULTIPLE-VALUE-SETQ  (NIL (REPEAT (SET)) EVAL))
X(define-walker-template PROGN                (NIL REPEAT (EVAL)))
X(define-walker-template PROGV                (NIL EVAL EVAL REPEAT (EVAL)))
X(define-walker-template QUOTE                (NIL QUOTE))
X(define-walker-template RETURN-FROM          (NIL QUOTE REPEAT (RETURN)))
X(define-walker-template SETQ                 (NIL REPEAT (SET EVAL)))
X(define-walker-template TAGBODY              walk-tagbody)
X(define-walker-template THE                  (NIL QUOTE EVAL))
X(define-walker-template THROW                (NIL EVAL EVAL))
X(define-walker-template UNWIND-PROTECT       (NIL RETURN REPEAT (EVAL)))
X
X;;; The new special form.
X;(define-walker-template pcl::LOAD-TIME-EVAL       (NIL EVAL))
X
X;;;
X;;; And the extra templates...
X;;;
X(define-walker-template DO      walk-do)
X(define-walker-template DO*     walk-do*)
X(define-walker-template PROG    walk-let)
X(define-walker-template PROG*   walk-let*)
X(define-walker-template COND    (NIL REPEAT ((TEST REPEAT (EVAL)))))
X
X
X  ;;   
X;;;;;; WALK-FORM
X  ;;   
X;;;
X;;; The main entry-point is walk-form, calls back in should use walk-form-internal.
X;;; 
X
X(defun walk-form (form &key ((:declarations *declarations*) ())
X			    ((:lexical-variables *lexical-variables*) ())
X			    ((:environment *environment*) ())
X			    ((:walk-function *walk-function*) #'(lambda (x y)
X								  y x)))
X  (walk-form-internal form 'eval))
X
X;;;
X;;; WALK-FORM-INTERNAL is the main driving function for the code walker. It
X;;; takes a form and the current context and walks the form calling itself or
X;;; the appropriate template recursively.
X;;;
X;;;   "It is recommended that a program-analyzing-program process a form
X;;;    that is a list whose car is a symbol as follows:
X;;;
X;;;     1. If the program has particular knowledge about the symbol,
X;;;        process the form using special-purpose code.  All of the
X;;;        standard special forms should fall into this category.
X;;;     2. Otherwise, if macro-function is true of the symbol apply
X;;;        either macroexpand or macroexpand-1 and start over.
X;;;     3. Otherwise, assume it is a function call. "
X;;;     
X
X(defun walk-form-internal (form context
X			   &aux newform newnewform
X				walk-no-more-p macrop
X				fn template)
X  ;; First apply the *walk-function* to perform whatever translation
X  ;; the user wants to to this form.  If the second value returned
X  ;; by *walk-function* is T then we don't recurse...
X  (multiple-value-setq (newform walk-no-more-p)
X    (funcall *walk-function* form context))
X  (cond (walk-no-more-p newform)
X	((not (eq form newform)) (walk-form-internal newform context))
X	((not (consp newform)) newform)
X	((setq template (get-walker-template (setq fn (car newform))))
X         (if (symbolp template)
X             (funcall template newform context)
X             (walk-template newform template context)))
X	((progn (multiple-value-setq (newnewform macrop)
X		  (macroexpand-1 newform *environment*))
X		macrop)
X	 (walk-form-internal newnewform context))
X	((and (symbolp fn)
X	      (not (fboundp fn))
X	      (special-form-p fn))
X	 (error
X	   "~S is a special form, not defined in the CommonLisp manual.~%~
X            This code walker doesn't know how to walk it.  Please define a~%~
X            template for this special form and try again."
X	   fn))
X	(t
X         ;; Otherwise, walk the form as if its just a standard function
X         ;; call using a template for standard function call.
X         (walk-template newform '(call repeat (eval)) context))))
X
X(defun walk-template (form template context)
X  (if (atom template)
X      (ecase template
X        ((QUOTE NIL) form)
X        ((EVAL FUNCTION TEST EFFECT RETURN)
X         (walk-form-internal form :EVAL))
X        (SET
X          (walk-form-internal form :SET))
X        ((LAMBDA CALL)
X	 (if (symbolp form)
X	     form
X	     (walk-lambda form context))))
X      (case (car template)
X        (IF
X          (let ((*walk-form* form))
X            (walk-template form
X			   (if (if (listp (cadr template))
X				   (eval (cadr template))
X				   (funcall (cadr template) form))
X			       (caddr template)
X			       (cadddr template))
X			   context)))
X        (REPEAT
X          (walk-template-handle-repeat form
X                                       (cdr template)
X				       ;; For the case where nothing happens
X				       ;; after the repeat optimize out the
X				       ;; call to length.
X				       (if (null (cddr template))
X					   ()
X					   (nthcdr (- (length form)
X						      (length
X							(cddr template)))
X						   form))
X                                       context))
X        (REMOTE
X          (walk-template form (cadr template) context))
X        (otherwise
X          (cond ((atom form) form)
X                (t (recons form
X                           (walk-template
X			     (car form) (car template) context)
X                           (walk-template
X			     (cdr form) (cdr template) context))))))))
X
X(defun walk-template-handle-repeat (form template stop-form context)
X  (if (eq form stop-form)
X      (walk-template form (cdr template) context)
X      (walk-template-handle-repeat-1 form
X				     template
X				     (car template)
X				     stop-form
X				     context)))
X
X(defun walk-template-handle-repeat-1 (form template repeat-template
X					   stop-form context)
X  (cond ((null form) ())
X        ((eq form stop-form)
X         (if (null repeat-template)
X             (walk-template stop-form (cdr template) context)       
X             (error "While handling repeat:
X                     ~%~Ran into stop while still in repeat template.")))
X        ((null repeat-template)
X         (walk-template-handle-repeat-1
X	   form template (car template) stop-form context))
X        (t
X         (recons form
X                 (walk-template (car form) (car repeat-template) context)
X                 (walk-template-handle-repeat-1 (cdr form)
X						template
X						(cdr repeat-template)
X						stop-form
X						context)))))
X
X(defun recons (x car cdr)
X  (if (or (not (eq (car x) car))
X          (not (eq (cdr x) cdr)))
X      (cons car cdr)
X      x))
X
X(defun relist* (x &rest args)
X  (relist*-internal x args))
X
X(defun relist*-internal (x args)
X  (if (null (cdr args))
X      (car args)
X      (recons x (car args) (relist*-internal (cdr x) (cdr args)))))
X
X
X  ;;   
X;;;;;; Special walkers
X  ;;
X
X(defun walk-declarations (body fn
X			       &optional doc-string-p declarations old-body
X			       &aux (form (car body)))
X  (cond ((and (stringp form)			;might be a doc string
X              (cdr body)			;isn't the returned value
X              (null doc-string-p)		;no doc string yet
X              (null declarations))		;no declarations yet
X         (recons body
X                 form
X                 (walk-declarations (cdr body) fn t)))
X        ((and (listp form) (eq (car form) 'declare))
X         ;; Got ourselves a real live declaration.  Record it, look for more.
X         (dolist (declaration (cdr form))
X           (note-declaration declaration)
X           (push declaration declarations))
X         (recons body
X                 form
X                 (walk-declarations
X		   (cdr body) fn doc-string-p declarations)))
X        ((and form
X	      (listp form)
X	      (null (get-walker-template (car form)))
X	      (not (eq form (setq form (macroexpand-1 form *environment*)))))
X         ;; When we macroexpanded this form we got something else back.
X         ;; Maybe this is a macro which expanded into a declare?
X	 ;; Recurse to find out.
X         (walk-declarations
X	   (cons form (cdr body)) fn doc-string-p declarations (or old-body
X								   body)))
X        (t
X         ;; Now that we have walked and recorded the declarations, call the
X	 ;; function our caller provided to expand the body.  We call that
X	 ;; function rather than passing the real-body back, because we are
X	 ;; RECONSING up the new body.
X         (funcall fn (or old-body body)))))
X
X(defun fix-lucid-1.2 (x) x)
X
X(defun walk-unexpected-declare (form context)
X  (declare (ignore context))
X  (warn "Encountered declare ~S in a place where a declare was not expected."
X	form)
X  form)
X
X(defun walk-arglist (arglist context &optional (destructuringp nil) &aux arg)
X  (cond ((null arglist) ())
X        ((symbolp (setq arg (car arglist)))
X         (or (member arg lambda-list-keywords :test #'eq)
X             (note-lexical-binding arg))
X         (recons arglist
X                 arg
X                 (walk-arglist (cdr arglist)
X                               context
X                               (and destructuringp
X				    (not (member arg lambda-list-keywords
X						 :test #'eq))))))
X        ((consp arg)
X         (prog1 (if destructuringp
X                    (walk-arglist arg context destructuringp)
X                    (recons arglist
X                            (relist* arg
X                                     (car arg)
X                                     (walk-form-internal (cadr arg) 'eval)
X                                     (cddr arg))
X                            (walk-arglist (cdr arglist) context nil)))
X                (if (symbolp (car arg))
X                    (note-lexical-binding (car arg))
X                    (note-lexical-binding (cadar arg)))
X                (or (null (cddr arg))
X                    (not (symbolp (caddr arg)))
X                    (note-lexical-binding arg))))
X          (t
X	   (error "Can't understand something in the arglist ~S" arglist))))
X
X(defun walk-let (form context)
X  (walk-let/let* form context nil))
X
X(defun walk-let* (form context)
X  (walk-let/let* form context t))
X
X(defun walk-do (form context)
X  (walk-do/do* form context nil))
X
X(defun walk-do* (form context)
X  (walk-do/do* form context t))
X
X(defun walk-let/let* (form context sequentialp)
X  (let ((old-declarations *declarations*)
X	(old-lexical-variables *lexical-variables*))
X    (with-new-contour
X      (let* ((let/let* (car form))
X             (bindings (cadr form))
X             (body (cddr form))
X             walked-bindings
X             (walked-body
X               (walk-declarations 
X                 body
X                 #'(lambda (real-body)
X                     (setq walked-bindings
X                           (walk-bindings-1 bindings
X					    old-declarations
X					    old-lexical-variables
X					    context
X					    sequentialp))
X                     (walk-template real-body '(repeat (eval)) context)))))
X        (relist*
X	  form let/let* (fix-lucid-1.2 walked-bindings) walked-body)))))
X
X(defun walk-do/do* (form context sequentialp)
X  (let ((old-declarations *declarations*)
X	(old-lexical-variables *lexical-variables*))
X    (with-new-contour
X      (let* ((do/do* (car form))
X             (bindings (cadr form))
X             (end-test (caddr form))
X             (body (cdddr form))
X             walked-bindings
X             (walked-body
X               (walk-declarations
X                 body
X                 #'(lambda (real-body)
X                     (setq walked-bindings
X                           (walk-bindings-1 bindings
X					    old-declarations
X					    old-lexical-variables
X					    context
X					    sequentialp))
X                     (walk-template real-body '(repeat (eval)) context)))))
X        (relist* form
X                 do/do*
X                 (walk-bindings-2 bindings walked-bindings context)
X                 (walk-template end-test '(test repeat (eval)) context)
X                 walked-body)))))
X                            
X(defun walk-bindings-1 (bindings old-declarations old-lexical-variables
X				 context sequentialp)
X  (and bindings
X       (let ((binding (car bindings)))
X         (recons bindings
X                 (if (symbolp binding)
X                     (prog1 binding
X                            (note-lexical-binding binding))
X                     (prog1 (let ((*declarations* old-declarations)
X				  (*lexical-variables*
X				    (if sequentialp
X					*lexical-variables*
X					old-lexical-variables)))
X                              (relist* binding
X                                       (car binding)
X                                       (walk-form-internal (cadr binding)
X							   context)
X                                       (cddr binding)))	;save cddr for DO/DO*
X						        ;it is the next value
X						        ;form. Don't walk it
X						        ;now though.
X                            (note-lexical-binding (car binding))))
X                 (walk-bindings-1 (cdr bindings)
X				  old-declarations old-lexical-variables
X				  context sequentialp)))))
X
X(defun walk-bindings-2 (bindings walked-bindings context)
X  (and bindings
X       (let ((binding (car bindings))
X             (walked-binding (car walked-bindings)))
X         (recons bindings
X		 (if (symbolp binding)
X		     binding
X		     (relist* binding
X			      (car walked-binding)
X			      (cadr walked-binding)
X			      (walk-template (cddr binding) '(eval) context)))		 
X                 (walk-bindings-2 (cdr bindings)
X				  (cdr walked-bindings)
X				  context)))))
X
X(defun walk-lambda (form context)
X  (with-new-contour    
X    (let* ((arglist (cadr form))
X           (body (cddr form))
X           (walked-arglist nil)
X           (walked-body
X             (walk-declarations body
X	       #'(lambda (real-body)
X		   (setq walked-arglist (walk-arglist arglist context))
X		   (walk-template real-body '(repeat (eval)) context)))))
X      (relist* form
X               (car form)
X               (fix-lucid-1.2 walked-arglist)
X               walked-body))))
X
X(defun walk-tagbody (form context)
X  (recons form (car form) (walk-tagbody-1 (cdr form) context)))
X
X(defun walk-tagbody-1 (form context)
X  (and form
X       (recons form
X               (walk-form-internal (car form)
X				   (if (symbolp (car form)) 'quote context))
X               (walk-tagbody-1 (cdr form) context))))
X
X(defun walk-compiler-let (form context)
X  (with-new-contour
X    (let ((vars ())
X	  (vals ()))
X      (dolist (binding (cadr form))
X	(cond ((symbolp binding) (push binding vars) (push nil vals))
X	      (t
X	       (push (car binding) vars)
X	       (push (eval (cadr binding)) vals))))
X      (relist* form
X               (car form)
X               (cadr form)
X               (progv vars vals
X                 (note-declaration (cons 'special vars))
X                 (walk-template (cddr form) '(repeat (eval)) context))))))
X
X(defun walk-macrolet (form context)
X  (labels ((walk-definitions (definitions)
X             (and (not (null definitions))
X                  (let ((definition (car definitions)))
X                    (recons definitions
X                            (with-new-contour
X                              (relist* definition
X                                       (car definition)
X                                       (walk-arglist (cadr definition)
X						     context t)
X                                       (walk-declarations (cddr definition)
X					 #'(lambda (real-body)
X					     (walk-template
X					       real-body
X					       '(repeat (eval))
X					       context)))))
X                            (walk-definitions (cdr definitions)))))))
X    (with-new-contour
X      (relist* form
X               (car form)
X               (walk-definitions (cadr form))
X               (progn (setq *environment*
X			    (make-lexical-environment form *environment*))
X                      (walk-declarations (cddr form)
X			#'(lambda (real-body)
X			    (walk-template real-body
X						    '(repeat (eval))
X						    context))))))))
X
X(defun walk-flet/labels (form context)
X  (with-new-contour
X    (labels ((walk-definitions (definitions)
X               (if (null definitions)
X                   ()
X                   (recons definitions
X                           (walk-lambda (car definitions) context)
X                           (walk-definitions (cdr definitions)))))
X             (update-environment ()
X               (setq *environment*
X		     (make-lexical-environment form *environment*))))
X      (relist* form
X               (car form)
X               (ecase (car form)
X                 (flet
X                   (prog1 (walk-definitions (cadr form))
X                          (update-environment)))
X                 (labels
X                   (update-environment)
X                   (walk-definitions (cadr form))))
X               (walk-declarations (cddr form)
X		 #'(lambda (real-body)
X		     (walk-template real-body '(repeat (eval)) context)))))))
X
X;;; make-lexical-environemnt is kind of gross.  It would be less gross if
X;;; EVAL took an environment argument.
X;;;
X;;; Common Lisp nit:
X;;;    if Common Lisp should provide mechanisms for playing with
X;;;    environments explicitly.  making them, finding out what
X;;;    functions are bound in them etc.  Maybe compile should
X;;;    take an environment argument too?
X;;;    
X
X(defun make-lexical-environment (macrolet/flet/labels-form environment)
X  (evalhook (list (car macrolet/flet/labels-form)
X                  (cadr macrolet/flet/labels-form)
X                  (list 'make-lexical-environment-2))
X            'make-lexical-environment-1
X            ()
X            environment))
X
X(defun make-lexical-environment-1 (form env)
X  (setq form (macroexpand form #-excl env
X			       #+excl (cadr env)))
X  (evalhook form  'make-lexical-environment-1 nil env))
X
X(defmacro make-lexical-environment-2 (&environment env)
X  (list 'quote (copy-tree env)))
X
X  ;;   
X;;;;;; Tests tests tests
X  ;;
X
X#|
X
X(defmacro take-it-out-for-a-test-walk (form)
X  `(progn 
X     (terpri)
X     (terpri)
X     (let ((copy-of-form (copy-tree ',form))
X           (result (walk-form ',form :walk-function
X                              '(lambda (x y)
X                                 (format t "~&Form: ~S ~3T Context: ~A" x y)
X                                 (when (symbolp x)
X				   (multiple-value-bind (lexical special)
X				       (variable-lexically-boundp x)
X                                     (when lexical
X                                       (format t ";~3T")
X                                       (format t "lexically bound"))
X                                     (when special
X                                       (format t ";~3T")
X                                       (format t "declared special"))
X                                     (when (boundp x)
X                                       (format t ";~3T")
X                                       (format t "bound: ~S " (eval x)))))
X                                 x))))
X       (cond ((not (equal result copy-of-form))
X              (format t "~%Warning: Result not EQUAL to copy of start."))
X             ((not (eq result ',form))
X              (format t "~%Warning: Result not EQ to copy of start.")))
X       (#+Symbolics zl:grind-top-level
X        #-Symbolics print
X                                  result)
X       result)))
X
X(defun foo (&rest ignore) ())
X
X(defmacro bar (x) `'(global-bar-expanded ,x))
X
X(defun baz (&rest ignore) ())
X
X(take-it-out-for-a-test-walk (foo arg1 arg2 arg3))
X(take-it-out-for-a-test-walk (foo (baz 1 2) (baz 3 4 5)))
X
X(take-it-out-for-a-test-walk (block block-name a b c))
X(take-it-out-for-a-test-walk (block block-name (foo a) b c))
X
X(take-it-out-for-a-test-walk (catch catch-tag (foo a) b c))
X(take-it-out-for-a-test-walk (compiler-let ((a 1) (b 2)) (foo a) b))
X(take-it-out-for-a-test-walk (prog () (declare (special a b))))
X(take-it-out-for-a-test-walk (let (a b c)
X                               (declare (special a b))
X                               (foo a) b c))
X(take-it-out-for-a-test-walk (let (a b c)
X                               (declare (special a) (special b))
X                               (foo a) b c))
X(take-it-out-for-a-test-walk (let (a b c)
X                               (declare (special a))
X                               (declare (special b))
X                               (foo a) b c))
X(take-it-out-for-a-test-walk (let (a b c)
X                               (declare (special a))
X                               (declare (special b))
X                               (let ((a 1))
X                                 (foo a) b c)))
X(take-it-out-for-a-test-walk (eval-when ()
X                               a
X                               (foo a)))
X(take-it-out-for-a-test-walk (eval-when (eval when load)
X                               a
X                               (foo a)))
X(take-it-out-for-a-test-walk (progn (function foo)))
X(take-it-out-for-a-test-walk (progn a b (go a)))
X(take-it-out-for-a-test-walk (if a b c))
X(take-it-out-for-a-test-walk (if a b))
X(take-it-out-for-a-test-walk ((lambda (a b) (list a b)) 1 2))
X(take-it-out-for-a-test-walk ((lambda (a b) (declare (special a)) (list a b))
X			      1 2))
X(take-it-out-for-a-test-walk (let ((a a) (b a) (c b)) (list a b c)))
X(take-it-out-for-a-test-walk (let* ((a a) (b a) (c b)) (list a b c)))
X(take-it-out-for-a-test-walk (let ((a a) (b a) (c b))
X                               (declare (special a b))
X                               (list a b c)))
X(take-it-out-for-a-test-walk (let* ((a a) (b a) (c b))
X                               (declare (special a b))
X                               (list a b c)))
X(take-it-out-for-a-test-walk (let ((a 1) (b 2))
X                               (foo bar)
X                               (declare (special a))
X                               (foo a b)))
X(take-it-out-for-a-test-walk (multiple-value-call #'foo a b c))
X(take-it-out-for-a-test-walk (multiple-value-prog1 a b c))
X(take-it-out-for-a-test-walk (progn a b c))
X(take-it-out-for-a-test-walk (progv vars vals a b c))
X(take-it-out-for-a-test-walk (quote a))
X(take-it-out-for-a-test-walk (return-from block-name a b c))
X(take-it-out-for-a-test-walk (setq a 1))
X(take-it-out-for-a-test-walk (setq a (foo 1) b (bar 2) c 3))
X(take-it-out-for-a-test-walk (tagbody a b c (go a)))
X(take-it-out-for-a-test-walk (the foo (foo-form a b c)))
X(take-it-out-for-a-test-walk (throw tag-form a))
X(take-it-out-for-a-test-walk (unwind-protect (foo a b) d e f))
X
X
X(take-it-out-for-a-test-walk (flet ((flet-1 (a b) (list a b)))
X                               (flet-1 1 2)
X                               (foo 1 2)))
X(take-it-out-for-a-test-walk (labels ((label-1 (a b) (list a b)))
X                               (label-1 1 2)
X                               (foo 1 2)))
X(take-it-out-for-a-test-walk (macrolet ((macrolet-1 (a b) (list a b)))
X                               (macrolet-1 a b)
X                               (foo 1 2)))
X
X(take-it-out-for-a-test-walk (macrolet ((foo (a) `(inner-foo-expanded ,a)))
X                               (foo 1)))
X
X(take-it-out-for-a-test-walk (progn (bar 1)
X                                    (macrolet ((bar (a)
X						 `(inner-bar-expanded ,a)))
X                                      (bar 1))))
X
X(take-it-out-for-a-test-walk (progn (bar 1)
X                                    (macrolet ((bar (s)
X						 (bar s)
X						 `(inner-bar-expanded ,s)))
X                                      (bar 2))))
X
X(take-it-out-for-a-test-walk (cond (a b)
X                                   ((foo bar) a (foo a))))
X
X
X(let ((the-lexical-variables ()))
X  (walk-form '(let ((a 1) (b 2))
X		#'(lambda (x) (list a b x y)))
X	     :walk-function #'(lambda (form context)
X				(when (and (symbolp form)
X					   (variable-lexical-p form))
X				  (push form the-lexical-variables))
X				form))
X  (or (and (= (length the-lexical-variables) 3)
X	   (member 'a the-lexical-variables)
X	   (member 'b the-lexical-variables)
X	   (member 'x the-lexical-variables))
X      (error "Walker didn't do lexical variables of a closure properly.")))
X
X|#
X
X()
X
END_OF_FILE
if test 33372 -ne `wc -c <'walk.l'`; then
    echo shar: \"'walk.l'\" unpacked with wrong size!
fi
# end of 'walk.l'
fi
echo shar: End of archive 9 \(of 13\).
cp /dev/null ark9isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 13 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 

Rich $alz			"Anger is an energy"
Cronus Project, BBN Labs	rsalz@bbn.com
Moderator, comp.sources.unix	sources@uunet.uu.net