help-gnu-emacs
[Top][All Lists]
Advanced

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

Re: macro temp variables


From: Pascal J. Bourguignon
Subject: Re: macro temp variables
Date: Fri, 19 Sep 2014 19:33:30 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.3 (gnu/linux)

Eric Abrahamsen <eric@ericabrahamsen.net> writes:

> I've never actually needed to write a macro that provided temporary
> local variables, and consequently am not very good at it. Despite having
> read the docs and basically followed the examples there, my attempt is
> producing errors.
>
> The idea with the below is to make a macro that iterates over Org
> headlines, and runs the body once for each headline: for each run, a
> handful of temporary variables should be bound to various bits of the
> headline.
>
> It should be fairly clear from looking at it. "tree" should be bound
> once, at the top level of the call. All the other make-symbol variables
> should be re-bound with each pass of org-element-map.
>
> I tested this with a little stub call that tried to access the 'todo
> symbol, and that gets me "symbol's value as variable is void" for 'todo.
> I tried replacing the inner "setq" series with a let*, and got the same
> result. Clearly this is just not the way you do it, but I've tried
> several different things and nothing works. Am I supposed to be using
> nested back-quotes? Can someone tell me how to fix this?
>
> (defmacro org-iter-headings (&rest body)
>   (declare (indent 0))
>   (let ((tree (make-symbol "tree"))
>       (head (make-symbol "head"))
>       (item (make-symbol "item"))
>       (todo (make-symbol "todo"))
>       (tags (make-symbol "tags"))
>       (body-pars (make-symbol "body")))
>     `(save-restriction
>        (org-narrow-to-subtree
>       (outline-next-heading) ; Get off the parent heading.
>       (let ((,tree (org-element-parse-buffer)))
>         (org-element-map ,tree 'headline
>           (lambda (h)
>             (setq ,head (org-element-at-point)
>                   ,item (org-element-property :raw-value ,head)
>                   ,todo (cons
>                          (org-element-property :todo-type ,head)
>                          (org-element-property :todo-keyword ,head))
>                   ,tags (org-element-property :tags ,head)
>                   ,body-pars (org-element-map ,head 'paragraph 'identity))
>             ,@body)))))))

You should indeed better use a let for rather than setq, but the problem
is not here.

The problem is that you want your body to access those variables.  So
the body must know their names.  But you are computing new names that
are uninterned, and therefore unaccessible.  Therefore there's no way to
access those temporary variables, from the body.  Only code generated by
your macro could access those variables (since the macro has their name
stored in its head .. tags variables.


In general, when you want to provide access to variables by the body,
you must let the user name those variables.

(setf lexical-binding t) ; always
(require 'cl)            ; always

(defmacro* org-iterating-headings ((head item todo tags body-pars) &rest body)
 `(call-org-iterating-headings
    (lambda (,head ,item ,todo ,tags ,body-pars) ,@body)))


You could write also it with emacs defmacro, but you would have to
write:

    (defmacro org-iterating-headings (vars &rest body)
      (destructuring-bind (head item todo tags body-pars) vars
       `(call-org-iterating-headings
          (lambda (,head ,item ,todo ,tags ,body-pars) ,@body))))



(defun call-org-iterating-headings (thunk)
  (save-restriction
   (org-narrow-to-subtree
    (outline-next-heading) ; Get off the parent heading.
    (let ((tree (org-element-parse-buffer)))
      (org-element-map tree 'headline
                       (lambda (h)
                         (let* ((head (org-element-at-point))
                                (item (org-element-property :raw-value head))
                                (todo (cons
                                       (org-element-property :todo-type head)
                                       (org-element-property :todo-keyword 
head)))
                                (tags (org-element-property :tags head))
                                (body-pars (org-element-map head 'paragraph 
'identity)))
                           (funcall thunk head item todo tags body-pars))))))))

You would use it as:

(org-iterating-headings (h i to ta ps)
    (do-something-with-head h)
    (mapc (function do-something-else-with-par) ps))

which expands to:

(pp (macroexpand '(org-iterating-headings (h i to ta ps)
                    (do-something-with-head h)
                    (mapc (function do-something-else-with-par) ps))))

(call-org-iterating-headings
 (lambda (h i to ta ps)
   (do-something-with-head h)
   (mapc #'do-something-else-with-par ps)))



-- 
__Pascal Bourguignon__                 http://www.informatimago.com/
“The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.” -- Carl Bass CEO Autodesk


reply via email to

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