[comp.lang.lisp] Common Lisp Macro Expander Wanted

malcolm@spar.SPAR.SLB.COM (Malcolm Slaney) (04/05/88)

Can anybody point me towards some code that will take any Common Lisp
expression and macroexpand all of the macros.  I would like as output
a s'exp that just has the functions from Steele.  I don't think this is too
hard but if somebody else has already done it I would prefer to reuse the
code.  (Don't special forms make this non trivial???)

I want to use this as part of a tool to record storage usage.  I'll first 
do the necessary macroexpands, then wrap a form around each primitive Common
Lisp form that records the memory usage and then write a tool that will
pretty print the information.

Thanks for the help.

						Malcolm Slaney
						malcolm@spar.slb.com

mao@hpclmao.HP.COM (Mike Ogush) (04/07/88)

/ malcolm@spar.SPAR.SLB.COM (Malcolm Slaney) writes:
>Can anybody point me towards some code that will take any Common Lisp
>expression and macroexpand all of the macros.  I would like as output
>a s'exp that just has the functions from Steele.  I don't think this is too
>hard but if somebody else has already done it I would prefer to reuse the
>code.  (Don't special forms make this non trivial???)
>
>I want to use this as part of a tool to record storage usage.  I'll first 
>do the necessary macroexpands, then wrap a form around each primitive Common
>Lisp form that records the memory usage and then write a tool that will
>pretty print the information.
>
>Thanks for the help.
>
>						Malcolm Slaney
>						malcolm@spar.slb.com
>----------
>

When I was learning Common LISP, I wrote just such a function so I could see
exactly what s-expressions were being expanded into:


(defun fully-expand-macro (exp)
  (if (consp exp)
    (let ((car-exp (car exp)))
      (if (and (symbolp car-exp) (macro-function car-exp))
        (fully-expand-macro (macroexpand exp))
        (cons car-exp
              (mapcar #'fully-expand-macro (cdr exp)))))
    exp)
  )


Hope this helps


    Mike Ogush

    Computer Languages Lab  
    Hewlett-Packard Company

    ...!hplabs!hpda!mao  or mao%hpclmao@hplabs.hp.com

larus@paris.Berkeley.EDU (James Larus) (04/07/88)

How about:

	(defun macroexpand-all (f)
	  (cond ((atom f) f)
		((and (symbolp (car f)) (macro-function (car f)))
		 (macroexpand-all (macroexpand f)))
		(t (mapcar #'macroexpand-all f))))


/Jim
larus@ginger.Berkeley.EDU
ucbvax!larus

skef@SPICE.CS.CMU.EDU (Skef Wholey) (04/08/88)

Jim Larus writes:

    From: larus@paris.Berkeley.EDU (James Larus)
    Subject: Re: Common Lisp Macro Expander Wanted

    How about:

	    (defun macroexpand-all (f)
	      (cond ((atom f) f)
		    ((and (symbolp (car f)) (macro-function (car f)))
		     (macroexpand-all (macroexpand f)))
		    (t (mapcar #'macroexpand-all f))))

This is no good, because it doesn't know the syntax of special forms.
For example, this call causes an error:

  (macroexpand-all '(let ((with-open-file t)) blag))

because it tries to descend blindly into the LET binding list as if it
were a macro call, which it obvously ain't.

It isn't too hard to write a code walker that Does The Right Thing for
special forms in Common Lisp, but unfortunately some implementations
ignore the rules stated in CLtL and make this more difficult than it
ought to be.  Older versions of Symbolics CL were pretty nasty in this
respect, because they had macros expanding into implementation-dependent
special forms and didn't supply macroexpansion functions for all the
things specified as macros in CLtL.  I believe this should have been
fixed by now, but I can't easily check this.

I have this suspicion that dozens of good CL code-walkers have been
written independently, but none have made it into the public domain
because they were written in profit-oriented institutions.  Does anyone
out there have one to donate?

--Skef

gateley@mips.csc.ti.com (John Gateley) (04/10/88)

In article <23539@ucbvax.BERKELEY.EDU> larus@paris.Berkeley.EDU.UUCP writes:
>How about:
>
>	(defun macroexpand-all (f)
>	  (cond ((atom f) f)
>		((and (symbolp (car f)) (macro-function (car f)))
>		 (macroexpand-all (macroexpand f)))
>		(t (mapcar #'macroexpand-all f))))
>
>
>/Jim
>larus@ginger.Berkeley.EDU
>ucbvax!larus

Almost works, but you have to check special forms to make sure that
they are macro expanded only in the proper places, and then there is
always lambda to deal with. (Hint: Dont macro expand the argument list
for lambdas, and the same is true for the variables in a let). I cant find
anything in CLtL which verifies this, but I think this is correct.

girgenso@ifistg.UUCP (04/11/88)

Here are some functions to expand all macros in a lisp expression. I hope
this will help.

Andreas Girgensohn
girgenso%ifistg.uucp%unido.uucp@uunet.uu.net

------------------------------- cut here -------------------------------

(defun macroexpand-all (form &optional env)
  (setq form (macroexpand form env))
  (if (atom form)
      form
      (case (car form)
	((catch if multiple-value-call multiple-value-prog1 progn progv
		setq tagbody throw unwind-protect)
	 (macroexpand-lambda-call form env))
	((declare go quote) form)
	((block eval-when return-from the)
	 (let ((body (macroexpand-body (cddr form) env)))
	   (if (eq body (cddr form)) form (list* (car form) (cadr form) body))))
	((flet labels macrolet) (macroexpand-flet form env))
	(function (let ((func (macroexpand-lambda (cadr form) env)))
		    (if (eq func (cadr form)) form (list (car form) func))))
	((compiler-let let let*) (macroexpand-let form env))
	(t (if (or (consp (car form))
		   (not (special-form-p (car form))))
	       (macroexpand-lambda-call form env)
	       form)))))			; unknown special form

(defun macroexpand-lambda-call (form env)
  (let ((func (macroexpand-lambda (car form) env))
	(args (macroexpand-body (cdr form) env)))
    (if (and (eq func (car form))
	     (eq args (cdr form)))
	form
	(cons func args))))

(defun macroexpand-lambda (func env)
  (if (atom func)
      func
      (let ((arglist (macroexpand-lambda-list (cadr func) env))
	    (body (macroexpand-body (cddr func) env)))
	(if (and (eq arglist (cadr func))
		 (eq body (cddr func)))
	    func
	    (list* (car func) arglist body)))))

(defun macroexpand-lambda-list (list env)
  env
  list)

(defun macroexpand-body (body env)
  (if (atom body)
      body
      (let ((first (macroexpand-all (car body) env))
	    (rest (macroexpand-body (cdr body) env)))
	(if (and (eq first (car body))
		 (eq rest (cdr body)))
	    body
	    (cons first rest)))))

(defun macroexpand-flet (form env)
  (let ((fdefs (macroexpand-flet-definitions (cadr form) env))
	(body (macroexpand-body (cddr form) env)))
    (if (and (eq fdefs (cadr form))
	     (eq body (cddr form)))
	form
	(list* (car form) fdefs body))))

(defun macroexpand-flet-definitions (defs env)
  (if (atom defs)
      defs
      (let ((first (macroexpand-lambda (car defs) env))
	    (rest (macroexpand-flet-definitions (cdr defs) env)))
	(if (and (eq first (car defs))
		 (eq rest (cdr defs)))
	    defs
	    (cons first rest)))))

(defun macroexpand-let (form env)
  (let ((bdgs (macroexpand-bindings (cadr form) env))
	(body (macroexpand-body (cddr form) env)))
    (if (and (eq bdgs (cadr form))
	     (eq body (cddr form)))
	form
	(list* (car form) bdgs body))))

(defun macroexpand-bindings (bdgs env)
  (if (atom bdgs)
      bdgs
      (let ((first (if (atom (car bdgs))
		       (car bdgs)
		       (let ((values (macroexpand-body (cdar bdgs) env)))
			 (if (eq values (cdar bdgs))
			     (car bdgs)
			     (cons (caar bdgs) values)))))
	    (rest (macroexpand-bindings (cdr bdgs) env)))
	(if (and (eq first (car bdgs))
		 (eq rest (cdr bdgs)))
	    bdgs
	    (cons first rest)))))

ilan@lcuxlm.UUCP (04/17/88)

In article <46438@ti-csl.CSNET>, gateley@mips.csc.ti.com (John Gateley) writes:
> 
> Almost works, but you have to check special forms to make sure that
> they are macro expanded only in the proper places, and then there is
> always lambda to deal with. (Hint: Dont macro expand the argument list
> for lambdas, and the same is true for the variables in a let). I cant find
> anything in CLtL which verifies this, but I think this is correct.

I think it turns out that a fully-fledged macroexpanding preprocessor
looks very much like the first pass of a compiler - and among things
you end up having to treat each special-form specially (that's why 
they're special).

Do you have any ides of how to handle local macros (defined 
by MACROLET)?  We ended up just ignoring them - essentially because
(1) MACRO-FUNCTION only knows about global macros (because it's a 
top-level function and (2) to be useful MACROEXPAND would need the 
correct &ENVIRONMENT arg set up.  All in all a hassle... but maybe
I'm missing something... like a screw...


--ilan caron ..!ucbvax!vax135!lcuxlj!ilan
-- 

--Ilan Caron 201-580-5664 ..!allegra!lcuxlj!ilan
#<Standard Disclaimer>

aboulang@bbn.com (Albert Boulanger) (04/18/88)

You can pick up PCL (portable common loops)from XEROX and use its code
walker...

Albert Boulanger
aboulanger@bbn.com
Albert Boulanger
BBN Labs Inc.
ABoulanger@bbn.com (arpa)
Phone: (617)873-3891

gateley@mips.csc.ti.com (John Gateley) (04/18/88)

In article <1683@lcuxlm.UUCP> ilan@lcuxlm.UUCP writes:
> [...]
>Do you have any ides of how to handle local macros (defined 
>by MACROLET)?  We ended up just ignoring them - essentially because
>(1) MACRO-FUNCTION only knows about global macros (because it's a 
>top-level function and (2) to be useful MACROEXPAND would need the 
>correct &ENVIRONMENT arg set up.  All in all a hassle... but maybe
>I'm missing something... like a screw...
>
>--ilan caron ..!ucbvax!vax135!lcuxlj!ilan
>--Ilan Caron 201-580-5664 ..!allegra!lcuxlj!ilan
>#<Standard Disclaimer>

You mentioned that it is just like the first pass of a compiler, and that is
the answer to your question. You just have to pass around an argument
which contains an environment mapping symbols to local macro functions.
When you reach a form which is a local macro function, you just eval
the macro function applied to the form (Yucch ... eval). Macrolet just
extends the environment. Of course, you have to worry about scoping to
do it correctly (where a local macro name might be shadowed by something else).
Hope this helps
John Gateley