From: "Pascal J. Bourguignon" <pjb@informatimago.com>
To: help-gnu-emacs@gnu.org
Subject: Re: macro temp variables
Date: Fri, 19 Sep 2014 19:33:30 +0200 [thread overview]
Message-ID: <87sijny0ph.fsf@kuiper.lan.informatimago.com> (raw)
In-Reply-To: mailman.9181.1411123560.1147.help-gnu-emacs@gnu.org
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
next parent reply other threads:[~2014-09-19 17:33 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <mailman.9181.1411123560.1147.help-gnu-emacs@gnu.org>
2014-09-19 17:33 ` Pascal J. Bourguignon [this message]
2014-09-19 17:45 ` macro temp variables Stefan Monnier
[not found] ` <mailman.9214.1411148764.1147.help-gnu-emacs@gnu.org>
2014-09-19 18:51 ` Pascal J. Bourguignon
2014-09-21 4:37 ` Eric Abrahamsen
[not found] ` <mailman.9302.1411299483.1147.help-gnu-emacs@gnu.org>
2014-09-21 20:59 ` Pascal J. Bourguignon
2014-09-19 10:49 Eric Abrahamsen
2014-09-19 12:11 ` Nicolas Richard
2014-09-19 15:48 ` Nicolas Richard
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87sijny0ph.fsf@kuiper.lan.informatimago.com \
--to=pjb@informatimago.com \
--cc=help-gnu-emacs@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).