emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RFC: `macro-eval'


From: Stuart D. Herring
Subject: RFC: `macro-eval'
Date: Wed, 30 Aug 2006 10:11:43 -0700 (PDT)
User-agent: SquirrelMail/1.4.6-7.el3.7lanl

I wrote a macro to automatically generate the boilerplate "delayed
evaluation with uninterned symbol" code needed in many macros (and
described at (elisp)Surprising Local Vars), which I present for comment. 
As far as my testing shows, it works both in interpreted and compiled
environments, with the one caveat that, as it uses `progv', it both
requires the cl library at runtime and generates spurious warnings upon
compilation because the byte compiler doesn't recognize that `progv' binds
variables.  (This of course is not easily fixed, since `progv' can
calculate the lists of variables to bind at runtime!)

(put 'macro-eval 'lisp-indent-function 1)
(defmacro macro-eval (spec &rest body)
  "Eval BODY with symbols in SPEC bound to temporary symbols.
For use in macros.  Each element of SPEC looks like VAR or (VAR VAL).  At
reevaluation time, each symbol will be bound to the evaluated form of the
corresponding VAL, or to the evaluated form of VAR's value if no VAL.
Note that evaluation and binding occurs like `let' and not `let*'."
  (let* ((vars (mapcar (lambda (e) (or (car-safe e) e)) spec))
         (vals (mapcar (lambda (e) (if (consp e) `',(cadr e) e)) spec))
         (syms (mapcar 'gensym (mapcar 'symbol-name vars)))
         (comma '\,))
    (list '\` `(progv ',syms
                   (mapcar 'eval ',(list comma `(mapcar 'eval ',vals)))
                 ,(list comma `(progv ',vars ',syms ,@body))))))


With this, it becomes possible to write the example `for' macro thusly:

(defmacro inc (var) `(setq ,var (1+ ,var)))

(defmacro for (var from init to final do &rest body)
  "Execute a simple for loop: (for i from 1 to 10 do (print i))."
  ;; We evaluate init this way to provide the correct evaluation order.
  (macro-eval (init final)
    `(let ((,var ,init))
       (while (<= ,var ,final)
         ,@body
         (inc ,var)))))

The savings in code are more pronounced when the macro does not bind
client variables, as often the `let's can be discarded entirely.

I'm quite willing to believe there are better ways of achieving this;
improvements and suggestions are welcome.  (For instance, is it possible
to do without the `comma' variable?)  If it seems useful, I can also
implement a `progv'-like macro that requires static variable selection so
the compiler might be happier.  (Of course, let me know if that already
exists!)

Davis

-- 
This product is sold by volume, not by mass.  If it appears too dense or
too sparse, it is because mass-energy conversion has occurred during
shipping.




reply via email to

[Prev in Thread] Current Thread [Next in Thread]