I want to load an s-expression from a file and expand macros in it,
and I don't want to define these macros at the top level. What my
situation boils down to is something like this:
(let ((expr '(hello 1 2 3)))
(macrolet ((hello (&rest lst) `(list ,@lst)))
(macroexpand expr))) ; => (HELLO 1 2 3)
I want this expression to macroexpand (hello 1 2 3) to '(list 1 2 3).
Unfortunately, macroexpand doesn't seem to take its environment into
account when expanding things.
I eventually came up with this hideously ugly alternative:
(defmacro expand-var (form &environment env) ; Cribbed from c.l.lisp
(multiple-value-bind (expansion expanded-flag)
(macroexpand-1 (eval form) env)
`(values ',expansion ',expanded-flag)))
(defvar *ugly-argument-passing-variable*)
(let ((expr '(hello 1 2 3)))
(macrolet ((hello (&rest lst) `(list ,@lst)))
(setq *ugly-argument-passing-variable* expr)
(expand-var *ugly-argument-passing-variable*))) ; => (LIST 1 2 3)
AFAICT the expand-var macro expands its argument in the current macro
environment, while the nasty global hack is necessary to get the value
to be expanded into the dynamic environment. I could be wrong, since
I'm still fairly new to lisp. Can anyone tell me a better way to do
this?
Thanks,
-Peter
In article <···························@posting.google.com>,
·········@chase3000.com (Peter Scott) wrote:
> I want to load an s-expression from a file and expand macros in it,
> and I don't want to define these macros at the top level. What my
> situation boils down to is something like this:
>
> (let ((expr '(hello 1 2 3)))
> (macrolet ((hello (&rest lst) `(list ,@lst)))
> (macroexpand expr))) ; => (HELLO 1 2 3)
>
> I want this expression to macroexpand (hello 1 2 3) to '(list 1 2 3).
> Unfortunately, macroexpand doesn't seem to take its environment into
> account when expanding things.
That's to be expected. Macros are normally expanded at compile time,
but variable bindings don't happen until run time.
Perhaps you should examine *why* you think you need to do this.
--
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Joe Marshall
Subject: Re: Problem with macroexpansion and macrolet
Date:
Message-ID: <1xnouayx.fsf@ccs.neu.edu>
·········@chase3000.com (Peter Scott) writes:
> I want to load an s-expression from a file and expand macros in it,
> and I don't want to define these macros at the top level. What my
> situation boils down to is something like this:
>
> (let ((expr '(hello 1 2 3)))
> (macrolet ((hello (&rest lst) `(list ,@lst)))
> (macroexpand expr))) ; => (HELLO 1 2 3)
>
> I want this expression to macroexpand (hello 1 2 3) to '(list 1 2 3).
> Unfortunately, macroexpand doesn't seem to take its environment into
> account when expanding things.
(macrolet ((get-macroenvironment (&environment env)
`',env))
(let ((expr '(hello 1 2 3)))
(macrolet ((hello (&rest lst) `(list ,@lst)))
(format t "~&~s" (macroexpand expr (get-macroenvironment))))))
In article <············@ccs.neu.edu>, Joe Marshall <···@ccs.neu.edu>
wrote:
> (macrolet ((get-macroenvironment (&environment env)
> `',env))
>
> (let ((expr '(hello 1 2 3)))
> (macrolet ((hello (&rest lst) `(list ,@lst)))
> (format t "~&~s" (macroexpand expr (get-macroenvironment))))))
I thought macro environments had dynamic extent, per 3.4.4:
"The object that is bound to the environment parameter has dynamic
extent."
So I don't see how this is portable.
--
Brian Mastenbrook
http://www.cs.indiana.edu/~bmastenb/
From: Joe Marshall
Subject: Re: Problem with macroexpansion and macrolet
Date:
Message-ID: <fzc4suzv.fsf@ccs.neu.edu>
Brian Mastenbrook <··············@cs.indiana.edu> writes:
> In article <············@ccs.neu.edu>, Joe Marshall <···@ccs.neu.edu>
> wrote:
>
>> (macrolet ((get-macroenvironment (&environment env)
>> `',env))
>>
>> (let ((expr '(hello 1 2 3)))
>> (macrolet ((hello (&rest lst) `(list ,@lst)))
>> (format t "~&~s" (macroexpand expr (get-macroenvironment))))))
>
> I thought macro environments had dynamic extent, per 3.4.4:
>
> "The object that is bound to the environment parameter has dynamic
> extent."
>
> So I don't see how this is portable.
It probably isn't conforming. It does seem to work in CLisp and
Lispworks, though. I imagine that it would be more likely to work if
it isn't compiled because the evaluator would need the macro
enviromnent just in case.
In article <···························@posting.google.com>, Peter
Scott <·········@chase3000.com> wrote:
> I want to load an s-expression from a file and expand macros in it,
> and I don't want to define these macros at the top level. What my
> situation boils down to is something like this:
>
> (let ((expr '(hello 1 2 3)))
> (macrolet ((hello (&rest lst) `(list ,@lst)))
> (macroexpand expr))) ; => (HELLO 1 2 3)
>
> I want this expression to macroexpand (hello 1 2 3) to '(list 1 2 3).
> Unfortunately, macroexpand doesn't seem to take its environment into
> account when expanding things.
That's because you're attempting to macroexpand at run-time, by which
time the macrolet is already long gone. I'm wondering why you can't do
the expansion of the form at macroexpansion time?
(defun foo ()
(symbol-macrolet ((expr (hello 1 2 3)))
(macrolet ((hello (&rest list) `(list ,@list)))
(macrolet ((expand-symbol (sym &environment env)
(list 'quote (macroexpand-1
(macroexpand-1 sym env) env))))
(expand-symbol expr)))))
(foo) => (list 1 2 3)
This way everything happens at macroexpansion time, which works.
--
Brian Mastenbrook
http://www.cs.indiana.edu/~bmastenb/
Brian Mastenbrook <··············@cs.indiana.edu> wrote:
> That's because you're attempting to macroexpand at run-time, by which
> time the macrolet is already long gone. I'm wondering why you can't do
> the expansion of the form at macroexpansion time?
> [...]
> This way everything happens at macroexpansion time, which works.
That's fine for my simplified example (sorry it's inadequate...), but
what I really need to do is read the expression to be expanded from a
file at runtime. I think that's different.
Peter Scott wrote:
> I want to load an s-expression from a file and expand
> macros in it
I worried about this too, then I realised that I was
labouring under a mis-apprehension. I had not grasped that
once (read) has fetched an S-expresion containing data, it
is easy to write ones own ``macro-expander'' to expand
abbreviations and the like. Start by rewriting copy-tree
using suggestive names
(defun null-data-macro(tree)
(cond ((atom tree) tree)
(t (mapcar (function null-data-macro) tree))))
Then hack in some expansion code
(defun explicit-data-macro(tree)
(cond ((atom tree) tree)
((eql 'sqr (car tree))
(assert (= (length tree) 2))
(let ((x (explicit-data-macro (second tree))))
`(rect ,x ,x)))
(t (mapcar (function explicit-data-macro) tree))))
Now, instead of writing (rect 2 2) we can write (sqr 2)
(explicit-data-macro '(tri 1 (sqr 2) 3))
=> (TRI 1 (RECT 2 2) 3)
and this works recursively
(explicit-data-macro '(tri 1 (sqr (sqr 2)) 3))
=> (TRI 1 (RECT (RECT 2 2) (RECT 2 2)) 3)
though perhaps my example is over simple and makes little
sense.
That is also a bad software architecture. Perhaps we should
put the expansion functions on the data-expander property of
the relevant symbol:
(defun implicit-data-macro(tree)
(cond ((atom tree) tree)
((get (car tree) 'data-expander)
(apply (get (car tree) 'data-expander)
(mapcar (function implicit-data-macro)
(cdr tree))))
(t (mapcar (function implicit-data-macro) tree))))
(setf (get 'sqr 'data-expander)
(lambda(x)
(format t "SQR: expanding ~A~%" x)
`(rect ,x ,x)))
Trying it out:
(implicit-data-macro '(tri 1 (sqr (sqr 2)) 3 4))
SQR: expanding 2
SQR: expanding (RECT 2 2)
(TRI 1 (RECT (RECT 2 2) (RECT 2 2)) 3 4)
Obviously one can define a def-data-expander macro, but I
stopped playing with this because of a different issue. What
I have written is more like evaluating functions than
expanding macros, because the arguments are pre-expanded. It
is easy enough to change the code so that they are
post-expanded.
(defun implicit-data-macro(tree)
(cond ((atom tree) tree)
((get (car tree) 'data-expander)
(mapcar (function implicit-data-macro)
(apply (get (car tree) 'data-expander)
(cdr tree))))
(t (mapcar (function implicit-data-macro) tree))))
Trying it out:
(implicit-data-macro '(tri 1 (sqr (sqr 2)) 3 4))
SQR: expanding (SQR 2)
SQR: expanding 2
SQR: expanding 2
(TRI 1 (RECT (RECT 2 2) (RECT 2 2)) 3 4)
Suddenly one is facing subtle issues of semantics. What does
one want ones macroexpander to do? The problem is completely
different to what I had imagined it to be. Writing the
expander is trivial. The hard part is working out what one
actually wants it to do. CL give me more power than I know
what to do with. I decided to leave well alone, until I was
working on a serious application, at which point the
requirements of the application would provide answers to
otherwise imponderable questions.
Alan Crowe
Edinburgh
Scotland
Alan Crowe <····@cawtech.freeserve.co.uk> wrote:
> Suddenly one is facing subtle issues of semantics. What does
> one want ones macroexpander to do? The problem is completely
> different to what I had imagined it to be. Writing the
> expander is trivial. The hard part is working out what one
> actually wants it to do. CL give me more power than I know
> what to do with. I decided to leave well alone, until I was
> working on a serious application, at which point the
> requirements of the application would provide answers to
> otherwise imponderable questions.
Yes, you've put your finger on my real problem. The macroexpansion
issue is hacky and unpleasant, but it can be surmounted. The problem
is deciding what I *really* want.
In this case, I'm writing my own replacement for the usual "make"
program. I want to be able to do transformations on dependency trees,
like this:
(ocaml-executable "foo.exe" ("foo.ml") :libs ("graphics.cma"))
=> (("all" ("foo.exe")
("foo.exe" ("foo.ml" "graphics.cma"))
The reasons I decided to use macros rather than just doing the
transformations manually are because I hoped it would be simpler to
implement and because I wanted the semantics to be constrained to the
normal lisp ones. I'll probably end up using your approach. I had a
bad experience doing something similar, but now I'm more experienced
with lisp.
I still think that it should be easier to use the standard
macroexpansion facilities. Oh well.
Thanks, everyone.
-Peter
·········@chase3000.com (Peter Scott) writes:
> Alan Crowe <····@cawtech.freeserve.co.uk> wrote:
> > Suddenly one is facing subtle issues of semantics. What does
> > one want ones macroexpander to do? The problem is completely
> > different to what I had imagined it to be. Writing the
> > expander is trivial. The hard part is working out what one
> > actually wants it to do. CL give me more power than I know
> > what to do with. I decided to leave well alone, until I was
> > working on a serious application, at which point the
> > requirements of the application would provide answers to
> > otherwise imponderable questions.
>
> Yes, you've put your finger on my real problem. The macroexpansion
> issue is hacky and unpleasant, but it can be surmounted. The problem
> is deciding what I *really* want.
>
> In this case, I'm writing my own replacement for the usual "make"
> program. I want to be able to do transformations on dependency trees,
> like this:
>
> (ocaml-executable "foo.exe" ("foo.ml") :libs ("graphics.cma"))
> => (("all" ("foo.exe")
> ("foo.exe" ("foo.ml" "graphics.cma"))
>
> The reasons I decided to use macros rather than just doing the
> transformations manually are because I hoped it would be simpler to
> implement and because I wanted the semantics to be constrained to the
> normal lisp ones. I'll probably end up using your approach. I had a
> bad experience doing something similar, but now I'm more experienced
> with lisp.
>
> I still think that it should be easier to use the standard
> macroexpansion facilities. Oh well.
Introducing a macro is only a question of deciding that some
computation must be executed at compilation time instead of run time.
All but the most simple macros are actually implemented by calling
functions that do the actual job:
(defun my-hairy-function (args)
;; ...
;; three pages later
;; ...
)
(defmacro my-hairy-macro (args)
(my-hairy-function args))
(defmacro my-simple-macro (args)
`(lets
expand
a few
lines))
So, do you need to do your "make" processing AT compile time, or do
you need to do it at run time of a "make" program to be executed
before compiling?
Are your dependencies to be considered "source" of a program, or are
they "data"?
--
__Pascal_Bourguignon__ http://www.informatimago.com/
There is no worse tyranny than to force a man to pay for what he doesn't
want merely because you think it would be good for him.--Robert Heinlein
http://www.theadvocates.org/
Peter Scott <·········@chase3000.com> wrote:
+---------------
| ...deciding what I *really* want.
|
| In this case, I'm writing my own replacement for the usual "make"
| program. I want to be able to do transformations on dependency trees...
+---------------
Have you considered using ASDF <URL:http://www.cliki.net/asdf>
as a starting place? It might be sufficiently powerful as it
stands to do your job, but if not, you can certainly extend or
customize it easier than writing one from scratch...
-Rob
-----
Rob Warnock <····@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607